]> www.fi.muni.cz Git - evince.git/commitdiff
Imported Xpdf 2.03 and fixed build.
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>
Sun, 16 May 2004 22:45:42 +0000 (22:45 +0000)
committerMartin Kretzschmar <mkretzschmar@src.gnome.org>
Sun, 16 May 2004 22:45:42 +0000 (22:45 +0000)
* ANNOUNCE:
* CHANGES:
* README:
* aconf2.h:
* configure.in:
* dj_make.bat:
* doc/pdffonts.1:
* doc/pdffonts.cat:
* doc/pdffonts.hlp:
* doc/pdfimages.1:
* doc/pdfimages.cat:
* doc/pdfimages.hlp:
* doc/pdfinfo.1:
* doc/pdfinfo.cat:
* doc/pdfinfo.hlp:
* doc/pdftopbm.1:
* doc/pdftopbm.cat:
* doc/pdftopbm.hlp:
* doc/pdftops.1:
* doc/pdftops.cat:
* doc/pdftops.hlp:
* doc/pdftotext.1:
* doc/pdftotext.cat:
* doc/pdftotext.hlp:
* doc/xpdf.1:
* doc/xpdf.cat:
* doc/xpdf.hlp:
* doc/xpdfrc.5:
* doc/xpdfrc.cat:
* doc/xpdfrc.hlp:
* goo/gfile.cc:
* ms_make.bat:
* vms_make.com:
* xpdf/Annot.cc:
* xpdf/Array.cc:
* xpdf/BuiltinFontTables.cc:
* xpdf/CMap.cc:
* xpdf/CMap.h:
* xpdf/Catalog.cc:
* xpdf/CharCodeToUnicode.cc:
* xpdf/CharCodeToUnicode.h:
* xpdf/Decrypt.cc:
* xpdf/Dict.cc:
* xpdf/ErrorCodes.h:
* xpdf/FTFont.cc:
* xpdf/FTFont.h:
* xpdf/FontFile.cc:
* xpdf/FontFile.h:
* xpdf/Function.cc:
* xpdf/Gfx.cc:
* xpdf/Gfx.h:
* xpdf/GfxFont.cc:
* xpdf/GfxFont.h:
* xpdf/GfxState.cc:
* xpdf/GfxState.h:
* xpdf/GlobalParams.cc:
* xpdf/GlobalParams.h:
* xpdf/JBIG2Stream.cc:
* xpdf/Link.cc:
* xpdf/Link.h:
* xpdf/Makefile.am:
* xpdf/OutputDev.h:
* xpdf/PDFDoc.cc:
* xpdf/PDFDoc.h:
* xpdf/PSOutputDev.cc:
* xpdf/PSOutputDev.h:
* xpdf/Page.cc:
* xpdf/Page.h:
* xpdf/Parser.cc:
* xpdf/Stream.cc:
* xpdf/Stream.h:
* xpdf/TTFont.cc:
* xpdf/TTFont.h:
* xpdf/TextOutputDev.cc:
* xpdf/TextOutputDev.h:
* xpdf/UnicodeMap.cc:
* xpdf/UnicodeMap.h:
* xpdf/UnicodeTypeTable.cc:
* xpdf/UnicodeTypeTable.h:
* xpdf/XOutputDev.cc:
* xpdf/XOutputDev.h:
* xpdf/XPDFApp.cc:
* xpdf/XPDFCore.cc:
* xpdf/XPDFCore.h:
* xpdf/XPDFViewer.cc:
* xpdf/XPDFViewer.h:
* xpdf/XRef.cc:
* xpdf/about-text.h:
* xpdf/config.h:
* xpdf/gpdf-control.cc:
* xpdf/gpdf-link-canvas-item.cc:
* xpdf/gpdf-links-canvas-layer.cc:
* xpdf/pdffonts.cc:
* xpdf/pdfimages.cc:
* xpdf/pdfinfo.cc:
* xpdf/pdftopbm.cc:
* xpdf/pdftops.cc:
* xpdf/pdftotext.cc:
* xpdf/tests/test-links.cc:
* xpdf/vms_make.com:
* xpdf/xpdf.cc: Imported Xpdf 2.03 and fixed build.

56 files changed:
pdf/goo/gfile.cc
pdf/xpdf/Annot.cc
pdf/xpdf/Array.cc
pdf/xpdf/BuiltinFontTables.cc
pdf/xpdf/CMap.cc
pdf/xpdf/CMap.h
pdf/xpdf/Catalog.cc
pdf/xpdf/CharCodeToUnicode.cc
pdf/xpdf/CharCodeToUnicode.h
pdf/xpdf/Decrypt.cc
pdf/xpdf/Dict.cc
pdf/xpdf/ErrorCodes.h
pdf/xpdf/FTFont.cc
pdf/xpdf/FTFont.h
pdf/xpdf/FontFile.cc
pdf/xpdf/FontFile.h
pdf/xpdf/Function.cc
pdf/xpdf/Gfx.cc
pdf/xpdf/Gfx.h
pdf/xpdf/GfxFont.cc
pdf/xpdf/GfxFont.h
pdf/xpdf/GfxState.cc
pdf/xpdf/GfxState.h
pdf/xpdf/GlobalParams.cc
pdf/xpdf/GlobalParams.h
pdf/xpdf/Link.cc
pdf/xpdf/Link.h
pdf/xpdf/Makefile.am
pdf/xpdf/OutputDev.h
pdf/xpdf/PDFDoc.cc
pdf/xpdf/PDFDoc.h
pdf/xpdf/PSOutputDev.cc
pdf/xpdf/PSOutputDev.h
pdf/xpdf/Page.cc
pdf/xpdf/Page.h
pdf/xpdf/Parser.cc
pdf/xpdf/Stream.cc
pdf/xpdf/Stream.h
pdf/xpdf/TTFont.cc
pdf/xpdf/TTFont.h
pdf/xpdf/TextOutputDev.cc
pdf/xpdf/TextOutputDev.h
pdf/xpdf/UnicodeMap.cc
pdf/xpdf/UnicodeMap.h
pdf/xpdf/XOutputDev.cc
pdf/xpdf/XOutputDev.h
pdf/xpdf/XRef.cc
pdf/xpdf/pdffonts.cc
pdf/xpdf/pdfimages.cc
pdf/xpdf/pdfinfo.cc
pdf/xpdf/pdftopbm.cc
pdf/xpdf/pdftops.cc
pdf/xpdf/pdftotext.cc
pdf/xpdf/vms_make.com
pdf/xpdf/xpdf.cc
pdf/xpdf/xpdfconfig.h

index b4fb616c0718553b0e69747d4f7256c98ff0afa6..11f5cf69e200715f1ff3252c950213743288d89c 100644 (file)
 
 #include <aconf.h>
 
-#ifdef WIN32
-   extern "C" {
-#  ifndef _MSC_VER
-#    include <kpathsea/win32lib.h>
-#  endif
-   }
-#else // !WIN32
+#ifndef WIN32
 #  if defined(MACOS)
 #    include <sys/stat.h>
 #  elif !defined(ACORN)
@@ -647,10 +641,14 @@ GDirEntry *GDir::getNextEntry() {
   GDirEntry *e;
 
 #if defined(WIN32)
-  e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
-  if (hnd  && !FindNextFile(hnd, &ffd)) {
-    FindClose(hnd);
-    hnd = NULL;
+  if (hnd) {
+    e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
+    if (hnd  && !FindNextFile(hnd, &ffd)) {
+      FindClose(hnd);
+      hnd = NULL;
+    }
+  } else {
+    e = NULL;
   }
 #elif defined(ACORN)
 #elif defined(MACOS)
@@ -694,6 +692,7 @@ void GDir::rewind() {
   tmp = path->copy();
   tmp->append("/*.*");
   hnd = FindFirstFile(tmp->getCString(), &ffd);
+  delete tmp;
 #elif defined(ACORN)
 #elif defined(MACOS)
 #else
index 20fe24bb34b8a40edb052c2dd4c3cd91241e36ad..42bf8499e0989fb1ff37f12b485c0d74d9a26a66 100644 (file)
@@ -100,7 +100,7 @@ void Annot::draw(Gfx *gfx) {
 
 Annots::Annots(XRef *xref, Object *annotsObj) {
   Annot *annot;
-  Object obj1, obj2;
+  Object obj1;
   int size;
   int i;
 
@@ -111,18 +111,16 @@ Annots::Annots(XRef *xref, Object *annotsObj) {
   if (annotsObj->isArray()) {
     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
-       obj1.dictLookup("Subtype", &obj2);
-        annot = new Annot(xref, obj1.getDict());
-        if (annot->isOk()) {
-                if (nAnnots >= size) {
-                        size += 16;
-                        annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
-                }
-                annots[nAnnots++] = annot;
-        } else {
-                delete annot;
+       annot = new Annot(xref, obj1.getDict());
+       if (annot->isOk()) {
+         if (nAnnots >= size) {
+           size += 16;
+           annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
+         }
+         annots[nAnnots++] = annot;
+       } else {
+         delete annot;
        }
-       obj2.free();
       }
       obj1.free();
     }
index 27ecbe9e7a1ae4a1559da5c3ac125d9ab71a4a5b..a6c6db19231e72fccd533a5381d8d67425a7f181 100644 (file)
@@ -38,8 +38,12 @@ Array::~Array() {
 }
 
 void Array::add(Object *elem) {
-  if (length + 1 > size) {
-    size += 8;
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
     elems = (Object *)grealloc(elems, size * sizeof(Object));
   }
   elems[length] = *elem;
index 296e5645e1b65387d85dc6aa87040b236f288a5b..9c362389c1be7fa37c33938ffaf0670ed5e5d7df 100644 (file)
 
 static BuiltinFontWidth courierWidthsTab[] = {
   { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
   { "comma",                             600, NULL },
   { "cedilla",                           600, NULL },
   { "plusminus",                         600, NULL },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
   { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
   { "yen",                               600, NULL },
   { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
   { "questiondown",                      600, NULL },
   { "emdash",                            600, NULL },
   { "Agrave",                            600, NULL },
   { "three",                             600, NULL },
   { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
   { "A",                                 600, NULL },
   { "B",                                 600, NULL },
   { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
   { "D",                                 600, NULL },
   { "E",                                 600, NULL },
   { "onequarter",                        600, NULL },
@@ -46,14 +56,18 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "I",                                 600, NULL },
   { "J",                                 600, NULL },
   { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
   { "L",                                 600, NULL },
   { "backslash",                         600, NULL },
   { "periodcentered",                    600, NULL },
   { "M",                                 600, NULL },
   { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
   { "O",                                 600, NULL },
   { "P",                                 600, NULL },
   { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
   { "R",                                 600, NULL },
   { "Aacute",                            600, NULL },
   { "caron",                             600, NULL },
@@ -62,9 +76,7 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "equal",                             600, NULL },
   { "question",                          600, NULL },
   { "X",                                 600, NULL },
@@ -72,6 +84,7 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -88,44 +101,57 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "l",                                 600, NULL },
   { "m",                                 600, NULL },
   { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
   { "o",                                 600, NULL },
   { "ordfeminine",                       600, NULL },
   { "ring",                              600, NULL },
   { "p",                                 600, NULL },
   { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
   { "r",                                 600, NULL },
   { "twosuperior",                       600, NULL },
-  { "largebullet",                       600, NULL },
   { "aacute",                            600, NULL },
   { "s",                                 600, NULL },
   { "OE",                                600, NULL },
   { "t",                                 600, NULL },
   { "divide",                            600, NULL },
   { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
   { "v",                                 600, NULL },
   { "w",                                 600, NULL },
   { "x",                                 600, NULL },
   { "y",                                 600, NULL },
   { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
   { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
   { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
   { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
   { "Scaron",                            600, NULL },
   { "Lslash",                            600, NULL },
   { "semicolon",                         600, NULL },
   { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
   { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
   { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
   { "trademark",                         600, NULL },
   { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
   { "macron",                            600, NULL },
   { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
   { "ellipsis",                          600, NULL },
   { "scaron",                            600, NULL },
   { "AE",                                600, NULL },
   { "Ucircumflex",                       600, NULL },
   { "lslash",                            600, NULL },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "hyphen",                            600, NULL },
   { "guilsinglright",                    600, NULL },
@@ -134,9 +160,11 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -144,14 +172,17 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       600, NULL },
+  { "emacron",                           600, NULL },
   { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
   { "bracketleft",                       600, NULL },
   { "Ugrave",                            600, NULL },
   { "quoteright",                        600, NULL },
   { "Udieresis",                         600, NULL },
   { "perthousand",                       600, NULL },
   { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
   { "Eacute",                            600, NULL },
   { "adieresis",                         600, NULL },
   { "egrave",                            600, NULL },
@@ -167,139 +198,173 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "nine",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            600, NULL },
   { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
   { "zero",                              600, NULL },
   { "multiply",                          600, NULL },
-  { "Scedilla",                          600, NULL },
   { "eth",                               600, NULL },
+  { "Scedilla",                          600, NULL },
+  { "Racute",                            600, NULL },
   { "Ograve",                            600, NULL },
+  { "partialdiff",                       600, NULL },
   { "uacute",                            600, NULL },
   { "braceleft",                         600, NULL },
   { "Thorn",                             600, NULL },
   { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
   { "ccedilla",                          600, NULL },
-  { "gcaron",                            600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "scedilla",                          600, NULL },
   { "Oacute",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
-  { "scedilla",                          600, NULL },
   { "ogonek",                            600, NULL },
-  { "arrowdown",                         600, NULL },
   { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
   { "thorn",                             600, NULL },
   { "degree",                            600, NULL },
   { "registered",                        600, NULL },
-  { "percent",                           600, NULL },
+  { "radical",                           600, NULL },
   { "Aring",                             600, NULL },
+  { "percent",                           600, NULL },
   { "six",                               600, NULL },
   { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
   { "two",                               600, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            600, NULL },
-  { "oacute",                            600, NULL },
+  { "Lacute",                            600, NULL },
   { "ocircumflex",                       600, NULL },
+  { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
   { "asciicircum",                       600, NULL },
   { "aring",                             600, NULL },
-  { "square",                            600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "ampersand",                         600, NULL },
   { "Iacute",                            600, NULL },
+  { "lacute",                            600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            600, NULL },
+  { "Ncaron",                            600, NULL },
   { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
   { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
   { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
   { "threesuperior",                     600, NULL },
   { "acute",                             600, NULL },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "quotedblbase",                      600, NULL },
   { "iacute",                            600, NULL },
-  { "up",                                600, NULL },
+  { "ncaron",                            600, NULL },
   { "florin",                            600, NULL },
   { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
   { "fi",                                600, NULL },
   { "fl",                                600, NULL },
   { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
   { "Icircumflex",                       600, NULL },
   { "guillemotleft",                     600, NULL },
   { "germandbls",                        600, NULL },
   { "seven",                             600, NULL },
-  { "indent",                            600, NULL },
-  { "prescription",                      600, NULL },
-  { "dectab",                            600, NULL },
+  { "Amacron",                           600, NULL },
+  { "Sacute",                            600, NULL },
   { "ordmasculine",                      600, NULL },
   { "dotlessi",                          600, NULL },
   { "sterling",                          600, NULL },
-  { "IJ",                                600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
   { "acircumflex",                       600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "braceright",                        600, NULL },
   { "icircumflex",                       600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "Idot",                              600, NULL },
-  { "merge",                             600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "arrowright",                        600, NULL },
-  { "format",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
   { "atilde",                            600, NULL },
-  { "ij",                                600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         600, NULL },
   { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
   { "onesuperior",                       600, NULL }
 };
 
 static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
   { "comma",                             600, NULL },
   { "cedilla",                           600, NULL },
   { "plusminus",                         600, NULL },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
   { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
   { "yen",                               600, NULL },
   { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
   { "questiondown",                      600, NULL },
   { "emdash",                            600, NULL },
   { "Agrave",                            600, NULL },
   { "three",                             600, NULL },
   { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
   { "A",                                 600, NULL },
   { "B",                                 600, NULL },
   { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
   { "D",                                 600, NULL },
   { "E",                                 600, NULL },
   { "onequarter",                        600, NULL },
@@ -309,14 +374,18 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "I",                                 600, NULL },
   { "J",                                 600, NULL },
   { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
   { "backslash",                         600, NULL },
   { "L",                                 600, NULL },
   { "periodcentered",                    600, NULL },
   { "M",                                 600, NULL },
   { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
   { "O",                                 600, NULL },
   { "P",                                 600, NULL },
   { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
   { "R",                                 600, NULL },
   { "Aacute",                            600, NULL },
   { "caron",                             600, NULL },
@@ -325,9 +394,7 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -335,6 +402,7 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -351,44 +419,57 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "l",                                 600, NULL },
   { "m",                                 600, NULL },
   { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
   { "o",                                 600, NULL },
   { "ordfeminine",                       600, NULL },
   { "ring",                              600, NULL },
   { "p",                                 600, NULL },
   { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
   { "r",                                 600, NULL },
   { "twosuperior",                       600, NULL },
-  { "largebullet",                       600, NULL },
   { "aacute",                            600, NULL },
   { "s",                                 600, NULL },
   { "OE",                                600, NULL },
   { "t",                                 600, NULL },
   { "divide",                            600, NULL },
   { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
   { "v",                                 600, NULL },
   { "w",                                 600, NULL },
   { "x",                                 600, NULL },
   { "y",                                 600, NULL },
   { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
   { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
   { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
   { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
   { "Scaron",                            600, NULL },
   { "Lslash",                            600, NULL },
   { "semicolon",                         600, NULL },
   { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
   { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
   { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
   { "trademark",                         600, NULL },
   { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
   { "macron",                            600, NULL },
   { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
   { "ellipsis",                          600, NULL },
   { "scaron",                            600, NULL },
   { "AE",                                600, NULL },
   { "Ucircumflex",                       600, NULL },
   { "lslash",                            600, NULL },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -397,9 +478,11 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -407,14 +490,17 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       600, NULL },
+  { "emacron",                           600, NULL },
   { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
   { "bracketleft",                       600, NULL },
   { "Ugrave",                            600, NULL },
   { "quoteright",                        600, NULL },
   { "Udieresis",                         600, NULL },
   { "perthousand",                       600, NULL },
   { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
   { "Eacute",                            600, NULL },
   { "adieresis",                         600, NULL },
   { "egrave",                            600, NULL },
@@ -430,139 +516,173 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            600, NULL },
   { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
   { "multiply",                          600, NULL },
   { "zero",                              600, NULL },
   { "eth",                               600, NULL },
   { "Scedilla",                          600, NULL },
   { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
   { "uacute",                            600, NULL },
   { "braceleft",                         600, NULL },
   { "Thorn",                             600, NULL },
   { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
   { "ccedilla",                          600, NULL },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         600, NULL },
+  { "scedilla",                          600, NULL },
   { "ogonek",                            600, NULL },
   { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
   { "thorn",                             600, NULL },
   { "degree",                            600, NULL },
   { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
   { "Aring",                             600, NULL },
   { "percent",                           600, NULL },
   { "six",                               600, NULL },
   { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
   { "two",                               600, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
   { "ocircumflex",                       600, NULL },
   { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
   { "asciicircum",                       600, NULL },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
   { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
   { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
   { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
   { "threesuperior",                     600, NULL },
   { "acute",                             600, NULL },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                600, NULL },
+  { "ncaron",                            600, NULL },
   { "florin",                            600, NULL },
   { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
   { "fi",                                600, NULL },
   { "fl",                                600, NULL },
   { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
   { "Icircumflex",                       600, NULL },
   { "guillemotleft",                     600, NULL },
   { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
   { "seven",                             600, NULL },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            600, NULL },
+  { "Sacute",                            600, NULL },
   { "ordmasculine",                      600, NULL },
   { "dotlessi",                          600, NULL },
   { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
   { "acircumflex",                       600, NULL },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         600, NULL },
   { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
   { "onesuperior",                       600, NULL }
 };
 
 static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
   { "comma",                             600, NULL },
   { "cedilla",                           600, NULL },
   { "plusminus",                         600, NULL },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
   { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
   { "yen",                               600, NULL },
   { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
   { "questiondown",                      600, NULL },
   { "emdash",                            600, NULL },
   { "Agrave",                            600, NULL },
   { "three",                             600, NULL },
   { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
   { "A",                                 600, NULL },
   { "B",                                 600, NULL },
   { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
   { "D",                                 600, NULL },
   { "E",                                 600, NULL },
   { "onequarter",                        600, NULL },
@@ -572,14 +692,18 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "I",                                 600, NULL },
   { "J",                                 600, NULL },
   { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
   { "backslash",                         600, NULL },
   { "L",                                 600, NULL },
   { "periodcentered",                    600, NULL },
   { "M",                                 600, NULL },
   { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
   { "O",                                 600, NULL },
   { "P",                                 600, NULL },
   { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
   { "R",                                 600, NULL },
   { "Aacute",                            600, NULL },
   { "caron",                             600, NULL },
@@ -588,9 +712,7 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -598,6 +720,7 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -614,44 +737,57 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "l",                                 600, NULL },
   { "m",                                 600, NULL },
   { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
   { "o",                                 600, NULL },
   { "ordfeminine",                       600, NULL },
   { "ring",                              600, NULL },
   { "p",                                 600, NULL },
   { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
   { "r",                                 600, NULL },
   { "twosuperior",                       600, NULL },
-  { "largebullet",                       600, NULL },
   { "aacute",                            600, NULL },
   { "s",                                 600, NULL },
   { "OE",                                600, NULL },
   { "t",                                 600, NULL },
   { "divide",                            600, NULL },
   { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
   { "v",                                 600, NULL },
   { "w",                                 600, NULL },
   { "x",                                 600, NULL },
   { "y",                                 600, NULL },
   { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
   { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
   { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
   { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
   { "Scaron",                            600, NULL },
   { "Lslash",                            600, NULL },
   { "semicolon",                         600, NULL },
   { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
   { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
   { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
   { "trademark",                         600, NULL },
   { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
   { "macron",                            600, NULL },
   { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
   { "ellipsis",                          600, NULL },
   { "scaron",                            600, NULL },
   { "AE",                                600, NULL },
   { "Ucircumflex",                       600, NULL },
   { "lslash",                            600, NULL },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -660,9 +796,11 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -670,14 +808,17 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       600, NULL },
+  { "emacron",                           600, NULL },
   { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
   { "bracketleft",                       600, NULL },
   { "Ugrave",                            600, NULL },
   { "quoteright",                        600, NULL },
   { "Udieresis",                         600, NULL },
   { "perthousand",                       600, NULL },
   { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
   { "Eacute",                            600, NULL },
   { "adieresis",                         600, NULL },
   { "egrave",                            600, NULL },
@@ -693,139 +834,173 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            600, NULL },
   { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
   { "multiply",                          600, NULL },
   { "zero",                              600, NULL },
   { "eth",                               600, NULL },
   { "Scedilla",                          600, NULL },
   { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
   { "uacute",                            600, NULL },
   { "braceleft",                         600, NULL },
   { "Thorn",                             600, NULL },
   { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
   { "ccedilla",                          600, NULL },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         600, NULL },
+  { "scedilla",                          600, NULL },
   { "ogonek",                            600, NULL },
   { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
   { "thorn",                             600, NULL },
   { "degree",                            600, NULL },
   { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
   { "Aring",                             600, NULL },
   { "percent",                           600, NULL },
   { "six",                               600, NULL },
   { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
   { "two",                               600, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
   { "ocircumflex",                       600, NULL },
   { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
   { "asciicircum",                       600, NULL },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
   { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
   { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
   { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
   { "threesuperior",                     600, NULL },
   { "acute",                             600, NULL },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                600, NULL },
+  { "ncaron",                            600, NULL },
   { "florin",                            600, NULL },
   { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
   { "fi",                                600, NULL },
   { "fl",                                600, NULL },
   { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
   { "Icircumflex",                       600, NULL },
   { "guillemotleft",                     600, NULL },
   { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
   { "seven",                             600, NULL },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            600, NULL },
+  { "Sacute",                            600, NULL },
   { "ordmasculine",                      600, NULL },
   { "dotlessi",                          600, NULL },
   { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
   { "acircumflex",                       600, NULL },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         600, NULL },
   { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
   { "onesuperior",                       600, NULL }
 };
 
 static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
   { "comma",                             600, NULL },
   { "cedilla",                           600, NULL },
   { "plusminus",                         600, NULL },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
   { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
   { "yen",                               600, NULL },
   { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
   { "questiondown",                      600, NULL },
   { "emdash",                            600, NULL },
   { "Agrave",                            600, NULL },
   { "three",                             600, NULL },
   { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
   { "A",                                 600, NULL },
   { "B",                                 600, NULL },
   { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
   { "D",                                 600, NULL },
   { "E",                                 600, NULL },
   { "onequarter",                        600, NULL },
@@ -835,14 +1010,18 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "I",                                 600, NULL },
   { "J",                                 600, NULL },
   { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
   { "backslash",                         600, NULL },
   { "L",                                 600, NULL },
   { "periodcentered",                    600, NULL },
   { "M",                                 600, NULL },
   { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
   { "O",                                 600, NULL },
   { "P",                                 600, NULL },
   { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
   { "R",                                 600, NULL },
   { "Aacute",                            600, NULL },
   { "caron",                             600, NULL },
@@ -851,9 +1030,7 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -861,6 +1038,7 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -877,44 +1055,57 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "l",                                 600, NULL },
   { "m",                                 600, NULL },
   { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
   { "o",                                 600, NULL },
   { "ordfeminine",                       600, NULL },
   { "ring",                              600, NULL },
   { "p",                                 600, NULL },
   { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
   { "r",                                 600, NULL },
   { "twosuperior",                       600, NULL },
-  { "largebullet",                       600, NULL },
   { "aacute",                            600, NULL },
   { "s",                                 600, NULL },
   { "OE",                                600, NULL },
   { "t",                                 600, NULL },
   { "divide",                            600, NULL },
   { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
   { "v",                                 600, NULL },
   { "w",                                 600, NULL },
   { "x",                                 600, NULL },
   { "y",                                 600, NULL },
   { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
   { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
   { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
   { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
   { "Scaron",                            600, NULL },
   { "Lslash",                            600, NULL },
   { "semicolon",                         600, NULL },
   { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
   { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
   { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
   { "trademark",                         600, NULL },
   { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
   { "macron",                            600, NULL },
   { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
   { "ellipsis",                          600, NULL },
   { "scaron",                            600, NULL },
   { "AE",                                600, NULL },
   { "Ucircumflex",                       600, NULL },
   { "lslash",                            600, NULL },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -923,9 +1114,11 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -933,14 +1126,17 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       600, NULL },
+  { "emacron",                           600, NULL },
   { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
   { "bracketleft",                       600, NULL },
   { "Ugrave",                            600, NULL },
   { "quoteright",                        600, NULL },
   { "Udieresis",                         600, NULL },
   { "perthousand",                       600, NULL },
   { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
   { "Eacute",                            600, NULL },
   { "adieresis",                         600, NULL },
   { "egrave",                            600, NULL },
@@ -956,136 +1152,173 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            600, NULL },
   { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
   { "multiply",                          600, NULL },
   { "zero",                              600, NULL },
   { "eth",                               600, NULL },
   { "Scedilla",                          600, NULL },
   { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
   { "uacute",                            600, NULL },
   { "braceleft",                         600, NULL },
   { "Thorn",                             600, NULL },
   { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
   { "ccedilla",                          600, NULL },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         600, NULL },
+  { "scedilla",                          600, NULL },
   { "ogonek",                            600, NULL },
   { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
   { "thorn",                             600, NULL },
   { "degree",                            600, NULL },
   { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
   { "Aring",                             600, NULL },
   { "percent",                           600, NULL },
   { "six",                               600, NULL },
   { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
   { "two",                               600, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
   { "ocircumflex",                       600, NULL },
   { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
   { "asciicircum",                       600, NULL },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
   { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
   { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
   { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
   { "threesuperior",                     600, NULL },
   { "acute",                             600, NULL },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                600, NULL },
+  { "ncaron",                            600, NULL },
   { "florin",                            600, NULL },
   { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
   { "fi",                                600, NULL },
   { "fl",                                600, NULL },
   { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
   { "Icircumflex",                       600, NULL },
   { "guillemotleft",                     600, NULL },
   { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
   { "seven",                             600, NULL },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            600, NULL },
+  { "Sacute",                            600, NULL },
   { "ordmasculine",                      600, NULL },
   { "dotlessi",                          600, NULL },
   { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
   { "acircumflex",                       600, NULL },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         600, NULL },
   { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
   { "onesuperior",                       600, NULL }
 };
 
 static BuiltinFontWidth helveticaWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             278, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         584, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
   { "asciitilde",                        584, NULL },
   { "colon",                             278, NULL },
   { "onehalf",                           834, NULL },
   { "dollar",                            556, NULL },
+  { "Lcaron",                            556, NULL },
   { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
   { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
   { "yen",                               556, NULL },
   { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
   { "questiondown",                      611, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            667, NULL },
   { "three",                             556, NULL },
   { "numbersign",                        556, NULL },
+  { "lcaron",                            299, NULL },
   { "A",                                 667, NULL },
   { "B",                                 667, NULL },
   { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        834, NULL },
@@ -1095,14 +1328,18 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "I",                                 278, NULL },
   { "J",                                 500, NULL },
   { "K",                                 667, NULL },
+  { "iogonek",                           222, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 556, NULL },
   { "periodcentered",                    278, NULL },
   { "M",                                 833, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           556, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 778, NULL },
   { "P",                                 667, NULL },
   { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 722, NULL },
   { "Aacute",                            667, NULL },
   { "caron",                             333, NULL },
@@ -1119,6 +1356,7 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 500, NULL },
   { "d",                                 556, NULL },
@@ -1135,11 +1373,13 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "l",                                 222, NULL },
   { "m",                                 833, NULL },
   { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
   { "o",                                 556, NULL },
   { "ordfeminine",                       370, NULL },
   { "ring",                              333, NULL },
   { "p",                                 556, NULL },
   { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
   { "r",                                 333, NULL },
   { "twosuperior",                       333, NULL },
   { "aacute",                            556, NULL },
@@ -1148,24 +1388,37 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "t",                                 278, NULL },
   { "divide",                            584, NULL },
   { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
   { "v",                                 500, NULL },
   { "w",                                 722, NULL },
   { "x",                                 500, NULL },
   { "y",                                 500, NULL },
   { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          355, NULL },
+  { "gcommaaccent",                      556, NULL },
   { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            667, NULL },
   { "Lslash",                            556, NULL },
   { "semicolon",                         278, NULL },
   { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            500, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            556, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         556, NULL },
+  { "nacute",                            556, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            500, NULL },
   { "AE",                               1000, NULL },
@@ -1179,9 +1432,11 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1189,14 +1444,17 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       556, NULL },
+  { "emacron",                           556, NULL },
   { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
   { "bracketleft",                       278, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        222, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         667, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            556, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         556, NULL },
   { "egrave",                            556, NULL },
@@ -1212,111 +1470,173 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         500, NULL },
   { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
   { "tilde",                             333, NULL },
   { "at",                               1015, NULL },
   { "eacute",                            556, NULL },
   { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          584, NULL },
   { "zero",                              556, NULL },
   { "eth",                               556, NULL },
+  { "Scedilla",                          667, NULL },
   { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       476, NULL },
   { "uacute",                            556, NULL },
   { "braceleft",                         334, NULL },
   { "Thorn",                             667, NULL },
   { "zcaron",                            500, NULL },
+  { "scommaaccent",                      500, NULL },
   { "ccedilla",                          500, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
   { "Ocircumflex",                       778, NULL },
   { "Oacute",                            778, NULL },
+  { "scedilla",                          500, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            556, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             556, NULL },
   { "degree",                            400, NULL },
   { "registered",                        737, NULL },
+  { "radical",                           453, NULL },
   { "Aring",                             667, NULL },
   { "percent",                           889, NULL },
   { "six",                               556, NULL },
   { "paragraph",                         537, NULL },
+  { "dcaron",                            643, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               556, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            278, NULL },
+  { "Lacute",                            556, NULL },
   { "ocircumflex",                       556, NULL },
   { "oacute",                            556, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            317, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
   { "asciicircum",                       469, NULL },
   { "aring",                             556, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
   { "bracketright",                      278, NULL },
   { "Iacute",                            278, NULL },
   { "ampersand",                         667, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            222, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              584, NULL },
+  { "uring",                             556, NULL },
   { "quotesinglbase",                    222, NULL },
+  { "lcommaaccent",                      222, NULL },
   { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     556, NULL },
   { "threesuperior",                     333, NULL },
   { "acute",                             333, NULL },
   { "section",                           556, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      333, NULL },
+  { "ncaron",                            556, NULL },
   { "florin",                            556, NULL },
   { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
   { "fi",                                500, NULL },
   { "fl",                                500, NULL },
   { "Acircumflex",                       667, NULL },
+  { "Cacute",                            722, NULL },
   { "Icircumflex",                       278, NULL },
   { "guillemotleft",                     556, NULL },
   { "germandbls",                        611, NULL },
+  { "Amacron",                           667, NULL },
   { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
   { "ordmasculine",                      365, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       556, NULL },
+  { "cacute",                            500, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        334, NULL },
   { "quotedblright",                     333, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            500, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              556, NULL },
   { "currency",                          556, NULL },
   { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
   { "Atilde",                            667, NULL },
   { "breve",                             333, NULL },
   { "bar",                               260, NULL },
   { "fraction",                          167, NULL },
   { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            278, NULL },
   { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      667, NULL },
   { "greater",                           584, NULL },
   { "atilde",                            556, NULL },
   { "brokenbar",                         260, NULL },
   { "quoteleft",                         222, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       333, NULL }
 };
 
 static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             278, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         584, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
   { "asciitilde",                        584, NULL },
   { "colon",                             333, NULL },
   { "onehalf",                           834, NULL },
   { "dollar",                            556, NULL },
+  { "Lcaron",                            611, NULL },
   { "ntilde",                            611, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      611, NULL },
   { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
   { "yen",                               556, NULL },
   { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
   { "questiondown",                      611, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            722, NULL },
   { "three",                             556, NULL },
   { "numbersign",                        556, NULL },
+  { "lcaron",                            400, NULL },
   { "A",                                 722, NULL },
   { "B",                                 722, NULL },
   { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        834, NULL },
@@ -1326,14 +1646,18 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "I",                                 278, NULL },
   { "J",                                 556, NULL },
   { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 611, NULL },
   { "periodcentered",                    278, NULL },
   { "M",                                 833, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           611, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 778, NULL },
   { "P",                                 667, NULL },
   { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 722, NULL },
   { "Aacute",                            722, NULL },
   { "caron",                             333, NULL },
@@ -1350,6 +1674,7 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 611, NULL },
   { "c",                                 556, NULL },
   { "d",                                 611, NULL },
@@ -1366,11 +1691,13 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 889, NULL },
   { "n",                                 611, NULL },
+  { "tcommaaccent",                      333, NULL },
   { "o",                                 611, NULL },
   { "ordfeminine",                       370, NULL },
   { "ring",                              333, NULL },
   { "p",                                 611, NULL },
   { "q",                                 611, NULL },
+  { "uhungarumlaut",                     611, NULL },
   { "r",                                 389, NULL },
   { "twosuperior",                       333, NULL },
   { "aacute",                            556, NULL },
@@ -1379,24 +1706,37 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "t",                                 333, NULL },
   { "divide",                            584, NULL },
   { "u",                                 611, NULL },
+  { "Ccaron",                            722, NULL },
   { "v",                                 556, NULL },
   { "w",                                 778, NULL },
   { "x",                                 556, NULL },
   { "y",                                 556, NULL },
   { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          474, NULL },
+  { "gcommaaccent",                      611, NULL },
   { "mu",                                611, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            667, NULL },
   { "Lslash",                            611, NULL },
   { "semicolon",                         333, NULL },
   { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            556, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            611, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         556, NULL },
+  { "nacute",                            611, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            556, NULL },
   { "AE",                               1000, NULL },
@@ -1410,9 +1750,11 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1420,14 +1762,17 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       611, NULL },
+  { "emacron",                           556, NULL },
   { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       611, NULL },
   { "bracketleft",                       333, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        278, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         667, NULL },
+  { "umacron",                           611, NULL },
+  { "abreve",                            556, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         556, NULL },
   { "egrave",                            556, NULL },
@@ -1443,112 +1788,174 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         611, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         556, NULL },
   { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         556, NULL },
   { "tilde",                             333, NULL },
   { "dbldaggerumlaut",                   556, NULL },
   { "at",                                975, NULL },
   { "eacute",                            556, NULL },
   { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          584, NULL },
   { "zero",                              556, NULL },
   { "eth",                               611, NULL },
+  { "Scedilla",                          667, NULL },
   { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
   { "uacute",                            611, NULL },
   { "braceleft",                         389, NULL },
   { "Thorn",                             667, NULL },
   { "zcaron",                            500, NULL },
+  { "scommaaccent",                      556, NULL },
   { "ccedilla",                          556, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            611, NULL },
   { "Ocircumflex",                       778, NULL },
   { "Oacute",                            778, NULL },
+  { "scedilla",                          556, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            611, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             611, NULL },
   { "degree",                            400, NULL },
   { "registered",                        737, NULL },
+  { "radical",                           549, NULL },
   { "Aring",                             722, NULL },
   { "percent",                           889, NULL },
   { "six",                               556, NULL },
   { "paragraph",                         556, NULL },
+  { "dcaron",                            743, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               556, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            278, NULL },
+  { "Lacute",                            611, NULL },
   { "ocircumflex",                       611, NULL },
   { "oacute",                            611, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            389, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
   { "asciicircum",                       584, NULL },
   { "aring",                             556, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           611, NULL },
   { "bracketright",                      333, NULL },
   { "Iacute",                            278, NULL },
   { "ampersand",                         722, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              584, NULL },
+  { "uring",                             611, NULL },
   { "quotesinglbase",                    278, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     611, NULL },
   { "threesuperior",                     333, NULL },
   { "acute",                             333, NULL },
   { "section",                           556, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      500, NULL },
+  { "ncaron",                            611, NULL },
   { "florin",                            556, NULL },
   { "yacute",                            556, NULL },
+  { "Rcommaaccent",                      722, NULL },
   { "fi",                                611, NULL },
   { "fl",                                611, NULL },
   { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
   { "Icircumflex",                       278, NULL },
   { "guillemotleft",                     556, NULL },
   { "germandbls",                        611, NULL },
+  { "Amacron",                           722, NULL },
   { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
   { "ordmasculine",                      365, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       556, NULL },
+  { "cacute",                            556, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        389, NULL },
   { "quotedblright",                     500, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            556, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              556, NULL },
   { "currency",                          556, NULL },
   { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
   { "Atilde",                            722, NULL },
   { "breve",                             333, NULL },
   { "bar",                               280, NULL },
   { "fraction",                          167, NULL },
   { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            333, NULL },
   { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      722, NULL },
   { "greater",                           584, NULL },
   { "atilde",                            556, NULL },
   { "brokenbar",                         280, NULL },
   { "quoteleft",                         278, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       333, NULL }
 };
 
 static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             278, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         584, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
   { "asciitilde",                        584, NULL },
   { "colon",                             333, NULL },
   { "onehalf",                           834, NULL },
   { "dollar",                            556, NULL },
+  { "Lcaron",                            611, NULL },
   { "ntilde",                            611, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      611, NULL },
   { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
   { "yen",                               556, NULL },
   { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
   { "questiondown",                      611, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            722, NULL },
   { "three",                             556, NULL },
   { "numbersign",                        556, NULL },
+  { "lcaron",                            400, NULL },
   { "A",                                 722, NULL },
   { "B",                                 722, NULL },
   { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        834, NULL },
@@ -1558,14 +1965,18 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "I",                                 278, NULL },
   { "J",                                 556, NULL },
   { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 611, NULL },
   { "periodcentered",                    278, NULL },
   { "M",                                 833, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           611, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 778, NULL },
   { "P",                                 667, NULL },
   { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 722, NULL },
   { "Aacute",                            722, NULL },
   { "caron",                             333, NULL },
@@ -1582,6 +1993,7 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 611, NULL },
   { "c",                                 556, NULL },
   { "d",                                 611, NULL },
@@ -1598,11 +2010,13 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 889, NULL },
   { "n",                                 611, NULL },
+  { "tcommaaccent",                      333, NULL },
   { "o",                                 611, NULL },
   { "ordfeminine",                       370, NULL },
   { "ring",                              333, NULL },
   { "p",                                 611, NULL },
   { "q",                                 611, NULL },
+  { "uhungarumlaut",                     611, NULL },
   { "r",                                 389, NULL },
   { "twosuperior",                       333, NULL },
   { "aacute",                            556, NULL },
@@ -1611,24 +2025,37 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "t",                                 333, NULL },
   { "divide",                            584, NULL },
   { "u",                                 611, NULL },
+  { "Ccaron",                            722, NULL },
   { "v",                                 556, NULL },
   { "w",                                 778, NULL },
   { "x",                                 556, NULL },
   { "y",                                 556, NULL },
   { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          474, NULL },
+  { "gcommaaccent",                      611, NULL },
   { "mu",                                611, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            667, NULL },
   { "Lslash",                            611, NULL },
   { "semicolon",                         333, NULL },
   { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            556, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            611, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         556, NULL },
+  { "nacute",                            611, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            556, NULL },
   { "AE",                               1000, NULL },
@@ -1642,9 +2069,11 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1652,14 +2081,17 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       611, NULL },
+  { "emacron",                           556, NULL },
   { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       611, NULL },
   { "bracketleft",                       333, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        278, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         667, NULL },
+  { "umacron",                           611, NULL },
+  { "abreve",                            556, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         556, NULL },
   { "egrave",                            556, NULL },
@@ -1675,111 +2107,173 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         611, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         556, NULL },
   { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         556, NULL },
   { "tilde",                             333, NULL },
   { "at",                                975, NULL },
   { "eacute",                            556, NULL },
   { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          584, NULL },
   { "zero",                              556, NULL },
   { "eth",                               611, NULL },
+  { "Scedilla",                          667, NULL },
   { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
   { "uacute",                            611, NULL },
   { "braceleft",                         389, NULL },
   { "Thorn",                             667, NULL },
   { "zcaron",                            500, NULL },
+  { "scommaaccent",                      556, NULL },
   { "ccedilla",                          556, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            611, NULL },
   { "Ocircumflex",                       778, NULL },
   { "Oacute",                            778, NULL },
+  { "scedilla",                          556, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            611, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             611, NULL },
   { "degree",                            400, NULL },
   { "registered",                        737, NULL },
+  { "radical",                           549, NULL },
   { "Aring",                             722, NULL },
   { "percent",                           889, NULL },
   { "six",                               556, NULL },
   { "paragraph",                         556, NULL },
+  { "dcaron",                            743, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               556, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            278, NULL },
+  { "Lacute",                            611, NULL },
   { "ocircumflex",                       611, NULL },
   { "oacute",                            611, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            389, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
   { "asciicircum",                       584, NULL },
   { "aring",                             556, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           611, NULL },
   { "bracketright",                      333, NULL },
   { "Iacute",                            278, NULL },
   { "ampersand",                         722, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              584, NULL },
+  { "uring",                             611, NULL },
   { "quotesinglbase",                    278, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     611, NULL },
   { "threesuperior",                     333, NULL },
   { "acute",                             333, NULL },
   { "section",                           556, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      500, NULL },
+  { "ncaron",                            611, NULL },
   { "florin",                            556, NULL },
   { "yacute",                            556, NULL },
+  { "Rcommaaccent",                      722, NULL },
   { "fi",                                611, NULL },
   { "fl",                                611, NULL },
   { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
   { "Icircumflex",                       278, NULL },
   { "guillemotleft",                     556, NULL },
   { "germandbls",                        611, NULL },
+  { "Amacron",                           722, NULL },
   { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
   { "ordmasculine",                      365, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       556, NULL },
+  { "cacute",                            556, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        389, NULL },
   { "quotedblright",                     500, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            556, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              556, NULL },
   { "currency",                          556, NULL },
   { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
   { "Atilde",                            722, NULL },
   { "breve",                             333, NULL },
   { "bar",                               280, NULL },
   { "fraction",                          167, NULL },
   { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            333, NULL },
   { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      722, NULL },
   { "greater",                           584, NULL },
   { "atilde",                            556, NULL },
   { "brokenbar",                         280, NULL },
   { "quoteleft",                         278, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       333, NULL }
 };
 
 static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             278, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         584, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
   { "asciitilde",                        584, NULL },
   { "colon",                             278, NULL },
   { "onehalf",                           834, NULL },
   { "dollar",                            556, NULL },
+  { "Lcaron",                            556, NULL },
   { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
   { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
   { "yen",                               556, NULL },
   { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
   { "questiondown",                      611, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            667, NULL },
   { "three",                             556, NULL },
   { "numbersign",                        556, NULL },
+  { "lcaron",                            299, NULL },
   { "A",                                 667, NULL },
   { "B",                                 667, NULL },
   { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        834, NULL },
@@ -1789,14 +2283,18 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "I",                                 278, NULL },
   { "J",                                 500, NULL },
   { "K",                                 667, NULL },
+  { "iogonek",                           222, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 556, NULL },
   { "periodcentered",                    278, NULL },
   { "M",                                 833, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           556, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 778, NULL },
   { "P",                                 667, NULL },
   { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 722, NULL },
   { "Aacute",                            667, NULL },
   { "caron",                             333, NULL },
@@ -1813,6 +2311,7 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 500, NULL },
   { "d",                                 556, NULL },
@@ -1829,11 +2328,13 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "l",                                 222, NULL },
   { "m",                                 833, NULL },
   { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
   { "o",                                 556, NULL },
   { "ordfeminine",                       370, NULL },
   { "ring",                              333, NULL },
   { "p",                                 556, NULL },
   { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
   { "r",                                 333, NULL },
   { "twosuperior",                       333, NULL },
   { "aacute",                            556, NULL },
@@ -1842,24 +2343,37 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "t",                                 278, NULL },
   { "divide",                            584, NULL },
   { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
   { "v",                                 500, NULL },
   { "w",                                 722, NULL },
   { "x",                                 500, NULL },
   { "y",                                 500, NULL },
   { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          355, NULL },
+  { "gcommaaccent",                      556, NULL },
   { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            667, NULL },
   { "Lslash",                            556, NULL },
   { "semicolon",                         278, NULL },
   { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            500, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            556, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         556, NULL },
+  { "nacute",                            556, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            500, NULL },
   { "AE",                               1000, NULL },
@@ -1873,9 +2387,11 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1883,14 +2399,17 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       556, NULL },
+  { "emacron",                           556, NULL },
   { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
   { "bracketleft",                       278, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        222, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         667, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            556, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         556, NULL },
   { "egrave",                            556, NULL },
@@ -1906,85 +2425,134 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         500, NULL },
   { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
   { "tilde",                             333, NULL },
   { "at",                               1015, NULL },
   { "eacute",                            556, NULL },
   { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          584, NULL },
   { "zero",                              556, NULL },
   { "eth",                               556, NULL },
+  { "Scedilla",                          667, NULL },
   { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       476, NULL },
   { "uacute",                            556, NULL },
   { "braceleft",                         334, NULL },
   { "Thorn",                             667, NULL },
   { "zcaron",                            500, NULL },
+  { "scommaaccent",                      500, NULL },
   { "ccedilla",                          500, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
   { "Ocircumflex",                       778, NULL },
   { "Oacute",                            778, NULL },
+  { "scedilla",                          500, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            556, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             556, NULL },
   { "degree",                            400, NULL },
   { "registered",                        737, NULL },
+  { "radical",                           453, NULL },
   { "Aring",                             667, NULL },
   { "percent",                           889, NULL },
   { "six",                               556, NULL },
   { "paragraph",                         537, NULL },
+  { "dcaron",                            643, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               556, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            278, NULL },
+  { "Lacute",                            556, NULL },
   { "ocircumflex",                       556, NULL },
   { "oacute",                            556, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            317, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
   { "asciicircum",                       469, NULL },
   { "aring",                             556, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
   { "bracketright",                      278, NULL },
   { "Iacute",                            278, NULL },
   { "ampersand",                         667, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            222, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              584, NULL },
+  { "uring",                             556, NULL },
   { "quotesinglbase",                    222, NULL },
+  { "lcommaaccent",                      222, NULL },
   { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     556, NULL },
   { "threesuperior",                     333, NULL },
   { "acute",                             333, NULL },
   { "section",                           556, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      333, NULL },
+  { "ncaron",                            556, NULL },
   { "florin",                            556, NULL },
   { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
   { "fi",                                500, NULL },
   { "fl",                                500, NULL },
   { "Acircumflex",                       667, NULL },
+  { "Cacute",                            722, NULL },
   { "Icircumflex",                       278, NULL },
   { "guillemotleft",                     556, NULL },
   { "germandbls",                        611, NULL },
+  { "Amacron",                           667, NULL },
   { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
   { "ordmasculine",                      365, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       556, NULL },
+  { "cacute",                            500, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        334, NULL },
   { "quotedblright",                     333, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            500, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              556, NULL },
   { "currency",                          556, NULL },
   { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
   { "Atilde",                            667, NULL },
   { "breve",                             333, NULL },
   { "bar",                               260, NULL },
   { "fraction",                          167, NULL },
   { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            278, NULL },
   { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      667, NULL },
   { "greater",                           584, NULL },
   { "atilde",                            556, NULL },
   { "brokenbar",                         260, NULL },
   { "quoteleft",                         222, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       333, NULL }
 };
 
@@ -2099,6 +2667,7 @@ static BuiltinFontWidth symbolWidthsTab[] = {
   { "parenrighttp",                      384, NULL },
   { "eta",                               603, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              750, NULL },
   { "multiply",                          549, NULL },
   { "zero",                              500, NULL },
   { "partialdiff",                       494, NULL },
@@ -2182,27 +2751,40 @@ static BuiltinFontWidth symbolWidthsTab[] = {
 
 static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            444, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            667, NULL },
   { "comma",                             250, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         570, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
   { "asciitilde",                        520, NULL },
   { "colon",                             333, NULL },
   { "onehalf",                           750, NULL },
   { "dollar",                            500, NULL },
+  { "Lcaron",                            667, NULL },
   { "ntilde",                            556, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      556, NULL },
   { "minus",                             570, NULL },
+  { "Iogonek",                           389, NULL },
+  { "zacute",                            444, NULL },
   { "yen",                               500, NULL },
   { "space",                             250, NULL },
+  { "Omacron",                           778, NULL },
   { "questiondown",                      500, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            722, NULL },
   { "three",                             500, NULL },
   { "numbersign",                        500, NULL },
+  { "lcaron",                            394, NULL },
   { "A",                                 722, NULL },
   { "B",                                 667, NULL },
   { "C",                                 722, NULL },
+  { "aogonek",                           500, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        750, NULL },
@@ -2212,14 +2794,18 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "I",                                 389, NULL },
   { "J",                                 500, NULL },
   { "K",                                 778, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 667, NULL },
   { "periodcentered",                    250, NULL },
   { "M",                                 944, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      667, NULL },
   { "O",                                 778, NULL },
   { "P",                                 611, NULL },
   { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 722, NULL },
   { "Aacute",                            722, NULL },
   { "caron",                             333, NULL },
@@ -2236,6 +2822,7 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "Z",                                 667, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 444, NULL },
   { "d",                                 556, NULL },
@@ -2252,11 +2839,13 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 833, NULL },
   { "n",                                 556, NULL },
+  { "tcommaaccent",                      333, NULL },
   { "o",                                 500, NULL },
   { "ordfeminine",                       300, NULL },
   { "ring",                              333, NULL },
   { "p",                                 556, NULL },
   { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
   { "r",                                 444, NULL },
   { "twosuperior",                       300, NULL },
   { "aacute",                            500, NULL },
@@ -2265,24 +2854,37 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "t",                                 333, NULL },
   { "divide",                            570, NULL },
   { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
   { "v",                                 500, NULL },
   { "w",                                 722, NULL },
   { "x",                                 500, NULL },
   { "y",                                 500, NULL },
   { "z",                                 444, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        389, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          555, NULL },
+  { "gcommaaccent",                      500, NULL },
   { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            556, NULL },
   { "Lslash",                            667, NULL },
   { "semicolon",                         333, NULL },
   { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            500, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         500, NULL },
+  { "nacute",                            556, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            389, NULL },
   { "AE",                               1000, NULL },
@@ -2296,9 +2898,11 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         747, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         747, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -2306,14 +2910,17 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "Idieresis",                         389, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       556, NULL },
+  { "emacron",                           444, NULL },
   { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
   { "bracketleft",                       333, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        333, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         722, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            500, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         500, NULL },
   { "egrave",                            444, NULL },
@@ -2329,111 +2936,173 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            667, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         500, NULL },
   { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
   { "tilde",                             333, NULL },
   { "at",                                930, NULL },
   { "eacute",                            444, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          570, NULL },
   { "zero",                              500, NULL },
   { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
   { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
   { "uacute",                            556, NULL },
   { "braceleft",                         394, NULL },
   { "Thorn",                             611, NULL },
   { "zcaron",                            444, NULL },
+  { "scommaaccent",                      389, NULL },
   { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
   { "Ocircumflex",                       778, NULL },
   { "Oacute",                            778, NULL },
+  { "scedilla",                          389, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            500, NULL },
+  { "racute",                            444, NULL },
+  { "Tcaron",                            667, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             556, NULL },
   { "degree",                            400, NULL },
   { "registered",                        747, NULL },
+  { "radical",                           549, NULL },
   { "Aring",                             722, NULL },
   { "percent",                          1000, NULL },
   { "six",                               500, NULL },
   { "paragraph",                         540, NULL },
+  { "dcaron",                            672, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               500, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            389, NULL },
+  { "Lacute",                            667, NULL },
   { "ocircumflex",                       500, NULL },
   { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      667, NULL },
+  { "tcaron",                            416, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
   { "asciicircum",                       581, NULL },
   { "aring",                             500, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
   { "bracketright",                      333, NULL },
   { "Iacute",                            389, NULL },
   { "ampersand",                         833, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              570, NULL },
+  { "uring",                             556, NULL },
   { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            722, NULL },
+  { "ohungarumlaut",                     500, NULL },
   { "threesuperior",                     300, NULL },
   { "acute",                             333, NULL },
   { "section",                           500, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      500, NULL },
+  { "ncaron",                            556, NULL },
   { "florin",                            500, NULL },
   { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
   { "fi",                                556, NULL },
   { "fl",                                556, NULL },
   { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
   { "Icircumflex",                       389, NULL },
   { "guillemotleft",                     500, NULL },
   { "germandbls",                        556, NULL },
+  { "Amacron",                           722, NULL },
   { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
   { "ordmasculine",                      330, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           389, NULL },
+  { "rcommaaccent",                      444, NULL },
+  { "Zdotaccent",                        667, NULL },
   { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        394, NULL },
   { "quotedblright",                     500, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              500, NULL },
   { "currency",                          500, NULL },
   { "logicalnot",                        570, NULL },
+  { "zdotaccent",                        444, NULL },
   { "Atilde",                            722, NULL },
   { "breve",                             333, NULL },
   { "bar",                               220, NULL },
   { "fraction",                          167, NULL },
   { "less",                              570, NULL },
+  { "ecaron",                            444, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            333, NULL },
   { "period",                            250, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      778, NULL },
   { "greater",                           570, NULL },
   { "atilde",                            500, NULL },
   { "brokenbar",                         220, NULL },
   { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       300, NULL }
 };
 
 static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             250, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         570, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
   { "asciitilde",                        570, NULL },
   { "colon",                             333, NULL },
   { "onehalf",                           750, NULL },
   { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
   { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
   { "minus",                             606, NULL },
+  { "Iogonek",                           389, NULL },
+  { "zacute",                            389, NULL },
   { "yen",                               500, NULL },
   { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
   { "questiondown",                      500, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            667, NULL },
   { "three",                             500, NULL },
   { "numbersign",                        500, NULL },
+  { "lcaron",                            382, NULL },
   { "A",                                 667, NULL },
   { "B",                                 667, NULL },
   { "C",                                 667, NULL },
+  { "aogonek",                           500, NULL },
   { "D",                                 722, NULL },
   { "E",                                 667, NULL },
   { "onequarter",                        750, NULL },
@@ -2443,14 +3112,18 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "I",                                 389, NULL },
   { "J",                                 500, NULL },
   { "K",                                 667, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 611, NULL },
   { "periodcentered",                    250, NULL },
   { "M",                                 889, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 722, NULL },
   { "P",                                 611, NULL },
   { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 667, NULL },
   { "Aacute",                            667, NULL },
   { "caron",                             333, NULL },
@@ -2467,6 +3140,7 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2483,11 +3157,13 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 778, NULL },
   { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
   { "o",                                 500, NULL },
   { "ordfeminine",                       266, NULL },
   { "ring",                              333, NULL },
   { "p",                                 500, NULL },
   { "q",                                 500, NULL },
+  { "uhungarumlaut",                     556, NULL },
   { "r",                                 389, NULL },
   { "twosuperior",                       300, NULL },
   { "aacute",                            500, NULL },
@@ -2496,24 +3172,37 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "t",                                 278, NULL },
   { "divide",                            570, NULL },
   { "u",                                 556, NULL },
+  { "Ccaron",                            667, NULL },
   { "v",                                 444, NULL },
   { "w",                                 667, NULL },
   { "x",                                 500, NULL },
   { "y",                                 444, NULL },
   { "z",                                 389, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        389, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          555, NULL },
+  { "gcommaaccent",                      500, NULL },
   { "mu",                                576, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            556, NULL },
   { "Lslash",                            611, NULL },
   { "semicolon",                         333, NULL },
   { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
   { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            500, NULL },
   { "trademark",                        1000, NULL },
   { "daggerdbl",                         500, NULL },
+  { "nacute",                            556, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            722, NULL },
+  { "Emacron",                           667, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            389, NULL },
   { "AE",                                944, NULL },
@@ -2527,9 +3216,11 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "exclamdown",                        389, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         747, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         747, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -2537,14 +3228,17 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "Idieresis",                         389, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       556, NULL },
+  { "emacron",                           444, NULL },
   { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       556, NULL },
   { "bracketleft",                       333, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        333, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         611, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            500, NULL },
   { "Eacute",                            667, NULL },
   { "adieresis",                         500, NULL },
   { "egrave",                            444, NULL },
@@ -2560,111 +3254,173 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         444, NULL },
   { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         444, NULL },
   { "tilde",                             333, NULL },
   { "at",                                832, NULL },
   { "eacute",                            444, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          570, NULL },
   { "zero",                              500, NULL },
   { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
   { "Ograve",                            722, NULL },
+  { "Racute",                            667, NULL },
+  { "partialdiff",                       494, NULL },
   { "uacute",                            556, NULL },
   { "braceleft",                         348, NULL },
   { "Thorn",                             611, NULL },
   { "zcaron",                            389, NULL },
+  { "scommaaccent",                      389, NULL },
   { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
   { "Ocircumflex",                       722, NULL },
   { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            500, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
   { "thorn",                             500, NULL },
   { "degree",                            400, NULL },
   { "registered",                        747, NULL },
+  { "radical",                           549, NULL },
   { "Aring",                             667, NULL },
   { "percent",                           833, NULL },
   { "six",                               500, NULL },
   { "paragraph",                         500, NULL },
+  { "dcaron",                            608, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               500, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            389, NULL },
+  { "Lacute",                            611, NULL },
   { "ocircumflex",                       500, NULL },
   { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            366, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
   { "asciicircum",                       570, NULL },
   { "aring",                             500, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
   { "bracketright",                      333, NULL },
   { "Iacute",                            389, NULL },
   { "ampersand",                         778, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              570, NULL },
+  { "uring",                             556, NULL },
   { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            611, NULL },
+  { "ohungarumlaut",                     500, NULL },
   { "threesuperior",                     300, NULL },
   { "acute",                             333, NULL },
   { "section",                           500, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      500, NULL },
+  { "ncaron",                            556, NULL },
   { "florin",                            500, NULL },
   { "yacute",                            444, NULL },
+  { "Rcommaaccent",                      667, NULL },
   { "fi",                                556, NULL },
   { "fl",                                556, NULL },
   { "Acircumflex",                       667, NULL },
+  { "Cacute",                            667, NULL },
   { "Icircumflex",                       389, NULL },
   { "guillemotleft",                     500, NULL },
   { "germandbls",                        500, NULL },
+  { "Amacron",                           667, NULL },
   { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
   { "ordmasculine",                      300, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           389, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            667, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        348, NULL },
   { "quotedblright",                     500, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              500, NULL },
   { "currency",                          500, NULL },
   { "logicalnot",                        606, NULL },
+  { "zdotaccent",                        389, NULL },
   { "Atilde",                            667, NULL },
   { "breve",                             333, NULL },
   { "bar",                               220, NULL },
   { "fraction",                          167, NULL },
   { "less",                              570, NULL },
+  { "ecaron",                            444, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            389, NULL },
   { "period",                            250, NULL },
+  { "Rcaron",                            667, NULL },
+  { "Kcommaaccent",                      667, NULL },
   { "greater",                           570, NULL },
   { "atilde",                            500, NULL },
   { "brokenbar",                         220, NULL },
   { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        667, NULL },
   { "onesuperior",                       300, NULL }
 };
 
 static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "Ntilde",                            667, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      444, NULL },
+  { "Ncommaaccent",                      667, NULL },
+  { "Zacute",                            556, NULL },
   { "comma",                             250, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         675, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
   { "asciitilde",                        541, NULL },
   { "colon",                             333, NULL },
   { "onehalf",                           750, NULL },
   { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
   { "ntilde",                            500, NULL },
+  { "Aogonek",                           611, NULL },
+  { "ncommaaccent",                      500, NULL },
   { "minus",                             675, NULL },
+  { "Iogonek",                           333, NULL },
+  { "zacute",                            389, NULL },
   { "yen",                               500, NULL },
   { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
   { "questiondown",                      500, NULL },
   { "emdash",                            889, NULL },
   { "Agrave",                            611, NULL },
   { "three",                             500, NULL },
   { "numbersign",                        500, NULL },
+  { "lcaron",                            300, NULL },
   { "A",                                 611, NULL },
   { "B",                                 611, NULL },
   { "C",                                 667, NULL },
+  { "aogonek",                           500, NULL },
   { "D",                                 722, NULL },
   { "E",                                 611, NULL },
   { "onequarter",                        750, NULL },
@@ -2674,14 +3430,18 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "I",                                 333, NULL },
   { "J",                                 444, NULL },
   { "K",                                 667, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 556, NULL },
   { "periodcentered",                    250, NULL },
   { "M",                                 833, NULL },
   { "N",                                 667, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      556, NULL },
   { "O",                                 722, NULL },
   { "P",                                 611, NULL },
   { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 611, NULL },
   { "Aacute",                            611, NULL },
   { "caron",                             333, NULL },
@@ -2698,6 +3458,7 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "Z",                                 556, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2714,11 +3475,13 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 722, NULL },
   { "n",                                 500, NULL },
+  { "tcommaaccent",                      278, NULL },
   { "o",                                 500, NULL },
   { "ordfeminine",                       276, NULL },
   { "ring",                              333, NULL },
   { "p",                                 500, NULL },
   { "q",                                 500, NULL },
+  { "uhungarumlaut",                     500, NULL },
   { "r",                                 389, NULL },
   { "twosuperior",                       300, NULL },
   { "aacute",                            500, NULL },
@@ -2727,24 +3490,37 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "t",                                 278, NULL },
   { "divide",                            675, NULL },
   { "u",                                 500, NULL },
+  { "Ccaron",                            667, NULL },
   { "v",                                 444, NULL },
   { "w",                                 667, NULL },
   { "x",                                 444, NULL },
   { "y",                                 444, NULL },
   { "z",                                 389, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        333, NULL },
+  { "Nacute",                            667, NULL },
   { "quotedbl",                          420, NULL },
+  { "gcommaaccent",                      500, NULL },
   { "mu",                                500, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            500, NULL },
   { "Lslash",                            556, NULL },
   { "semicolon",                         333, NULL },
   { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
   { "Ecircumflex",                       611, NULL },
+  { "gbreve",                            500, NULL },
   { "trademark",                         980, NULL },
   { "daggerdbl",                         500, NULL },
+  { "nacute",                            500, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            722, NULL },
+  { "Emacron",                           611, NULL },
   { "ellipsis",                          889, NULL },
   { "scaron",                            389, NULL },
   { "AE",                                889, NULL },
@@ -2758,9 +3534,11 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "exclamdown",                        389, NULL },
   { "endash",                            500, NULL },
   { "oe",                                667, NULL },
+  { "Abreve",                            611, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         760, NULL },
   { "Adieresis",                         611, NULL },
+  { "copyright",                         760, NULL },
   { "Egrave",                            611, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         611, NULL },
@@ -2768,14 +3546,17 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "Idieresis",                         333, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       500, NULL },
+  { "emacron",                           444, NULL },
   { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       500, NULL },
   { "bracketleft",                       389, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        333, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         556, NULL },
+  { "umacron",                           500, NULL },
+  { "abreve",                            500, NULL },
   { "Eacute",                            611, NULL },
   { "adieresis",                         500, NULL },
   { "egrave",                            444, NULL },
@@ -2791,111 +3572,173 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         500, NULL },
   { "Zcaron",                            556, NULL },
+  { "Scommaaccent",                      500, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         444, NULL },
   { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         444, NULL },
   { "tilde",                             333, NULL },
   { "at",                                920, NULL },
   { "eacute",                            444, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          675, NULL },
   { "zero",                              500, NULL },
   { "eth",                               500, NULL },
+  { "Scedilla",                          500, NULL },
   { "Ograve",                            722, NULL },
+  { "Racute",                            611, NULL },
+  { "partialdiff",                       476, NULL },
   { "uacute",                            500, NULL },
   { "braceleft",                         400, NULL },
   { "Thorn",                             611, NULL },
   { "zcaron",                            389, NULL },
+  { "scommaaccent",                      389, NULL },
   { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
   { "Ocircumflex",                       722, NULL },
   { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            500, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            556, NULL },
+  { "Eogonek",                           611, NULL },
   { "thorn",                             500, NULL },
   { "degree",                            400, NULL },
   { "registered",                        760, NULL },
+  { "radical",                           453, NULL },
   { "Aring",                             611, NULL },
   { "percent",                           833, NULL },
   { "six",                               500, NULL },
   { "paragraph",                         523, NULL },
+  { "dcaron",                            544, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               500, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            333, NULL },
+  { "Lacute",                            556, NULL },
   { "ocircumflex",                       500, NULL },
   { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            300, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
   { "asciicircum",                       422, NULL },
   { "aring",                             500, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           500, NULL },
   { "bracketright",                      389, NULL },
   { "Iacute",                            333, NULL },
   { "ampersand",                         778, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            667, NULL },
   { "plus",                              675, NULL },
+  { "uring",                             500, NULL },
   { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            556, NULL },
+  { "ohungarumlaut",                     500, NULL },
   { "threesuperior",                     300, NULL },
   { "acute",                             333, NULL },
   { "section",                           500, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      556, NULL },
+  { "ncaron",                            500, NULL },
   { "florin",                            500, NULL },
   { "yacute",                            444, NULL },
+  { "Rcommaaccent",                      611, NULL },
   { "fi",                                500, NULL },
   { "fl",                                500, NULL },
   { "Acircumflex",                       611, NULL },
+  { "Cacute",                            667, NULL },
   { "Icircumflex",                       333, NULL },
   { "guillemotleft",                     500, NULL },
   { "germandbls",                        500, NULL },
+  { "Amacron",                           611, NULL },
   { "seven",                             500, NULL },
+  { "Sacute",                            500, NULL },
   { "ordmasculine",                      310, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           333, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        556, NULL },
   { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            611, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        400, NULL },
   { "quotedblright",                     556, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              500, NULL },
   { "currency",                          500, NULL },
   { "logicalnot",                        675, NULL },
+  { "zdotaccent",                        389, NULL },
   { "Atilde",                            611, NULL },
   { "breve",                             333, NULL },
   { "bar",                               275, NULL },
   { "fraction",                          167, NULL },
   { "less",                              675, NULL },
+  { "ecaron",                            444, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            333, NULL },
   { "period",                            250, NULL },
+  { "Rcaron",                            611, NULL },
+  { "Kcommaaccent",                      667, NULL },
   { "greater",                           675, NULL },
   { "atilde",                            500, NULL },
   { "brokenbar",                         275, NULL },
   { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        611, NULL },
   { "onesuperior",                       300, NULL }
 };
 
 static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
   { "comma",                             250, NULL },
   { "cedilla",                           333, NULL },
   { "plusminus",                         564, NULL },
   { "circumflex",                        333, NULL },
   { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
   { "asciitilde",                        541, NULL },
   { "colon",                             278, NULL },
   { "onehalf",                           750, NULL },
   { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
   { "ntilde",                            500, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      500, NULL },
   { "minus",                             564, NULL },
+  { "Iogonek",                           333, NULL },
+  { "zacute",                            444, NULL },
   { "yen",                               500, NULL },
   { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
   { "questiondown",                      444, NULL },
   { "emdash",                           1000, NULL },
   { "Agrave",                            722, NULL },
   { "three",                             500, NULL },
   { "numbersign",                        500, NULL },
+  { "lcaron",                            344, NULL },
   { "A",                                 722, NULL },
   { "B",                                 667, NULL },
   { "C",                                 667, NULL },
+  { "aogonek",                           444, NULL },
   { "D",                                 722, NULL },
   { "E",                                 611, NULL },
   { "onequarter",                        750, NULL },
@@ -2905,14 +3748,18 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "I",                                 333, NULL },
   { "J",                                 389, NULL },
   { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
   { "backslash",                         278, NULL },
   { "L",                                 611, NULL },
   { "periodcentered",                    250, NULL },
   { "M",                                 889, NULL },
   { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      611, NULL },
   { "O",                                 722, NULL },
   { "P",                                 556, NULL },
   { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
   { "R",                                 667, NULL },
   { "Aacute",                            722, NULL },
   { "caron",                             333, NULL },
@@ -2929,6 +3776,7 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              500, NULL },
   { "a",                                 444, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2945,11 +3793,13 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "l",                                 278, NULL },
   { "m",                                 778, NULL },
   { "n",                                 500, NULL },
+  { "tcommaaccent",                      278, NULL },
   { "o",                                 500, NULL },
   { "ordfeminine",                       276, NULL },
   { "ring",                              333, NULL },
   { "p",                                 500, NULL },
   { "q",                                 500, NULL },
+  { "uhungarumlaut",                     500, NULL },
   { "r",                                 333, NULL },
   { "twosuperior",                       300, NULL },
   { "aacute",                            444, NULL },
@@ -2958,24 +3808,37 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "t",                                 278, NULL },
   { "divide",                            564, NULL },
   { "u",                                 500, NULL },
+  { "Ccaron",                            667, NULL },
   { "v",                                 500, NULL },
   { "w",                                 722, NULL },
   { "x",                                 500, NULL },
   { "y",                                 500, NULL },
   { "z",                                 444, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
   { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        333, NULL },
+  { "Nacute",                            722, NULL },
   { "quotedbl",                          408, NULL },
+  { "gcommaaccent",                      500, NULL },
   { "mu",                                500, NULL },
+  { "greaterequal",                      549, NULL },
   { "Scaron",                            556, NULL },
   { "Lslash",                            611, NULL },
   { "semicolon",                         278, NULL },
   { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
   { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
   { "Ecircumflex",                       611, NULL },
+  { "gbreve",                            500, NULL },
   { "trademark",                         980, NULL },
   { "daggerdbl",                         500, NULL },
+  { "nacute",                            500, NULL },
   { "macron",                            333, NULL },
   { "Otilde",                            722, NULL },
+  { "Emacron",                           611, NULL },
   { "ellipsis",                         1000, NULL },
   { "scaron",                            389, NULL },
   { "AE",                                889, NULL },
@@ -2989,9 +3852,11 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         760, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         760, NULL },
   { "Egrave",                            611, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         611, NULL },
@@ -2999,14 +3864,17 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "Idieresis",                         333, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       500, NULL },
+  { "emacron",                           444, NULL },
   { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       500, NULL },
   { "bracketleft",                       333, NULL },
   { "Ugrave",                            722, NULL },
   { "quoteright",                        333, NULL },
   { "Udieresis",                         722, NULL },
   { "perthousand",                      1000, NULL },
   { "Ydieresis",                         722, NULL },
+  { "umacron",                           500, NULL },
+  { "abreve",                            444, NULL },
   { "Eacute",                            611, NULL },
   { "adieresis",                         444, NULL },
   { "egrave",                            444, NULL },
@@ -3022,85 +3890,134 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         500, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         500, NULL },
   { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         500, NULL },
   { "tilde",                             333, NULL },
   { "at",                                921, NULL },
   { "eacute",                            444, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
   { "multiply",                          564, NULL },
   { "zero",                              500, NULL },
   { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
   { "Ograve",                            722, NULL },
+  { "Racute",                            667, NULL },
+  { "partialdiff",                       476, NULL },
   { "uacute",                            500, NULL },
   { "braceleft",                         480, NULL },
   { "Thorn",                             556, NULL },
   { "zcaron",                            444, NULL },
+  { "scommaaccent",                      389, NULL },
   { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
   { "Ocircumflex",                       722, NULL },
   { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
   { "ogonek",                            333, NULL },
   { "ograve",                            500, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           611, NULL },
   { "thorn",                             500, NULL },
   { "degree",                            400, NULL },
   { "registered",                        760, NULL },
+  { "radical",                           453, NULL },
   { "Aring",                             722, NULL },
   { "percent",                           833, NULL },
   { "six",                               500, NULL },
   { "paragraph",                         453, NULL },
+  { "dcaron",                            588, NULL },
+  { "Uogonek",                           722, NULL },
   { "two",                               500, NULL },
+  { "summation",                         600, NULL },
   { "Igrave",                            333, NULL },
+  { "Lacute",                            611, NULL },
   { "ocircumflex",                       500, NULL },
   { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            326, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
   { "asciicircum",                       469, NULL },
   { "aring",                             444, NULL },
   { "grave",                             333, NULL },
+  { "uogonek",                           500, NULL },
   { "bracketright",                      333, NULL },
   { "Iacute",                            333, NULL },
   { "ampersand",                         778, NULL },
   { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
   { "plus",                              564, NULL },
+  { "uring",                             500, NULL },
   { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
   { "Yacute",                            722, NULL },
+  { "ohungarumlaut",                     500, NULL },
   { "threesuperior",                     300, NULL },
   { "acute",                             333, NULL },
   { "section",                           500, NULL },
   { "dieresis",                          333, NULL },
   { "iacute",                            278, NULL },
   { "quotedblbase",                      444, NULL },
+  { "ncaron",                            500, NULL },
   { "florin",                            500, NULL },
   { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      667, NULL },
   { "fi",                                556, NULL },
   { "fl",                                556, NULL },
   { "Acircumflex",                       722, NULL },
+  { "Cacute",                            667, NULL },
   { "Icircumflex",                       333, NULL },
   { "guillemotleft",                     500, NULL },
   { "germandbls",                        500, NULL },
+  { "Amacron",                           722, NULL },
   { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
   { "ordmasculine",                      310, NULL },
   { "dotlessi",                          278, NULL },
   { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           333, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
   { "acircumflex",                       444, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            611, NULL },
   { "icircumflex",                       278, NULL },
   { "braceright",                        480, NULL },
   { "quotedblright",                     444, NULL },
+  { "amacron",                           444, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
   { "cent",                              500, NULL },
   { "currency",                          500, NULL },
   { "logicalnot",                        564, NULL },
+  { "zdotaccent",                        444, NULL },
   { "Atilde",                            722, NULL },
   { "breve",                             333, NULL },
   { "bar",                               200, NULL },
   { "fraction",                          167, NULL },
   { "less",                              564, NULL },
+  { "ecaron",                            444, NULL },
   { "guilsinglleft",                     333, NULL },
   { "exclam",                            333, NULL },
   { "period",                            250, NULL },
+  { "Rcaron",                            667, NULL },
+  { "Kcommaaccent",                      722, NULL },
   { "greater",                           564, NULL },
   { "atilde",                            444, NULL },
   { "brokenbar",                         200, NULL },
   { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        611, NULL },
   { "onesuperior",                       300, NULL }
 };
 
@@ -3279,10 +4196,10 @@ static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
   { "a203",                              762, NULL },
   { "a123",                              788, NULL },
   { "a204",                              759, NULL },
-  { "a205",                              509, NULL },
   { "a124",                              788, NULL },
-  { "a206",                              410, NULL },
+  { "a205",                              509, NULL },
   { "a125",                              788, NULL },
+  { "a206",                              410, NULL },
   { "a126",                              788, NULL },
   { "a127",                              788, NULL },
   { "a128",                              788, NULL },
@@ -3310,19 +4227,19 @@ static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
 };
 
 BuiltinFont builtinFonts[] = {
-  { "Courier",               standardEncoding,            624, -207, { -40, -290,  640,  795}, NULL },
-  { "Courier-Bold",          standardEncoding,            674, -257, {-100, -350,  700,  855}, NULL },
-  { "Courier-BoldOblique",   standardEncoding,            674, -257, {-145, -350,  817,  855}, NULL },
-  { "Courier-Oblique",       standardEncoding,            624, -207, { -85, -290,  759,  795}, NULL },
-  { "Helvetica",             standardEncoding,            729, -219, {-174, -220, 1001,  944}, NULL },
-  { "Helvetica-Bold",        standardEncoding,            729, -219, {-173, -221, 1003,  936}, NULL },
-  { "Helvetica-BoldOblique", standardEncoding,            729, -219, {-177, -221, 1107,  936}, NULL },
-  { "Helvetica-Oblique",     standardEncoding,            729, -219, {-178, -220, 1108,  944}, NULL },
+  { "Courier",               standardEncoding,            629, -157, { -23, -250,  715,  805}, NULL },
+  { "Courier-Bold",          standardEncoding,            629, -157, {-113, -250,  749,  801}, NULL },
+  { "Courier-BoldOblique",   standardEncoding,            629, -157, { -57, -250,  869,  801}, NULL },
+  { "Courier-Oblique",       standardEncoding,            629, -157, { -27, -250,  849,  805}, NULL },
+  { "Helvetica",             standardEncoding,            718, -207, {-166, -225, 1000,  931}, NULL },
+  { "Helvetica-Bold",        standardEncoding,            718, -207, {-170, -228, 1003,  962}, NULL },
+  { "Helvetica-BoldOblique", standardEncoding,            718, -207, {-174, -228, 1114,  962}, NULL },
+  { "Helvetica-Oblique",     standardEncoding,            718, -207, {-170, -225, 1116,  931}, NULL },
   { "Symbol",                symbolEncoding,             1010, -293, {-180, -293, 1090, 1010}, NULL },
-  { "Times-Bold",            standardEncoding,            670, -210, {-172, -256, 1008,  965}, NULL },
-  { "Times-BoldItalic",      standardEncoding,            682, -203, {-168, -232, 1014,  894}, NULL },
-  { "Times-Italic",          standardEncoding,            684, -206, {-176, -252,  990,  930}, NULL },
-  { "Times-Roman",           standardEncoding,            682, -217, {-170, -223, 1024,  896}, NULL },
+  { "Times-Bold",            standardEncoding,            683, -217, {-168, -218, 1000,  935}, NULL },
+  { "Times-BoldItalic",      standardEncoding,            683, -217, {-200, -218,  996,  921}, NULL },
+  { "Times-Italic",          standardEncoding,            683, -217, {-169, -217, 1010,  883}, NULL },
+  { "Times-Roman",           standardEncoding,            683, -217, {-168, -218, 1000,  898}, NULL },
   { "ZapfDingbats",          zapfDingbatsEncoding,        820, -143, {  -1, -143,  981,  820}, NULL }
 };
 
@@ -3342,19 +4259,19 @@ BuiltinFont *builtinFontSubst[] = {
 };
 
 void initBuiltinFontTables() {
-  builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 260);
-  builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 260);
-  builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 260);
-  builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 260);
-  builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 228);
-  builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 229);
-  builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 228);
-  builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 228);
-  builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 189);
-  builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 228);
-  builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 228);
-  builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 228);
-  builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 228);
+  builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315);
+  builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315);
+  builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315);
+  builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315);
+  builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315);
+  builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316);
+  builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315);
+  builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315);
+  builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190);
+  builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315);
+  builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315);
+  builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315);
+  builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315);
   builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202);
 }
 
index c60ce3c3761338d57e7e779eafc35cb5b2e185a3..25f3af75e7066d81c3a5dc7ffb22d1cc3860cbaf 100644 (file)
@@ -144,6 +144,9 @@ CMap::CMap(GString *collectionA, GString *cMapNameA) {
     vector[i].cid = 0;
   }
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
@@ -152,6 +155,9 @@ CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
   wMode = wModeA;
   vector = NULL;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 void CMap::useCMap(CMapCache *cache, char *useName) {
@@ -252,6 +258,9 @@ CMap::~CMap() {
   if (vector) {
     freeCMapVector(vector);
   }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
 }
 
 void CMap::freeCMapVector(CMapVectorEntry *vec) {
@@ -266,11 +275,26 @@ void CMap::freeCMapVector(CMapVectorEntry *vec) {
 }
 
 void CMap::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
   ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
 }
 
 void CMap::decRefCnt() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
index b44f07a0d8d943becefbfc32dfff475ef212b935..eff2a819ad80571784a9357e91aad31db82b8394 100644 (file)
 #include "gtypes.h"
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 class GString;
 struct CMapVectorEntry;
 class CMapCache;
@@ -69,6 +73,9 @@ private:
   CMapVectorEntry *vector;     // vector for first byte (NULL for
                                //   identity CMap)
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
index ad0821bc5c872c67972d253af4f9124e8d111e7d..c645fd001be0719f67cd52f722a49ecdf91308ec 100644 (file)
@@ -56,12 +56,13 @@ Catalog::Catalog(XRef *xrefA) {
     goto err2;
   }
   pagesDict.dictLookup("Count", &obj);
-  if (!obj.isInt()) {
+  // some PDF files actually use real numbers here ("/Count 9.0")
+  if (!obj.isNum()) {
     error(-1, "Page count in top-level pages object is wrong type (%s)",
          obj.getTypeName());
     goto err3;
   }
-  pagesSize = numPages0 = obj.getInt();
+  pagesSize = numPages0 = (int)obj.getNum();
   obj.free();
   pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
   pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
@@ -307,8 +308,8 @@ Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
        } else if (cmp < 0) {
          done = gTrue;
        }
-       name1.free();
       }
+      name1.free();
     }
     names.free();
     if (!found)
index a374b1bc352259a8b4e0702de5b2f5b85298e1d0..2e2ad478905bb1b8f36b4c440f6990082929f3e9 100644 (file)
@@ -54,7 +54,8 @@ static int getCharFromFile(void *data) {
 
 //------------------------------------------------------------------------
 
-CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
+CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
+                                                       GString *collection) {
   FILE *f;
   Unicode *mapA;
   CharCode size, mapLenA;
@@ -62,9 +63,9 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
   Unicode u;
   CharCodeToUnicode *ctu;
 
-  if (!(f = globalParams->getCIDToUnicodeFile(collectionA))) {
-    error(-1, "Couldn't find cidToUnicode file for the '%s' collection",
-         collectionA->getCString());
+  if (!(f = fopen(fileName->getCString(), "r"))) {
+    error(-1, "Couldn't open cidToUnicode file '%s'",
+         fileName->getCString());
     return NULL;
   }
 
@@ -80,22 +81,110 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
     if (sscanf(buf, "%x", &u) == 1) {
       mapA[mapLenA] = u;
     } else {
-      error(-1, "Bad line (%d) in cidToUnicode file for the '%s' collection",
-           (int)(mapLenA + 1), collectionA->getCString());
+      error(-1, "Bad line (%d) in cidToUnicode file '%s'",
+           (int)(mapLenA + 1), fileName->getCString());
       mapA[mapLenA] = 0;
     }
     ++mapLenA;
   }
   fclose(f);
 
-  ctu = new CharCodeToUnicode(collectionA->copy(), mapA, mapLenA, gTrue,
-                             NULL, 0);
+  ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue,
+                             NULL, 0, 0);
+  gfree(mapA);
+  return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
+                                                   GString *fileName) {
+  FILE *f;
+  Unicode *mapA;
+  CharCodeToUnicodeString *sMapA;
+  CharCode size, oldSize, len, sMapSizeA, sMapLenA;
+  char buf[256];
+  char *tok;
+  Unicode u0;
+  Unicode uBuf[maxUnicodeString];
+  CharCodeToUnicode *ctu;
+  int line, n, i;
+
+  if (!(f = fopen(fileName->getCString(), "r"))) {
+    error(-1, "Couldn't open unicodeToUnicode file '%s'",
+         fileName->getCString());
+    return NULL;
+  }
+
+  size = 4096;
+  mapA = (Unicode *)gmalloc(size * sizeof(Unicode));
+  memset(mapA, 0, size * sizeof(Unicode));
+  len = 0;
+  sMapA = NULL;
+  sMapSizeA = sMapLenA = 0;
+
+  line = 0;
+  while (getLine(buf, sizeof(buf), f)) {
+    ++line;
+    if (!(tok = strtok(buf, " \t\r\n")) ||
+       sscanf(tok, "%x", &u0) != 1) {
+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+           line, fileName->getCString());
+      continue;
+    }
+    n = 0;
+    while (n < maxUnicodeString) {
+      if (!(tok = strtok(NULL, " \t\r\n"))) {
+       break;
+      }
+      if (sscanf(tok, "%x", &uBuf[n]) != 1) {
+       error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+             line, fileName->getCString());
+       break;
+      }
+      ++n;
+    }
+    if (n < 1) {
+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+           line, fileName->getCString());
+      continue;
+    }
+    if (u0 >= size) {
+      oldSize = size;
+      while (u0 >= size) {
+       size *= 2;
+      }
+      mapA = (Unicode *)grealloc(mapA, size * sizeof(Unicode));
+      memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
+    }
+    if (n == 1) {
+      mapA[u0] = uBuf[0];
+    } else {
+      mapA[u0] = 0;
+      if (sMapLenA == sMapSizeA) {
+       sMapSizeA += 16;
+       sMapA = (CharCodeToUnicodeString *)
+                 grealloc(sMapA, sMapSizeA * sizeof(CharCodeToUnicodeString));
+      }
+      sMapA[sMapLenA].c = u0;
+      for (i = 0; i < n; ++i) {
+       sMapA[sMapLenA].u[i] = uBuf[i];
+      }
+      sMapA[sMapLenA].len = n;
+      ++sMapLenA;
+    }
+    if (u0 >= len) {
+      len = u0 + 1;
+    }
+  }
+  fclose(f);
+
+  ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue,
+                             sMapA, sMapLenA, sMapSizeA);
   gfree(mapA);
   return ctu;
 }
 
 CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) {
-  return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0);
+  return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
 }
 
 CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
@@ -108,16 +197,20 @@ CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
   return ctu;
 }
 
+void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {
+  char *p;
+
+  p = buf->getCString();
+  parseCMap1(&getCharFromString, &p, nBits);
+}
+
 void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
                                   int nBits) {
   PSTokenizer *pst;
   char tok1[256], tok2[256], tok3[256];
   int nDigits, n1, n2, n3;
-  CharCode oldLen, i;
+  CharCode i;
   CharCode code1, code2;
-  Unicode u;
-  char uHex[5];
-  int j;
   GString *name;
   FILE *f;
 
@@ -158,38 +251,7 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
          error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
          continue;
        }
-       if (code1 >= mapLen) {
-         oldLen = mapLen;
-         mapLen = (code1 + 256) & ~255;
-         map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
-         for (i = oldLen; i < mapLen; ++i) {
-           map[i] = 0;
-         }
-       }
-       if (n2 == 6) {
-         if (sscanf(tok2 + 1, "%x", &u) != 1) {
-           error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
-           continue;
-         }
-         map[code1] = u;
-       } else {
-         map[code1] = 0;
-         if (sMapLen == sMapSize) {
-           sMapSize += 8;
-           sMap = (CharCodeToUnicodeString *)
-               grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
-         }
-         sMap[sMapLen].c = code1;
-         sMap[sMapLen].len = (n2 - 2) / 4;
-         for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
-           strncpy(uHex, tok2 + 1 + j*4, 4);
-           uHex[4] = '\0';
-           if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
-             error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
-           }
-         }
-         ++sMapLen;
-       }
+       addMapping(code1, tok2 + 1, n2 - 1, 0);
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
     } else if (!strcmp(tok2, "beginbfrange")) {
@@ -205,53 +267,39 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
          break;
        }
        if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
-             n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>' &&
-             tok3[0] == '<' && tok3[n3 - 1] == '>')) {
+             n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>')) {
          error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
          continue;
        }
-       tok1[n1 - 1] = tok2[n2 - 1] = tok3[n3 - 1] = '\0';
+       tok1[n1 - 1] = tok2[n2 - 1] = '\0';
        if (sscanf(tok1 + 1, "%x", &code1) != 1 ||
            sscanf(tok2 + 1, "%x", &code2) != 1) {
          error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
          continue;
        }
-       if (code2 >= mapLen) {
-         oldLen = mapLen;
-         mapLen = (code2 + 256) & ~255;
-         map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
-         for (i = oldLen; i < mapLen; ++i) {
-           map[i] = 0;
-         }
-       }
-       if (n3 <= 6) {
-         if (sscanf(tok3 + 1, "%x", &u) != 1) {
-           error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
-           continue;
-         }
-         for (; code1 <= code2; ++code1) {
-           map[code1] = u++;
-         }
-       } else {
-         if (sMapLen + (int)(code2 - code1 + 1) > sMapSize) {
-           sMapSize = (sMapSize + (code2 - code1 + 1) + 7) & ~7;
-           sMap = (CharCodeToUnicodeString *)
-               grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+       if (!strcmp(tok3, "[")) {
+         i = 0;
+         while (pst->getToken(tok1, sizeof(tok1), &n1) &&
+                code1 + i <= code2) {
+           if (!strcmp(tok1, "]")) {
+             break;
+           }
+           if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
+             tok1[n1 - 1] = '\0';
+             addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+           } else {
+             error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+           }
+           ++i;
          }
+       } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') {
+         tok3[n3 - 1] = '\0';
          for (i = 0; code1 <= code2; ++code1, ++i) {
-           map[code1] = 0;
-           sMap[sMapLen].c = code1;
-           sMap[sMapLen].len = (n3 - 2) / 4;
-           for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
-             strncpy(uHex, tok3 + 1 + j*4, 4);
-             uHex[4] = '\0';
-             if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
-               error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
-             }
-           }
-           sMap[sMapLen].u[sMap[sMapLen].len - 1] += i;
-           ++sMapLen;
+           addMapping(code1, tok3 + 1, n3 - 2, i);
          }
+
+       } else {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
        }
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
@@ -262,10 +310,52 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
   delete pst;
 }
 
-CharCodeToUnicode::CharCodeToUnicode(GString *collectionA) {
+void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
+                                  int offset) {
+  CharCode oldLen, i;
+  Unicode u;
+  char uHex[5];
+  int j;
+
+  if (code >= mapLen) {
+    oldLen = mapLen;
+    mapLen = (code + 256) & ~255;
+    map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
+    for (i = oldLen; i < mapLen; ++i) {
+      map[i] = 0;
+    }
+  }
+  if (n <= 4) {
+    if (sscanf(uStr, "%x", &u) != 1) {
+      error(-1, "Illegal entry in ToUnicode CMap");
+      return;
+    }
+    map[code] = u + offset;
+  } else {
+    if (sMapLen >= sMapSize) {
+      sMapSize = sMapSize + 16;
+      sMap = (CharCodeToUnicodeString *)
+              grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+    }
+    map[code] = 0;
+    sMap[sMapLen].c = code;
+    sMap[sMapLen].len = n / 4;
+    for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
+      strncpy(uHex, uStr + j*4, 4);
+      uHex[4] = '\0';
+      if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
+       error(-1, "Illegal entry in ToUnicode CMap");
+      }
+    }
+    sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
+    ++sMapLen;
+  }
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
   CharCode i;
 
-  collection = collectionA;
+  tag = tagA;
   mapLen = 256;
   map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
   for (i = 0; i < mapLen; ++i) {
@@ -274,13 +364,16 @@ CharCodeToUnicode::CharCodeToUnicode(GString *collectionA) {
   sMap = NULL;
   sMapLen = sMapSize = 0;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
-CharCodeToUnicode::CharCodeToUnicode(GString *collectionA, Unicode *mapA,
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
                                     CharCode mapLenA, GBool copyMap,
                                     CharCodeToUnicodeString *sMapA,
-                                    int sMapLenA) {
-  collection = collectionA;
+                                    int sMapLenA, int sMapSizeA) {
+  tag = tagA;
   mapLen = mapLenA;
   if (copyMap) {
     map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
@@ -289,32 +382,75 @@ CharCodeToUnicode::CharCodeToUnicode(GString *collectionA, Unicode *mapA,
     map = mapA;
   }
   sMap = sMapA;
-  sMapLen = sMapSize = sMapLenA;
+  sMapLen = sMapLenA;
+  sMapSize = sMapSizeA;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 CharCodeToUnicode::~CharCodeToUnicode() {
-  if (collection) {
-    delete collection;
+  if (tag) {
+    delete tag;
   }
   gfree(map);
   if (sMap) {
     gfree(sMap);
   }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
 }
 
 void CharCodeToUnicode::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
   ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
 }
 
 void CharCodeToUnicode::decRefCnt() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
 
-GBool CharCodeToUnicode::match(GString *collectionA) {
-  return collection && !collection->cmp(collectionA);
+GBool CharCodeToUnicode::match(GString *tagA) {
+  return tag && !tag->cmp(tagA);
+}
+
+void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
+  int i;
+
+  if (len == 1) {
+    map[c] = u[0];
+  } else {
+    map[c] = 0;
+    if (sMapLen == sMapSize) {
+      sMapSize += 8;
+      sMap = (CharCodeToUnicodeString *)
+              grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+    }
+    sMap[sMapLen].c = c;
+    sMap[sMapLen].len = len;
+    for (i = 0; i < len && i < maxUnicodeString; ++i) {
+      sMap[sMapLen].u[i] = u[i];
+    }
+    ++sMapLen;
+  }
 }
 
 int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
@@ -340,34 +476,37 @@ int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
 
 //------------------------------------------------------------------------
 
-CIDToUnicodeCache::CIDToUnicodeCache() {
+CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
   int i;
 
-  for (i = 0; i < cidToUnicodeCacheSize; ++i) {
+  size = sizeA;
+  cache = (CharCodeToUnicode **)gmalloc(size * sizeof(CharCodeToUnicode *));
+  for (i = 0; i < size; ++i) {
     cache[i] = NULL;
   }
 }
 
-CIDToUnicodeCache::~CIDToUnicodeCache() {
+CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
   int i;
 
-  for (i = 0; i < cidToUnicodeCacheSize; ++i) {
+  for (i = 0; i < size; ++i) {
     if (cache[i]) {
       cache[i]->decRefCnt();
     }
   }
+  gfree(cache);
 }
 
-CharCodeToUnicode *CIDToUnicodeCache::getCIDToUnicode(GString *collection) {
+CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
   CharCodeToUnicode *ctu;
   int i, j;
 
-  if (cache[0] && cache[0]->match(collection)) {
+  if (cache[0] && cache[0]->match(tag)) {
     cache[0]->incRefCnt();
     return cache[0];
   }
-  for (i = 1; i < cidToUnicodeCacheSize; ++i) {
-    if (cache[i] && cache[i]->match(collection)) {
+  for (i = 1; i < size; ++i) {
+    if (cache[i] && cache[i]->match(tag)) {
       ctu = cache[i];
       for (j = i; j >= 1; --j) {
        cache[j] = cache[j - 1];
@@ -377,16 +516,18 @@ CharCodeToUnicode *CIDToUnicodeCache::getCIDToUnicode(GString *collection) {
       return ctu;
     }
   }
-  if ((ctu = CharCodeToUnicode::parseCIDToUnicode(collection))) {
-    if (cache[cidToUnicodeCacheSize - 1]) {
-      cache[cidToUnicodeCacheSize - 1]->decRefCnt();
-    }
-    for (j = cidToUnicodeCacheSize - 1; j >= 1; --j) {
-      cache[j] = cache[j - 1];
-    }
-    cache[0] = ctu;
-    ctu->incRefCnt();
-    return ctu;
-  }
   return NULL;
 }
+
+void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) {
+  int i;
+
+  if (cache[size - 1]) {
+    cache[size - 1]->decRefCnt();
+  }
+  for (i = size - 1; i >= 1; --i) {
+    cache[i] = cache[i - 1];
+  }
+  cache[0] = ctu;
+  ctu->incRefCnt();
+}
index b20f323c14fe6c913bd3832dbb6386d84cb81f4f..605e2bd19d9b29eeaaea560bd1b05d231596b24b 100644 (file)
 
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 struct CharCodeToUnicodeString;
 
 //------------------------------------------------------------------------
@@ -26,10 +30,16 @@ struct CharCodeToUnicodeString;
 class CharCodeToUnicode {
 public:
 
-  // Create the CID-to-Unicode mapping specified by <collection>.
-  // This reads a .cidToUnicode file from disk.  Sets the initial
-  // reference count to 1.  Returns NULL on failure.
-  static CharCodeToUnicode *parseCIDToUnicode(GString *collectionA);
+  // Read the CID-to-Unicode mapping for <collection> from the file
+  // specified by <fileName>.  Sets the initial reference count to 1.
+  // Returns NULL on failure.
+  static CharCodeToUnicode *parseCIDToUnicode(GString *fileName,
+                                             GString *collection);
+
+  // Create a Unicode-to-Unicode mapping from the file specified by
+  // <fileName>.  Sets the initial reference count to 1.  Returns NULL
+  // on failure.
+  static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName);
 
   // Create the CharCode-to-Unicode mapping for an 8-bit font.
   // <toUnicode> is an array of 256 Unicode indexes.  Sets the initial
@@ -39,13 +49,20 @@ public:
   // Parse a ToUnicode CMap for an 8- or 16-bit font.
   static CharCodeToUnicode *parseCMap(GString *buf, int nBits);
 
+  // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into
+  // <this>.
+  void mergeCMap(GString *buf, int nBits);
+
   ~CharCodeToUnicode();
 
   void incRefCnt();
   void decRefCnt();
 
-  // Return true if this mapping matches the specified <collectionA>.
-  GBool match(GString *collectionA);
+  // Return true if this mapping matches the specified <tagA>.
+  GBool match(GString *tagA);
+
+  // Set the mapping for <c>.
+  void setMapping(CharCode c, Unicode *u, int len);
 
   // Map a CharCode to Unicode.
   int mapToUnicode(CharCode c, Unicode *u, int size);
@@ -53,38 +70,44 @@ public:
 private:
 
   void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
-  CharCodeToUnicode(GString *collectionA);
-  CharCodeToUnicode(GString *collectionA, Unicode *mapA,
+  void addMapping(CharCode code, char *uStr, int n, int offset);
+  CharCodeToUnicode(GString *tagA);
+  CharCodeToUnicode(GString *tagA, Unicode *mapA,
                    CharCode mapLenA, GBool copyMap,
-                   CharCodeToUnicodeString *sMapA, int sMapLenA);
+                   CharCodeToUnicodeString *sMapA,
+                   int sMapLenA, int sMapSizeA);
 
-  GString *collection;
+  GString *tag;
   Unicode *map;
   CharCode mapLen;
   CharCodeToUnicodeString *sMap;
   int sMapLen, sMapSize;
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
 
-#define cidToUnicodeCacheSize 4
-
-class CIDToUnicodeCache {
+class CharCodeToUnicodeCache {
 public:
 
-  CIDToUnicodeCache();
-  ~CIDToUnicodeCache();
+  CharCodeToUnicodeCache(int sizeA);
+  ~CharCodeToUnicodeCache();
+
+  // Get the CharCodeToUnicode object for <tag>.  Increments its
+  // reference count; there will be one reference for the cache plus
+  // one for the caller of this function.  Returns NULL on failure.
+  CharCodeToUnicode *getCharCodeToUnicode(GString *tag);
 
-  // Get the CharCodeToUnicode object for <collection>.  Increments
-  // its reference count; there will be one reference for the cache
-  // plus one for the caller of this function.  Returns NULL on
-  // failure.
-  CharCodeToUnicode *getCIDToUnicode(GString *collection);
+  // Insert <ctu> into the cache, in the most-recently-used position.
+  void add(CharCodeToUnicode *ctu);
 
 private:
 
-  CharCodeToUnicode *cache[cidToUnicodeCacheSize];
+  CharCodeToUnicode **cache;
+  int size;
 };
 
 #endif
index dca3762c1b37c96458fca0c47f05bfc2d67a7f34..dab075093c3b7ab5a16516a9026fb8d2952de89b 100644 (file)
@@ -74,6 +74,7 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
   int len, i, j;
 
   // try using the supplied owner password to generate the user password
+  *ownerPasswordOk = gFalse;
   if (ownerPassword) {
     len = ownerPassword->getLength();
     if (len < 32) {
@@ -82,43 +83,40 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
     } else {
       memcpy(test, ownerPassword->getCString(), 32);
     }
-  } else {
-    memcpy(test, passwordPad, 32);
-  }
-  md5(test, 32, test);
-  if (encRevision == 3) {
-    for (i = 0; i < 50; ++i) {
-      md5(test, 16, test);
-    }
-  }
-  if (encRevision == 2) {
-    rc4InitKey(test, keyLength, fState);
-    fx = fy = 0;
-    for (i = 0; i < 32; ++i) {
-      test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
-    }
-  } else {
-    memcpy(test2, ownerKey->getCString(), 32);
-    for (i = 19; i >= 0; --i) {
-      for (j = 0; j < keyLength; ++j) {
-       tmpKey[j] = test[j] ^ i;
+    md5(test, 32, test);
+    if (encRevision == 3) {
+      for (i = 0; i < 50; ++i) {
+       md5(test, 16, test);
       }
-      rc4InitKey(tmpKey, keyLength, fState);
+    }
+    if (encRevision == 2) {
+      rc4InitKey(test, keyLength, fState);
       fx = fy = 0;
-      for (j = 0; j < 32; ++j) {
-       test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+      for (i = 0; i < 32; ++i) {
+       test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+      }
+    } else {
+      memcpy(test2, ownerKey->getCString(), 32);
+      for (i = 19; i >= 0; --i) {
+       for (j = 0; j < keyLength; ++j) {
+         tmpKey[j] = test[j] ^ i;
+       }
+       rc4InitKey(tmpKey, keyLength, fState);
+       fx = fy = 0;
+       for (j = 0; j < 32; ++j) {
+         test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+       }
       }
     }
-  }
-  userPassword2 = new GString((char *)test2, 32);
-  if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-                  permissions, fileID, userPassword2, fileKey)) {
-    *ownerPasswordOk = gTrue;
+    userPassword2 = new GString((char *)test2, 32);
+    if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+                    permissions, fileID, userPassword2, fileKey)) {
+      *ownerPasswordOk = gTrue;
+      delete userPassword2;
+      return gTrue;
+    }
     delete userPassword2;
-    return gTrue;
   }
-  *ownerPasswordOk = gFalse;
-  delete userPassword2;
 
   // try using the supplied user password
   return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
index 9575e4cbf108f523870fca77fba975ddb45e7a1a..627459078a0a7c8fcb5d315f0cfae38d75471e70 100644 (file)
@@ -41,8 +41,12 @@ Dict::~Dict() {
 }
 
 void Dict::add(char *key, Object *val) {
-  if (length + 1 > size) {
-    size += 8;
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
     entries = (DictEntry *)grealloc(entries, size * sizeof(DictEntry));
   }
   entries[length].key = key;
index 8de2b01b880a188649bcdfe7176cd9418fe9c720..6eb14350c77451deeb5863e7a1aaf23bab1c7fa0 100644 (file)
 
 #define errHighlightFile    5  // nonexistent or invalid highlight file
 
+#define errBadPrinter       6   // invalid printer
+
+#define errPrinting         7   // error during printing
+
+#define errPermission       8  // PDF file doesn't allow that operation
+
+#define errBadPageNum       9  // invalid page number
+
 #endif
index c360eb7540c18b80bb365169712f6c9757f71eb2..1a5ecfa1bf014ea389bf7474aa7f2fdea6a4264f 100644 (file)
@@ -48,11 +48,9 @@ FTFontEngine::~FTFontEngine() {
 //------------------------------------------------------------------------
 
 FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName,
-                      char **fontEnc, GBool pdfFontHasEncoding,
-                      GBool pdfFontIsSymbolic) {
+                      char **fontEnc, Gushort *codeToGID) {
   char *name;
-  int unicodeCmap, macRomanCmap, msSymbolCmap;
-  int i, j;
+  int i;
 
   ok = gFalse;
   engine = engineA;
@@ -66,7 +64,6 @@ FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName,
 
   if (!strcmp(face->driver->root.clazz->module_name, "type1") ||
       !strcmp(face->driver->root.clazz->module_name, "cff")) {
-
     mode = ftFontModeCodeMapDirect;
     codeMap = (Guint *)gmalloc(256 * sizeof(Guint));
     for (i = 0; i < 256; ++i) {
@@ -77,73 +74,10 @@ FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName,
     }
 
   } else {
-
-    // To match up with the Adobe-defined behaviour, we choose a cmap
-    // like this:
-    // 1. If the PDF font has an encoding:
-    //    1a. If the TrueType font has a Microsoft Unicode cmap, use it,
-    //        and use the Unicode indexes, not the char codes.
-    //    1b. If the PDF font is symbolic and the TrueType font has a
-    //        Microsoft Symbol cmap, use it, and use (0xf000 + char code).
-    //    1c. If the TrueType font has a Macintosh Roman cmap, use it,
-    //        and reverse map the char names through MacRomanEncoding to
-    //        get char codes.
-    // 2. If the PDF font does not have an encoding:
-    //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
-    //        and use char codes directly.
-    //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
-    //        and use (0xf000 + char code).
-    // 3. If none of these rules apply, use the first cmap and hope for
-    //    the best (this shouldn't happen).
-    unicodeCmap = macRomanCmap = msSymbolCmap = 0xffff;
-    for (i = 0; i < face->num_charmaps; ++i) {
-      if ((face->charmaps[i]->platform_id == 3 &&
-          face->charmaps[i]->encoding_id == 1) ||
-         face->charmaps[i]->platform_id == 0) {
-       unicodeCmap = i;
-      } else if (face->charmaps[i]->platform_id == 1 &&
-                face->charmaps[i]->encoding_id == 0) {
-       macRomanCmap = i;
-      } else if (face->charmaps[i]->platform_id == 3 &&
-                face->charmaps[i]->encoding_id == 0) {
-       msSymbolCmap = i;
-      }
-    }
-    i = 0;
-    mode = ftFontModeCharCode;
-    charMapOffset = 0;
-    if (pdfFontHasEncoding) {
-      if (unicodeCmap != 0xffff) {
-       i = unicodeCmap;
-       mode = ftFontModeUnicode;
-      } else if (pdfFontIsSymbolic && msSymbolCmap != 0xffff) {
-       i = msSymbolCmap;
-       mode = ftFontModeCharCodeOffset;
-       charMapOffset = 0xf000;
-      } else if (macRomanCmap != 0xffff) {
-       i = macRomanCmap;
-       mode = ftFontModeCodeMap;
-       codeMap = (Guint *)gmalloc(256 * sizeof(Guint));
-       for (j = 0; j < 256; ++j) {
-         if (fontEnc[j]) {
-           codeMap[j] = globalParams->getMacRomanCharCode(fontEnc[j]);
-         } else {
-           codeMap[j] = 0;
-         }
-       }
-      }
-    } else {
-      if (macRomanCmap != 0xffff) {
-       i = macRomanCmap;
-       mode = ftFontModeCharCode;
-      } else if (msSymbolCmap != 0xffff) {
-       i = msSymbolCmap;
-       mode = ftFontModeCharCodeOffset;
-       charMapOffset = 0xf000;
-      }
-    }
-    if (FT_Set_Charmap(face, face->charmaps[i])) {
-      return;
+    mode = ftFontModeCodeMapDirect;
+    codeMap = (Guint *)gmalloc(256 * sizeof(Guint));
+    for (i = 0; i < 256; ++i) {
+      codeMap[i] = (int)codeToGID[i];
     }
   }
 
@@ -558,7 +492,9 @@ Guchar *FTFont::getGlyphPixmap(CharCode c, Unicode u,
   idx = getGlyphIndex(c, u);
   // if we have the FT2 bytecode interpreter, autohinting won't be used
 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
-  if (FT_Load_Glyph(fontFile->face, idx, FT_LOAD_DEFAULT)) {
+  if (FT_Load_Glyph(fontFile->face, idx,
+                   fontFile->engine->aa ? FT_LOAD_NO_BITMAP
+                                        : FT_LOAD_DEFAULT)) {
     return gFalse;
   }
 #else
@@ -567,8 +503,8 @@ Guchar *FTFont::getGlyphPixmap(CharCode c, Unicode u,
   // anti-aliasing is disabled, this seems to be a tossup - some fonts
   // look better with hinting, some without, so leave hinting on
   if (FT_Load_Glyph(fontFile->face, idx,
-                   fontFile->engine->aa ? FT_LOAD_NO_HINTING
-                                        : FT_LOAD_DEFAULT)) {
+               fontFile->engine->aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP
+                                    : FT_LOAD_DEFAULT)) {
     return gFalse;
   }
 #endif
@@ -721,22 +657,6 @@ FT_UInt FTFont::getGlyphIndex(CharCode c, Unicode u) {
   case ftFontModeUnicode:
     idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)u);
     break;
-  case ftFontModeCharCode:
-    idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)c);
-    break;
-  case ftFontModeCharCodeOffset:
-    if ((idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)c)) == 0) {
-      idx = FT_Get_Char_Index(fontFile->face,
-                             (FT_ULong)(c + fontFile->charMapOffset));
-    }
-    break;
-  case ftFontModeCodeMap:
-    if (c <= 0xff) {
-      idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)fontFile->codeMap[c]);
-    } else {
-      idx = 0;
-    }
-    break;
   case ftFontModeCodeMapDirect:
     if (c <= 0xff) {
       idx = (FT_UInt)fontFile->codeMap[c];
index 52bc149b180a3d8f74fc7763ae7f6441b3bc1173..8da5558219e30bcf1a1cca2e627150646f7b7a46 100644 (file)
@@ -48,9 +48,6 @@ private:
 
 enum FTFontIndexMode {
   ftFontModeUnicode,
-  ftFontModeCharCode,
-  ftFontModeCharCodeOffset,
-  ftFontModeCodeMap,
   ftFontModeCodeMapDirect,
   ftFontModeCIDToGIDMap,
   ftFontModeCFFCharset,
@@ -62,8 +59,7 @@ public:
 
   // 8-bit font, TrueType or Type 1/1C
   FTFontFile(FTFontEngine *engineA, char *fontFileName,
-            char **fontEnc, GBool pdfFontHasEncoding,
-            GBool pdfFontIsSymbolic);
+            char **fontEnc, Gushort *codeToGID);
 
   // CID font, TrueType
   FTFontFile(FTFontEngine *engineA, char *fontFileName,
@@ -81,7 +77,6 @@ private:
   FTFontEngine *engine;
   FT_Face face;
   FTFontIndexMode mode;
-  int charMapOffset;
   Guint *codeMap;
   Gushort *cidToGID;
   int cidToGIDLen;
index fde0ae6e41dd0596111105216c788e9883c3db0f..0c44422906a767c670d989875312fcc5a504cd0d 100644 (file)
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <ctype.h>
 #include "gmem.h"
+#include "GHash.h"
 #include "Error.h"
 #include "GlobalParams.h"
 #include "CharCodeToUnicode.h"
@@ -33,7 +34,7 @@
 static inline char *nextLine(char *line, char *end) {
   while (line < end && *line != '\n' && *line != '\r')
     ++line;
-  while (line < end && *line == '\n' || *line == '\r')
+  while (line < end && (*line == '\n' || *line == '\r'))
     ++line;
   return line;
 }
@@ -354,10 +355,12 @@ void Type1CFontFile::readEncoding() {
        c = file[pos++];
        nLeft = file[pos++];
        for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
-         if (encoding[c]) {
-           gfree(encoding[c]);
+         if (c < 256) {
+           if (encoding[c]) {
+             gfree(encoding[c]);
+           }
+           encoding[c] = copyString(getString(glyphNames[nCodes], buf));
          }
-         encoding[c] = copyString(getString(glyphNames[nCodes], buf));
          ++nCodes;
          ++c;
        }
@@ -415,7 +418,10 @@ void Type1CFontFile::convertToType1(FontFileOutputFunc outputFuncA,
     (*outputFunc)(outputStream, buf, strlen(buf));
   }
   (*outputFunc)(outputStream, "\n", 1);
-  (*outputFunc)(outputStream, "11 dict begin\n", 14);
+  // the dictionary needs room for 12 entries: the following 9, plus
+  // Private and CharStrings (in the eexec section) and FID (which is
+  // added by definefont)
+  (*outputFunc)(outputStream, "12 dict begin\n", 14);
   (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
   if (dict.version != 0) {
     (*outputFunc)(outputStream, "/version (", 10);
@@ -590,7 +596,8 @@ void Type1CFontFile::convertToType1(FontFileOutputFunc outputFuncA,
   eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n");
   eexecWrite("/ND {noaccess def} executeonly def\n");
   eexecWrite("/NP {noaccess put} executeonly def\n");
-  eexecWrite("/MinFeature {16 16} ND\n");
+  eexecWrite("/MinFeature {16 16} def\n");
+  eexecWrite("/password 5839 def\n");
   readPrivateDict(&privateDict, dict.privateOffset, dict.privateSize);
   eexecWrite(privateDict.dictData->getCString());
   defaultWidthX = privateDict.defaultWidthX;
@@ -599,7 +606,7 @@ void Type1CFontFile::convertToType1(FontFileOutputFunc outputFuncA,
   nominalWidthXFP = privateDict.nominalWidthXFP;
 
   // set up subroutines
-  subrIdxPos = dict.privateOffset + privateDict.subrsOffset;
+  subrIdxPos = privateDict.subrsOffset;
   i = getIndexLen(gsubrIdxPos);
   gsubrBias = (i < 1240) ? 107 : (i < 33900) ? 1131 : 32768;
   i = getIndexLen(subrIdxPos);
@@ -806,7 +813,7 @@ void Type1CFontFile::convertToCIDType0(char *psName,
        defaultWidthXFP = privateDicts[j].defaultWidthXFP;
        nominalWidthX = privateDicts[j].nominalWidthX;
        nominalWidthXFP = privateDicts[j].nominalWidthXFP;
-       subrIdxPos = dict.privateOffset + privateDicts[j].subrsOffset;
+       subrIdxPos = privateDicts[j].subrsOffset;
        k = getIndexLen(subrIdxPos);
        subrBias = (k < 1240) ? 107 : (k < 33900) ? 1131 : 32768;
        cvtGlyph(idxPos, idxLen, gTrue);
@@ -1148,6 +1155,10 @@ void Type1CFontFile::convertToType0(char *psName,
       sprintf(buf, "dup %d /c%02x put\n", j, j);
       (*outputFunc)(outputStream, buf, strlen(buf));
     }
+    if (j < 256) {
+      sprintf(buf, "%d 1 255 { 1 index exch /.notdef put } for\n", j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
     (*outputFunc)(outputStream, "readonly def\n", 13);
     (*outputFunc)(outputStream, "currentdict end\n", 16);
 
@@ -1162,7 +1173,8 @@ void Type1CFontFile::convertToType0(char *psName,
     eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n");
     eexecWrite("/ND {noaccess def} executeonly def\n");
     eexecWrite("/NP {noaccess put} executeonly def\n");
-    eexecWrite("/MinFeature {16 16} ND\n");
+    eexecWrite("/MinFeature {16 16} def\n");
+    eexecWrite("/password 5839 def\n");
     eexecWrite(privateDicts[fd].dictData->getCString());
     defaultWidthX = privateDicts[fd].defaultWidthX;
     defaultWidthXFP = privateDicts[fd].defaultWidthXFP;
@@ -1170,7 +1182,7 @@ void Type1CFontFile::convertToType0(char *psName,
     nominalWidthXFP = privateDicts[fd].nominalWidthXFP;
 
     // set up the subroutines
-    subrIdxPos = dict.privateOffset + privateDicts[fd].subrsOffset;
+    subrIdxPos = privateDicts[fd].subrsOffset;
     j = getIndexLen(subrIdxPos);
     subrBias = (j < 1240) ? 107 : (j < 33900) ? 1131 : 32768;
 
@@ -1445,7 +1457,7 @@ void Type1CFontFile::readPrivateDict(Type1CPrivateDict *privateDict,
        error(-1, "Got Type 1C InitialRandomSeed");
        break;
       case 0x0013:
-       privateDict->subrsOffset = (int)op[0];
+       privateDict->subrsOffset = offset + (int)op[0];
        break;
       case 0x0014:
        privateDict->defaultWidthX = op[0];
@@ -1711,7 +1723,7 @@ void Type1CFontFile::cvtGlyph(int pos, int n, GBool top) {
     } else if (file[i] == 19) {        // hintmask
       // ignored
       if (firstOp) {
-       cvtGlyphWidth(nOps == 1);
+       cvtGlyphWidth(nOps & 1);
        firstOp = gFalse;
       }
       if (nOps > 0) {
@@ -1726,7 +1738,7 @@ void Type1CFontFile::cvtGlyph(int pos, int n, GBool top) {
     } else if (file[i] == 20) {        // cntrmask
       // ignored
       if (firstOp) {
-       cvtGlyphWidth(nOps == 1);
+       cvtGlyphWidth(nOps & 1);
        firstOp = gFalse;
       }
       if (nOps > 0) {
@@ -2092,6 +2104,13 @@ void Type1CFontFile::cvtGlyph(int pos, int n, GBool top) {
        }
        nHints += nOps / 2;
        break;
+      case 15:                 // (obsolete)
+       // this op is ignored, but we need the glyph width
+       if (firstOp) {
+         cvtGlyphWidth(nOps > 0);
+         firstOp = gFalse;
+       }
+       break;
       case 18:                 // hstemhm
        // ignored
        if (firstOp) {
@@ -2343,7 +2362,7 @@ int Type1CFontFile::getIndexValPos(int indexPos, int i, int *valLen) {
 int Type1CFontFile::getIndexEnd(int indexPos) {
   int n, offSize, idxStartPos;
 
-  if (indexPos + 3 > len) {
+  if (indexPos < 0 || indexPos + 3 > len) {
     return -1;
   }
   n = (int)getWord(indexPos, 2);
@@ -2545,6 +2564,14 @@ struct T42Table {
   GBool required;              // required by the TrueType spec?
 };
 
+struct TTFontCmap {
+  int platform;
+  int encoding;
+  int offset;
+  int len;
+  int fmt;
+};
+
 // TrueType tables to be embedded in Type 42 fonts.
 // NB: the table names must be in alphabetical order here.
 #define nT42Tables 11
@@ -2828,13 +2855,6 @@ static char *macGlyphNames[258] = {
   "dmacron"
 };
 
-enum T42FontIndexMode {
-  t42FontModeUnicode,
-  t42FontModeCharCode,
-  t42FontModeCharCodeOffset,
-  t42FontModeMacRoman
-};
-
 struct TrueTypeLoca {
   int idx;
   int pos;
@@ -2842,13 +2862,15 @@ struct TrueTypeLoca {
 };
 
 TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) {
-  int pos, i, idx, n, length;
+  int pos, pos2, i, idx, n, length;
   Guint size, startPos, endPos;
 
   file = fileA;
   len = lenA;
 
   encoding = NULL;
+  cmaps = NULL;
+  nCmaps = 0;
 
   // read table directory
   nTables = getUShort(4);
@@ -2917,6 +2939,23 @@ TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) {
   // read the 'maxp' table
   pos = seekTable("maxp");
   nGlyphs = getUShort(pos + 4);
+
+  // read the 'cmap' table
+  if ((pos = seekTable("cmap")) >= 0) {
+    pos2 = pos + 2;
+    if ((nCmaps = getUShort(pos2)) > 0) {
+      pos2 += 2;
+      cmaps = (TTFontCmap *)gmalloc(nCmaps * sizeof(TTFontCmap));
+      for (i = 0; i < nCmaps; ++i) {
+       cmaps[i].platform = getUShort(pos2);
+       cmaps[i].encoding = getUShort(pos2 + 2);
+       cmaps[i].offset = pos + getULong(pos2 + 4);
+       pos2 += 8;
+       cmaps[i].fmt = getUShort(cmaps[i].offset);
+       cmaps[i].len = getUShort(cmaps[i].offset + 2);
+      }
+    }
+  }
 }
 
 TrueTypeFontFile::~TrueTypeFontFile() {
@@ -2928,6 +2967,9 @@ TrueTypeFontFile::~TrueTypeFontFile() {
     }
     gfree(encoding);
   }
+  if (cmaps) {
+    gfree(cmaps);
+  }
   gfree(tableHdrs);
 }
 
@@ -2936,146 +2978,117 @@ char *TrueTypeFontFile::getName() {
 }
 
 char **TrueTypeFontFile::getEncoding() {
-  int cmap[256];
-  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt;
-  int pos, i, j;
-  Guint fmt;
-  GString *s;
-  int stringIdx, stringPos, n;
+  int i;
 
-  if (encoding) {
-    return encoding;
+  if (!encoding) {
+    encoding = (char **)gmalloc(256 * sizeof(char *));
+    for (i = 0; i < 256; ++i) {
+      encoding[i] = NULL;
+    }
   }
+  return encoding;
+}
 
-  //----- construct the (char code) -> (glyph idx) mapping
+int TrueTypeFontFile::getNumCmaps() {
+  return nCmaps;
+}
 
-  // map everything to the missing glyph
-  for (i = 0; i < 256; ++i) {
-    cmap[i] = 0;
-  }
+int TrueTypeFontFile::getCmapPlatform(int i) {
+  return cmaps[i].platform;
+}
 
-  // look for the 'cmap' table
-  if ((pos = seekTable("cmap")) >= 0) {
-    nCmaps = getUShort(pos+2);
-
-    // if the font has a Windows-symbol cmap, use it;
-    // otherwise, use the first cmap in the table
-    cmapPlatform = 0; // make gcc happy
-    cmapEncoding = 0; // make gcc happy
-    for (i = 0; i < nCmaps; ++i) {
-      cmapPlatform = getUShort(pos + 4 + 8*i);
-      cmapEncoding = getUShort(pos + 4 + 8*i + 2);
-      if (cmapPlatform == 3 && cmapEncoding == 0) {
-       break;
-      }
-    }
-    if (i >= nCmaps) {
-      i = 0;
-      cmapPlatform = getUShort(pos + 4);
-      cmapEncoding = getUShort(pos + 4 + 2);
-    }
-    pos += getULong(pos + 4 + 8*i + 4);
+int TrueTypeFontFile::getCmapEncoding(int i) {
+  return cmaps[i].encoding;
+}
 
-    // read the cmap
-    cmapFmt = getUShort(pos);
-    for (i = 0; i < 256; ++i) {
-      cmap[i] = getCmapEntry(cmapFmt, pos, i);
-    }
-    // Windows-symbol sometimes uses char codes 0xf000 - 0xf0ff, so
-    // we use these to override 0x0000 - 0x00ff
-    if (cmapPlatform == 3 && cmapEncoding == 0) {
-      for (i = 0; i < 256; ++i) {
-       if ((j = getCmapEntry(cmapFmt, pos, 0xf000 + i)) != 0) {
-         cmap[i] = j;
-       }
-      }
+int TrueTypeFontFile::findCmap(int platform, int enc) {
+  int i;
+
+  for (i = 0; i < nCmaps; ++i) {
+    if (cmaps[i].platform == platform && cmaps[i].encoding == enc) {
+      return i;
     }
   }
+  return -1;
+}
+
+Gushort TrueTypeFontFile::mapCodeToGID(int i, int c) {
+  if (i < 0 || i >= nCmaps) {
+    return 0;
+  }
+  return (Gushort)getCmapEntry(cmaps[i].fmt, cmaps[i].offset, c);
+}
 
-  //----- construct the (glyph idx) -> (glyph name) mapping
-  //----- and compute the (char code) -> (glyph name) mapping
+GHash *TrueTypeFontFile::getNameToGID() {
+  GHash *nameToGID;
+  Guint fmt;
+  GString *s;
+  int stringIdx, stringPos, pos, i, j, n;
 
-  encoding = (char **)gmalloc(256 * sizeof(char *));
-  for (i = 0; i < 256; ++i) {
-    encoding[i] = NULL;
+  if ((pos = seekTable("post")) < 0) {
+    return NULL;
   }
 
-  if ((pos = seekTable("post")) >= 0) {
-    fmt = getULong(pos);
+  fmt = getULong(pos);
+  nameToGID = NULL;
 
-    // Apple font
-    if (fmt == 0x00010000) {
-      for (i = 0; i < 256; ++i) {
-       j = (cmap[i] < 258) ? cmap[i] : 0;
-       encoding[i] = copyString(macGlyphNames[j]);
-      }
+  // Apple font
+  if (fmt == 0x00010000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < 258; ++i) {
+      nameToGID->add(new GString(macGlyphNames[i]), (void *)i);
+    }
 
-    // Microsoft font
-    } else if (fmt == 0x00020000) {
-      stringIdx = 0;
-      stringPos = pos + 34 + 2*nGlyphs;
-      for (i = 0; i < 256; ++i) {
-       if (cmap[i] < nGlyphs) {
-         j = getUShort(pos + 34 + 2 * cmap[i]);
-         if (j < 258) {
-           encoding[i] = copyString(macGlyphNames[j]);
-         } else {
-           j -= 258;
-           if (j != stringIdx) {
-             for (stringIdx = 0, stringPos = pos + 34 + 2*nGlyphs;
-                  stringIdx < j;
-                  ++stringIdx, stringPos += 1 + getByte(stringPos)) ;
-           }
-           n = getByte(stringPos);
-           if (stringPos >= 0 && stringPos + 1 + n <= len) {
-             s = new GString(file + stringPos + 1, n);
-             encoding[i] = copyString(s->getCString());
-             delete s;
-           } else {
-             encoding[i] = copyString(macGlyphNames[0]);
-           }
-           ++stringIdx;
-           stringPos += 1 + n;
-         }
-       } else {
-         encoding[i] = copyString(macGlyphNames[0]);
+  // Microsoft font
+  } else if (fmt == 0x00020000) {
+    nameToGID = new GHash(gTrue);
+    n = getUShort(pos + 32);
+    if (n > nGlyphs) {
+      n = nGlyphs;
+    }
+    stringIdx = 0;
+    stringPos = pos + 34 + 2*nGlyphs;
+    for (i = 0; i < nGlyphs; ++i) {
+      j = getUShort(pos + 34 + 2*i);
+      if (j < 258) {
+       nameToGID->remove(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), (void *)i);
+      } else {
+       j -= 258;
+       if (j != stringIdx) {
+         for (stringIdx = 0, stringPos = pos + 34 + 2*nGlyphs;
+              stringIdx < j;
+              ++stringIdx, stringPos += 1 + getByte(stringPos)) ;
        }
-      }
-
-    // Apple subset
-    } else if (fmt == 0x000280000) {
-      for (i = 0; i < 256; ++i) {
-       if (cmap[i] < nGlyphs) {
-         j = i + getChar(pos + 32 + cmap[i]);
-       } else {
-         j = 0;
+       n = getByte(stringPos);
+       if (stringPos >= 0 && stringPos + 1 + n <= len) {
+         s = new GString(file + stringPos + 1, n);
+         nameToGID->remove(s);
+         nameToGID->add(s, (void *)i);
        }
-       encoding[i] = copyString(macGlyphNames[j]);
-      }
-
-    // Ugh, just assume the Apple glyph set
-    } else {
-      for (i = 0; i < 256; ++i) {
-       j = (cmap[i] < 258) ? cmap[i] : 0;
-       encoding[i] = copyString(macGlyphNames[j]);
+       ++stringIdx;
+       stringPos += 1 + n;
       }
     }
 
-  // no "post" table: assume the Apple glyph set
-  } else {
-    for (i = 0; i < 256; ++i) {
-      j = (cmap[i] < 258) ? cmap[i] : 0;
-      encoding[i] = copyString(macGlyphNames[j]);
+  // Apple subset
+  } else if (fmt == 0x000280000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < nGlyphs; ++i) {
+      j = getByte(pos + 32 + i);
+      if (j < 258) {
+       nameToGID->remove(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), (void *)i);
+      }
     }
   }
 
-  return encoding;
+  return nameToGID;
 }
 
 void TrueTypeFontFile::convertToType42(char *name, char **encodingA,
-                                      CharCodeToUnicode *toUnicode,
                                       GBool pdfFontHasEncoding,
-                                      GBool pdfFontIsSymbolic,
+                                      Gushort *codeToGID,
                                       FontFileOutputFunc outputFunc,
                                       void *outputStream) {
   char buf[512];
@@ -3098,7 +3111,7 @@ void TrueTypeFontFile::convertToType42(char *name, char **encodingA,
 
   // write the guts of the dictionary
   cvtEncoding(encodingA, pdfFontHasEncoding, outputFunc, outputStream);
-  cvtCharStrings(encodingA, toUnicode, pdfFontHasEncoding, pdfFontIsSymbolic,
+  cvtCharStrings(encodingA, pdfFontHasEncoding, codeToGID,
                 outputFunc, outputStream);
   cvtSfnts(outputFunc, outputStream, NULL);
 
@@ -3403,96 +3416,26 @@ void TrueTypeFontFile::cvtEncoding(char **encodingA, GBool pdfFontHasEncoding,
 }
 
 void TrueTypeFontFile::cvtCharStrings(char **encodingA,
-                                     CharCodeToUnicode *toUnicode,
                                      GBool pdfFontHasEncoding,
-                                     GBool pdfFontIsSymbolic,
+                                     Gushort *codeToGID,
                                      FontFileOutputFunc outputFunc,
                                      void *outputStream) {
-  int unicodeCmap, macRomanCmap, msSymbolCmap;
-  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapOffset;
-  T42FontIndexMode mode;
   char *name;
   char buf[64], buf2[16];
-  Unicode u;
-  int pos, i, j, k;
+  int i, k;
 
   // always define '.notdef'
   (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
   (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
 
   // if there's no 'cmap' table, punt
-  if ((pos = seekTable("cmap")) < 0) {
-    goto err;
-  }
-
-  // To match up with the Adobe-defined behaviour, we choose a cmap
-  // like this:
-  // 1. If the PDF font has an encoding:
-  //    1a. If the TrueType font has a Microsoft Unicode cmap, use it,
-  //        and use the Unicode indexes, not the char codes.
-  //    1b. If the PDF font is symbolic and the TrueType font has a
-  //        Microsoft Symbol cmap, use it, and use (0xf000 + char code).
-  //    1c. If the TrueType font has a Macintosh Roman cmap, use it,
-  //        and reverse map the char names through MacRomanEncoding to
-  //        get char codes.
-  // 2. If the PDF font does not have an encoding:
-  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
-  //        and use char codes directly.
-  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
-  //        and use (0xf000 + char code).
-  // 3. If none of these rules apply, use the first cmap and hope for
-  //    the best (this shouldn't happen).
-  nCmaps = getUShort(pos+2);
-  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
-  cmapOffset = 0;
-  for (i = 0; i < nCmaps; ++i) {
-    cmapPlatform = getUShort(pos + 4 + 8*i);
-    cmapEncoding = getUShort(pos + 4 + 8*i + 2);
-    if ((cmapPlatform == 3 && cmapEncoding == 1) || cmapPlatform == 0) {
-      unicodeCmap = i;
-    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
-      macRomanCmap = i;
-    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
-      msSymbolCmap = i;
-    }
-  }
-  i = 0;
-  mode = t42FontModeCharCode;
-  if (pdfFontHasEncoding) {
-    if (unicodeCmap >= 0) {
-      i = unicodeCmap;
-      mode = t42FontModeUnicode;
-    } else if (pdfFontIsSymbolic && msSymbolCmap >= 0) {
-      i = msSymbolCmap;
-      mode = t42FontModeCharCodeOffset;
-      cmapOffset = 0xf000;
-    } else if (macRomanCmap >= 0) {
-      i = macRomanCmap;
-      mode = t42FontModeMacRoman;
-    }
-  } else {
-    if (macRomanCmap >= 0) {
-      i = macRomanCmap;
-      mode = t42FontModeCharCode;
-    } else if (msSymbolCmap >= 0) {
-      i = msSymbolCmap;
-      mode = t42FontModeCharCodeOffset;
-      cmapOffset = 0xf000;
-    }
-  }
-  cmapPlatform = getUShort(pos + 4 + 8*i);
-  cmapEncoding = getUShort(pos + 4 + 8*i + 2);
-  pos += getULong(pos + 4 + 8*i + 4);
-  cmapFmt = getUShort(pos);
-  if (cmapFmt != 0 && cmapFmt != 4 && cmapFmt != 6) {
-    error(-1, "Unimplemented cmap format (%d) in TrueType font file",
-         cmapFmt);
+  if (nCmaps == 0) {
     goto err;
   }
 
   // map char name to glyph index:
   // 1. use encoding to map name to char code
-  // 2. use cmap to map char code to glyph index
+  // 2. use codeToGID to map char code to glyph index
   // N.B. We do this in reverse order because font subsets can have
   //      weird encodings that use the same character name twice, and
   //      the first definition is probably the one we want.
@@ -3505,24 +3448,7 @@ void TrueTypeFontFile::cvtCharStrings(char **encodingA,
       name = buf2;
     }
     if (name && strcmp(name, ".notdef")) {
-      switch (mode) {
-      case t42FontModeUnicode:
-       toUnicode->mapToUnicode((CharCode)i, &u, 1);
-       k = getCmapEntry(cmapFmt, pos, (int)u);
-       break;
-      case t42FontModeCharCode:
-       k = getCmapEntry(cmapFmt, pos, i);
-       break;
-      case t42FontModeCharCodeOffset:
-       if ((k = getCmapEntry(cmapFmt, pos, cmapOffset + i)) == 0) {
-         k = getCmapEntry(cmapFmt, pos, i);
-       }
-       break;
-      case t42FontModeMacRoman:
-       j = globalParams->getMacRomanCharCode(name);
-       k = getCmapEntry(cmapFmt, pos, j);
-       break;
-      }
+      k = codeToGID[i];
       // note: Distiller (maybe Adobe's PS interpreter in general)
       // doesn't like TrueType fonts that have CharStrings entries
       // which point to nonexistent glyphs, hence the (k < nGlyphs)
@@ -3929,7 +3855,7 @@ void TrueTypeFontFile::writeTTF(FILE *out) {
   TrueTypeLoca *origLocaTable;
   char *locaTable;
   int length, glyfLength;
-  Guint t, pos, pos2;
+  Guint t, pos, pos2, pos3;
   int i, j, k;
 
   // check for missing/broken tables
@@ -3937,18 +3863,19 @@ void TrueTypeFontFile::writeTTF(FILE *out) {
   haveName = seekTable("name") >= 0;
   havePost = seekTable("post") >= 0;
   unsortedLoca = gFalse;
-  pos = 0;
+  pos = seekTable("loca");
+  pos2 = 0;
   for (i = 0; i <= nGlyphs; ++i) {
     if (locaFmt) {
-      pos2 = getULong(pos + 4*i);
+      pos3 = getULong(pos + 4*i);
     } else {
-      pos2 = 2 * getUShort(pos + 2*i);
+      pos3 = 2 * getUShort(pos + 2*i);
     }
-    if (pos2 < pos) {
+    if (pos3 < pos2) {
       unsortedLoca = gTrue;
       break;
     }
-    pos = pos2;
+    pos2 = pos3;
   }
   nNewTables = (haveCmap ? 0 : 1) + (haveName ? 0 : 1) + (havePost ? 0 : 1);
   nZeroLengthTables = 0;
index a71653c2646a898d1742561a1e2a94e92e901e6d..7aa5ba9d0064c261d70d0e772d213c0c19981e8b 100644 (file)
@@ -20,6 +20,7 @@
 #include "GString.h"
 #include "CharTypes.h"
 
+class GHash;
 class CharCodeToUnicode;
 
 //------------------------------------------------------------------------
@@ -156,6 +157,7 @@ private:
 //------------------------------------------------------------------------
 
 struct TTFontTableHdr;
+struct TTFontCmap;
 
 class TrueTypeFontFile: public FontFile {
 public:
@@ -170,15 +172,34 @@ public:
 
   virtual char **getEncoding();
 
+  // Return the number of cmaps defined by this font.
+  int getNumCmaps();
+
+  // Return the platform ID of the <i>th cmap.
+  int getCmapPlatform(int i);
+
+  // Return the encoding ID of the <i>th cmap.
+  int getCmapEncoding(int i);
+
+  // Return the index of the cmap for <platform>, <encoding>.  Returns
+  // -1 if there is no corresponding cmap.
+  int findCmap(int platform, int enc);
+
+  // Return the GID corresponding to <c> according to the <i>th cmap.
+  Gushort mapCodeToGID(int i, int c);
+
+  // Return a name-to-GID mapping, constructed from the font's post
+  // table.  Returns NULL if there is no post table.
+  GHash *getNameToGID();
+
   // Convert to a Type 42 font, suitable for embedding in a PostScript
   // file.  The name will be used as the PostScript font name (so we
   // don't need to depend on the 'name' table in the font).  The
   // encoding is needed because the PDF Font object can modify the
   // encoding.
   void convertToType42(char *name, char **encodingA,
-                      CharCodeToUnicode *toUnicode,
                       GBool pdfFontHasEncoding,
-                      GBool pdfFontIsSymbolic,
+                      Gushort *codeToGID,
                       FontFileOutputFunc outputFunc, void *outputStream);
 
   // Convert to a Type 2 CIDFont, suitable for embedding in a
@@ -213,6 +234,8 @@ private:
   int locaFmt;
   int nGlyphs;
   GBool mungedCmapSize;
+  TTFontCmap *cmaps;
+  int nCmaps;
 
   int getByte(int pos);
   int getChar(int pos);
@@ -224,8 +247,8 @@ private:
   int seekTableIdx(char *tag);
   void cvtEncoding(char **encodingA, GBool pdfFontHasEncoding,
                   FontFileOutputFunc outputFunc, void *outputStream);
-  void cvtCharStrings(char **encodingA, CharCodeToUnicode *toUnicode,
-                     GBool pdfFontHasEncoding, GBool pdfFontIsSymbolic,
+  void cvtCharStrings(char **encodingA, GBool pdfFontHasEncoding,
+                     Gushort *codeToGID,
                      FontFileOutputFunc outputFunc, void *outputStream);
   int getCmapEntry(int cmapFmt, int pos, int code);
   void cvtSfnts(FontFileOutputFunc outputFunc, void *outputStream,
index 28eed87a5e3862ea21785428adaf07756fceaa59..d9d4a934ed04ecb79de01fc6466afa49601da741 100644 (file)
@@ -381,8 +381,8 @@ void SampledFunction::transform(double *in, double *out) {
 
     // pull 2^m values out of the sample array
     for (j = 0; j < (1<<m); ++j) {
-      idx = e[j & 1][m - 1];
-      for (k = m - 2; k >= 0; --k) {
+      idx = 0;
+      for (k = m - 1; k >= 0; --k) {
        idx = idx * sampleSize[k] + e[(j >> k) & 1][k];
       }
       idx = idx * n + i;
@@ -617,9 +617,13 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
 }
 
 StitchingFunction::StitchingFunction(StitchingFunction *func) {
+  int i;
+
   k = func->k;
   funcs = (Function **)gmalloc(k * sizeof(Function *));
-  memcpy(funcs, func->funcs, k * sizeof(Function *));
+  for (i = 0; i < k; ++i) {
+    funcs[i] = func->funcs[i]->copy();
+  }
   bounds = (double *)gmalloc((k + 1) * sizeof(double));
   memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
   encode = (double *)gmalloc(2 * k * sizeof(double));
@@ -630,9 +634,11 @@ StitchingFunction::StitchingFunction(StitchingFunction *func) {
 StitchingFunction::~StitchingFunction() {
   int i;
 
-  for (i = 0; i < k; ++i) {
-    if (funcs[i]) {
-      delete funcs[i];
+  if (funcs) {
+    for (i = 0; i < k; ++i) {
+      if (funcs[i]) {
+       delete funcs[i];
+      }
     }
   }
   gfree(funcs);
@@ -1246,7 +1252,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 && b2);
+         stack->pushBool(b1 && b2);
        }
        break;
       case psOpAtan:
@@ -1313,8 +1319,8 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        stack->roll(2, 1);
        break;
       case psOpExp:
-       r2 = stack->popInt();
-       r1 = stack->popInt();
+       r2 = stack->popNum();
+       r1 = stack->popNum();
        stack->pushReal(pow(r1, r2));
        break;
       case psOpFalse:
@@ -1426,7 +1432,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        if (stack->topIsInt()) {
          stack->pushInt(~stack->popInt());
        } else {
-         stack->pushReal(!stack->popBool());
+         stack->pushBool(!stack->popBool());
        }
        break;
       case psOpOr:
@@ -1437,7 +1443,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 || b2);
+         stack->pushBool(b1 || b2);
        }
        break;
       case psOpPop:
@@ -1455,7 +1461,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        }
        break;
       case psOpSin:
-       stack->pushReal(cos(stack->popNum()));
+       stack->pushReal(sin(stack->popNum()));
        break;
       case psOpSqrt:
        stack->pushReal(sqrt(stack->popNum()));
@@ -1488,7 +1494,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 ^ b2);
+         stack->pushBool(b1 ^ b2);
        }
        break;
       case psOpIf:
index 63896d89120e248d6f961de2db7009016eccbdc6..d10820854d52622e398d438b4cc8e5aeeec8f0be 100644 (file)
 // constants
 //------------------------------------------------------------------------
 
+// Max recursive depth for a function shading fill.
+#define functionMaxDepth 6
+
+// Max delta allowed in any color component for a function shading fill.
+#define functionColorDelta (1 / 256.0)
+
 // Max number of splits along the t axis for an axial shading fill.
 #define axialMaxSplits 256
 
 // Operator table
 //------------------------------------------------------------------------
 
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+#  pragma optimize("",off)
+#endif
+
 Operator Gfx::opTab[] = {
   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
           &Gfx::opMoveSetShowText},
@@ -212,6 +222,10 @@ Operator Gfx::opTab[] = {
           &Gfx::opCurveTo2},
 };
 
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+#  pragma optimize("",on)
+#endif
+
 #define numOps (sizeof(opTab) / sizeof(Operator))
 
 //------------------------------------------------------------------------
@@ -219,15 +233,23 @@ Operator Gfx::opTab[] = {
 //------------------------------------------------------------------------
 
 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
-  Object obj1;
+  Object obj1, obj2;
+  Ref r;
 
   if (resDict) {
 
     // build font dictionary
     fonts = NULL;
-    resDict->lookup("Font", &obj1);
-    if (obj1.isDict()) {
-      fonts = new GfxFontDict(xref, obj1.getDict());
+    resDict->lookupNF("Font", &obj1);
+    if (obj1.isRef()) {
+      obj1.fetch(xref, &obj2);
+      if (obj2.isDict()) {
+       r = obj1.getRef();
+       fonts = new GfxFontDict(xref, &r, obj2.getDict());
+      }
+      obj2.free();
+    } else if (obj1.isDict()) {
+      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
     }
     obj1.free();
 
@@ -251,6 +273,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
     xObjDict.initNull();
     colorSpaceDict.initNull();
     patternDict.initNull();
+    shadingDict.initNull();
     gStateDict.initNull();
   }
 
@@ -381,8 +404,9 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
 // Gfx
 //------------------------------------------------------------------------
 
-Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
-        PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+        double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+        PDFRectangle *cropBox, int rotate,
         GBool (*abortCheckCbkA)(void *data),
         void *abortCheckCbkDataA) {
   int i;
@@ -396,7 +420,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
 
   // initialize
   out = outA;
-  state = new GfxState(dpi, box, rotate, out->upsideDown());
+  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
   fontChanged = gFalse;
   clip = clipNone;
   ignoreUndef = 0;
@@ -406,6 +430,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
   for (i = 0; i < 6; ++i) {
     baseMatrix[i] = state->getCTM()[i];
   }
+  formDepth = 0;
   abortCheckCbk = abortCheckCbkA;
   abortCheckCbkData = abortCheckCbkDataA;
 
@@ -437,13 +462,14 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
 
   // initialize
   out = outA;
-  state = new GfxState(72, box, 0, gFalse);
+  state = new GfxState(72, 72, box, 0, gFalse);
   fontChanged = gFalse;
   clip = clipNone;
   ignoreUndef = 0;
   for (i = 0; i < 6; ++i) {
     baseMatrix[i] = state->getCTM()[i];
   }
+  formDepth = 0;
   abortCheckCbk = abortCheckCbkA;
   abortCheckCbkData = abortCheckCbkDataA;
 
@@ -462,8 +488,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
 
 Gfx::~Gfx() {
   while (state->hasSaves()) {
-    state = state->restore();
-    out->restoreState(state);
+    restoreState();
   }
   if (!subPage) {
     out->endPage();
@@ -591,6 +616,7 @@ void Gfx::go(GBool topLevel) {
 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
   Operator *op;
   char *name;
+  Object *argPtr;
   int i;
 
   // find operator
@@ -602,12 +628,19 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
   }
 
   // type check args
+  argPtr = args;
   if (op->numArgs >= 0) {
-    if (numArgs != op->numArgs) {
-      error(getPos(), "Wrong number (%d) of args to '%s' operator",
-           numArgs, name);
+    if (numArgs < op->numArgs) {
+      error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
       return;
     }
+    if (numArgs > op->numArgs) {
+#if 0
+      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
+#endif
+      argPtr += numArgs - op->numArgs;
+      numArgs = op->numArgs;
+    }
   } else {
     if (numArgs > -op->numArgs) {
       error(getPos(), "Too many (%d) args to '%s' operator",
@@ -616,15 +649,15 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
     }
   }
   for (i = 0; i < numArgs; ++i) {
-    if (!checkArg(&args[i], op->tchk[i])) {
+    if (!checkArg(&argPtr[i], op->tchk[i])) {
       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
-           i, name, args[i].getTypeName());
+           i, name, argPtr[i].getTypeName());
       return;
     }
   }
 
   // do it
-  (this->*op->func)(args, numArgs);
+  (this->*op->func)(argPtr, numArgs);
 }
 
 Operator *Gfx::findOp(char *name) {
@@ -672,13 +705,11 @@ int Gfx::getPos() {
 //------------------------------------------------------------------------
 
 void Gfx::opSave(Object args[], int numArgs) {
-  out->saveState(state);
-  state = state->save();
+  saveState();
 }
 
 void Gfx::opRestore(Object args[], int numArgs) {
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
 }
 
 void Gfx::opConcat(Object args[], int numArgs) {
@@ -1194,18 +1225,7 @@ void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
 }
 
 void Gfx::doPatternFill(GBool eoFill) {
-  GfxPatternColorSpace *patCS;
   GfxPattern *pattern;
-  GfxTilingPattern *tPat;
-  GfxColorSpace *cs;
-  double xMin, yMin, xMax, yMax, x, y, x1, y1;
-  double cxMin, cyMin, cxMax, cyMax;
-  int xi0, yi0, xi1, yi1, xi, yi;
-  double *ctm, *btm, *ptm;
-  double m[6], ictm[6], m1[6], imb[6];
-  double det;
-  double xstep, ystep;
-  int i;
 
   // this is a bit of a kludge -- patterns can be really slow, so we
   // skip them if we're only doing text extraction, since they almost
@@ -1214,17 +1234,37 @@ void Gfx::doPatternFill(GBool eoFill) {
     return;
   }
 
-  // get color space
-  patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
-
-  // get pattern
   if (!(pattern = state->getFillPattern())) {
     return;
   }
-  if (pattern->getType() != 1) {
-    return;
+  switch (pattern->getType()) {
+  case 1:
+    doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
+    break;
+  case 2:
+    doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
+    break;
+  default:
+    error(getPos(), "Unimplemented pattern type (%d) in fill",
+         pattern->getType());
+    break;
   }
-  tPat = (GfxTilingPattern *)pattern;
+}
+
+void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
+  GfxPatternColorSpace *patCS;
+  GfxColorSpace *cs;
+  double xMin, yMin, xMax, yMax, x, y, x1, y1;
+  double cxMin, cyMin, cxMax, cyMax;
+  int xi0, yi0, xi1, yi1, xi, yi;
+  double *ctm, *btm, *ptm;
+  double m[6], ictm[6], m1[6], imb[6];
+  double det;
+  double xstep, ystep;
+  int i;
+
+  // get color space
+  patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
 
   // construct a (pattern space) -> (current space) transform matrix
   ctm = state->getCTM();
@@ -1263,17 +1303,25 @@ void Gfx::doPatternFill(GBool eoFill) {
   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  saveState();
 
-  // set underlying color space (for uncolored tiling patterns)
+  // set underlying color space (for uncolored tiling patterns); set
+  // various other parameters (stroke color, line width) to match
+  // Adobe's behavior
   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
     state->setFillColorSpace(cs->copy());
+    state->setStrokeColorSpace(cs->copy());
+    state->setStrokeColor(state->getFillColor());
   } else {
     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
   }
   state->setFillPattern(NULL);
   out->updateFillColor(state);
+  state->setStrokePattern(NULL);
+  out->updateStrokeColor(state);
+  state->setLineWidth(0);
+  out->updateLineWidth(state);
 
   // clip to current path
   state->clip();
@@ -1339,8 +1387,8 @@ void Gfx::doPatternFill(GBool eoFill) {
   }
   for (yi = yi0; yi < yi1; ++yi) {
     for (xi = xi0; xi < xi1; ++xi) {
-      x = xi * xstep;
-      y = yi * ystep;
+      x = xi * xstep - tPat->getBBox()[0];
+      y = yi * ystep - tPat->getBBox()[1];
       m1[4] = x * m[0] + y * m[2] + m[4];
       m1[5] = x * m[1] + y * m[3] + m[5];
       doForm1(tPat->getContentStream(), tPat->getResDict(),
@@ -1349,8 +1397,92 @@ void Gfx::doPatternFill(GBool eoFill) {
   }
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
+}
+
+void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
+  GfxShading *shading;
+  double *ctm, *btm, *ptm;
+  double m[6], ictm[6], m1[6];
+  double xMin, yMin, xMax, yMax;
+  double det;
+
+  shading = sPat->getShading();
+
+  // save current graphics state
+  saveState();
+
+  // clip to bbox
+  if (shading->getHasBBox()) {
+    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+    state->moveTo(xMin, yMin);
+    state->lineTo(xMax, yMin);
+    state->lineTo(xMax, yMax);
+    state->lineTo(xMin, yMax);
+    state->closePath();
+    state->clip();
+    out->clip(state);
+    state->clearPath();
+  }
+
+  // clip to current path
+  state->clip();
+  if (eoFill) {
+    out->eoClip(state);
+  } else {
+    out->clip(state);
+  }
+  state->clearPath();
+
+  // construct a (pattern space) -> (current space) transform matrix
+  ctm = state->getCTM();
+  btm = baseMatrix;
+  ptm = sPat->getMatrix();
+  // iCTM = invert CTM
+  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+  ictm[0] = ctm[3] * det;
+  ictm[1] = -ctm[1] * det;
+  ictm[2] = -ctm[2] * det;
+  ictm[3] = ctm[0] * det;
+  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+  // m1 = PTM * BTM = PTM * base transform matrix
+  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+  // set the new matrix
+  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
+  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+
+  // do shading type-specific operations
+  switch (shading->getType()) {
+  case 1:
+    doFunctionShFill((GfxFunctionShading *)shading);
+    break;
+  case 2:
+    doAxialShFill((GfxAxialShading *)shading);
+    break;
+  case 3:
+    doRadialShFill((GfxRadialShading *)shading);
+    break;
+  }
+
+  // restore graphics state
+  restoreState();
 }
 
 void Gfx::opShFill(Object args[], int numArgs) {
@@ -1362,8 +1494,7 @@ void Gfx::opShFill(Object args[], int numArgs) {
   }
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  saveState();
 
   // clip to bbox
   if (shading->getHasBBox()) {
@@ -1383,6 +1514,9 @@ void Gfx::opShFill(Object args[], int numArgs) {
 
   // do shading type-specific operations
   switch (shading->getType()) {
+  case 1:
+    doFunctionShFill((GfxFunctionShading *)shading);
+    break;
   case 2:
     doAxialShFill((GfxAxialShading *)shading);
     break;
@@ -1392,12 +1526,131 @@ void Gfx::opShFill(Object args[], int numArgs) {
   }
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
 
   delete shading;
 }
 
+void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
+  double x0, y0, x1, y1;
+  GfxColor colors[4];
+
+  shading->getDomain(&x0, &y0, &x1, &y1);
+  shading->getColor(x0, y0, &colors[0]);
+  shading->getColor(x0, y1, &colors[1]);
+  shading->getColor(x1, y0, &colors[2]);
+  shading->getColor(x1, y1, &colors[3]);
+  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+}
+
+void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
+                           double x0, double y0,
+                           double x1, double y1,
+                           GfxColor *colors, int depth) {
+  GfxColor fillColor;
+  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
+  GfxColor colors2[4];
+  double *matrix;
+  double xM, yM;
+  int nComps, i, j;
+
+  nComps = shading->getColorSpace()->getNComps();
+  matrix = shading->getMatrix();
+
+  // compare the four corner colors
+  for (i = 0; i < 4; ++i) {
+    for (j = 0; j < nComps; ++j) {
+      if (fabs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
+       break;
+      }
+    }
+    if (j < nComps) {
+      break;
+    }
+  }
+
+  // center of the rectangle
+  xM = 0.5 * (x0 + x1);
+  yM = 0.5 * (y0 + y1);
+
+  // the four corner colors are close (or we hit the recursive limit)
+  // -- fill the rectangle; but require at least one subdivision
+  // (depth==0) to avoid problems when the four outer corners of the
+  // shaded region are the same color
+  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
+
+    // use the center color
+    shading->getColor(xM, yM, &fillColor);
+    state->setFillColor(&fillColor);
+    out->updateFillColor(state);
+
+    // fill the rectangle
+    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
+                 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
+    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
+                 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
+    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
+                 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
+    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
+                 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+
+  // the four corner colors are not close enough -- subdivide the
+  // rectangle
+  } else {
+
+    // colors[0]       colorM0       colors[2]
+    //   (x0,y0)       (xM,y0)       (x1,y0)
+    //         +----------+----------+
+    //         |          |          |
+    //         |    UL    |    UR    |
+    // color0M |       colorMM       | color1M
+    // (x0,yM) +----------+----------+ (x1,yM)
+    //         |       (xM,yM)       |
+    //         |    LL    |    LR    |
+    //         |          |          |
+    //         +----------+----------+
+    // colors[1]       colorM1       colors[3]
+    //   (x0,y1)       (xM,y1)       (x1,y1)
+
+    shading->getColor(x0, yM, &color0M);
+    shading->getColor(x1, yM, &color1M);
+    shading->getColor(xM, y0, &colorM0);
+    shading->getColor(xM, y1, &colorM1);
+    shading->getColor(xM, yM, &colorMM);
+
+    // upper-left sub-rectangle
+    colors2[0] = colors[0];
+    colors2[1] = color0M;
+    colors2[2] = colorM0;
+    colors2[3] = colorMM;
+    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
+    
+    // lower-left sub-rectangle
+    colors2[0] = color0M;
+    colors2[1] = colors[1];
+    colors2[2] = colorMM;
+    colors2[3] = colorM1;
+    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
+    
+    // upper-right sub-rectangle
+    colors2[0] = colorM0;
+    colors2[1] = colorMM;
+    colors2[2] = colors[2];
+    colors2[3] = color1M;
+    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
+
+    // lower-right sub-rectangle
+    colors2[0] = colorMM;
+    colors2[1] = colorM1;
+    colors2[2] = color1M;
+    colors2[3] = colors[3];
+    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
+  }
+}
+
 void Gfx::doAxialShFill(GfxAxialShading *shading) {
   double xMin, yMin, xMax, yMax;
   double x0, y0, x1, y1;
@@ -1480,11 +1733,14 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   // difference across a region is small enough, and then the region
   // is painted with a single color.
 
-  // set up
+  // set up: require at least one split to avoid problems when the two
+  // ends of the t axis have the same color
   nComps = shading->getColorSpace()->getNComps();
   ta[0] = tMin;
+  next[0] = axialMaxSplits / 2;
+  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
+  next[axialMaxSplits / 2] = axialMaxSplits;
   ta[axialMaxSplits] = tMax;
-  next[0] = axialMaxSplits;
 
   // compute the color at t = tMin
   if (tMin < 0) {
@@ -1749,7 +2005,9 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
     // go as far along the t axis (toward t1) as we can, such that the
     // color difference is within the tolerance (radialColorDelta) --
     // this uses bisection (between the current value, t, and t1),
-    // limited to radialMaxSplits points along the t axis
+    // limited to radialMaxSplits points along the t axis; require at
+    // least one split to avoid problems when the innermost and
+    // outermost colors are the same
     ib = radialMaxSplits;
     sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
     tb = t0 + sb * (t1 - t0);
@@ -1766,7 +2024,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
          break;
        }
       }
-      if (k == nComps) {
+      if (k == nComps && ib < radialMaxSplits) {
        break;
       }
       ib = (ia + ib) / 2;
@@ -1862,6 +2120,7 @@ void Gfx::opBeginText(Object args[], int numArgs) {
 }
 
 void Gfx::opEndText(Object args[], int numArgs) {
+  out->endTextObject(state);
 }
 
 //------------------------------------------------------------------------
@@ -2099,8 +2358,7 @@ void Gfx::doShowText(GString *s) {
       dy *= state->getFontSize();
       state->textTransformDelta(dx, dy, &tdx, &tdy);
       state->transform(curX + riseX, curY + riseY, &x, &y);
-      out->saveState(state);
-      state = state->save();
+      saveState();
       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
       //~ out->updateCTM(???)
       if (!out->beginType3Char(state, code, u, uLen)) {
@@ -2119,8 +2377,7 @@ void Gfx::doShowText(GString *s) {
        }
        charProc.free();
       }
-      state = state->restore();
-      out->restoreState(state);
+      restoreState();
       // GfxState::restore() does *not* restore the current position,
       // so we deal with it here using (curX, curY) and (lineX, lineY)
       curX += tdx;
@@ -2313,9 +2570,13 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
     obj1.free();
     dict->lookup("BPC", &obj1);
   }
-  if (!obj1.isInt())
+  if (obj1.isInt()) {
+    bits = obj1.getInt();
+  } else if (mask) {
+    bits = 1;
+  } else {
     goto err2;
-  bits = obj1.getInt();
+  }
   obj1.free();
 
   // display a mask
@@ -2419,6 +2680,11 @@ void Gfx::doForm(Object *str) {
   Object obj1;
   int i;
 
+  // check for excessive recursion
+  if (formDepth > 20) {
+    return;
+  }
+
   // get stream dict
   dict = str->streamGetDict();
 
@@ -2464,7 +2730,9 @@ void Gfx::doForm(Object *str) {
   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
   // draw it
+  ++formDepth;
   doForm1(str, resDict, m, bbox);
+  --formDepth;
 
   resObj.free();
 }
@@ -2592,8 +2860,10 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   pushResources(resDict);
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  saveState();
+
+  // kill any pre-existing path
+  state->clearPath();
 
   // save current parser
   oldParser = parser;
@@ -2632,8 +2902,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   parser = oldParser;
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
 
   // pop resource stack
   popResources();
@@ -2641,18 +2910,6 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   return;
 }
 
-void Gfx::pushResources(Dict *resDict) {
-  res = new GfxResources(xref, resDict, res);
-}
-
-void Gfx::popResources() {
-  GfxResources *resPtr;
-
-  resPtr = res->getNext();
-  delete res;
-  res = resPtr;
-}
-
 //------------------------------------------------------------------------
 // in-line image operators
 //------------------------------------------------------------------------
@@ -2780,3 +3037,29 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
     fflush(stdout);
   }
 }
+
+//------------------------------------------------------------------------
+// misc
+//------------------------------------------------------------------------
+
+void Gfx::saveState() {
+  out->saveState(state);
+  state = state->save();
+}
+
+void Gfx::restoreState() {
+  state = state->restore();
+  out->restoreState(state);
+}
+
+void Gfx::pushResources(Dict *resDict) {
+  res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+  GfxResources *resPtr;
+
+  resPtr = res->getNext();
+  delete res;
+  res = resPtr;
+}
index c7aef1117910671500508df429a044d67ae69a13..20898ec8cedcf8cb989e51872d05cd88bc002a95 100644 (file)
@@ -28,10 +28,14 @@ class OutputDev;
 class GfxFontDict;
 class GfxFont;
 class GfxPattern;
+class GfxTilingPattern;
+class GfxShadingPattern;
 class GfxShading;
+class GfxFunctionShading;
 class GfxAxialShading;
 class GfxRadialShading;
 class GfxState;
+class GfxColor;
 class Gfx;
 class PDFRectangle;
 
@@ -97,8 +101,9 @@ class Gfx {
 public:
 
   // Constructor for regular output.
-  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
-      PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
+  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+      double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+      PDFRectangle *cropBox, int rotate,
       GBool (*abortCheckCbkA)(void *data) = NULL,
       void *abortCheckCbkDataA = NULL);
 
@@ -118,8 +123,11 @@ public:
   void doAnnot(Object *str, double xMin, double yMin,
               double xMax, double yMax);
 
-  void pushResources(Dict *resDict);
-  void popResources();
+  // Save graphics state.
+  void saveState();
+
+  // Restore graphics state.
+  void restoreState();
 
 private:
 
@@ -136,6 +144,7 @@ private:
   int ignoreUndef;             // current BX/EX nesting level
   double baseMatrix[6];                // default matrix for most recent
                                //   page/form/pattern
+  int formDepth;
 
   Parser *parser;              // parser for page content stream(s)
 
@@ -198,7 +207,14 @@ private:
   void opEOFillStroke(Object args[], int numArgs);
   void opCloseEOFillStroke(Object args[], int numArgs);
   void doPatternFill(GBool eoFill);
+  void doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill);
+  void doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill);
   void opShFill(Object args[], int numArgs);
+  void doFunctionShFill(GfxFunctionShading *shading);
+  void doFunctionShFill1(GfxFunctionShading *shading,
+                        double x0, double y0,
+                        double x1, double y1,
+                        GfxColor *colors, int depth);
   void doAxialShFill(GfxAxialShading *shading);
   void doRadialShFill(GfxRadialShading *shading);
   void doEndPath();
@@ -257,6 +273,9 @@ private:
   void opBeginMarkedContent(Object args[], int numArgs);
   void opEndMarkedContent(Object args[], int numArgs);
   void opMarkPoint(Object args[], int numArgs);
+
+  void pushResources(Dict *resDict);
+  void popResources();
 };
 
 #endif
index d6875881fa698fd003534ffec72776026dc9c919..6f83676398bf6e5f0a0811af58613604af033257 100644 (file)
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <ctype.h>
 #include "gmem.h"
+#include "GHash.h"
 #include "Error.h"
 #include "Object.h"
 #include "Dict.h"
@@ -136,12 +137,16 @@ GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
   tag = new GString(tagA);
   id = idA;
   name = nameA;
+  origName = nameA;
   embFontName = NULL;
   extFontFile = NULL;
 }
 
 GfxFont::~GfxFont() {
   delete tag;
+  if (origName && origName != name) {
+    delete origName;
+  }
   if (name) {
     delete name;
   }
@@ -286,8 +291,8 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
   obj1.free();
 }
 
-CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) {
-  CharCodeToUnicode *ctu;
+CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
+                                             CharCodeToUnicode *ctu) {
   GString *buf;
   Object obj1;
   int c;
@@ -303,7 +308,11 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) {
   }
   obj1.streamClose();
   obj1.free();
-  ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+  if (ctu) {
+    ctu->mergeCMap(buf, nBits);
+  } else {
+    ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+  }
   delete buf;
   return ctu;
 }
@@ -334,7 +343,8 @@ char *GfxFont::readExtFontFile(int *len) {
   fseek(f, 0, SEEK_SET);
   buf = (char *)gmalloc(*len);
   if ((int)fread(buf, 1, *len, f) != *len) {
-    error(-1, "Error reading external font file '%s'", extFontFile);
+    error(-1, "Error reading external font file '%s'",
+         extFontFile->getCString());
   }
   fclose(f);
   return buf;
@@ -395,6 +405,8 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   char *charName;
   GBool missing, hex;
   Unicode toUnicode[256];
+  CharCodeToUnicode *utu, *ctu2;
+  Unicode uBuf[8];
   double mul;
   int firstChar, lastChar;
   Gushort w;
@@ -419,7 +431,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
       }
     }
     if (!name->cmp(stdFontMap[a].altName)) {
-      delete name;
       name = new GString(stdFontMap[a].properName);
     }
   }
@@ -504,6 +515,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
 
   // check FontDict for base encoding
   hasEncoding = gFalse;
+  usesMacRomanEnc = gFalse;
   baseEnc = NULL;
   baseEncFromFontFile = gFalse;
   fontDict->lookup("Encoding", &obj1);
@@ -511,6 +523,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     obj1.dictLookup("BaseEncoding", &obj2);
     if (obj2.isName("MacRomanEncoding")) {
       hasEncoding = gTrue;
+      usesMacRomanEnc = gTrue;
       baseEnc = macRomanEncoding;
     } else if (obj2.isName("MacExpertEncoding")) {
       hasEncoding = gTrue;
@@ -525,6 +538,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     obj2.free();
   } else if (obj1.isName("MacRomanEncoding")) {
     hasEncoding = gTrue;
+    usesMacRomanEnc = gTrue;
     baseEnc = macRomanEncoding;
   } else if (obj1.isName("MacExpertEncoding")) {
     hasEncoding = gTrue;
@@ -609,7 +623,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
        if (obj3.isInt()) {
          code = obj3.getInt();
        } else if (obj3.isName()) {
-         if (code < 256) {
+         if (code >= 0 && code < 256) {
            if (encFree[code]) {
              gfree(enc[code]);
            }
@@ -633,76 +647,97 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
 
   //----- build the mapping to Unicode -----
 
-  // look for a ToUnicode CMap
-  if (!(ctu = readToUnicodeCMap(fontDict, 8))) {
-
-    // no ToUnicode CMap, so use the char names
+  // pass 1: use the name-to-Unicode mapping table
+  missing = hex = gFalse;
+  for (code = 0; code < 256; ++code) {
+    if ((charName = enc[code])) {
+      if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
+         strcmp(charName, ".notdef")) {
+       // if it wasn't in the name-to-Unicode table, check for a
+       // name that looks like 'Axx' or 'xx', where 'A' is any letter
+       // and 'xx' is two hex digits
+       if ((strlen(charName) == 3 &&
+            isalpha(charName[0]) &&
+            isxdigit(charName[1]) && isxdigit(charName[2]) &&
+            ((charName[1] >= 'a' && charName[1] <= 'f') ||
+             (charName[1] >= 'A' && charName[1] <= 'F') ||
+             (charName[2] >= 'a' && charName[2] <= 'f') ||
+             (charName[2] >= 'A' && charName[2] <= 'F'))) ||
+           (strlen(charName) == 2 &&
+            isxdigit(charName[0]) && isxdigit(charName[1]) &&
+            ((charName[0] >= 'a' && charName[0] <= 'f') ||
+             (charName[0] >= 'A' && charName[0] <= 'F') ||
+             (charName[1] >= 'a' && charName[1] <= 'f') ||
+             (charName[1] >= 'A' && charName[1] <= 'F')))) {
+         hex = gTrue;
+       }
+       missing = gTrue;
+      }
+    } else {
+      toUnicode[code] = 0;
+    }
+  }
 
-    // pass 1: use the name-to-Unicode mapping table
-    missing = hex = gFalse;
+  // pass 2: try to fill in the missing chars, looking for names of
+  // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
+  // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
+  // decimal digits
+  if (missing && globalParams->getMapNumericCharNames()) {
     for (code = 0; code < 256; ++code) {
-      if ((charName = enc[code])) {
-       if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
-           strcmp(charName, ".notdef")) {
-         // if it wasn't in the name-to-Unicode table, check for a
-         // name that looks like 'Axx' or 'xx', where 'A' is any letter
-         // and 'xx' is two hex digits
-         if ((strlen(charName) == 3 &&
-              isalpha(charName[0]) &&
-              isxdigit(charName[1]) && isxdigit(charName[2]) &&
-              ((charName[1] >= 'a' && charName[1] <= 'f') ||
-               (charName[1] >= 'A' && charName[1] <= 'F') ||
-               (charName[2] >= 'a' && charName[2] <= 'f') ||
-               (charName[2] >= 'A' && charName[2] <= 'F'))) ||
-             (strlen(charName) == 2 &&
-              isxdigit(charName[0]) && isxdigit(charName[1]) &&
-              ((charName[0] >= 'a' && charName[0] <= 'f') ||
-               (charName[0] >= 'A' && charName[0] <= 'F') ||
-               (charName[1] >= 'a' && charName[1] <= 'f') ||
-               (charName[1] >= 'A' && charName[1] <= 'F')))) {
-           hex = gTrue;
-         }
-         missing = gTrue;
+      if ((charName = enc[code]) && !toUnicode[code] &&
+         strcmp(charName, ".notdef")) {
+       n = strlen(charName);
+       code2 = -1;
+       if (hex && n == 3 && isalpha(charName[0]) &&
+           isxdigit(charName[1]) && isxdigit(charName[2])) {
+         sscanf(charName+1, "%x", &code2);
+       } else if (hex && n == 2 &&
+                  isxdigit(charName[0]) && isxdigit(charName[1])) {
+         sscanf(charName, "%x", &code2);
+       } else if (!hex && n >= 2 && n <= 4 &&
+                  isdigit(charName[0]) && isdigit(charName[1])) {
+         code2 = atoi(charName);
+       } else if (n >= 3 && n <= 5 &&
+                  isdigit(charName[1]) && isdigit(charName[2])) {
+         code2 = atoi(charName+1);
+       } else if (n >= 4 && n <= 6 &&
+                  isdigit(charName[2]) && isdigit(charName[3])) {
+         code2 = atoi(charName+2);
+       }
+       if (code2 >= 0 && code2 <= 0xff) {
+         toUnicode[code] = (Unicode)code2;
        }
-      } else {
-       toUnicode[code] = 0;
       }
     }
+  }
 
-    // pass 2: try to fill in the missing chars, looking for names of
-    // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
-    // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
-    // decimal digits
-    if (missing && globalParams->getMapNumericCharNames()) {
-      for (code = 0; code < 256; ++code) {
-       if ((charName = enc[code]) && !toUnicode[code] &&
-           strcmp(charName, ".notdef")) {
-         n = strlen(charName);
-         code2 = -1;
-         if (hex && n == 3 && isalpha(charName[0]) &&
-             isxdigit(charName[1]) && isxdigit(charName[2])) {
-           sscanf(charName+1, "%x", &code2);
-         } else if (hex && n == 2 &&
-                    isxdigit(charName[0]) && isxdigit(charName[1])) {
-           sscanf(charName, "%x", &code2);
-         } else if (!hex && n >= 2 && n <= 4 &&
-                    isdigit(charName[0]) && isdigit(charName[1])) {
-           code2 = atoi(charName);
-         } else if (n >= 3 && n <= 5 &&
-                    isdigit(charName[1]) && isdigit(charName[2])) {
-           code2 = atoi(charName+1);
-         } else if (n >= 4 && n <= 6 &&
-                    isdigit(charName[2]) && isdigit(charName[3])) {
-           code2 = atoi(charName+2);
-         }
-         if (code2 >= 0 && code2 <= 0xff) {
-           toUnicode[code] = (Unicode)code2;
-         }
+  // construct the char code -> Unicode mapping object
+  ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+
+  // merge in a ToUnicode CMap, if there is one -- this overwrites
+  // existing entries in ctu, i.e., the ToUnicode CMap takes
+  // precedence, but the other encoding info is allowed to fill in any
+  // holes
+  readToUnicodeCMap(fontDict, 8, ctu);
+
+  // look for a Unicode-to-Unicode mapping
+  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+    for (i = 0; i < 256; ++i) {
+      toUnicode[i] = 0;
+    }
+    ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+    for (i = 0; i < 256; ++i) {
+      n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
+      if (n >= 1) {
+       n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+       if (n >= 1) {
+         ctu2->setMapping((CharCode)i, uBuf, n);
        }
       }
     }
-
-    ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+    utu->decRefCnt();
+    delete ctu;
+    ctu = ctu2;
   }
 
   //----- get the character widths -----
@@ -716,9 +751,15 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   fontDict->lookup("FirstChar", &obj1);
   firstChar = obj1.isInt() ? obj1.getInt() : 0;
   obj1.free();
+  if (firstChar < 0 || firstChar > 255) {
+    firstChar = 0;
+  }
   fontDict->lookup("LastChar", &obj1);
   lastChar = obj1.isInt() ? obj1.getInt() : 255;
   obj1.free();
+  if (lastChar < 0 || lastChar > 255) {
+    lastChar = 255;
+  }
   mul = (type == fontType3) ? fontMat[0] : 0.001;
   fontDict->lookup("Widths", &obj1);
   if (obj1.isArray()) {
@@ -819,6 +860,124 @@ CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
   return ctu;
 }
 
+Gushort *Gfx8BitFont::getCodeToGIDMap(TrueTypeFontFile *ff) {
+  Gushort *map;
+  int cmapPlatform, cmapEncoding;
+  int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
+  GBool useMacRoman, useUnicode;
+  GHash *nameToGID;
+  char *charName;
+  Unicode u;
+  int code, i, n;
+
+  map = (Gushort *)gmalloc(256 * sizeof(Gushort));
+  for (i = 0; i < 256; ++i) {
+    map[i] = 0;
+  }
+
+  // To match up with the Adobe-defined behaviour, we choose a cmap
+  // like this:
+  // 1. If the PDF font has an encoding:
+  //    1a. If the PDF font specified MacRomanEncoding and the
+  //        TrueType font has a Macintosh Roman cmap, use it, and
+  //        reverse map the char names through MacRomanEncoding to
+  //        get char codes.
+  //    1b. If the TrueType font has a Microsoft Unicode cmap or a
+  //        non-Microsoft Unicode cmap, use it, and use the Unicode
+  //        indexes, not the char codes.
+  //    1c. If the PDF font is symbolic and the TrueType font has a
+  //        Microsoft Symbol cmap, use it, and use char codes
+  //        directly (possibly with an offset of 0xf000).
+  //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        as in case 1a.
+  // 2. If the PDF font does not have an encoding:
+  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and use char codes directly (possibly with an offset of
+  //        0xf000).
+  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
+  //        and use char codes directly (possible with an offset of
+  //        0xf000).
+  // 3. If none of these rules apply, use the first cmap and hope for
+  //    the best (this shouldn't happen).
+  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+  for (i = 0; i < ff->getNumCmaps(); ++i) {
+    cmapPlatform = ff->getCmapPlatform(i);
+    cmapEncoding = ff->getCmapEncoding(i);
+    if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+       cmapPlatform == 0) {
+      unicodeCmap = i;
+    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+      macRomanCmap = i;
+    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+      msSymbolCmap = i;
+    }
+  }
+  cmap = 0;
+  useMacRoman = gFalse;
+  useUnicode = gFalse;
+  if (hasEncoding) {
+    if (usesMacRomanEnc && macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+      useMacRoman = gTrue;
+    } else if (unicodeCmap >= 0) {
+      cmap = unicodeCmap;
+      useUnicode = gTrue;
+    } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
+      cmap = msSymbolCmap;
+    } else if (macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+      useMacRoman = gTrue;
+    }
+  } else {
+    if (macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+    } else if (msSymbolCmap >= 0) {
+      cmap = msSymbolCmap;
+    }
+  }
+
+  // reverse map the char names through MacRomanEncoding, then map the
+  // char codes through the cmap
+  if (useMacRoman) {
+    for (i = 0; i < 256; ++i) {
+      if ((charName = enc[i])) {
+       if ((code = globalParams->getMacRomanCharCode(charName))) {
+         map[i] = ff->mapCodeToGID(cmap, code);
+       }
+      }
+    }
+
+  // map Unicode through the cmap
+  } else if (useUnicode) {
+    for (i = 0; i < 256; ++i) {
+      if ((n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+       map[i] = ff->mapCodeToGID(cmap, u);
+      }
+    }
+
+  // map the char codes through the cmap, possibly with an offset of
+  // 0xf000
+  } else {
+    for (i = 0; i < 256; ++i) {
+      if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
+       map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
+      }
+    }
+  }
+
+  // try the TrueType 'post' table to handle any unmapped characters
+  if ((nameToGID = ff->getNameToGID())) {
+    for (i = 0; i < 256; ++i) {
+      if (!map[i] && (charName = enc[i])) {
+       map[i] = (Gushort)(int)nameToGID->lookup(charName);
+      }
+    }
+    delete nameToGID;
+  }
+
+  return map;
+}
+
 Dict *Gfx8BitFont::getCharProcs() {
   return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
 }
@@ -930,7 +1089,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   obj1.free();
 
   // look for a ToUnicode CMap
-  if (!(ctu = readToUnicodeCMap(fontDict, 16))) {
+  if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
 
     // the "Adobe-Identity" and "Adobe-UCS" collections don't have
     // cidToUnicode files
@@ -1058,11 +1217,11 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   if (desFontDict->lookup("DW2", &obj1)->isArray() &&
       obj1.arrayGetLength() == 2) {
     if (obj1.arrayGet(0, &obj2)->isNum()) {
-      widths.defVY = obj1.getNum() * 0.001;
+      widths.defVY = obj2.getNum() * 0.001;
     }
     obj2.free();
     if (obj1.arrayGet(1, &obj2)->isNum()) {
-      widths.defHeight = obj1.getNum() * 0.001;
+      widths.defHeight = obj2.getNum() * 0.001;
     }
     obj2.free();
   }
@@ -1073,8 +1232,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     excepsSize = 0;
     i = 0;
     while (i + 1 < obj1.arrayGetLength()) {
-      obj1.arrayGet(0, &obj2);
-      obj2.arrayGet(0, &obj3);
+      obj1.arrayGet(i, &obj2);
+      obj1.arrayGet(i+ 1, &obj3);
       if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
        if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
            obj1.arrayGet(i + 3, &obj5)->isNum() &&
@@ -1107,10 +1266,10 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
                     excepsSize * sizeof(GfxFontCIDWidthExcepV));
        }
        j = obj2.getInt();
-       for (k = 0; k < obj3.arrayGetLength(); ++k) {
+       for (k = 0; k < obj3.arrayGetLength(); k += 3) {
          if (obj3.arrayGet(k, &obj4)->isNum() &&
-             obj3.arrayGet(k, &obj5)->isNum() &&
-             obj3.arrayGet(k, &obj6)->isNum()) {
+             obj3.arrayGet(k+1, &obj5)->isNum() &&
+             obj3.arrayGet(k+2, &obj6)->isNum()) {
            widths.excepsV[widths.nExceps].first = j;
            widths.excepsV[widths.nExceps].last = j;
            widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;
@@ -1259,7 +1418,7 @@ GString *GfxCIDFont::getCollection() {
 // GfxFontDict
 //------------------------------------------------------------------------
 
-GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) {
+GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
   int i;
   Object obj1, obj2;
   Ref r;
@@ -1277,7 +1436,11 @@ GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) {
        // (legal generation numbers are five digits, so any 6-digit
        // number would be safe)
        r.num = i;
-       r.gen = 999999;
+       if (fontDictRef) {
+         r.gen = 100000 + fontDictRef->num;
+       } else {
+         r.gen = 999999;
+       }
       }
       fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
                                   r, obj2.getDict());
index 23371b3fb8838af981d8cee0b200ffddad7494e4..ddd88be3e55fb3ff1cbfa008074fb74e59ef61f4 100644 (file)
@@ -23,6 +23,7 @@
 class Dict;
 class CMap;
 class CharCodeToUnicode;
+class TrueTypeFontFile;
 struct GfxFontCIDWidths;
 
 //------------------------------------------------------------------------
@@ -104,6 +105,10 @@ public:
   // Get base font name.
   GString *getName() { return name; }
 
+  // Get the original font name (ignornig any munging that might have
+  // been done to map to a canonical Base-14 font name).
+  GString *getOrigName() { return origName; }
+
   // Get font type.
   GfxFontType getType() { return type; }
   virtual GBool isCIDFont() { return gFalse; }
@@ -158,12 +163,14 @@ public:
 protected:
 
   void readFontDescriptor(XRef *xref, Dict *fontDict);
-  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits);
+  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
+                                      CharCodeToUnicode *ctu);
   void findExtFontFile();
 
   GString *tag;                        // PDF font tag
   Ref id;                      // reference (used as unique ID)
   GString *name;               // font name
+  GString *origName;           // original font name
   GfxFontType type;            // type of font
   int flags;                   // font descriptor flags
   GString *embFontName;                // name of embedded font
@@ -205,9 +212,16 @@ public:
   // Returns true if the PDF font specified an encoding.
   GBool getHasEncoding() { return hasEncoding; }
 
-  // Get width of a character or string.
+  // Returns true if the PDF font specified MacRomanEncoding.
+  GBool getUsesMacRomanEnc() { return usesMacRomanEnc; }
+
+  // Get width of a character.
   double getWidth(Guchar c) { return widths[c]; }
 
+  // Return a char code-to-GID mapping for the provided font file.
+  // (This is only useful for TrueType fonts.)
+  Gushort *getCodeToGIDMap(TrueTypeFontFile *ff);
+
   // Return the Type 3 CharProc dictionary, or NULL if none.
   Dict *getCharProcs();
 
@@ -224,6 +238,7 @@ private:
                                //   the string is malloc'ed
   CharCodeToUnicode *ctu;      // char code --> Unicode
   GBool hasEncoding;
+  GBool usesMacRomanEnc;
   double widths[256];          // character widths
   Object charProcs;            // Type 3 CharProcs dictionary
   Object resources;            // Type 3 Resources dictionary
@@ -279,7 +294,7 @@ class GfxFontDict {
 public:
 
   // Build the font dictionary, given the PDF font dictionary.
-  GfxFontDict(XRef *xref, Dict *fontDict);
+  GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);
 
   // Destructor.
   ~GfxFontDict();
index 7efd0b9ad130ee8ac561e4731ba5b344af74fa7c..d20293974753b9a12294dea87b928379f91082c7 100644 (file)
@@ -98,7 +98,7 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
     } else if (obj1.isName("Pattern")) {
       cs = GfxPatternColorSpace::parse(csObj->getArray());
     } else {
-      error(-1, "Bad color space '%s'", csObj->getName());
+      error(-1, "Bad color space");
     }
     obj1.free();
   } else {
@@ -1181,18 +1181,22 @@ GfxPattern::~GfxPattern() {
 
 GfxPattern *GfxPattern::parse(Object *obj) {
   GfxPattern *pattern;
-  Dict *dict;
   Object obj1;
 
+  if (obj->isDict()) {
+    obj->dictLookup("PatternType", &obj1);
+  } else if (obj->isStream()) {
+    obj->streamGetDict()->lookup("PatternType", &obj1);
+  } else {
+    return NULL;
+  }
   pattern = NULL;
-  if (obj->isStream()) {
-    dict = obj->streamGetDict();
-    dict->lookup("PatternType", &obj1);
-    if (obj1.isInt() && obj1.getInt() == 1) {
-      pattern = new GfxTilingPattern(dict, obj);
-    }
-    obj1.free();
+  if (obj1.isInt() && obj1.getInt() == 1) {
+    pattern = GfxTilingPattern::parse(obj);
+  } else if (obj1.isInt() && obj1.getInt() == 2) {
+    pattern = GfxShadingPattern::parse(obj);
   }
+  obj1.free();
   return pattern;
 }
 
@@ -1200,33 +1204,42 @@ GfxPattern *GfxPattern::parse(Object *obj) {
 // GfxTilingPattern
 //------------------------------------------------------------------------
 
-GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
-  GfxPattern(1)
-{
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+  GfxTilingPattern *pat;
+  Dict *dict;
+  int paintTypeA, tilingTypeA;
+  double bboxA[4], matrixA[6];
+  double xStepA, yStepA;
+  Object resDictA;
   Object obj1, obj2;
   int i;
 
-  if (streamDict->lookup("PaintType", &obj1)->isInt()) {
-    paintType = obj1.getInt();
+  if (!patObj->isStream()) {
+    return NULL;
+  }
+  dict = patObj->streamGetDict();
+
+  if (dict->lookup("PaintType", &obj1)->isInt()) {
+    paintTypeA = obj1.getInt();
   } else {
-    paintType = 1;
+    paintTypeA = 1;
     error(-1, "Invalid or missing PaintType in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("TilingType", &obj1)->isInt()) {
-    tilingType = obj1.getInt();
+  if (dict->lookup("TilingType", &obj1)->isInt()) {
+    tilingTypeA = obj1.getInt();
   } else {
-    tilingType = 1;
+    tilingTypeA = 1;
     error(-1, "Invalid or missing TilingType in pattern");
   }
   obj1.free();
-  bbox[0] = bbox[1] = 0;
-  bbox[2] = bbox[3] = 1;
-  if (streamDict->lookup("BBox", &obj1)->isArray() &&
+  bboxA[0] = bboxA[1] = 0;
+  bboxA[2] = bboxA[3] = 1;
+  if (dict->lookup("BBox", &obj1)->isArray() &&
       obj1.arrayGetLength() == 4) {
     for (i = 0; i < 4; ++i) {
       if (obj1.arrayGet(i, &obj2)->isNum()) {
-       bbox[i] = obj2.getNum();
+       bboxA[i] = obj2.getNum();
       }
       obj2.free();
     }
@@ -1234,39 +1247,65 @@ GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
     error(-1, "Invalid or missing BBox in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("XStep", &obj1)->isNum()) {
-    xStep = obj1.getNum();
+  if (dict->lookup("XStep", &obj1)->isNum()) {
+    xStepA = obj1.getNum();
   } else {
-    xStep = 1;
+    xStepA = 1;
     error(-1, "Invalid or missing XStep in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("YStep", &obj1)->isNum()) {
-    yStep = obj1.getNum();
+  if (dict->lookup("YStep", &obj1)->isNum()) {
+    yStepA = obj1.getNum();
   } else {
-    yStep = 1;
+    yStepA = 1;
     error(-1, "Invalid or missing YStep in pattern");
   }
   obj1.free();
-  if (!streamDict->lookup("Resources", &resDict)->isDict()) {
-    resDict.free();
-    resDict.initNull();
+  if (!dict->lookup("Resources", &resDictA)->isDict()) {
+    resDictA.free();
+    resDictA.initNull();
     error(-1, "Invalid or missing Resources in pattern");
   }
-  matrix[0] = 1; matrix[1] = 0;
-  matrix[2] = 0; matrix[3] = 1;
-  matrix[4] = 0; matrix[5] = 0;
-  if (streamDict->lookup("Matrix", &obj1)->isArray() &&
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
       obj1.arrayGetLength() == 6) {
     for (i = 0; i < 6; ++i) {
       if (obj1.arrayGet(i, &obj2)->isNum()) {
-       matrix[i] = obj2.getNum();
+       matrixA[i] = obj2.getNum();
       }
       obj2.free();
     }
   }
   obj1.free();
-  stream->copy(&contentStream);
+
+  pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
+                            &resDictA, matrixA, patObj);
+  resDictA.free();
+  return pat;
+}
+
+GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                                  double *bboxA, double xStepA, double yStepA,
+                                  Object *resDictA, double *matrixA,
+                                  Object *contentStreamA):
+  GfxPattern(1)
+{
+  int i;
+
+  paintType = paintTypeA;
+  tilingType = tilingTypeA;
+  for (i = 0; i < 4; ++i) {
+    bbox[i] = bboxA[i];
+  }
+  xStep = xStepA;
+  yStep = yStepA;
+  resDictA->copy(&resDict);
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+  contentStreamA->copy(&contentStream);
 }
 
 GfxTilingPattern::~GfxTilingPattern() {
@@ -1275,127 +1314,341 @@ GfxTilingPattern::~GfxTilingPattern() {
 }
 
 GfxPattern *GfxTilingPattern::copy() {
-  return new GfxTilingPattern(this);
+  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+                             &resDict, matrix, &contentStream);
 }
 
-GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat):
-  GfxPattern(1)
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+  Dict *dict;
+  GfxShading *shadingA;
+  double matrixA[6];
+  Object obj1, obj2;
+  int i;
+
+  if (!patObj->isDict()) {
+    return NULL;
+  }
+  dict = patObj->getDict();
+
+  dict->lookup("Shading", &obj1);
+  shadingA = GfxShading::parse(&obj1);
+  obj1.free();
+  if (!shadingA) {
+    return NULL;
+  }
+
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    for (i = 0; i < 6; ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       matrixA[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  return new GfxShadingPattern(shadingA, matrixA);
+}
+
+GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
+  GfxPattern(2)
 {
-  memcpy(this, pat, sizeof(GfxTilingPattern));
-  pat->resDict.copy(&resDict);
-  pat->contentStream.copy(&contentStream);
+  int i;
+
+  shading = shadingA;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+}
+
+GfxShadingPattern::~GfxShadingPattern() {
+  delete shading;
+}
+
+GfxPattern *GfxShadingPattern::copy() {
+  return new GfxShadingPattern(shading->copy(), matrix);
 }
 
 //------------------------------------------------------------------------
 // GfxShading
 //------------------------------------------------------------------------
 
-GfxShading::GfxShading() {
+GfxShading::GfxShading(int typeA) {
+  type = typeA;
+  colorSpace = NULL;
+}
+
+GfxShading::GfxShading(GfxShading *shading) {
+  int i;
+
+  type = shading->type;
+  colorSpace = shading->colorSpace->copy();
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = shading->background.c[i];
+  }
+  hasBackground = shading->hasBackground;
+  xMin = shading->xMin;
+  yMin = shading->yMin;
+  xMax = shading->xMax;
+  yMax = shading->yMax;
+  hasBBox = shading->hasBBox;
 }
 
 GfxShading::~GfxShading() {
-  delete colorSpace;
+  if (colorSpace) {
+    delete colorSpace;
+  }
 }
 
 GfxShading *GfxShading::parse(Object *obj) {
   GfxShading *shading;
+  Dict *dict;
   int typeA;
-  GfxColorSpace *colorSpaceA;
-  GfxColor backgroundA;
-  GBool hasBackgroundA;
-  double xMinA, yMinA, xMaxA, yMaxA;
-  GBool hasBBoxA;
-  Object obj1, obj2;
-  int i;
+  Object obj1;
 
-  shading = NULL;
   if (obj->isDict()) {
+    dict = obj->getDict();
+  } else if (obj->isStream()) {
+    dict = obj->streamGetDict();
+  } else {
+    return NULL;
+  }
 
-    if (!obj->dictLookup("ShadingType", &obj1)->isInt()) {
-      error(-1, "Invalid ShadingType in shading dictionary");
-      obj1.free();
-      goto err1;
-    }
-    typeA = obj1.getInt();
+  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+    error(-1, "Invalid ShadingType in shading dictionary");
     obj1.free();
+    return NULL;
+  }
+  typeA = obj1.getInt();
+  obj1.free();
 
-    obj->dictLookup("ColorSpace", &obj1);
-    if (!(colorSpaceA = GfxColorSpace::parse(&obj1))) {
-      error(-1, "Bad color space in shading dictionary");
-      obj1.free();
-      goto err1;
-    }
-    obj1.free();
+  switch (typeA) {
+  case 1:
+    shading = GfxFunctionShading::parse(dict);
+    break;
+  case 2:
+    shading = GfxAxialShading::parse(dict);
+    break;
+  case 3:
+    shading = GfxRadialShading::parse(dict);
+    break;
+  default:
+    error(-1, "Unimplemented shading type %d", typeA);
+    goto err1;
+  }
 
-    for (i = 0; i < gfxColorMaxComps; ++i) {
-      backgroundA.c[i] = 0;
-    }
-    hasBackgroundA = gFalse;
-    if (obj->dictLookup("Background", &obj1)->isArray()) {
-      if (obj1.arrayGetLength() == colorSpaceA->getNComps()) {
-       hasBackgroundA = gTrue;
-       for (i = 0; i < colorSpaceA->getNComps(); ++i) {
-         backgroundA.c[i] = obj1.arrayGet(i, &obj2)->getNum();
-         obj2.free();
-       }
-      } else {
-       error(-1, "Bad Background in shading dictionary");
-      }
-    }
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GBool GfxShading::init(Dict *dict) {
+  Object obj1, obj2;
+  int i;
+
+  dict->lookup("ColorSpace", &obj1);
+  if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad color space in shading dictionary");
     obj1.free();
+    return gFalse;
+  }
+  obj1.free();
 
-    xMinA = yMinA = xMaxA = yMaxA = 0;
-    hasBBoxA = gFalse;
-    if (obj->dictLookup("BBox", &obj1)->isArray()) {
-      if (obj1.arrayGetLength() == 4) {
-       hasBBoxA = gTrue;
-       xMinA = obj1.arrayGet(0, &obj2)->getNum();
-       obj2.free();
-       yMinA = obj1.arrayGet(1, &obj2)->getNum();
-       obj2.free();
-       xMaxA = obj1.arrayGet(2, &obj2)->getNum();
-       obj2.free();
-       yMaxA = obj1.arrayGet(3, &obj2)->getNum();
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = 0;
+  }
+  hasBackground = gFalse;
+  if (dict->lookup("Background", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+      hasBackground = gTrue;
+      for (i = 0; i < colorSpace->getNComps(); ++i) {
+       background.c[i] = obj1.arrayGet(i, &obj2)->getNum();
        obj2.free();
-      } else {
-       error(-1, "Bad BBox in shading dictionary");
       }
+    } else {
+      error(-1, "Bad Background in shading dictionary");
     }
-    obj1.free();
+  }
+  obj1.free();
 
-    switch (typeA) {
-    case 2:
-      shading = GfxAxialShading::parse(obj->getDict());
-      break;
-    case 3:
-      shading = GfxRadialShading::parse(obj->getDict());
-      break;
-    default:
-      error(-1, "Unimplemented shading type %d", typeA);
-      goto err1;
+  xMin = yMin = xMax = yMax = 0;
+  hasBBox = gFalse;
+  if (dict->lookup("BBox", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == 4) {
+      hasBBox = gTrue;
+      xMin = obj1.arrayGet(0, &obj2)->getNum();
+      obj2.free();
+      yMin = obj1.arrayGet(1, &obj2)->getNum();
+      obj2.free();
+      xMax = obj1.arrayGet(2, &obj2)->getNum();
+      obj2.free();
+      yMax = obj1.arrayGet(3, &obj2)->getNum();
+      obj2.free();
+    } else {
+      error(-1, "Bad BBox in shading dictionary");
     }
+  }
+  obj1.free();
 
-    if (shading) {
-      shading->type = typeA;
-      shading->colorSpace = colorSpaceA;
-      shading->background = backgroundA;
-      shading->hasBackground = hasBackgroundA;
-      shading->xMin = xMinA;
-      shading->yMin = yMinA;
-      shading->xMax = xMaxA;
-      shading->yMax = yMaxA;
-      shading->hasBBox = hasBBoxA;
-    } else {
-      delete colorSpaceA;
+  return gTrue;
+}
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
+                                      double x1A, double y1A,
+                                      double *matrixA,
+                                      Function **funcsA, int nFuncsA):
+  GfxShading(1)
+{
+  int i;
+
+  x0 = x0A;
+  y0 = y0A;
+  x1 = x1A;
+  y1 = y1A;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = shading->matrix[i];
+  }
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxFunctionShading::~GfxFunctionShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+  GfxFunctionShading *shading;
+  double x0A, y0A, x1A, y1A;
+  double matrixA[6];
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = 0;
+  x1A = y1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 4) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    y1A = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+  }
+  obj1.free();
+
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
+    obj2.free();
+    matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
+    obj2.free();
+  }
+  obj1.free();
+
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       goto err2;
+      }
+      obj2.free();
+    }
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      goto err1;
     }
   }
+  obj1.free();
 
+  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
+                                  funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
   return shading;
 
+ err2:
+  obj2.free();
  err1:
+  obj1.free();
   return NULL;
 }
 
+GfxShading *GfxFunctionShading::copy() {
+  return new GfxFunctionShading(this);
+}
+
+void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
+  double in[2];
+  int i;
+
+  in[0] = x;
+  in[1] = y;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(in, &color->c[i]);
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxAxialShading
 //------------------------------------------------------------------------
@@ -1404,7 +1657,9 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A,
                                 double x1A, double y1A,
                                 double t0A, double t1A,
                                 Function **funcsA, int nFuncsA,
-                                GBool extend0A, GBool extend1A) {
+                                GBool extend0A, GBool extend1A):
+  GfxShading(2)
+{
   int i;
 
   x0 = x0A;
@@ -1421,6 +1676,25 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A,
   extend1 = extend1A;
 }
 
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
+
 GfxAxialShading::~GfxAxialShading() {
   int i;
 
@@ -1430,6 +1704,7 @@ GfxAxialShading::~GfxAxialShading() {
 }
 
 GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+  GfxAxialShading *shading;
   double x0A, y0A, x1A, y1A;
   double t0A, t1A;
   Function *funcsA[gfxColorMaxComps];
@@ -1469,6 +1744,10 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
   dict->lookup("Function", &obj1);
   if (obj1.isArray()) {
     nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
     for (i = 0; i < nFuncsA; ++i) {
       obj1.arrayGet(i, &obj2);
       if (!(funcsA[i] = Function::parse(&obj2))) {
@@ -1497,16 +1776,27 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
   }
   obj1.free();
 
-  return new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
-                            funcsA, nFuncsA, extend0A, extend1A);
+  shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+                               funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
 
  err1:
   return NULL;
 }
 
+GfxShading *GfxAxialShading::copy() {
+  return new GfxAxialShading(this);
+}
+
 void GfxAxialShading::getColor(double t, GfxColor *color) {
   int i;
 
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
   for (i = 0; i < nFuncs; ++i) {
     funcs[i]->transform(&t, &color->c[i]);
   }
@@ -1520,7 +1810,9 @@ GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
                                   double x1A, double y1A, double r1A,
                                   double t0A, double t1A,
                                   Function **funcsA, int nFuncsA,
-                                  GBool extend0A, GBool extend1A) {
+                                  GBool extend0A, GBool extend1A):
+  GfxShading(3)
+{
   int i;
 
   x0 = x0A;
@@ -1539,6 +1831,27 @@ GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
   extend1 = extend1A;
 }
 
+GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  r0 = shading->r0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  r1 = shading->r1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
+
 GfxRadialShading::~GfxRadialShading() {
   int i;
 
@@ -1548,6 +1861,7 @@ GfxRadialShading::~GfxRadialShading() {
 }
 
 GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+  GfxRadialShading *shading;
   double x0A, y0A, r0A, x1A, y1A, r1A;
   double t0A, t1A;
   Function *funcsA[gfxColorMaxComps];
@@ -1591,6 +1905,10 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
   dict->lookup("Function", &obj1);
   if (obj1.isArray()) {
     nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
     for (i = 0; i < nFuncsA; ++i) {
       obj1.arrayGet(i, &obj2);
       if (!(funcsA[i] = Function::parse(&obj2))) {
@@ -1619,16 +1937,27 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
   }
   obj1.free();
 
-  return new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
-                             funcsA, nFuncsA, extend0A, extend1A);
+  shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+                                funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
 
  err1:
   return NULL;
 }
 
+GfxShading *GfxRadialShading::copy() {
+  return new GfxRadialShading(this);
+}
+
 void GfxRadialShading::getColor(double t, GfxColor *color) {
   int i;
 
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
   for (i = 0; i < nFuncs; ++i) {
     funcs[i]->transform(&t, &color->c[i]);
   }
@@ -1741,6 +2070,36 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
   ok = gFalse;
 }
 
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+  int n, i;
+
+  colorSpace = colorMap->colorSpace->copy();
+  bits = colorMap->bits;
+  nComps = colorMap->nComps;
+  nComps2 = colorMap->nComps2;
+  colorSpace2 = NULL;
+  lookup = NULL;
+  if (colorSpace->getMode() == csIndexed) {
+    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+    n = ((GfxIndexedColorSpace *)colorSpace)->getIndexHigh();
+    n = (n + 1) * nComps2 * sizeof(double);
+  } else if (colorSpace->getMode() == csSeparation) {
+    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+    n = (1 << bits) - 1;
+    n = (n + 1) * nComps2 * sizeof(double);
+  } else {
+    n = (1 << bits) - 1;
+    n = (n + 1) * nComps * sizeof(double);
+  }
+  lookup = (double *)gmalloc(n);
+  memcpy(lookup, colorMap->lookup, n);
+  for (i = 0; i < nComps; ++i) {
+    decodeLow[i] = colorMap->decodeLow[i];
+    decodeRange[i] = colorMap->decodeRange[i];
+  }
+  ok = gTrue;
+}
+
 GfxImageColorMap::~GfxImageColorMap() {
   delete colorSpace;
   gfree(lookup);
@@ -1886,6 +2245,15 @@ void GfxSubpath::close() {
   closed = gTrue;
 }
 
+void GfxSubpath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    x[i] += dx;
+    y[i] += dy;
+  }
+}
+
 GfxPath::GfxPath() {
   justMoved = gFalse;
   size = 16;
@@ -1968,55 +2336,78 @@ void GfxPath::close() {
   subpaths[n-1]->close();
 }
 
+void GfxPath::append(GfxPath *path) {
+  int i;
+
+  if (n + path->n > size) {
+    size = n + path->n;
+    subpaths = (GfxSubpath **)
+                 grealloc(subpaths, size * sizeof(GfxSubpath *));
+  }
+  for (i = 0; i < path->n; ++i) {
+    subpaths[n++] = path->subpaths[i]->copy();
+  }
+  justMoved = gFalse;
+}
+
+void GfxPath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    subpaths[i]->offset(dx, dy);
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxState
 //------------------------------------------------------------------------
 
-GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
-                  GBool upsideDown) {
-  double k;
+GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+                  int rotate, GBool upsideDown) {
+  double kx, ky;
 
   px1 = pageBox->x1;
   py1 = pageBox->y1;
   px2 = pageBox->x2;
   py2 = pageBox->y2;
-  k = dpi / 72.0;
+  kx = hDPI / 72.0;
+  ky = vDPI / 72.0;
   if (rotate == 90) {
     ctm[0] = 0;
-    ctm[1] = upsideDown ? k : -k;
-    ctm[2] = k;
+    ctm[1] = upsideDown ? ky : -ky;
+    ctm[2] = kx;
     ctm[3] = 0;
-    ctm[4] = -k * py1;
-    ctm[5] = k * (upsideDown ? -px1 : px2);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = -kx * py1;
+    ctm[5] = ky * (upsideDown ? -px1 : px2);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else if (rotate == 180) {
-    ctm[0] = -k;
+    ctm[0] = -kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? k : -k;
-    ctm[4] = k * px2;
-    ctm[5] = k * (upsideDown ? -py1 : py2);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    ctm[3] = upsideDown ? ky : -ky;
+    ctm[4] = kx * px2;
+    ctm[5] = ky * (upsideDown ? -py1 : py2);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
   } else if (rotate == 270) {
     ctm[0] = 0;
-    ctm[1] = upsideDown ? -k : k;
-    ctm[2] = -k;
+    ctm[1] = upsideDown ? -ky : ky;
+    ctm[2] = -kx;
     ctm[3] = 0;
-    ctm[4] = k * py2;
-    ctm[5] = k * (upsideDown ? px2 : -px1);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = kx * py2;
+    ctm[5] = ky * (upsideDown ? px2 : -px1);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else {
-    ctm[0] = k;
+    ctm[0] = kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? -k : k;
-    ctm[4] = -k * px1;
-    ctm[5] = k * (upsideDown ? py2 : -py1);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    ctm[3] = upsideDown ? -ky : ky;
+    ctm[4] = -kx * px1;
+    ctm[5] = ky * (upsideDown ? py2 : -py1);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
   }
 
   fillColorSpace = new GfxDeviceGrayColorSpace();
@@ -2032,7 +2423,7 @@ GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
   lineDash = NULL;
   lineDashLength = 0;
   lineDashStart = 0;
-  flatness = 0;
+  flatness = 1;
   lineJoin = 0;
   lineCap = 0;
   miterLimit = 10;
index a0b1d14aee56440b9e05a0851e4d7e27739150ef..d072fd3a13cced9222ef4c45d499f24474b53cd5 100644 (file)
@@ -22,6 +22,7 @@
 class Array;
 class GfxFont;
 class PDFRectangle;
+class GfxShading;
 
 //------------------------------------------------------------------------
 // GfxColor
@@ -402,7 +403,7 @@ private:
 class GfxDeviceNColorSpace: public GfxColorSpace {
 public:
 
-  GfxDeviceNColorSpace(int nComps, GfxColorSpace *alt, Function *func);
+  GfxDeviceNColorSpace(int nCompsA, GfxColorSpace *alt, Function *func);
   virtual ~GfxDeviceNColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceN; }
@@ -489,7 +490,7 @@ private:
 class GfxTilingPattern: public GfxPattern {
 public:
 
-  GfxTilingPattern(Dict *streamDict, Object *stream);
+  static GfxTilingPattern *parse(Object *patObj);
   virtual ~GfxTilingPattern();
 
   virtual GfxPattern *copy();
@@ -506,7 +507,10 @@ public:
 
 private:
 
-  GfxTilingPattern(GfxTilingPattern *pat);
+  GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                  double *bboxA, double xStepA, double yStepA,
+                  Object *resDictA, double *matrixA,
+                  Object *contentStreamA);
 
   int paintType;
   int tilingType;
@@ -517,6 +521,29 @@ private:
   Object contentStream;
 };
 
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+class GfxShadingPattern: public GfxPattern {
+public:
+
+  static GfxShadingPattern *parse(Object *patObj);
+  virtual ~GfxShadingPattern();
+
+  virtual GfxPattern *copy();
+
+  GfxShading *getShading() { return shading; }
+  double *getMatrix() { return matrix; }
+
+private:
+
+  GfxShadingPattern(GfxShading *shadingA, double *matrixA);
+
+  GfxShading *shading;
+  double matrix[6];
+};
+
 //------------------------------------------------------------------------
 // GfxShading
 //------------------------------------------------------------------------
@@ -524,11 +551,14 @@ private:
 class GfxShading {
 public:
 
-  GfxShading();
+  GfxShading(int typeA);
+  GfxShading(GfxShading *shading);
   virtual ~GfxShading();
 
   static GfxShading *parse(Object *obj);
 
+  virtual GfxShading *copy() = 0;
+
   int getType() { return type; }
   GfxColorSpace *getColorSpace() { return colorSpace; }
   GfxColor *getBackground() { return &background; }
@@ -537,7 +567,9 @@ public:
     { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
   GBool getHasBBox() { return hasBBox; }
 
-private:
+protected:
+
+  GBool init(Dict *dict);
 
   int type;
   GfxColorSpace *colorSpace;
@@ -547,6 +579,37 @@ private:
   GBool hasBBox;
 };
 
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+class GfxFunctionShading: public GfxShading {
+public:
+
+  GfxFunctionShading(double x0A, double y0A,
+                    double x1A, double y1A,
+                    double *matrixA,
+                    Function **funcsA, int nFuncsA);
+  GfxFunctionShading(GfxFunctionShading *shading);
+  virtual ~GfxFunctionShading();
+
+  static GfxFunctionShading *parse(Dict *dict);
+
+  virtual GfxShading *copy();
+
+  void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
+    { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+  double *getMatrix() { return matrix; }
+  void getColor(double x, double y, GfxColor *color);
+
+private:
+
+  double x0, y0, x1, y1;
+  double matrix[6];
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
 //------------------------------------------------------------------------
 // GfxAxialShading
 //------------------------------------------------------------------------
@@ -559,10 +622,13 @@ public:
                  double t0A, double t1A,
                  Function **funcsA, int nFuncsA,
                  GBool extend0A, GBool extend1A);
+  GfxAxialShading(GfxAxialShading *shading);
   virtual ~GfxAxialShading();
 
   static GfxAxialShading *parse(Dict *dict);
 
+  virtual GfxShading *copy();
+
   void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
     { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
   double getDomain0() { return t0; }
@@ -592,10 +658,13 @@ public:
                   double t0A, double t1A,
                   Function **funcsA, int nFuncsA,
                   GBool extend0A, GBool extend1A);
+  GfxRadialShading(GfxRadialShading *shading);
   virtual ~GfxRadialShading();
 
   static GfxRadialShading *parse(Dict *dict);
 
+  virtual GfxShading *copy();
+
   void getCoords(double *x0A, double *y0A, double *r0A,
                 double *x1A, double *y1A, double *r1A)
     { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
@@ -627,6 +696,9 @@ public:
   // Destructor.
   ~GfxImageColorMap();
 
+  // Return a copy of this color map.
+  GfxImageColorMap *copy() { return new GfxImageColorMap(this); }
+
   // Is color map valid?
   GBool isOk() { return ok; }
 
@@ -649,6 +721,8 @@ public:
 
 private:
 
+  GfxImageColorMap(GfxImageColorMap *colorMap);
+
   GfxColorSpace *colorSpace;   // the image color space
   int bits;                    // bits per component
   int nComps;                  // number of components in a pixel
@@ -699,6 +773,9 @@ public:
   void close();
   GBool isClosed() { return closed; }
 
+  // Add (<dx>, <dy>) to each point in the subpath.
+  void offset(double dx, double dy);
+
 private:
 
   double *x, *y;               // points
@@ -751,6 +828,12 @@ public:
   // Close the last subpath.
   void close();
 
+  // Append <path> to <this>.
+  void append(GfxPath *path);
+
+  // Add (<dx>, <dy>) to each point in the path.
+  void offset(double dx, double dy);
+
 private:
 
   GBool justMoved;             // set if a new subpath was just started
@@ -770,11 +853,11 @@ private:
 class GfxState {
 public:
 
-  // Construct a default GfxState, for a device with resolution <dpi>,
-  // page box <pageBox>, page rotation <rotate>, and coordinate system
-  // specified by <upsideDown>.
-  GfxState(double dpi, PDFRectangle *pageBox, int rotate,
-          GBool upsideDown);
+  // Construct a default GfxState, for a device with resolution <hDPI>
+  // x <vDPI>, page box <pageBox>, page rotation <rotate>, and
+  // coordinate system specified by <upsideDown>.
+  GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+          int rotate, GBool upsideDown);
 
   // Destructor.
   ~GfxState();
@@ -795,7 +878,7 @@ public:
   void getFillGray(double *gray)
     { fillColorSpace->getGray(&fillColor, gray); }
   void getStrokeGray(double *gray)
-    { strokeColorSpace->getGray(&fillColor, gray); }
+    { strokeColorSpace->getGray(&strokeColor, gray); }
   void getFillRGB(GfxRGB *rgb)
     { fillColorSpace->getRGB(&fillColor, rgb); }
   void getStrokeRGB(GfxRGB *rgb)
index c5083b2afc41ef1f758a21e8e28c4a78d412fbb5..9aa54e9e7ad32645176b6643933017711cf1939a 100644 (file)
 #include "GlobalParams.h"
 
 #if MULTITHREADED
-#  define globalParamsLock gLockMutex(&mutex)
-#  define globalParamsUnlock gUnlockMutex(&mutex)
+#  define lockGlobalParams            gLockMutex(&mutex)
+#  define lockUnicodeMapCache         gLockMutex(&unicodeMapCacheMutex)
+#  define lockCMapCache               gLockMutex(&cMapCacheMutex)
+#  define unlockGlobalParams          gUnlockMutex(&mutex)
+#  define unlockUnicodeMapCache       gUnlockMutex(&unicodeMapCacheMutex)
+#  define unlockCMapCache             gUnlockMutex(&cMapCacheMutex)
 #else
-#  define globalParamsLock
-#  define globalParamsUnlock
+#  define lockGlobalParams
+#  define lockUnicodeMapCache
+#  define lockCMapCache
+#  define unlockGlobalParams
+#  define unlockUnicodeMapCache
+#  define unlockCMapCache
 #endif
 
 #include "NameToUnicodeTable.h"
 
 //------------------------------------------------------------------------
 
+#define cidToUnicodeCacheSize     4
+#define unicodeToUnicodeCacheSize 4
+
+//------------------------------------------------------------------------
+
 GlobalParams *globalParams = NULL;
 
 //------------------------------------------------------------------------
@@ -134,6 +147,8 @@ GlobalParams::GlobalParams(char *cfgFileName) {
 
 #if MULTITHREADED
   gInitMutex(&mutex);
+  gInitMutex(&unicodeMapCacheMutex);
+  gInitMutex(&cMapCacheMutex);
 #endif
 
   initBuiltinFontTables();
@@ -149,6 +164,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
 
   nameToUnicode = new NameToCharCode();
   cidToUnicodes = new GHash(gTrue);
+  unicodeToUnicodes = new GHash(gTrue);
   residentUnicodeMaps = new GHash();
   unicodeMaps = new GHash(gTrue);
   cMapDirs = new GHash(gTrue);
@@ -194,9 +210,10 @@ GlobalParams::GlobalParams(char *cfgFileName) {
 #else
   textEOL = eolUnix;
 #endif
+  textPageBreaks = gTrue;
   textKeepTinyChars = gFalse;
   fontDirs = new GList();
-  initialZoom = new GString("1");
+  initialZoom = new GString("125");
   t1libControl = fontRastAALow;
   freetypeControl = fontRastAALow;
   urlCommand = NULL;
@@ -205,7 +222,9 @@ GlobalParams::GlobalParams(char *cfgFileName) {
   printCommands = gFalse;
   errQuiet = gFalse;
 
-  cidToUnicodeCache = new CIDToUnicodeCache();
+  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
+  unicodeToUnicodeCache =
+      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
   unicodeMapCache = new UnicodeMapCache();
   cMapCache = new CMapCache();
 
@@ -329,6 +348,8 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        parseNameToUnicode(tokens, fileName, line);
       } else if (!cmd->cmp("cidToUnicode")) {
        parseCIDToUnicode(tokens, fileName, line);
+      } else if (!cmd->cmp("unicodeToUnicode")) {
+       parseUnicodeToUnicode(tokens, fileName, line);
       } else if (!cmd->cmp("unicodeMap")) {
        parseUnicodeMap(tokens, fileName, line);
       } else if (!cmd->cmp("cMapDir")) {
@@ -393,6 +414,9 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        parseTextEncoding(tokens, fileName, line);
       } else if (!cmd->cmp("textEOL")) {
        parseTextEOL(tokens, fileName, line);
+      } else if (!cmd->cmp("textPageBreaks")) {
+       parseYesNo("textPageBreaks", &textPageBreaks,
+                  tokens, fileName, line);
       } else if (!cmd->cmp("textKeepTinyChars")) {
        parseYesNo("textKeepTinyChars", &textKeepTinyChars,
                   tokens, fileName, line);
@@ -483,6 +507,23 @@ void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
   cidToUnicodes->add(collection->copy(), name->copy());
 }
 
+void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
+                                        int line) {
+  GString *font, *file, *old;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  font = (GString *)tokens->get(1);
+  file = (GString *)tokens->get(2);
+  if ((old = (GString *)unicodeToUnicodes->remove(font))) {
+    delete old;
+  }
+  unicodeToUnicodes->add(font->copy(), file->copy());
+}
+
 void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
                                   int line) {
   GString *encodingName, *name, *old;
@@ -787,6 +828,7 @@ GlobalParams::~GlobalParams() {
 
   delete nameToUnicode;
   deleteGHash(cidToUnicodes, GString);
+  deleteGHash(unicodeToUnicodes, GString);
   deleteGHash(residentUnicodeMaps, UnicodeMap);
   deleteGHash(unicodeMaps, GString);
   deleteGList(toUnicodeDirs, GString);
@@ -816,11 +858,14 @@ GlobalParams::~GlobalParams() {
   delete cMapDirs;
 
   delete cidToUnicodeCache;
+  delete unicodeToUnicodeCache;
   delete unicodeMapCache;
   delete cMapCache;
 
 #if MULTITHREADED
   gDestroyMutex(&mutex);
+  gDestroyMutex(&unicodeMapCacheMutex);
+  gDestroyMutex(&cMapCacheMutex);
 #endif
 }
 
@@ -829,33 +874,39 @@ GlobalParams::~GlobalParams() {
 //------------------------------------------------------------------------
 
 CharCode GlobalParams::getMacRomanCharCode(char *charName) {
+  // no need to lock - macRomanReverseMap is constant
   return macRomanReverseMap->lookup(charName);
 }
 
 Unicode GlobalParams::mapNameToUnicode(char *charName) {
+  // no need to lock - nameToUnicode is constant
   return nameToUnicode->lookup(charName);
 }
 
-FILE *GlobalParams::getCIDToUnicodeFile(GString *collection) {
-  GString *fileName;
+UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
+  UnicodeMap *map;
 
-  if (!(fileName = (GString *)cidToUnicodes->lookup(collection))) {
-    return NULL;
+  lockGlobalParams;
+  map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+  unlockGlobalParams;
+  if (map) {
+    map->incRefCnt();
   }
-  return fopen(fileName->getCString(), "r");
-}
-
-UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
-  return (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+  return map;
 }
 
 FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
   GString *fileName;
+  FILE *f;
 
-  if (!(fileName = (GString *)unicodeMaps->lookup(encodingName))) {
-    return NULL;
+  lockGlobalParams;
+  if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
+    f = fopen(fileName->getCString(), "r");
+  } else {
+    f = NULL;
   }
-  return fopen(fileName->getCString(), "r");
+  unlockGlobalParams;
+  return f;
 }
 
 FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
@@ -865,7 +916,9 @@ FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
   FILE *f;
   int i;
 
+  lockGlobalParams;
   if (!(list = (GList *)cMapDirs->lookup(collection))) {
+    unlockGlobalParams;
     return NULL;
   }
   for (i = 0; i < list->getLength(); ++i) {
@@ -874,9 +927,11 @@ FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
     f = fopen(fileName->getCString(), "r");
     delete fileName;
     if (f) {
+      unlockGlobalParams;
       return f;
     }
   }
+  unlockGlobalParams;
   return NULL;
 }
 
@@ -885,24 +940,27 @@ FILE *GlobalParams::findToUnicodeFile(GString *name) {
   FILE *f;
   int i;
 
+  lockGlobalParams;
   for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
     dir = (GString *)toUnicodeDirs->get(i);
     fileName = appendToPath(dir->copy(), name->getCString());
     f = fopen(fileName->getCString(), "r");
     delete fileName;
     if (f) {
+      unlockGlobalParams;
       return f;
     }
   }
+  unlockGlobalParams;
   return NULL;
 }
 
 DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {
   DisplayFontParam *dfp;
 
-  globalParamsLock;
+  lockGlobalParams;
   dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
-  globalParamsUnlock;
+  unlockGlobalParams;
   return dfp;
 }
 
@@ -910,60 +968,67 @@ DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,
                                                  GString *collection) {
   DisplayFontParam *dfp;
 
+  lockGlobalParams;
   if (!fontName ||
       !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {
     dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);
   }
+  unlockGlobalParams;
   return dfp;
 }
 
 GString *GlobalParams::getPSFile() {
   GString *s;
 
-  globalParamsLock;
+  lockGlobalParams;
   s = psFile ? psFile->copy() : (GString *)NULL;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return s;
 }
 
 int GlobalParams::getPSPaperWidth() {
   int w;
 
-  globalParamsLock;
+  lockGlobalParams;
   w = psPaperWidth;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return w;
 }
 
 int GlobalParams::getPSPaperHeight() {
   int h;
 
-  globalParamsLock;
+  lockGlobalParams;
   h = psPaperHeight;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return h;
 }
 
 GBool GlobalParams::getPSDuplex() {
   GBool d;
 
-  globalParamsLock;
+  lockGlobalParams;
   d = psDuplex;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return d;
 }
 
 PSLevel GlobalParams::getPSLevel() {
   PSLevel level;
 
-  globalParamsLock;
+  lockGlobalParams;
   level = psLevel;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return level;
 }
 
 PSFontParam *GlobalParams::getPSFont(GString *fontName) {
-  return (PSFontParam *)psFonts->lookup(fontName);
+  PSFontParam *p;
+
+  lockGlobalParams;
+  p = (PSFontParam *)psFonts->lookup(fontName);
+  unlockGlobalParams;
+  return p;
 }
 
 PSFontParam *GlobalParams::getPSFont16(GString *fontName,
@@ -971,6 +1036,7 @@ PSFontParam *GlobalParams::getPSFont16(GString *fontName,
   PSFontParam *p;
   int i;
 
+  lockGlobalParams;
   p = NULL;
   if (fontName) {
     for (i = 0; i < psNamedFonts16->getLength(); ++i) {
@@ -992,78 +1058,97 @@ PSFontParam *GlobalParams::getPSFont16(GString *fontName,
       p = NULL;
     }
   }
+  unlockGlobalParams;
   return p;
 }
 
 GBool GlobalParams::getPSEmbedType1() {
   GBool e;
 
-  globalParamsLock;
+  lockGlobalParams;
   e = psEmbedType1;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return e;
 }
 
 GBool GlobalParams::getPSEmbedTrueType() {
   GBool e;
 
-  globalParamsLock;
+  lockGlobalParams;
   e = psEmbedTrueType;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return e;
 }
 
 GBool GlobalParams::getPSEmbedCIDPostScript() {
   GBool e;
 
-  globalParamsLock;
+  lockGlobalParams;
   e = psEmbedCIDPostScript;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return e;
 }
 
 GBool GlobalParams::getPSEmbedCIDTrueType() {
   GBool e;
 
-  globalParamsLock;
+  lockGlobalParams;
   e = psEmbedCIDTrueType;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return e;
 }
 
 GBool GlobalParams::getPSOPI() {
   GBool opi;
 
-  globalParamsLock;
+  lockGlobalParams;
   opi = psOPI;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return opi;
 }
 
 GBool GlobalParams::getPSASCIIHex() {
   GBool ah;
 
-  globalParamsLock;
+  lockGlobalParams;
   ah = psASCIIHex;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return ah;
 }
 
+GString *GlobalParams::getTextEncodingName() {
+  GString *s;
+
+  lockGlobalParams;
+  s = textEncoding->copy();
+  unlockGlobalParams;
+  return s;
+}
+
 EndOfLineKind GlobalParams::getTextEOL() {
   EndOfLineKind eol;
 
-  globalParamsLock;
+  lockGlobalParams;
   eol = textEOL;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return eol;
 }
 
+GBool GlobalParams::getTextPageBreaks() {
+  GBool pageBreaks;
+
+  lockGlobalParams;
+  pageBreaks = textPageBreaks;
+  unlockGlobalParams;
+  return pageBreaks;
+}
+
 GBool GlobalParams::getTextKeepTinyChars() {
   GBool tiny;
 
-  globalParamsLock;
+  lockGlobalParams;
   tiny = textKeepTinyChars;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return tiny;
 }
 
@@ -1073,100 +1158,132 @@ GString *GlobalParams::findFontFile(GString *fontName, char **exts) {
   FILE *f;
   int i;
 
+  lockGlobalParams;
   for (i = 0; i < fontDirs->getLength(); ++i) {
     dir = (GString *)fontDirs->get(i);
     for (ext = exts; *ext; ++ext) {
       fileName = appendToPath(dir->copy(), fontName->getCString());
       fileName->append(*ext);
-      if ((f = fopen(fileName->getCString(), "r"))) {
+      if ((f = fopen(fileName->getCString(), "rb"))) {
        fclose(f);
+       unlockGlobalParams;
        return fileName;
       }
       delete fileName;
     }
   }
+  unlockGlobalParams;
   return NULL;
 }
 
 GString *GlobalParams::getInitialZoom() {
   GString *s;
 
-  globalParamsLock;
+  lockGlobalParams;
   s = initialZoom->copy();
-  globalParamsUnlock;
+  unlockGlobalParams;
   return s;
 }
 
 FontRastControl GlobalParams::getT1libControl() {
   FontRastControl c;
 
-  globalParamsLock;
+  lockGlobalParams;
   c = t1libControl;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return c;
 }
 
 FontRastControl GlobalParams::getFreeTypeControl() {
   FontRastControl c;
 
-  globalParamsLock;
+  lockGlobalParams;
   c = freetypeControl;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return c;
 }
 
 GBool GlobalParams::getMapNumericCharNames() {
   GBool map;
 
-  globalParamsLock;
+  lockGlobalParams;
   map = mapNumericCharNames;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return map;
 }
 
 GBool GlobalParams::getPrintCommands() {
   GBool p;
 
-  globalParamsLock;
+  lockGlobalParams;
   p = printCommands;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return p;
 }
 
 GBool GlobalParams::getErrQuiet() {
   GBool q;
 
-  globalParamsLock;
+  lockGlobalParams;
   q = errQuiet;
-  globalParamsUnlock;
+  unlockGlobalParams;
   return q;
 }
 
 CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
+  GString *fileName;
   CharCodeToUnicode *ctu;
 
-  globalParamsLock;
-  ctu = cidToUnicodeCache->getCIDToUnicode(collection);
-  globalParamsUnlock;
+  lockGlobalParams;
+  if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
+    if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
+       (ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
+      cidToUnicodeCache->add(ctu);
+    }
+  }
+  unlockGlobalParams;
   return ctu;
 }
 
-UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
-  UnicodeMap *map;
+CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
+  CharCodeToUnicode *ctu;
+  GHashIter *iter;
+  GString *fontPattern, *fileName;
 
-  globalParamsLock;
-  map = getUnicodeMap2(encodingName);
-  globalParamsUnlock;
-  return map;
+  lockGlobalParams;
+  fileName = NULL;
+  unicodeToUnicodes->startIter(&iter);
+  while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
+    if (strstr(fontName->getCString(), fontPattern->getCString())) {
+      unicodeToUnicodes->killIter(&iter);
+      break;
+    }
+    fileName = NULL;
+  }
+  if (fileName) {
+    if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
+      if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
+       unicodeToUnicodeCache->add(ctu);
+      }
+    }
+  } else {
+    ctu = NULL;
+  }
+  unlockGlobalParams;
+  return ctu;
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
+  return getUnicodeMap2(encodingName);
 }
 
 UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
   UnicodeMap *map;
 
-  if ((map = getResidentUnicodeMap(encodingName))) {
-    map->incRefCnt();
-  } else {
+  if (!(map = getResidentUnicodeMap(encodingName))) {
+    lockUnicodeMapCache;
     map = unicodeMapCache->getUnicodeMap(encodingName);
+    unlockUnicodeMapCache;
   }
   return map;
 }
@@ -1174,19 +1291,14 @@ UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
 CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
   CMap *cMap;
 
-  globalParamsLock;
+  lockCMapCache;
   cMap = cMapCache->getCMap(collection, cMapName);
-  globalParamsUnlock;
+  unlockCMapCache;
   return cMap;
 }
 
 UnicodeMap *GlobalParams::getTextEncoding() {
-  UnicodeMap *map;
-
-  globalParamsLock;
-  map = getUnicodeMap2(textEncoding);
-  globalParamsUnlock;
-  return map;
+  return getUnicodeMap2(textEncoding);
 }
 
 //------------------------------------------------------------------------
@@ -1196,25 +1308,25 @@ UnicodeMap *GlobalParams::getTextEncoding() {
 void GlobalParams::addDisplayFont(DisplayFontParam *param) {
   DisplayFontParam *old;
 
-  globalParamsLock;
+  lockGlobalParams;
   if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {
     delete old;
   }
   displayFonts->add(param->name, param);
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSFile(char *file) {
-  globalParamsLock;
+  lockGlobalParams;
   if (psFile) {
     delete psFile;
   }
   psFile = new GString(file);
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 GBool GlobalParams::setPSPaperSize(char *size) {
-  globalParamsLock;
+  lockGlobalParams;
   if (!strcmp(size, "match")) {
     psPaperWidth = psPaperHeight = -1;
   } else if (!strcmp(size, "letter")) {
@@ -1230,82 +1342,82 @@ GBool GlobalParams::setPSPaperSize(char *size) {
     psPaperWidth = 842;
     psPaperHeight = 1190;
   } else {
-    globalParamsUnlock;
+    unlockGlobalParams;
     return gFalse;
   }
-  globalParamsUnlock;
+  unlockGlobalParams;
   return gTrue;
 }
 
 void GlobalParams::setPSPaperWidth(int width) {
-  globalParamsLock;
+  lockGlobalParams;
   psPaperWidth = width;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSPaperHeight(int height) {
-  globalParamsLock;
+  lockGlobalParams;
   psPaperHeight = height;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSDuplex(GBool duplex) {
-  globalParamsLock;
+  lockGlobalParams;
   psDuplex = duplex;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSLevel(PSLevel level) {
-  globalParamsLock;
+  lockGlobalParams;
   psLevel = level;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSEmbedType1(GBool embed) {
-  globalParamsLock;
+  lockGlobalParams;
   psEmbedType1 = embed;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSEmbedTrueType(GBool embed) {
-  globalParamsLock;
+  lockGlobalParams;
   psEmbedTrueType = embed;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
-  globalParamsLock;
+  lockGlobalParams;
   psEmbedCIDPostScript = embed;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
-  globalParamsLock;
+  lockGlobalParams;
   psEmbedCIDTrueType = embed;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSOPI(GBool opi) {
-  globalParamsLock;
+  lockGlobalParams;
   psOPI = opi;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPSASCIIHex(GBool hex) {
-  globalParamsLock;
+  lockGlobalParams;
   psASCIIHex = hex;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setTextEncoding(char *encodingName) {
-  globalParamsLock;
+  lockGlobalParams;
   delete textEncoding;
   textEncoding = new GString(encodingName);
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 GBool GlobalParams::setTextEOL(char *s) {
-  globalParamsLock;
+  lockGlobalParams;
   if (!strcmp(s, "unix")) {
     textEOL = eolUnix;
   } else if (!strcmp(s, "dos")) {
@@ -1313,45 +1425,52 @@ GBool GlobalParams::setTextEOL(char *s) {
   } else if (!strcmp(s, "mac")) {
     textEOL = eolMac;
   } else {
-    globalParamsUnlock;
+    unlockGlobalParams;
     return gFalse;
   }
-  globalParamsUnlock;
+  unlockGlobalParams;
   return gTrue;
 }
 
+void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
+  lockGlobalParams;
+  textPageBreaks = pageBreaks;
+  unlockGlobalParams;
+}
+
 void GlobalParams::setTextKeepTinyChars(GBool keep) {
-  globalParamsLock;
+  lockGlobalParams;
   textKeepTinyChars = keep;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setInitialZoom(char *s) {
-  globalParamsLock;
+  lockGlobalParams;
   delete initialZoom;
   initialZoom = new GString(s);
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 GBool GlobalParams::setT1libControl(char *s) {
   GBool ok;
 
-  globalParamsLock;
+  lockGlobalParams;
   ok = setFontRastControl(&t1libControl, s);
-  globalParamsUnlock;
+  unlockGlobalParams;
   return ok;
 }
 
 GBool GlobalParams::setFreeTypeControl(char *s) {
   GBool ok;
 
-  globalParamsLock;
+  lockGlobalParams;
   ok = setFontRastControl(&freetypeControl, s);
-  globalParamsUnlock;
+  unlockGlobalParams;
   return ok;
 }
 
 GBool GlobalParams::setFontRastControl(FontRastControl *val, char *s) {
+  lockGlobalParams;
   if (!strcmp(s, "none")) {
     *val = fontRastNone;
   } else if (!strcmp(s, "plain")) {
@@ -1361,25 +1480,27 @@ GBool GlobalParams::setFontRastControl(FontRastControl *val, char *s) {
   } else if (!strcmp(s, "high")) {
     *val = fontRastAAHigh;
   } else {
+    unlockGlobalParams;
     return gFalse;
   }
+  unlockGlobalParams;
   return gTrue;
 }
 
 void GlobalParams::setMapNumericCharNames(GBool map) {
-  globalParamsLock;
+  lockGlobalParams;
   mapNumericCharNames = map;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setPrintCommands(GBool printCommandsA) {
-  globalParamsLock;
+  lockGlobalParams;
   printCommands = printCommandsA;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
 
 void GlobalParams::setErrQuiet(GBool errQuietA) {
-  globalParamsLock;
+  lockGlobalParams;
   errQuiet = errQuietA;
-  globalParamsUnlock;
+  unlockGlobalParams;
 }
index dee9e25c62979ba127fed63e431e9a3e06261b84..472beed9902fddf13916c047fd821daac9bab4b7 100644 (file)
@@ -28,7 +28,7 @@ class GList;
 class GHash;
 class NameToCharCode;
 class CharCodeToUnicode;
-class CIDToUnicodeCache;
+class CharCodeToUnicodeCache;
 class UnicodeMap;
 class UnicodeMapCache;
 class CMap;
@@ -134,7 +134,6 @@ public:
   CharCode getMacRomanCharCode(char *charName);
 
   Unicode mapNameToUnicode(char *charName);
-  FILE *getCIDToUnicodeFile(GString *collection);
   UnicodeMap *getResidentUnicodeMap(GString *encodingName);
   FILE *getUnicodeMapFile(GString *encodingName);
   FILE *findCMapFile(GString *collection, GString *cMapName);
@@ -154,7 +153,9 @@ public:
   GBool getPSEmbedCIDTrueType();
   GBool getPSOPI();
   GBool getPSASCIIHex();
+  GString *getTextEncodingName();
   EndOfLineKind getTextEOL();
+  GBool getTextPageBreaks();
   GBool getTextKeepTinyChars();
   GString *findFontFile(GString *fontName, char **exts);
   GString *getInitialZoom();
@@ -167,6 +168,7 @@ public:
   GBool getErrQuiet();
 
   CharCodeToUnicode *getCIDToUnicode(GString *collection);
+  CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
   UnicodeMap *getUnicodeMap(GString *encodingName);
   CMap *getCMap(GString *collection, GString *cMapName);
   UnicodeMap *getTextEncoding();
@@ -188,6 +190,7 @@ public:
   void setPSASCIIHex(GBool hex);
   void setTextEncoding(char *encodingName);
   GBool setTextEOL(char *s);
+  void setTextPageBreaks(GBool pageBreaks);
   void setTextKeepTinyChars(GBool keep);
   void setInitialZoom(char *s);
   GBool setT1libControl(char *s);
@@ -201,6 +204,7 @@ private:
   void parseFile(GString *fileName, FILE *f);
   void parseNameToUnicode(GList *tokens, GString *fileName, int line);
   void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
+  void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
   void parseUnicodeMap(GList *tokens, GString *fileName, int line);
   void parseCMapDir(GList *tokens, GString *fileName, int line);
   void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
@@ -238,6 +242,8 @@ private:
   GHash *cidToUnicodes;                // files for mappings from char collections
                                //   to Unicode, indexed by collection name
                                //   [GString]
+  GHash *unicodeToUnicodes;    // files for Unicode-to-Unicode mappings,
+                               //   indexed by font name pattern [GString]
   GHash *residentUnicodeMaps;  // mappings from Unicode to char codes,
                                //   indexed by encoding name [UnicodeMap]
   GHash *unicodeMaps;          // files for mappings from Unicode to char
@@ -270,6 +276,7 @@ private:
                                //   output
   EndOfLineKind textEOL;       // type of EOL marker to use for text
                                //   output
+  GBool textPageBreaks;                // insert end-of-page markers?
   GBool textKeepTinyChars;     // keep all characters in text output
   GList *fontDirs;             // list of font dirs [GString]
   GString *initialZoom;                // initial zoom level
@@ -282,12 +289,15 @@ private:
   GBool printCommands;         // print the drawing commands
   GBool errQuiet;              // suppress error messages?
 
-  CIDToUnicodeCache *cidToUnicodeCache;
+  CharCodeToUnicodeCache *cidToUnicodeCache;
+  CharCodeToUnicodeCache *unicodeToUnicodeCache;
   UnicodeMapCache *unicodeMapCache;
   CMapCache *cMapCache;
 
-#ifdef MULTITHREADED
+#if MULTITHREADED
   GMutex mutex;
+  GMutex unicodeMapCacheMutex;
+  GMutex cMapCacheMutex;
 #endif
 };
 
index c87f6ce7b5e1f2a70353cd10a1f88c26291faef8..bfb41b7d80edc58970047c646874f1b42f410979 100644 (file)
@@ -582,14 +582,43 @@ LinkUnknown::~LinkUnknown() {
   delete action;
 }
 
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
+                                double *dashA, int dashLengthA,
+                                double rA, double gA, double bA) {
+  type = typeA;
+  width = widthA;
+  dash = dashA;
+  dashLength = dashLengthA;
+  r = rA;
+  g = gA;
+  b = bA;
+}
+
+LinkBorderStyle::~LinkBorderStyle() {
+  if (dash) {
+    gfree(dash);
+  }
+}
+
 //------------------------------------------------------------------------
 // Link
 //------------------------------------------------------------------------
 
 Link::Link(Dict *dict, GString *baseURI) {
-  Object obj1, obj2;
+  Object obj1, obj2, obj3;
+  LinkBorderType borderType;
+  double borderWidth;
+  double *borderDash;
+  int borderDashLength;
+  double borderR, borderG, borderB;
   double t;
+  int i;
 
+  borderStyle = NULL;
   action = NULL;
   ok = gFalse;
 
@@ -634,19 +663,92 @@ Link::Link(Dict *dict, GString *baseURI) {
     y2 = t;
   }
 
-  // get border
-  borderW = 1;
-  if (!dict->lookup("Border", &obj1)->isNull()) {
-    if (obj1.isArray() && obj1.arrayGetLength() >= 3) {
-      if (obj1.arrayGet(2, &obj2)->isNum()) {
-       borderW = obj2.getNum();
-      } else {
-       error(-1, "Bad annotation border");
+  // get the border style info
+  borderType = linkBorderSolid;
+  borderWidth = 1;
+  borderDash = NULL;
+  borderDashLength = 0;
+  borderR = 0;
+  borderG = 0;
+  borderB = 1;
+  if (dict->lookup("BS", &obj1)->isDict()) {
+    if (obj1.dictLookup("S", &obj2)->isName()) {
+      if (obj2.isName("S")) {
+       borderType = linkBorderSolid;
+      } else if (obj2.isName("D")) {
+       borderType = linkBorderDashed;
+      } else if (obj2.isName("B")) {
+       borderType = linkBorderEmbossed;
+      } else if (obj2.isName("I")) {
+       borderType = linkBorderEngraved;
+      } else if (obj2.isName("U")) {
+       borderType = linkBorderUnderlined;
       }
-      obj2.free();
     }
+    obj2.free();
+    if (obj1.dictLookup("W", &obj2)->isNum()) {
+      borderWidth = obj2.getNum();
+    }
+    obj2.free();
+    if (obj1.dictLookup("D", &obj2)->isArray()) {
+      borderDashLength = obj2.arrayGetLength();
+      borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
+      for (i = 0; i < borderDashLength; ++i) {
+       if (obj2.arrayGet(i, &obj3)->isNum()) {
+         borderDash[i] = obj3.getNum();
+       } else {
+         borderDash[i] = 1;
+       }
+       obj3.free();
+      }
+    }
+    obj2.free();
+  } else {
+    obj1.free();
+    if (dict->lookup("Border", &obj1)->isArray()) {
+      if (obj1.arrayGetLength() >= 3) {
+       if (obj1.arrayGet(2, &obj2)->isNum()) {
+         borderWidth = obj2.getNum();
+       }
+       obj2.free();
+       if (obj1.arrayGetLength() >= 4) {
+         if (obj1.arrayGet(3, &obj2)->isArray()) {
+           borderType = linkBorderDashed;
+           borderDashLength = obj2.arrayGetLength();
+           borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
+           for (i = 0; i < borderDashLength; ++i) {
+             if (obj2.arrayGet(i, &obj3)->isNum()) {
+               borderDash[i] = obj3.getNum();
+             } else {
+               borderDash[i] = 1;
+             }
+             obj3.free();
+           }
+         }
+         obj2.free();
+       }
+      }
+    }
+  }
+  obj1.free();
+  if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
+    if (obj1.arrayGet(0, &obj2)->isNum()) {
+      borderR = obj2.getNum();
+    }
+    obj1.free();
+    if (obj1.arrayGet(1, &obj2)->isNum()) {
+      borderG = obj2.getNum();
+    }
+    obj1.free();
+    if (obj1.arrayGet(2, &obj2)->isNum()) {
+      borderB = obj2.getNum();
+    }
+    obj1.free();
   }
   obj1.free();
+  borderStyle = new LinkBorderStyle(borderType, borderWidth,
+                                   borderDash, borderDashLength,
+                                   borderR, borderG, borderB);
 
   // look for destination
   if (!dict->lookup("Dest", &obj1)->isNull()) {
@@ -675,8 +777,12 @@ Link::Link(Dict *dict, GString *baseURI) {
 }
 
 Link::~Link() {
-  if (action)
+  if (borderStyle) {
+    delete borderStyle;
+  }
+  if (action) {
     delete action;
+  }
 }
 
 //------------------------------------------------------------------------
index 20ed4500fab8be4bd085cd2cdd826943edcd7e58..aa8727faec7eb3f15cbba32494f365c44b884923 100644 (file)
@@ -301,6 +301,42 @@ private:
   GString *action;             // action subtype
 };
 
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+enum LinkBorderType {
+  linkBorderSolid,
+  linkBorderDashed,
+  linkBorderEmbossed,
+  linkBorderEngraved,
+  linkBorderUnderlined
+};
+
+class LinkBorderStyle {
+public:
+
+  LinkBorderStyle(LinkBorderType typeA, double widthA,
+                 double *dashA, int dashLengthA,
+                 double rA, double gA, double bA);
+  ~LinkBorderStyle();
+
+  LinkBorderType getType() { return type; }
+  double getWidth() { return width; }
+  void getDash(double **dashA, int *dashLengthA)
+    { *dashA = dash; *dashLengthA = dashLength; }
+  void getColor(double *rA, double *gA, double *bA)
+    { *rA = r; *gA = g; *bA = b; }
+
+private:
+
+  LinkBorderType type;
+  double width;
+  double *dash;
+  int dashLength;
+  double r, g, b;
+};
+
 //------------------------------------------------------------------------
 // Link
 //------------------------------------------------------------------------
@@ -315,25 +351,27 @@ public:
   ~Link();
 
   // Was the link created successfully?
-  GBool isOk() const { return ok; }
+  GBool isOk() { return ok; }
 
   // Check if point is inside the link rectangle.
-  GBool inRect(double x, double y) const
+  GBool inRect(double x, double y)
     { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }
 
   // Get action.
-  LinkAction *getAction() const { return action; }
+  LinkAction *getAction() { return action; }
+
+  // Get the link rectangle.
+  void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
+    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }
 
-  // Get border corners and width.
-  void getBorder(double *xa1, double *ya1, double *xa2, double *ya2,
-                double *wa) const
-    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; *wa = borderW; }
+  // Get the border style info.
+  LinkBorderStyle *getBorderStyle() { return borderStyle; }
 
 private:
 
   double x1, y1;               // lower left corner
   double x2, y2;               // upper right corner
-  double borderW;              // border width
+  LinkBorderStyle *borderStyle;        // border style
   LinkAction *action;          // action
   GBool ok;                    // is link valid?
 };
index 5ff9a34e4bf1e8f48d42478e61ce23741597b34a..96d57ec267e41302962764bd283c0007a53e8f37 100644 (file)
@@ -97,6 +97,7 @@ libxpdf_a_SOURCES =           \
        UnicodeMap.cc           \
        UnicodeMap.h            \
        UnicodeMapTables.h      \
+       UnicodeTypeTable.cc     \
        XRef.cc                 \
        XRef.h
 
index f050c0396dc759a40f83d506560096cb43c06801..67737af52edfc5d00bfa3533454b3c61a2f76fbb 100644 (file)
@@ -129,6 +129,7 @@ public:
   virtual GBool beginType3Char(GfxState *state,
                               CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state) {}
+  virtual void endTextObject(GfxState *state) {}
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
index 9108220db48fb0496f67919042ddbee2d1aba182..c92048bd9896392882aec54a8cd6567bf894f946 100644 (file)
@@ -114,6 +114,8 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
 }
 
 GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
+  str->reset();
+
   // check header
   checkHeader();
 
@@ -203,7 +205,7 @@ void PDFDoc::checkHeader() {
   }
 }
 
-void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
+void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
                         int rotate, GBool doLinks,
                         GBool (*abortCheckCbk)(void *data),
                         void *abortCheckCbkData,
@@ -221,18 +223,18 @@ void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
       links = NULL;
     }
     getLinks(p);
-    p->display(out, zoom, rotate, links, catalog,
+    p->display(out, hDPI, vDPI, rotate, links, catalog,
               abortCheckCbk, abortCheckCbkData,
                annotDisplayDecideCbk, annotDisplayDecideCbkData);
   } else {
-    p->display(out, zoom, rotate, NULL, catalog,
+    p->display(out, hDPI, vDPI, rotate, NULL, catalog,
               abortCheckCbk, abortCheckCbkData,
                annotDisplayDecideCbk, annotDisplayDecideCbkData);
   }
 }
 
 void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
-                         int zoom, int rotate, GBool doLinks,
+                         double hDPI, double vDPI, int rotate, GBool doLinks,
                          GBool (*abortCheckCbk)(void *data),
                          void *abortCheckCbkData,
                           GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
@@ -240,13 +242,14 @@ void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
   int page;
 
   for (page = firstPage; page <= lastPage; ++page) {
-    displayPage(out, page, zoom, rotate, doLinks,
+    displayPage(out, page, hDPI, vDPI, rotate, doLinks,
                abortCheckCbk, abortCheckCbkData,
                 annotDisplayDecideCbk, annotDisplayDecideCbkData);
   }
 }
 
-void PDFDoc::displayPageSlice(OutputDev *out, int page, double zoom,
+void PDFDoc::displayPageSlice(OutputDev *out, int page,
+                             double hDPI, double vDPI,
                              int rotate, int sliceX, int sliceY,
                              int sliceW, int sliceH,
                              GBool (*abortCheckCbk)(void *data),
@@ -256,7 +259,7 @@ void PDFDoc::displayPageSlice(OutputDev *out, int page, double zoom,
   Page *p;
 
   p = catalog->getPage(page);
-  p->displaySlice(out, zoom, rotate, sliceX, sliceY, sliceW, sliceH,
+  p->displaySlice(out, hDPI, vDPI, rotate, sliceX, sliceY, sliceW, sliceH,
                  NULL, catalog,
                   abortCheckCbk, abortCheckCbkData,
                   annotDisplayDecideCbk, annotDisplayDecideCbkData);
index ea73282b6437b195a550738620198117c4175864..2d060dc1984e88214aa9adf3aab5958ec6d2544c 100644 (file)
@@ -80,7 +80,7 @@ public:
   Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }
 
   // Display a page.
-  void displayPage(OutputDev *out, int page, double zoom,
+  void displayPage(OutputDev *out, int page, double hDPI, double vDPI,
                   int rotate, GBool doLinks,
                   GBool (*abortCheckCbk)(void *data) = NULL,
                   void *abortCheckCbkData = NULL,
@@ -89,14 +89,15 @@ public:
 
   // Display a range of pages.
   void displayPages(OutputDev *out, int firstPage, int lastPage,
-                   int zoom, int rotate, GBool doLinks,
+                   double hDPI, double vDPI, int rotate, GBool doLinks,
                    GBool (*abortCheckCbk)(void *data) = NULL,
                    void *abortCheckCbkData = NULL,
                     GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = NULL,
                     void *annotDisplayDecideCbkData = NULL);
 
   // Display part of a page.
-  void displayPageSlice(OutputDev *out, int page, double zoom,
+  void displayPageSlice(OutputDev *out, int page,
+                       double hDPI, double vDPI,
                        int rotate, int sliceX, int sliceY,
                        int sliceW, int sliceH,
                        GBool (*abortCheckCbk)(void *data) = NULL,
@@ -110,7 +111,8 @@ public:
 
   // If point <x>,<y> is in a link, return the associated action;
   // else return NULL.
-  LinkAction *findLink(double x, double y) { return links->find(x, y); }
+  LinkAction *findLink(double x, double y)
+    { return links ? links->find(x, y) : (LinkAction *)NULL; }
 
   // Return true if <x>,<y> is in a link.
   GBool onLink(double x, double y) { return links->onLink(x, y); }
index 7bef193e82db1581ffa804587ab41bc8e0c27941..34d7fdc96bf406addfbca1f48710743ab3c57e30 100644 (file)
@@ -27,7 +27,6 @@
 #include "Gfx.h"
 #include "GfxState.h"
 #include "GfxFont.h"
-#include "CharCodeToUnicode.h"
 #include "UnicodeMap.h"
 #include "FontFile.h"
 #include "Catalog.h"
 static char *prolog[] = {
   "/xpdf 75 dict def xpdf begin",
   "% PDF special state",
-  "/pdfDictSize 14 def",
+  "/pdfDictSize 15 def",
+  "~1",
+  "/pdfStates 64 array def",
+  "  0 1 63 {",
+  "    pdfStates exch pdfDictSize dict",
+  "    dup /pdfStateIdx 3 index put",
+  "    put",
+  "  } for",
+  "~a",
   "/pdfSetup {",
   "  3 1 roll 2 array astore",
   "  /setpagedevice where {",
@@ -62,8 +69,19 @@ static char *prolog[] = {
   "    pop pop",
   "  } ifelse",
   "} def",
+  "~1",
+  "/pdfOpNames [",
+  "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
+  "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
+  "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
+  "] def",
+  "~a",
   "/pdfStartPage {",
+  "~1",
+  "  pdfStates 0 get begin",
+  "~2",
   "  pdfDictSize dict begin",
+  "~a",
   "  /pdfFill [0] def",
   "  /pdfStroke [0] def",
   "  /pdfLastFill false def",
@@ -75,6 +93,7 @@ static char *prolog[] = {
   "  /pdfTextRise 0 def",
   "  /pdfWordSpacing 0 def",
   "  /pdfHorizScaling 1 def",
+  "  /pdfTextClipPath [] def",
   "} def",
   "/pdfEndPage { end } def",
   "% separation convention operators",
@@ -191,7 +210,16 @@ static char *prolog[] = {
   "  } ifelse",
   "} def",
   "% graphics state operators",
+  "~1",
+  "/q {",
+  "  gsave",
+  "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
+  "  pdfStates pdfStateIdx 1 add get begin",
+  "  pdfOpNames { exch def } forall",
+  "} def",
+  "~2",
   "/q { gsave pdfDictSize dict begin } def",
+  "~a",
   "/Q { end grestore } def",
   "/cm { concat } def",
   "/d { setdash } def",
@@ -248,56 +276,100 @@ static char *prolog[] = {
   "/Td { pdfTextMat transform moveto } def",
   "/Tm { /pdfTextMat exch def } def",
   "% text string operators",
-  "/awcp { % awidthcharpath",
+  "/cshow where {",
+  "  pop",
+  "  /cshow2 {",
+  "    dup {",
+  "      pop pop",
+  "      1 string dup 0 3 index put 3 index exec",
+  "    } exch cshow",
+  "    pop pop",
+  "  } def",
+  "}{",
+  "  /cshow2 {",
+  "    currentfont /FontType get 0 eq {",
+  "      0 2 2 index length 1 sub {",
+  "        2 copy get exch 1 add 2 index exch get",
+  "        2 copy exch 256 mul add",
+  "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",
+  "        3 index exec",
+  "      } for",
+  "    } {",
+  "      dup {",
+  "        1 string dup 0 3 index put 3 index exec",
+  "      } forall",
+  "    } ifelse",
+  "    pop pop",
+  "  } def",
+  "} ifelse",
+  "/awcp {", // awidthcharpath
   "  exch {",
-  "    1 string dup 0 3 index put 2 index charpath",
-  "    3 index 3 index rmoveto",
-  "    4 index eq { 5 index 5 index rmoveto } if",
-  "  } forall",
+  "    false charpath",
+  "    5 index 5 index rmoveto",
+  "    6 index eq { 7 index 7 index rmoveto } if",
+  "  } exch cshow2",
   "  6 {pop} repeat",
   "} def",
-  "/Tj { fCol",  // because stringwidth has to draw Type 3 chars
-  "      0 pdfTextRise pdfTextMat dtransform rmoveto",
-  "      1 index stringwidth pdfTextMat idtransform pop",
-  "      sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
-  "      pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
-  "      4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
-  "      pdfTextMat dtransform",
-  "      6 5 roll",
-  "      currentpoint 8 2 roll",
-  "      pdfTextRender 1 and 0 eq {",
-  "        6 copy awidthshow",
-  "      } if",
-  "      pdfTextRender 3 and dup 1 eq exch 2 eq or {",
-  "        8 6 roll moveto",
-  "        currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
-  "        false awcp currentpoint stroke moveto",
-  "      } {",
-  "        8 {pop} repeat",
-  "      } ifelse",
-  "      0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
-  "/Tj16 { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
-  "        0 pdfTextRise pdfTextMat dtransform rmoveto",
-  "        2 index stringwidth pdfTextMat idtransform pop",
-  "        sub exch div",
-  "        pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
-  "        4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
-  "        pdfTextMat dtransform",
-  "        6 5 roll awidthshow",
-  "        0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
-  "/Tj16V { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
-  "         0 pdfTextRise pdfTextMat dtransform rmoveto",
-  "         2 index stringwidth pdfTextMat idtransform exch pop",
-  "         sub exch div",
-  "         0 pdfWordSpacing pdfTextMat dtransform 32",
-  "         4 3 roll pdfCharSpacing add 0 exch",
-  "         pdfTextMat dtransform",
-  "         6 5 roll awidthshow",
-  "         0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
+  "/Tj {",
+  "  fCol",  // because stringwidth has to draw Type 3 chars
+  "  1 index stringwidth pdfTextMat idtransform pop",
+  "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
+  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
+  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
+  "  pdfTextMat dtransform",
+  "  6 5 roll Tj1",
+  "} def",
+  "/Tj16 {",
+  "  fCol",  // because stringwidth has to draw Type 3 chars
+  "  2 index stringwidth pdfTextMat idtransform pop",
+  "  sub exch div",
+  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
+  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
+  "  pdfTextMat dtransform",
+  "  6 5 roll Tj1",
+  "} def",
+  "/Tj16V {",
+  "  fCol",  // because stringwidth has to draw Type 3 chars
+  "  2 index stringwidth pdfTextMat idtransform exch pop",
+  "  sub exch div",
+  "  0 pdfWordSpacing pdfTextMat dtransform 32",
+  "  4 3 roll pdfCharSpacing add 0 exch",
+  "  pdfTextMat dtransform",
+  "  6 5 roll Tj1",
+  "} def",
+  "/Tj1 {",
+  "  0 pdfTextRise pdfTextMat dtransform rmoveto",
+  "  currentpoint 8 2 roll",
+  "  pdfTextRender 1 and 0 eq {",
+  "    6 copy awidthshow",
+  "  } if",
+  "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
+  "    7 index 7 index moveto",
+  "    6 copy",
+  "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
+  "    false awcp currentpoint stroke moveto",
+  "  } if",
+  "  pdfTextRender 4 and 0 ne {",
+  "    8 6 roll moveto",
+  "    false awcp",
+  "    /pdfTextClipPath [ pdfTextClipPath aload pop",
+  "      {/moveto cvx}",
+  "      {/lineto cvx}",
+  "      {/curveto cvx}",
+  "      {/closepath cvx}",
+  "    pathforall ] def",
+  "    currentpoint newpath moveto",
+  "  } {",
+  "    8 {pop} repeat",
+  "  } ifelse",
+  "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
+  "} def",
   "/TJm { pdfFontSize 0.001 mul mul neg 0",
   "       pdfTextMat dtransform rmoveto } def",
   "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
   "        pdfTextMat dtransform rmoveto } def",
+  "/Tclip { pdfTextClipPath cvx exec clip",
+  "         /pdfTextClipPath [] def } def",
   "% Level 1 image operators",
   "/pdfIm1 {",
   "  /pdfImBuf1 4 index string def",
@@ -500,10 +572,17 @@ static void outputToFile(void *stream, char *data, int len) {
 }
 
 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
-                        int firstPage, int lastPage, PSOutMode modeA) {
+                        int firstPage, int lastPage, PSOutMode modeA,
+                        int paperWidthA, int paperHeightA,
+                        GBool manualCtrlA) {
   FILE *f;
   PSFileType fileTypeA;
 
+  underlayCbk = NULL;
+  underlayCbkData = NULL;
+  overlayCbk = NULL;
+  overlayCbkData = NULL;
+
   fontIDs = NULL;
   fontFileIDs = NULL;
   fontFileNames = NULL;
@@ -511,6 +590,7 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
   xobjStack = NULL;
   embFontList = NULL;
   customColors = NULL;
+  haveTextClip = gFalse;
   t3String = NULL;
 
   // open file or pipe
@@ -543,12 +623,15 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
   }
 
   init(outputToFile, f, fileTypeA,
-       xrefA, catalog, firstPage, lastPage, modeA);
+       xrefA, catalog, firstPage, lastPage, modeA,
+       paperWidthA, paperHeightA, manualCtrlA);
 }
 
 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
                         XRef *xrefA, Catalog *catalog,
-                        int firstPage, int lastPage, PSOutMode modeA) {
+                        int firstPage, int lastPage, PSOutMode modeA,
+                        int paperWidthA, int paperHeightA,
+                        GBool manualCtrlA) {
   fontIDs = NULL;
   fontFileIDs = NULL;
   fontFileNames = NULL;
@@ -556,23 +639,20 @@ PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
   xobjStack = NULL;
   embFontList = NULL;
   customColors = NULL;
+  haveTextClip = gFalse;
   t3String = NULL;
 
   init(outputFuncA, outputStreamA, psGeneric,
-       xrefA, catalog, firstPage, lastPage, modeA);
+       xrefA, catalog, firstPage, lastPage, modeA,
+       paperWidthA, paperHeightA, manualCtrlA);
 }
 
 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
                       PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
-                      int firstPage, int lastPage, PSOutMode modeA) {
+                      int firstPage, int lastPage, PSOutMode modeA,
+                      int paperWidthA, int paperHeightA,
+                      GBool manualCtrlA) {
   Page *page;
-  PDFRectangle *box;
-  Dict *resDict;
-  Annots *annots;
-  char **p;
-  int pg;
-  Object obj1, obj2;
-  int i;
 
   // initialize
   ok = gTrue;
@@ -582,13 +662,20 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
   xref = xrefA;
   level = globalParams->getPSLevel();
   mode = modeA;
-  paperWidth = globalParams->getPSPaperWidth();
-  paperHeight = globalParams->getPSPaperHeight();
+  paperWidth = paperWidthA;
+  paperHeight = paperHeightA;
+  if (paperWidth == 0) {
+    paperWidth = globalParams->getPSPaperWidth();
+  }
+  if (paperHeight == 0) {
+    paperHeight = globalParams->getPSPaperHeight();
+  }
   if (paperWidth < 0 || paperHeight < 0) {
     page = catalog->getPage(firstPage);
     paperWidth = (int)(page->getWidth() + 0.5);
     paperHeight = (int)(page->getHeight() + 0.5);
   }
+  manualCtrl = manualCtrlA;
   if (mode == psModeForm) {
     lastPage = firstPage;
   }
@@ -611,14 +698,95 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
   fontFileNameSize = 64;
   fontFileNameLen = 0;
   fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
+  nextTrueTypeNum = 0;
   font16EncLen = 0;
   font16EncSize = 0;
+
   xobjStack = new GList();
+  numSaves = 0;
 
   // initialize embedded font resource comment list
   embFontList = new GString();
 
-  // write header
+  if (!manualCtrl) {
+    writeHeader(firstPage, lastPage, catalog->getPage(firstPage)->getBox());
+    if (mode != psModeForm) {
+      writePS("%%BeginProlog\n");
+    }
+    writeXpdfProcset();
+    if (mode != psModeForm) {
+      writePS("%%EndProlog\n");
+      writePS("%%BeginSetup\n");
+    }
+    writeDocSetup(catalog, firstPage, lastPage);
+    if (mode != psModeForm) {
+      writePS("%%EndSetup\n");
+    }
+  }
+
+  // initialize sequential page number
+  seqPage = 1;
+}
+
+PSOutputDev::~PSOutputDev() {
+  PSOutCustomColor *cc;
+  int i;
+
+  if (ok) {
+    if (!manualCtrl) {
+      writePS("%%Trailer\n");
+      writeTrailer();
+      if (mode != psModeForm) {
+       writePS("%%EOF\n");
+      }
+    }
+    if (fileType == psFile) {
+#ifdef MACOS
+      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
+#endif
+      fclose((FILE *)outputStream);
+    }
+#ifdef HAVE_POPEN
+    else if (fileType == psPipe) {
+      pclose((FILE *)outputStream);
+#ifndef WIN32
+      signal(SIGPIPE, (SignalFunc)SIG_DFL);
+#endif
+    }
+#endif
+  }
+  if (embFontList) {
+    delete embFontList;
+  }
+  if (fontIDs) {
+    gfree(fontIDs);
+  }
+  if (fontFileIDs) {
+    gfree(fontFileIDs);
+  }
+  if (fontFileNames) {
+    for (i = 0; i < fontFileNameLen; ++i) {
+      delete fontFileNames[i];
+    }
+    gfree(fontFileNames);
+  }
+  if (font16Enc) {
+    for (i = 0; i < font16EncLen; ++i) {
+      delete font16Enc[i].enc;
+    }
+    gfree(font16Enc);
+  }
+  if (xobjStack) {
+    delete xobjStack;
+  }
+  while (customColors) {
+    cc = customColors;
+    customColors = cc->next;
+    delete cc;
+  }
+}
+
+void PSOutputDev::writeHeader(int firstPage, int lastPage, PDFRectangle *box) {
   switch (mode) {
   case psModePS:
     writePS("%!PS-Adobe-3.0\n");
@@ -633,6 +801,7 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
     writePS("%%DocumentSuppliedResources: (atend)\n");
     writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
               paperWidth, paperHeight);
+    writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
     writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
     writePS("%%EndComments\n");
     writePS("%%BeginDefaults\n");
@@ -649,8 +818,6 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
       writePS("%%DocumentProcessColors: (atend)\n");
       writePS("%%DocumentCustomColors: (atend)\n");
     }
-    page = catalog->getPage(firstPage);
-    box = page->getBox();
     writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
               (int)floor(box->x1), (int)floor(box->y1),
               (int)ceil(box->x2), (int)ceil(box->y2));
@@ -676,8 +843,6 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
     }
     writePS("%%DocumentSuppliedResources: (atend)\n");
     writePS("%%EndComments\n");
-    page = catalog->getPage(firstPage);
-    box = page->getBox();
     writePS("32 dict dup begin\n");
     writePSFmt("/BBox [%d %d %d %d] def\n",
               (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
@@ -685,31 +850,48 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
     writePS("/Matrix [1 0 0 1 0 0] def\n");
     break;
   }
+}
+
+void PSOutputDev::writeXpdfProcset() {
+  char prologLevel;
+  char **p;
 
-  // write prolog
-  if (mode != psModeForm) {
-    writePS("%%BeginProlog\n");
-  }
   writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
+  prologLevel = 'a';
   for (p = prolog; *p; ++p) {
-    writePSFmt("%s\n", *p);
+    if ((*p)[0] == '~' && (*p)[1] == '1') {
+      prologLevel = '1';
+    } else if ((*p)[0] == '~' && (*p)[1] == '2') {
+      prologLevel = '2';
+    } else if ((*p)[0] == '~' && (*p)[1] == 'a') {
+      prologLevel = 'a';
+    } else if (prologLevel == 'a' ||
+              (prologLevel == '1' && level < psLevel2) ||
+              (prologLevel == '2' && level >= psLevel2)) {
+      writePSFmt("%s\n", *p);
+    }
   }
   writePS("%%EndResource\n");
+
   if (level >= psLevel3) {
     for (p = cmapProlog; *p; ++p) {
       writePSFmt("%s\n", *p);
     }
   }
-  if (mode != psModeForm) {
-    writePS("%%EndProlog\n");
-  }
+}
+
+void PSOutputDev::writeDocSetup(Catalog *catalog,
+                               int firstPage, int lastPage) {
+  Page *page;
+  Dict *resDict;
+  Annots *annots;
+  Object obj1, obj2;
+  int pg, i;
 
-  // set up fonts and images
   if (mode == psModeForm) {
     // swap the form and xpdf dicts
     writePS("xpdf end begin dup begin\n");
   } else {
-    writePS("%%BeginSetup\n");
     writePS("xpdf begin\n");
   }
   for (pg = firstPage; pg <= lastPage; ++pg) {
@@ -732,7 +914,7 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
     delete annots;
   }
   if (mode != psModeForm) {
-    if (mode != psModeEPS) {
+    if (mode != psModeEPS && !manualCtrl) {
       writePSFmt("%d %d %s pdfSetup\n",
                 paperWidth, paperHeight,
                 globalParams->getPSDuplex() ? "true" : "false");
@@ -742,97 +924,51 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
       writePS("/opiMatrix matrix currentmatrix def\n");
     }
 #endif
-    writePS("%%EndSetup\n");
   }
+}
 
-  // initialize sequential page number
-  seqPage = 1;
+void PSOutputDev::writePageTrailer() {
+  if (mode != psModeForm) {
+    writePS("pdfEndPage\n");
+  }
 }
 
-PSOutputDev::~PSOutputDev() {
+void PSOutputDev::writeTrailer() {
   PSOutCustomColor *cc;
-  int i;
 
-  if (ok) {
-    if (mode == psModeForm) {
-      writePS("/Foo exch /Form defineresource pop\n");
-    } else {
-      writePS("%%Trailer\n");
-      writePS("end\n");
-      writePS("%%DocumentSuppliedResources:\n");
-      writePS(embFontList->getCString());
-      if (level == psLevel1Sep || level == psLevel2Sep ||
-         level == psLevel3Sep) {
-         writePS("%%DocumentProcessColors:");
-         if (processColors & psProcessCyan) {
-          writePS(" Cyan");
-        }
-         if (processColors & psProcessMagenta) {
-          writePS(" Magenta");
-        }
-         if (processColors & psProcessYellow) {
-          writePS(" Yellow");
-        }
-         if (processColors & psProcessBlack) {
-          writePS(" Black");
-        }
-         writePS("\n");
-         writePS("%%DocumentCustomColors:");
-        for (cc = customColors; cc; cc = cc->next) {
-          writePSFmt(" (%s)", cc->name->getCString());
+  if (mode == psModeForm) {
+    writePS("/Foo exch /Form defineresource pop\n");
+  } else {
+    writePS("end\n");
+    writePS("%%DocumentSuppliedResources:\n");
+    writePS(embFontList->getCString());
+    if (level == psLevel1Sep || level == psLevel2Sep ||
+       level == psLevel3Sep) {
+      writePS("%%DocumentProcessColors:");
+      if (processColors & psProcessCyan) {
+       writePS(" Cyan");
         }
-         writePS("\n");
-         writePS("%%CMYKCustomColor:\n");
-        for (cc = customColors; cc; cc = cc->next) {
-          writePSFmt("%%%%+ %g %g %g %g (%s)\n",
+      if (processColors & psProcessMagenta) {
+       writePS(" Magenta");
+      }
+      if (processColors & psProcessYellow) {
+       writePS(" Yellow");
+      }
+      if (processColors & psProcessBlack) {
+       writePS(" Black");
+      }
+      writePS("\n");
+      writePS("%%DocumentCustomColors:");
+      for (cc = customColors; cc; cc = cc->next) {
+       writePSFmt(" (%s)", cc->name->getCString());
+      }
+      writePS("\n");
+      writePS("%%CMYKCustomColor:\n");
+      for (cc = customColors; cc; cc = cc->next) {
+       writePSFmt("%%%%+ %g %g %g %g (%s)\n",
                   cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
-        }
       }
-      writePS("%%EOF\n");
-    }
-    if (fileType == psFile) {
-#ifdef MACOS
-      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
-#endif
-      fclose((FILE *)outputStream);
-    }
-#ifdef HAVE_POPEN
-    else if (fileType == psPipe) {
-      pclose((FILE *)outputStream);
-#ifndef WIN32
-      signal(SIGPIPE, (SignalFunc)SIG_DFL);
-#endif
     }
-#endif
-  }
-  if (embFontList) {
-    delete embFontList;
-  }
-  if (fontIDs) {
-    gfree(fontIDs);
-  }
-  if (fontFileIDs) {
-    gfree(fontFileIDs);
-  }
-  if (fontFileNames) {
-    for (i = 0; i < fontFileNameLen; ++i) {
-      delete fontFileNames[i];
-    }
-    gfree(fontFileNames);
-  }
-  if (font16Enc) {
-    for (i = 0; i < font16EncLen; ++i) {
-      delete font16Enc[i].enc;
-    }
-    gfree(font16Enc);
-  }
-  if (xobjStack) {
-    delete xobjStack;
-  }
-  while (customColors) {
-    cc = customColors;
-    customColors = cc->next;
-    delete cc;
   }
 }
 
@@ -888,29 +1024,39 @@ void PSOutputDev::setupResources(Dict *resDict) {
 }
 
 void PSOutputDev::setupFonts(Dict *resDict) {
-  Object fontDict;
+  Object obj1, obj2;
+  Ref r;
   GfxFontDict *gfxFontDict;
   GfxFont *font;
   int i;
 
-  resDict->lookup("Font", &fontDict);
-  if (fontDict.isDict()) {
-    gfxFontDict = new GfxFontDict(xref, fontDict.getDict());
+  gfxFontDict = NULL;
+  resDict->lookupNF("Font", &obj1);
+  if (obj1.isRef()) {
+    obj1.fetch(xref, &obj2);
+    if (obj2.isDict()) {
+      r = obj1.getRef();
+      gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
+    }
+    obj2.free();
+  } else if (obj1.isDict()) {
+    gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
+  }
+  if (gfxFontDict) {
     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
       font = gfxFontDict->getFont(i);
       setupFont(font, resDict);
     }
     delete gfxFontDict;
   }
-  fontDict.free();
+  obj1.free();
 }
 
 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
   Ref fontFileID;
   GString *name;
   PSFontParam *fontParam;
-  GString *psNameStr;
-  char *psName;
+  GString *psName;
   char type3Name[64], buf[16];
   GBool subst;
   UnicodeMap *uMap;
@@ -937,28 +1083,25 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
   fontIDs[fontIDLen++] = *font->getID();
 
   xs = ys = 1;
-  psNameStr = NULL;
   subst = gFalse;
 
   // check for resident 8-bit font
   if (font->getName() &&
       (fontParam = globalParams->getPSFont(font->getName()))) {
-    psName = fontParam->psFontName->getCString();
+    psName = new GString(fontParam->psFontName->getCString());
 
   // check for embedded Type 1 font
   } else if (globalParams->getPSEmbedType1() &&
             font->getType() == fontType1 &&
             font->getEmbeddedFontID(&fontFileID)) {
-    psNameStr = filterPSName(font->getEmbeddedFontName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getEmbeddedFontName());
     setupEmbeddedType1Font(&fontFileID, psName);
 
   // check for embedded Type 1C font
   } else if (globalParams->getPSEmbedType1() &&
             font->getType() == fontType1C &&
             font->getEmbeddedFontID(&fontFileID)) {
-    psNameStr = filterPSName(font->getEmbeddedFontName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getEmbeddedFontName());
     setupEmbeddedType1CFont(font, &fontFileID, psName);
 
   // check for external Type 1 font file
@@ -966,45 +1109,41 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
             font->getType() == fontType1 &&
             font->getExtFontFile()) {
     // this assumes that the PS font name matches the PDF font name
-    psName = font->getName()->getCString();
+    psName = font->getName()->copy();
     setupExternalType1Font(font->getExtFontFile(), psName);
 
   // check for embedded TrueType font
   } else if (globalParams->getPSEmbedTrueType() &&
             font->getType() == fontTrueType &&
             font->getEmbeddedFontID(&fontFileID)) {
-    psNameStr = filterPSName(font->getEmbeddedFontName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getEmbeddedFontName());
     setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
 
   // check for external TrueType font file
   } else if (globalParams->getPSEmbedTrueType() &&
             font->getType() == fontTrueType &&
             font->getExtFontFile()) {
-    psNameStr = filterPSName(font->getName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getName());
     setupExternalTrueTypeFont(font, psName);
 
   // check for embedded CID PostScript font
   } else if (globalParams->getPSEmbedCIDPostScript() &&
             font->getType() == fontCIDType0C &&
             font->getEmbeddedFontID(&fontFileID)) {
-    psNameStr = filterPSName(font->getEmbeddedFontName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getEmbeddedFontName());
     setupEmbeddedCIDType0Font(font, &fontFileID, psName);
 
   // check for embedded CID TrueType font
   } else if (globalParams->getPSEmbedCIDTrueType() &&
             font->getType() == fontCIDType2 &&
             font->getEmbeddedFontID(&fontFileID)) {
-    psNameStr = filterPSName(font->getEmbeddedFontName());
-    psName = psNameStr->getCString();
+    psName = filterPSName(font->getEmbeddedFontName());
     setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName);
 
   } else if (font->getType() == fontType3) {
     sprintf(type3Name, "T3_%d_%d",
            font->getID()->num, font->getID()->gen);
-    psName = type3Name;
+    psName = new GString(type3Name);
     setupType3Font(font, psName, parentResDict);
 
   // do 8-bit font substitution
@@ -1015,7 +1154,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
     if (name) {
       for (i = 0; psFonts[i]; ++i) {
        if (name->cmp(psFonts[i]) == 0) {
-         psName = psFonts[i];
+         psName = new GString(psFonts[i]);
          break;
        }
       }
@@ -1034,7 +1173,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
       if (font->isItalic()) {
        i += 1;
       }
-      psName = psSubstFonts[i].psName;
+      psName = new GString(psSubstFonts[i].psName);
       for (code = 0; code < 256; ++code) {
        if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
            charName[0] == 'm' && charName[1] == '\0') {
@@ -1072,7 +1211,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
                            ((GfxCIDFont *)font)->getCollection(),
                            font->getWMode()))) {
     subst = gTrue;
-    psName = fontParam->psFontName->getCString();
+    psName = fontParam->psFontName->copy();
     if (font16EncLen >= font16EncSize) {
       font16EncSize += 16;
       font16Enc = (PSFont16Enc *)grealloc(font16Enc,
@@ -1102,16 +1241,17 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
   if (font->isCIDFont()) {
     if (level == psLevel3 || level == psLevel3Sep) {
       writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
-                font->getID()->num, font->getID()->gen, psName,
+                font->getID()->num, font->getID()->gen, psName->getCString(),
                 font->getWMode());
     } else {
       writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
-                font->getID()->num, font->getID()->gen, psName,
+                font->getID()->num, font->getID()->gen, psName->getCString(),
                 font->getWMode());
     }
   } else {
     writePSFmt("/F%d_%d /%s %g %g\n",
-              font->getID()->num, font->getID()->gen, psName, xs, ys);
+              font->getID()->num, font->getID()->gen, psName->getCString(),
+              xs, ys);
     for (i = 0; i < 256; i += 8) {
       writePSFmt((i == 0) ? "[ " : "  ");
       for (j = 0; j < 8; ++j) {
@@ -1136,16 +1276,14 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
     writePS("pdfMakeFont\n");
   }
 
-  if (psNameStr) {
-    delete psNameStr;
-  }
+  delete psName;
 }
 
-void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
+void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
   static char hexChar[17] = "0123456789abcdef";
-  Object refObj, strObj, obj1, obj2;
+  Object refObj, strObj, obj1, obj2, obj3;
   Dict *dict;
-  int length1, length2;
+  int length1, length2, length3;
   int c;
   int start[4];
   GBool binMode;
@@ -1179,21 +1317,25 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
   }
   dict->lookup("Length1", &obj1);
   dict->lookup("Length2", &obj2);
-  if (!obj1.isInt() || !obj2.isInt()) {
+  dict->lookup("Length3", &obj3);
+  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
     error(-1, "Missing length fields in embedded font stream dictionary");
     obj1.free();
     obj2.free();
+    obj3.free();
     goto err1;
   }
   length1 = obj1.getInt();
   length2 = obj2.getInt();
+  length3 = obj3.getInt();
   obj1.free();
   obj2.free();
+  obj3.free();
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // copy ASCII portion of font
@@ -1222,6 +1364,9 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
       writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
       writePSChar(hexChar[start[i] & 0x0f]);
     }
+    // if Length2 is incorrect (too small), font data gets chopped, so
+    // we take a few extra characters from the trailer just in case
+    length2 += length3 >= 8 ? 8 : length3;
     while (i < length2) {
       if ((c = strObj.streamGetChar()) == EOF) {
        break;
@@ -1266,7 +1411,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
 
 //~ This doesn't handle .pfb files or binary eexec data (which only
 //~ happens in pfb files?).
-void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
+void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
   FILE *fontFile;
   int c;
   int i;
@@ -1287,9 +1432,9 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
   fontFileNames[fontFileNameLen++] = fileName->copy();
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // copy the font file
@@ -1307,7 +1452,7 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
 }
 
 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
-                                         char *psName) {
+                                         GString *psName) {
   char *fontBuf;
   int fontLen;
   Type1CFontFile *t1cFile;
@@ -1328,9 +1473,9 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // convert it to a Type 1 font
@@ -1347,42 +1492,49 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
 }
 
 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
-                                           char *psName) {
+                                           GString *psName) {
+  char unique[32];
   char *fontBuf;
   int fontLen;
   TrueTypeFontFile *ttFile;
-  CharCodeToUnicode *ctu;
+  Gushort *codeToGID;
   int i;
 
   // check if font is already embedded
   for (i = 0; i < fontFileIDLen; ++i) {
     if (fontFileIDs[i].num == id->num &&
-       fontFileIDs[i].gen == id->gen)
-      return;
+       fontFileIDs[i].gen == id->gen) {
+      sprintf(unique, "_%d", nextTrueTypeNum++);
+      psName->append(unique);
+      break;
+    }
   }
 
   // add entry to fontFileIDs list
-  if (fontFileIDLen >= fontFileIDSize) {
-    fontFileIDSize += 64;
-    fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
+  if (i == fontFileIDLen) {
+    if (fontFileIDLen >= fontFileIDSize) {
+      fontFileIDSize += 64;
+      fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
+    }
+    fontFileIDs[fontFileIDLen++] = *id;
   }
-  fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // convert it to a Type 42 font
   fontBuf = font->readEmbFontFile(xref, &fontLen);
   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
-  ctu = ((Gfx8BitFont *)font)->getToUnicode();
-  ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
-                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
-                         ((Gfx8BitFont *)font)->isSymbolic(),
+  codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
+  ttFile->convertToType42(psName->getCString(),
+                         ((Gfx8BitFont *)font)->getEncoding(),
+                         ((Gfx8BitFont *)font)->getHasEncoding(),
+                         codeToGID,
                          outputFunc, outputStream);
-  ctu->decRefCnt();
+  gfree(codeToGID);
   delete ttFile;
   gfree(fontBuf);
 
@@ -1390,45 +1542,51 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
   writePS("%%EndResource\n");
 }
 
-void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
+void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
+  char unique[32];
   GString *fileName;
   char *fontBuf;
   int fontLen;
   TrueTypeFontFile *ttFile;
-  CharCodeToUnicode *ctu;
+  Gushort *codeToGID;
   int i;
 
   // check if font is already embedded
   fileName = font->getExtFontFile();
   for (i = 0; i < fontFileNameLen; ++i) {
     if (!fontFileNames[i]->cmp(fileName)) {
-      return;
+      sprintf(unique, "_%d", nextTrueTypeNum++);
+      psName->append(unique);
+      break;
     }
   }
 
   // add entry to fontFileNames list
-  if (fontFileNameLen >= fontFileNameSize) {
-    fontFileNameSize += 64;
-    fontFileNames = (GString **)grealloc(fontFileNames,
-                                        fontFileNameSize * sizeof(GString *));
+  if (i == fontFileNameLen) {
+    if (fontFileNameLen >= fontFileNameSize) {
+      fontFileNameSize += 64;
+      fontFileNames =
+       (GString **)grealloc(fontFileNames,
+                            fontFileNameSize * sizeof(GString *));
+    }
   }
   fontFileNames[fontFileNameLen++] = fileName->copy();
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // convert it to a Type 42 font
   fontBuf = font->readExtFontFile(&fontLen);
   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
-  ctu = ((Gfx8BitFont *)font)->getToUnicode();
-  ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
-                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
-                         ((Gfx8BitFont *)font)->isSymbolic(),
+  codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
+  ttFile->convertToType42(psName->getCString(),
+                         ((Gfx8BitFont *)font)->getEncoding(),
+                         ((Gfx8BitFont *)font)->getHasEncoding(),
+                         codeToGID,
                          outputFunc, outputStream);
-  ctu->decRefCnt();
   delete ttFile;
   gfree(fontBuf);
 
@@ -1437,7 +1595,7 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
 }
 
 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
-                                           char *psName) {
+                                           GString *psName) {
   char *fontBuf;
   int fontLen;
   Type1CFontFile *t1cFile;
@@ -1458,9 +1616,9 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // convert it to a Type 0 font
@@ -1469,10 +1627,11 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
   if (t1cFile->isOk()) {
     if (globalParams->getPSLevel() >= psLevel3) {
       // Level 3: use a CID font
-      t1cFile->convertToCIDType0(psName, outputFunc, outputStream);
+      t1cFile->convertToCIDType0(psName->getCString(),
+                                outputFunc, outputStream);
     } else {
       // otherwise: use a non-CID composite font
-      t1cFile->convertToType0(psName, outputFunc, outputStream);
+      t1cFile->convertToType0(psName->getCString(), outputFunc, outputStream);
     }
   }
   delete t1cFile;
@@ -1483,7 +1642,7 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
 }
 
 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
-                                              char *psName) {
+                                              GString *psName) {
   char *fontBuf;
   int fontLen;
   TrueTypeFontFile *ttFile;
@@ -1504,22 +1663,23 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // convert it to a Type 0 font
   fontBuf = font->readEmbFontFile(xref, &fontLen);
   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
   if (globalParams->getPSLevel() >= psLevel3) {
-    ttFile->convertToCIDType2(psName,
+    ttFile->convertToCIDType2(psName->getCString(),
                              ((GfxCIDFont *)font)->getCIDToGID(),
                              ((GfxCIDFont *)font)->getCIDToGIDLen(),
                              outputFunc, outputStream);
   } else {
     // otherwise: use a non-CID composite font
-    ttFile->convertToType0(psName, ((GfxCIDFont *)font)->getCIDToGID(),
+    ttFile->convertToType0(psName->getCString(),
+                          ((GfxCIDFont *)font)->getCIDToGID(),
                           ((GfxCIDFont *)font)->getCIDToGIDLen(),
                           outputFunc, outputStream);
   }
@@ -1530,7 +1690,7 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
   writePS("%%EndResource\n");
 }
 
-void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
+void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
                                 Dict *parentResDict) {
   Dict *resDict;
   Dict *charProcs;
@@ -1543,15 +1703,17 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
 
   // set up resources used by font
   if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+    inType3Char = gTrue;
     setupResources(resDict);
+    inType3Char = gFalse;
   } else {
     resDict = parentResDict;
   }
 
   // beginning comment
-  writePSFmt("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
   embFontList->append("%%+ font ");
-  embFontList->append(psName);
+  embFontList->append(psName->getCString());
   embFontList->append("\n");
 
   // font dictionary
@@ -1611,7 +1773,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
     writePS("end\n");
   }
   writePS("currentdict end\n");
-  writePSFmt("/%s exch definefont pop\n", psName);
+  writePSFmt("/%s exch definefont pop\n", psName->getCString());
 
   // ending comment
   writePS("%%EndResource\n");
@@ -1621,7 +1783,7 @@ void PSOutputDev::setupImages(Dict *resDict) {
   Object xObjDict, xObj, xObjRef, subtypeObj;
   int i;
 
-  if (mode != psModeForm) {
+  if (!(mode == psModeForm || inType3Char)) {
     return;
   }
 
@@ -1690,6 +1852,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str) {
   } while (c != '~' && c != EOF);
   ++size;
   writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
+  str->close();
 
   // write the data into the array
   str->reset();
@@ -1732,12 +1895,13 @@ void PSOutputDev::setupImage(Ref id, Stream *str) {
   } while (c != '~' && c != EOF);
   writePS("~> put\n");
   writePS("pop\n");
+  str->close();
 
   delete str;
 }
 
 void PSOutputDev::startPage(int pageNum, GfxState *state) {
-  int x1, y1, x2, y2, width, height, t;
+  int x1, y1, x2, y2, width, height, paperWidth2, paperHeight2;
 
 
   switch (mode) {
@@ -1761,9 +1925,8 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
       writePS("90 rotate\n");
       tx = -x1;
       ty = -(y1 + paperWidth);
-      t = width;
-      width = height;
-      height = t;
+      paperWidth2 = paperHeight;
+      paperHeight2 = paperWidth;
     } else {
       landscape = gFalse;
       writePSFmt("%%%%PageOrientation: %s\n",
@@ -1771,19 +1934,21 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
       writePS("pdfStartPage\n");
       tx = -x1;
       ty = -y1;
+      paperWidth2 = paperWidth;
+      paperHeight2 = paperHeight;
     }
-    if (width < paperWidth) {
-      tx += (paperWidth - width) / 2;
+    if (width < paperWidth2) {
+      tx += (paperWidth2 - width) / 2;
     }
-    if (height < paperHeight) {
-      ty += (paperHeight - height) / 2;
+    if (height < paperHeight2) {
+      ty += (paperHeight2 - height) / 2;
     }
     if (tx != 0 || ty != 0) {
       writePSFmt("%g %g translate\n", tx, ty);
     }
-    if (width > paperWidth || height > paperHeight) {
-      xScale = (double)paperWidth / (double)width;
-      yScale = (double)paperHeight / (double)height;
+    if (width > paperWidth2 || height > paperHeight2) {
+      xScale = (double)paperWidth2 / (double)width;
+      yScale = (double)paperHeight2 / (double)height;
       if (yScale < xScale) {
        xScale = yScale;
       } else {
@@ -1814,9 +1979,17 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
     landscape = gFalse;
     break;
   }
+
+  if (underlayCbk) {
+    (*underlayCbk)(this, underlayCbkData);
+  }
 }
 
 void PSOutputDev::endPage() {
+  if (overlayCbk) {
+    (*overlayCbk)(this, overlayCbkData);
+  }
+
 
   if (mode == psModeForm) {
     writePS("pdfEndPage\n");
@@ -1824,18 +1997,22 @@ void PSOutputDev::endPage() {
     writePS("} def\n");
     writePS("end end\n");
   } else {
-    writePS("showpage\n");
-    writePS("%%PageTrailer\n");
-    writePS("pdfEndPage\n");
+    if (!manualCtrl) {
+      writePS("showpage\n");
+      writePS("%%PageTrailer\n");
+      writePageTrailer();
+    }
   }
 }
 
 void PSOutputDev::saveState(GfxState *state) {
   writePS("q\n");
+  ++numSaves;
 }
 
 void PSOutputDev::restoreState(GfxState *state) {
   writePS("Q\n");
+  --numSaves;
 }
 
 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
@@ -2167,7 +2344,7 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
   int len, nChars, uLen, n, m, i, j;
 
   // check for invisible text -- this is used by Acrobat Capture
-  if ((state->getRender() & 3) == 3) {
+  if (state->getRender() == 3) {
     return;
   }
 
@@ -2254,6 +2431,17 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
   if (font->isCIDFont()) {
     delete s2;
   }
+
+  if (state->getRender() & 4) {
+    haveTextClip = gTrue;
+  }
+}
+
+void PSOutputDev::endTextObject(GfxState *state) {
+  if (haveTextClip) {
+    writePS("Tclip\n");
+    haveTextClip = gFalse;
+  }
 }
 
 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
@@ -2486,6 +2674,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
       } while (c != '~' && c != EOF);
       writePS("~>]\n");
       writePS("0\n");
+      str->close();
       delete str;
     } else {
       // set up to use the array already created by setupImages()
@@ -2541,7 +2730,8 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
 
     // data source
     writePS("  /DataSource currentfile\n");
-    s = str->getPSFilter("    ");
+    s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
+                        "    ");
     if (inlineImg || !s) {
       useRLE = gTrue;
       useASCII = gTrue;
@@ -2818,7 +3008,10 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
       writePS("\n");
     }
     writePS(">]");
+#if 0 //~ this shouldn't be here since the PS file doesn't actually refer
+      //~ to this colorant (it's converted to CMYK instead)
     addCustomColor(separationCS);
+#endif
     break;
 
   case csDeviceN:
index f7896ccc73963df346f7f54310997c1e5e80f4bd..06130fbcb853b91b9a5514561c4f95cbcfce9d7f 100644 (file)
@@ -25,6 +25,7 @@ class GfxPath;
 class GfxFont;
 class GfxColorSpace;
 class GfxSeparationColorSpace;
+class PDFRectangle;
 struct PSFont16Enc;
 class PSOutCustomColor;
 
@@ -52,12 +53,16 @@ public:
 
   // Open a PostScript output file, and write the prolog.
   PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
-             int firstPage, int lastPage, PSOutMode modeA);
+             int firstPage, int lastPage, PSOutMode modeA,
+             int paperWidthA = 0, int paperHeightA = 0,
+             GBool manualCtrlA = gFalse);
 
   // Open a PSOutputDev that will write to a generic stream.
   PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
              XRef *xrefA, Catalog *catalog,
-             int firstPage, int lastPage, PSOutMode modeA);
+             int firstPage, int lastPage, PSOutMode modeA,
+             int paperWidthA = 0, int paperHeightA = 0,
+             GBool manualCtrlA = gFalse);
 
   // Destructor -- writes the trailer and closes the file.
   virtual ~PSOutputDev();
@@ -78,6 +83,26 @@ public:
   // text in Type 3 fonts will be drawn with drawChar/drawString.
   virtual GBool interpretType3Chars() { return gFalse; }
 
+  //----- header/trailer (used only if manualCtrl is true)
+
+  // Write the document-level header.
+  void writeHeader(int firstPage, int lastPage, PDFRectangle *box);
+
+  // Write the Xpdf procset.
+  void writeXpdfProcset();
+
+  // Write the document-level setup.
+  void writeDocSetup(Catalog *catalog, int firstPage, int lastPage);
+
+  // Write the setup for the current page.
+  void writePageSetup();
+
+  // Write the trailer for the current page.
+  void writePageTrailer();
+
+  // Write the document trailer.
+  void writeTrailer();
+
   //----- initialization and control
 
   // Start a page.
@@ -124,6 +149,7 @@ public:
 
   //----- text drawing
   virtual void drawString(GfxState *state, GString *s);
+  virtual void endTextObject(GfxState *state);
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
@@ -147,23 +173,32 @@ public:
   //----- PostScript XObjects
   virtual void psXObject(Stream *psStream, Stream *level1Stream);
 
+  //----- miscellaneous
+  void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
+                     void *data)
+    { underlayCbk = cbk; underlayCbkData = data; }
+  void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
+                    void *data)
+    { overlayCbk = cbk; overlayCbkData = data; }
 
 private:
 
   void init(PSOutputFunc outputFuncA, void *outputStreamA,
            PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
-           int firstPage, int lastPage, PSOutMode modeA);
+           int firstPage, int lastPage, PSOutMode modeA,
+           int paperWidthA, int paperHeightA,
+           GBool manualCtrlA);
   void setupResources(Dict *resDict);
   void setupFonts(Dict *resDict);
   void setupFont(GfxFont *font, Dict *parentResDict);
-  void setupEmbeddedType1Font(Ref *id, char *psName);
-  void setupExternalType1Font(GString *fileName, char *psName);
-  void setupEmbeddedType1CFont(GfxFont *font, Ref *id, char *psName);
-  void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, char *psName);
-  void setupExternalTrueTypeFont(GfxFont *font, char *psName);
-  void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, char *psName);
-  void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, char *psName);
-  void setupType3Font(GfxFont *font, char *psName, Dict *parentResDict);
+  void setupEmbeddedType1Font(Ref *id, GString *psName);
+  void setupExternalType1Font(GString *fileName, GString *psName);
+  void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName);
+  void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName);
+  void setupExternalTrueTypeFont(GfxFont *font, GString *psName);
+  void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);
+  void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName);
+  void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);
   void setupImages(Dict *resDict);
   void setupImage(Ref id, Stream *str);
   void addProcessColor(double c, double m, double y, double k);
@@ -201,7 +236,12 @@ private:
   PSOutputFunc outputFunc;
   void *outputStream;
   PSFileType fileType;         // file / pipe / stdout
+  GBool manualCtrl;
   int seqPage;                 // current sequential page number
+  void (*underlayCbk)(PSOutputDev *psOut, void *data);
+  void *underlayCbkData;
+  void (*overlayCbk)(PSOutputDev *psOut, void *data);
+  void *overlayCbkData;
 
   XRef *xref;                  // the xref table for this PDF file
 
@@ -214,11 +254,14 @@ private:
   GString **fontFileNames;     // list of names of all embedded external fonts
   int fontFileNameLen;         // number of entries in fontFileNames array
   int fontFileNameSize;                // size of fontFileNames array
+  int nextTrueTypeNum;         // next unique number to append to a TrueType
+                               //   font name
   PSFont16Enc *font16Enc;      // encodings for substitute 16-bit fonts
   int font16EncLen;            // number of entries in font16Enc array
   int font16EncSize;           // size of font16Enc array
   GList *xobjStack;            // stack of XObject dicts currently being
                                //   processed
+  int numSaves;                        // current number of gsaves
 
   double tx, ty;               // global translation
   double xScale, yScale;       // global scaling
@@ -230,6 +273,9 @@ private:
   PSOutCustomColor             // used custom colors
     *customColors;
 
+  GBool haveTextClip;          // set if text has been drawn with a
+                               //   clipping render mode
+
   GBool inType3Char;           // inside a Type 3 CharProc
   GString *t3String;           // Type 3 content string
   double t3WX, t3WY,           // Type 3 character parameters
@@ -243,6 +289,8 @@ private:
 
   GBool ok;                    // set up ok?
 
+
+  friend class WinPDFPrinter;
 };
 
 #endif
index c7a1e1350fc6aa07f9e4ac204820ffa8f74edf05..e12e65cd2f68c1b71fae6f60c9b220d483f40db3 100644 (file)
@@ -59,8 +59,12 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
   readBox(dict, "MediaBox", &mediaBox);
 
   // crop box
-  cropBox = mediaBox;
-  haveCropBox = readBox(dict, "CropBox", &cropBox);
+  if (readBox(dict, "CropBox", &cropBox)) {
+    haveCropBox = gTrue;
+  }
+  if (!haveCropBox) {
+    cropBox = mediaBox;
+  }
 
   // if the MediaBox is excessively larger than the CropBox,
   // just use the CropBox
@@ -222,18 +226,18 @@ Page::~Page() {
   contents.free();
 }
 
-void Page::display(OutputDev *out, double dpi, int rotate,
+void Page::display(OutputDev *out, double hDPI, double vDPI, int rotate,
                   Links *links, Catalog *catalog,
                   GBool (*abortCheckCbk)(void *data),
                   void *abortCheckCbkData,
                    GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
                    void *annotDisplayDecideCbkData) {
-  displaySlice(out, dpi, rotate, -1, -1, -1, -1, links, catalog,
+  displaySlice(out, hDPI, vDPI, rotate, -1, -1, -1, -1, links, catalog,
               abortCheckCbk, abortCheckCbkData,
                annotDisplayDecideCbk, annotDisplayDecideCbkData);
 }
 
-void Page::displaySlice(OutputDev *out, double dpi, int rotate,
+void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate,
                        int sliceX, int sliceY, int sliceW, int sliceH,
                        Links *links, Catalog *catalog,
                        GBool (*abortCheckCbk)(void *data),
@@ -247,7 +251,7 @@ void Page::displaySlice(OutputDev *out, double dpi, int rotate,
   Object obj;
   Link *link;
   Annots *annotList;
-  double k;
+  double kx, ky;
   int i;
 
   rotate += getRotate();
@@ -259,46 +263,47 @@ void Page::displaySlice(OutputDev *out, double dpi, int rotate,
 
   mediaBox = getBox();
   if (sliceW >= 0 && sliceH >= 0) {
-    k = 72.0 / dpi;
+    kx = 72.0 / hDPI;
+    ky = 72.0 / vDPI;
     if (rotate == 90) {
       if (out->upsideDown()) {
-       box.x1 = mediaBox->x1 + k * sliceY;
-       box.x2 = mediaBox->x1 + k * (sliceY + sliceH);
+       box.x1 = mediaBox->x1 + ky * sliceY;
+       box.x2 = mediaBox->x1 + ky * (sliceY + sliceH);
       } else {
-       box.x1 = mediaBox->x2 - k * (sliceY + sliceH);
-       box.x2 = mediaBox->x2 - k * sliceY;
+       box.x1 = mediaBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - ky * sliceY;
       }
-      box.y1 = mediaBox->y1 + k * sliceX;
-      box.y2 = mediaBox->y1 + k * (sliceX + sliceW);
+      box.y1 = mediaBox->y1 + kx * sliceX;
+      box.y2 = mediaBox->y1 + kx * (sliceX + sliceW);
     } else if (rotate == 180) {
-      box.x1 = mediaBox->x2 - k * (sliceX + sliceW);
-      box.x2 = mediaBox->x2 - k * sliceX;
+      box.x1 = mediaBox->x2 - kx * (sliceX + sliceW);
+      box.x2 = mediaBox->x2 - kx * sliceX;
       if (out->upsideDown()) {
-       box.y1 = mediaBox->y1 + k * sliceY;
-       box.y2 = mediaBox->y1 + k * (sliceY + sliceH);
+       box.y1 = mediaBox->y1 + ky * sliceY;
+       box.y2 = mediaBox->y1 + ky * (sliceY + sliceH);
       } else {
-       box.y1 = mediaBox->y2 - k * (sliceY + sliceH);
-       box.y2 = mediaBox->y2 - k * sliceY;
+       box.y1 = mediaBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - ky * sliceY;
       }
     } else if (rotate == 270) {
       if (out->upsideDown()) {
-       box.x1 = mediaBox->x2 - k * (sliceY + sliceH);
-       box.x2 = mediaBox->x2 - k * sliceY;
+       box.x1 = mediaBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - ky * sliceY;
       } else {
-       box.x1 = mediaBox->x1 + k * sliceY;
-       box.x2 = mediaBox->x1 + k * (sliceY + sliceH);
+       box.x1 = mediaBox->x1 + ky * sliceY;
+       box.x2 = mediaBox->x1 + ky * (sliceY + sliceH);
       }
-      box.y1 = mediaBox->y2 - k * (sliceX + sliceW);
-      box.y2 = mediaBox->y2 - k * sliceX;
+      box.y1 = mediaBox->y2 - kx * (sliceX + sliceW);
+      box.y2 = mediaBox->y2 - kx * sliceX;
     } else {
-      box.x1 = mediaBox->x1 + k * sliceX;
-      box.x2 = mediaBox->x1 + k * (sliceX + sliceW);
+      box.x1 = mediaBox->x1 + kx * sliceX;
+      box.x2 = mediaBox->x1 + kx * (sliceX + sliceW);
       if (out->upsideDown()) {
-       box.y1 = mediaBox->y2 - k * (sliceY + sliceH);
-       box.y2 = mediaBox->y2 - k * sliceY;
+       box.y1 = mediaBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - ky * sliceY;
       } else {
-       box.y1 = mediaBox->y1 + k * sliceY;
-       box.y2 = mediaBox->y1 + k * (sliceY + sliceH);
+       box.y1 = mediaBox->y1 + ky * sliceY;
+       box.y2 = mediaBox->y1 + ky * (sliceY + sliceH);
       }
     }
   } else {
@@ -317,25 +322,28 @@ void Page::displaySlice(OutputDev *out, double dpi, int rotate,
   }
 
   gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
-               dpi, &box, isCropped(), cropBox, rotate,
+               hDPI, vDPI, &box, isCropped(), cropBox, rotate,
                abortCheckCbk, abortCheckCbkData);
   contents.fetch(xref, &obj);
   if (!obj.isNull()) {
+    gfx->saveState();
     gfx->display(&obj);
+    gfx->restoreState();
   }
   obj.free();
 
   // draw links
   if (links) {
+    gfx->saveState();
     for (i = 0; i < links->getNumLinks(); ++i) {
       link = links->getLink(i);
       out->drawLink(link, catalog);
     }
+    gfx->restoreState();
     out->dump();
   }
 
   // draw non-link annotations
-  //~ need to reset CTM ???
   annotList = new Annots(xref, annots.fetch(xref, &obj));
   obj.free();
 #ifdef USE_ANNOTS_VIEW
index 8b339eb49a7719600ce48e1dd3bda2ece7703b2e..8fa1ef603490aa9f1cf929573bd6984574fce0c8 100644 (file)
@@ -146,7 +146,7 @@ public:
   Object *getThumb(Object *obj) { return thumb.fetch(xref, obj); }
 
   // Display a page.
-  void display(OutputDev *out, double dpi, int rotate,
+  void display(OutputDev *out, double hDPI, double vDPI, int rotate,
               Links *links, Catalog *catalog,
               GBool (*abortCheckCbk)(void *data) = NULL,
               void *abortCheckCbkData = NULL,
@@ -154,7 +154,7 @@ public:
                void *annotDisplayDecideCbkData = NULL);
 
   // Display part of a page.
-  void displaySlice(OutputDev *out, double dpi, int rotate,
+  void displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate,
                    int sliceX, int sliceY, int sliceW, int sliceH,
                    Links *links, Catalog *catalog,
                    GBool (*abortCheckCbk)(void *data) = NULL,
index deb894aefb39591d427fb1921abaaee7fda1f871..2f881d46f1880e5a1a454a6b6c2f444e3f9b8bbc 100644 (file)
@@ -180,6 +180,12 @@ Stream *Parser::makeStream(Object *dict) {
     length = endPos - pos;
   }
 
+  // in badly damaged PDF files, we can run off the end of the input
+  // stream immediately after the "stream" token
+  if (!lexer->getStream()) {
+    return NULL;
+  }
+
   // make base stream
   str = lexer->getStream()->getBaseStream()->makeSubStream(pos, gTrue,
                                                           length, dict);
@@ -193,10 +199,12 @@ Stream *Parser::makeStream(Object *dict) {
   // refill token buffers and check for 'endstream'
   shift();  // kill '>>'
   shift();  // kill 'stream'
-  if (buf1.isCmd("endstream"))
+  if (buf1.isCmd("endstream")) {
     shift();
-  else
+  } else {
     error(getPos(), "Missing 'endstream'");
+    str->ignoreLength();
+  }
 
   return str;
 }
index 11b51b60fe8626a0c0020a19ed74897c7e94cae1..e770b61dec2953ae1f442e26b9125102ef2dd4d7 100644 (file)
@@ -84,7 +84,7 @@ char *Stream::getLine(char *buf, int size) {
   return buf;
 }
 
-GString *Stream::getPSFilter(char *indent) {
+GString *Stream::getPSFilter(int psLevel, char *indent) {
   return new GString();
 }
 
@@ -696,13 +696,14 @@ void FileStream::moveStart(int delta) {
 // MemStream
 //------------------------------------------------------------------------
 
-MemStream::MemStream(char *bufA, Guint lengthA, Object *dictA):
+MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
     BaseStream(dictA) {
   buf = bufA;
-  needFree = gFalse;
+  start = startA;
   length = lengthA;
-  bufEnd = buf + length;
-  bufPtr = buf;
+  bufEnd = buf + start + length;
+  bufPtr = buf + start;
+  needFree = gFalse;
 }
 
 MemStream::~MemStream() {
@@ -711,20 +712,22 @@ MemStream::~MemStream() {
   }
 }
 
-Stream *MemStream::makeSubStream(Guint start, GBool limited,
+Stream *MemStream::makeSubStream(Guint startA, GBool limited,
                                 Guint lengthA, Object *dictA) {
+  MemStream *subStr;
   Guint newLength;
 
-  if (!limited || start + lengthA > length) {
-    newLength = length - start;
+  if (!limited || startA + lengthA > start + length) {
+    newLength = start + length - startA;
   } else {
     newLength = lengthA;
   }
-  return new MemStream(buf + start, newLength, dictA);
+  subStr = new MemStream(buf, startA, newLength, dictA);
+  return subStr;
 }
 
 void MemStream::reset() {
-  bufPtr = buf;
+  bufPtr = buf + start;
 #ifndef NO_DECRYPTION
   if (decrypt) {
     decrypt->reset();
@@ -736,24 +739,24 @@ void MemStream::close() {
 }
 
 void MemStream::setPos(Guint pos, int dir) {
+  Guint i;
+
   if (dir >= 0) {
-    if (pos > length) {
-      bufPtr = bufEnd;
-    } else {
-      bufPtr = buf + pos;
-    }
+    i = pos;
   } else {
-    if (pos > length) {
-      bufPtr = buf;
-    } else {
-      bufPtr = bufEnd - pos;
-    }
+    i = start + length - pos;
   }
+  if (i < start) {
+    i = start;
+  } else if (i > start + length) {
+    i = start + length;
+  }
+  bufPtr = buf + i;
 }
 
 void MemStream::moveStart(int delta) {
-  buf += delta;
-  bufPtr = buf;
+  start += delta;
+  bufPtr = buf + start;
 }
 
 #ifndef NO_DECRYPTION
@@ -764,12 +767,13 @@ void MemStream::doDecryption(Guchar *fileKey, int keyLength,
 
   this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
   if (decrypt) {
-    newBuf = (char *)gmalloc(bufEnd - buf);
-    for (p = buf, q = newBuf; p < bufEnd; ++p, ++q) {
+    newBuf = (char *)gmalloc(length);
+    for (p = buf + start, q = newBuf; p < bufEnd; ++p, ++q) {
       *q = (char)decrypt->decryptByte((Guchar)*p);
     }
-    bufEnd = newBuf + (bufEnd - buf);
-    bufPtr = newBuf + (bufPtr - buf);
+    bufEnd = newBuf + length;
+    bufPtr = newBuf + (bufPtr - (buf + start));
+    start = 0;
     buf = newBuf;
     needFree = gTrue;
   }
@@ -880,10 +884,13 @@ int ASCIIHexStream::lookChar() {
   return buf;
 }
 
-GString *ASCIIHexStream::getPSFilter(char *indent) {
+GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCIIHexDecode filter\n");
@@ -958,10 +965,13 @@ int ASCII85Stream::lookChar() {
   return b[index];
 }
 
-GString *ASCII85Stream::getPSFilter(char *indent) {
+GString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCII85Decode filter\n");
@@ -1137,13 +1147,13 @@ int LZWStream::getCode() {
   return code;
 }
 
-GString *LZWStream::getPSFilter(char *indent) {
+GString *LZWStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (pred) {
+  if (psLevel < 2 || pred) {
     return NULL;
   }
-  if (!(s = str->getPSFilter(indent))) {
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/LZWDecode filter\n");
@@ -1174,10 +1184,13 @@ void RunLengthStream::reset() {
   eof = gFalse;
 }
 
-GString *RunLengthStream::getPSFilter(char *indent) {
+GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/RunLengthDecode filter\n");
@@ -1334,12 +1347,14 @@ int CCITTFaxStream::lookChar() {
              code2 += code3 = getWhiteCode();
            } while (code3 >= 64);
          }
-         codingLine[a0 + 1] = a0New + code1;
-         ++a0;
-         a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
-         ++a0;
-         while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
-           b1 += 2;
+         if (code1 > 0 || code2 > 0) {
+           codingLine[a0 + 1] = a0New + code1;
+           ++a0;
+           a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
+           ++a0;
+           while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
+             b1 += 2;
+         }
          break;
        case twoDimVert0:
          a0New = codingLine[++a0] = refLine[b1];
@@ -1504,7 +1519,7 @@ int CCITTFaxStream::lookChar() {
          return EOF;
        }
        eatBits(1);
-       code1 = look13Bits();
+       code1 = lookBits(13);
       } while ((code1 >> 1) != 0x001);
       eatBits(12); 
       codingLine[++a0] = columns;
@@ -1724,11 +1739,14 @@ short CCITTFaxStream::lookBits(int n) {
   return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
 }
 
-GString *CCITTFaxStream::getPSFilter(char *indent) {
+GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
   char s1[50];
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< ");
@@ -1864,6 +1882,7 @@ void DCTStream::reset() {
   numDCHuffTables = 0;
   numACHuffTables = 0;
   colorXform = 0;
+  gotJFIFMarker = gFalse;
   gotAdobeMarker = gFalse;
   restartInterval = 0;
 
@@ -1894,7 +1913,12 @@ void DCTStream::reset() {
 
   // figure out color transform
   if (!gotAdobeMarker && numComps == 3) {
-    if (compInfo[0].id == 1 && compInfo[1].id == 2 && compInfo[2].id == 3) {
+    if (gotJFIFMarker) {
+      colorXform = 1;
+    } else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
+              compInfo[2].id == 66) { // ASCII "RGB"
+      colorXform = 0;
+    } else {
       colorXform = 1;
     }
   }
@@ -2142,7 +2166,7 @@ GBool DCTStream::readMCURow() {
 void DCTStream::readScan() {
   int data[64];
   int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
-  int h, v, horiz, vert, hSub, vSub;
+  int h, v, horiz, vert, vSub;
   int *p1;
   int c;
 
@@ -2185,7 +2209,6 @@ void DCTStream::readScan() {
        v = compInfo[cc].vSample;
        horiz = mcuWidth / h;
        vert = mcuHeight / v;
-       hSub = horiz / 8;
        vSub = vert / 8;
        for (y2 = 0; y2 < dy1; y2 += vert) {
          for (x2 = 0; x2 < dx1; x2 += horiz) {
@@ -2810,7 +2833,6 @@ GBool DCTStream::readHeader() {
       break;
     case 0xd9:                 // EOI
       return gFalse;
-      break;
     case 0xda:                 // SOS
       if (!readScanInfo()) {
        return gFalse;
@@ -2827,6 +2849,11 @@ GBool DCTStream::readHeader() {
        return gFalse;
       }
       break;
+    case 0xe0:                 // APP0
+      if (!readJFIFMarker()) {
+       return gFalse;
+      }
+      break;
     case 0xee:                 // APP14
       if (!readAdobeMarker()) {
        return gFalse;
@@ -2923,14 +2950,21 @@ GBool DCTStream::readScanInfo() {
   }
   for (i = 0; i < scanInfo.numComps; ++i) {
     id = str->getChar();
-    for (j = 0; j < numComps; ++j) {
-      if (id == compInfo[j].id) {
-       break;
+    // some (broken) DCT streams reuse ID numbers, but at least they
+    // keep the components in order, so we check compInfo[i] first to
+    // work around the problem
+    if (id == compInfo[i].id) {
+      j = i;
+    } else {
+      for (j = 0; j < numComps; ++j) {
+       if (id == compInfo[j].id) {
+         break;
+       }
+      }
+      if (j == numComps) {
+       error(getPos(), "Bad DCT component ID in scan info block");
+       return gFalse;
       }
-    }
-    if (j == numComps) {
-      error(getPos(), "Bad DCT component ID in scan info block");
-      return gFalse;
     }
     scanInfo.comp[j] = gTrue;
     c = str->getChar();
@@ -3023,6 +3057,36 @@ GBool DCTStream::readRestartInterval() {
   return gTrue;
 }
 
+GBool DCTStream::readJFIFMarker() {
+  int length, i;
+  char buf[5];
+  int c;
+
+  length = read16();
+  length -= 2;
+  if (length >= 5) {
+    for (i = 0; i < 5; ++i) {
+      if ((c = str->getChar()) == EOF) {
+       error(getPos(), "Bad DCT APP0 marker");
+       return gFalse;
+      }
+      buf[i] = c;
+    }
+    length -= 5;
+    if (!memcmp(buf, "JFIF\0", 5)) {
+      gotJFIFMarker = gTrue;
+    }
+  }
+  while (length > 0) {
+    if (str->getChar() == EOF) {
+      error(getPos(), "Bad DCT APP0 marker");
+      return gFalse;
+    }
+    --length;
+  }
+  return gTrue;
+}
+
 GBool DCTStream::readAdobeMarker() {
   int length, i;
   char buf[12];
@@ -3090,10 +3154,13 @@ int DCTStream::read16() {
   return (c1 << 8) + c2;
 }
 
-GString *DCTStream::getPSFilter(char *indent) {
+GString *DCTStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< >> /DCTDecode filter\n");
@@ -3280,8 +3347,17 @@ int FlateStream::getRawChar() {
   return c;
 }
 
-GString *FlateStream::getPSFilter(char *indent) {
-  return NULL;
+GString *FlateStream::getPSFilter(int psLevel, char *indent) {
+  GString *s;
+
+  if (psLevel < 3 || pred) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
+  s->append(indent)->append("<< >> /FlateDecode filter\n");
+  return s;
 }
 
 GBool FlateStream::isBinary(GBool last) {
@@ -3658,9 +3734,6 @@ void FixedLengthEncoder::reset() {
   count = 0;
 }
 
-void FixedLengthEncoder::close() {
-}
-
 int FixedLengthEncoder::getChar() {
   if (length >= 0 && count >= length)
     return EOF;
@@ -3698,9 +3771,6 @@ void ASCIIHexEncoder::reset() {
   eof = gFalse;
 }
 
-void ASCIIHexEncoder::close() {
-}
-
 GBool ASCIIHexEncoder::fillBuf() {
   static char *hex = "0123456789abcdef";
   int c;
@@ -3747,9 +3817,6 @@ void ASCII85Encoder::reset() {
   eof = gFalse;
 }
 
-void ASCII85Encoder::close() {
-}
-
 GBool ASCII85Encoder::fillBuf() {
   Gulong t;
   char buf1[5];
@@ -3817,9 +3884,6 @@ void RunLengthEncoder::reset() {
   eof = gFalse;
 }
 
-void RunLengthEncoder::close() {
-}
-
 //
 // When fillBuf finishes, buf[] looks like this:
 //   +-----+--------------+-----------------+--
index 31c0a973528cb69cd1de42a07f7420477b072ff9..0121df19c508452d9ea6b229d251e937d59fb003 100644 (file)
@@ -87,7 +87,7 @@ public:
   virtual void setPos(Guint pos, int dir = 0) = 0;
 
   // Get PostScript command for the filter(s).
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
 
   // Does this stream type potentially contain non-printable chars?
   virtual GBool isBinary(GBool last = gTrue) = 0;
@@ -105,6 +105,11 @@ public:
   // Returns the new stream.
   Stream *addFilters(Object *dict);
 
+  // Tell this stream to ignore any length limitation -- this only
+  // applies to BaseStream subclasses, and is used as a hack to work
+  // around broken PDF files with incorrect stream lengths.
+  virtual void ignoreLength() {}
+
 private:
 
   Stream *makeFilter(char *name, Stream *str, Object *params);
@@ -166,6 +171,7 @@ public:
   virtual void setPos(Guint pos, int dir = 0);
   virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
   virtual Dict *getDict() { return str->getDict(); }
+  virtual void ignoreLength() { str->ignoreLength(); }
 
 protected:
 
@@ -268,6 +274,7 @@ public:
   virtual int getPos() { return bufPos + (bufPtr - buf); }
   virtual void setPos(Guint pos, int dir = 0);
   virtual GBool isBinary(GBool last = gTrue) { return last; }
+  virtual void ignoreLength() { limited = gFalse; }
   virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 
@@ -294,7 +301,7 @@ private:
 class MemStream: public BaseStream {
 public:
 
-  MemStream(char *bufA, Guint lengthA, Object *dictA);
+  MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
   virtual ~MemStream();
   virtual Stream *makeSubStream(Guint start, GBool limited,
                                Guint lengthA, Object *dictA);
@@ -305,10 +312,10 @@ public:
     { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
   virtual int lookChar()
     { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
-  virtual int getPos() { return bufPtr - buf; }
+  virtual int getPos() { return (int)(bufPtr - buf); }
   virtual void setPos(Guint pos, int dir = 0);
   virtual GBool isBinary(GBool last = gTrue) { return last; }
-  virtual Guint getStart() { return 0; }
+  virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 #ifndef NO_DECRYPTION
   virtual void doDecryption(Guchar *fileKey, int keyLength,
@@ -318,10 +325,11 @@ public:
 private:
 
   char *buf;
+  Guint start;
   Guint length;
-  GBool needFree;
   char *bufEnd;
   char *bufPtr;
+  GBool needFree;
 };
 
 //------------------------------------------------------------------------
@@ -370,7 +378,7 @@ public:
   virtual int getChar()
     { int c = lookChar(); buf = EOF; return c; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -393,7 +401,7 @@ public:
   virtual int getChar()
     { int ch = lookChar(); ++index; return ch; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -419,7 +427,7 @@ public:
   virtual int getChar();
   virtual int lookChar();
   virtual int getRawChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -463,7 +471,7 @@ public:
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -494,7 +502,7 @@ public:
   virtual int getChar()
     { int c = lookChar(); buf = EOF; return c; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -564,7 +572,7 @@ public:
   virtual void reset();
   virtual int getChar();
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
   Stream *getRawStream() { return str; }
 
@@ -579,6 +587,7 @@ private:
   DCTScanInfo scanInfo;                // info for the current scan
   int numComps;                        // number of components in image
   int colorXform;              // need YCbCr-to-RGB transform?
+  GBool gotJFIFMarker;         // set if APP0 JFIF marker was present
   GBool gotAdobeMarker;                // set if APP14 Adobe marker was present
   int restartInterval;         // restart interval, in MCUs
   Guchar quantTables[4][64];   // quantization tables
@@ -618,6 +627,7 @@ private:
   GBool readQuantTables();
   GBool readHuffmanTables();
   GBool readRestartInterval();
+  GBool readJFIFMarker();
   GBool readAdobeMarker();
   GBool readTrailer();
   int readMarker();
@@ -663,7 +673,7 @@ public:
   virtual int getChar();
   virtual int lookChar();
   virtual int getRawChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -712,7 +722,7 @@ public:
   virtual void reset() {}
   virtual int getChar() { return EOF; }
   virtual int lookChar() { return EOF; }
-  virtual GString *getPSFilter(char *indent)  { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent)  { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
 };
 
@@ -727,10 +737,9 @@ public:
   ~FixedLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar();
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
@@ -751,12 +760,11 @@ public:
   virtual ~ASCIIHexEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
@@ -782,12 +790,11 @@ public:
   virtual ~ASCII85Encoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
@@ -813,12 +820,11 @@ public:
   virtual ~RunLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
index fc6d84905ebcef49b63abd85e5aa7c5c61653d9e..c499cf14d37917b0537a8c5b80cde03654096911 100644 (file)
@@ -65,16 +65,25 @@ TTFontFile::TTFontFile(TTFontEngine *engineA, char *fontFileName,
   // To match up with the Adobe-defined behaviour, we choose a cmap
   // like this:
   // 1. If the PDF font has an encoding:
-  //    1a. If the TrueType font has a Microsoft Unicode cmap, use it,
-  //        and use the Unicode indexes, not the char codes.
-  //    1b. If the TrueType font has a Macintosh Roman cmap, use it,
-  //        and reverse map the char names through MacRomanEncoding to
+  //    1a. If the PDF font specified MacRomanEncoding and the
+  //        TrueType font has a Macintosh Roman cmap, use it, and
+  //        reverse map the char names through MacRomanEncoding to
   //        get char codes.
+  //    1b. If the TrueType font has a Microsoft Unicode cmap or a
+  //        non-Microsoft Unicode cmap, use it, and use the Unicode
+  //        indexes, not the char codes.
+  //    1c. If the PDF font is symbolic and the TrueType font has a
+  //        Microsoft Symbol cmap, use it, and use char codes
+  //        directly (possibly with an offset of 0xf000).
+  //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        as in case 1a.
   // 2. If the PDF font does not have an encoding:
   //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
-  //        and use char codes directly.
+  //        and use char codes directly (possibly with an offset of
+  //        0xf000).
   //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
-  //        and use (0xf000 + char code).
+  //        and use char codes directly (possible with an offset of
+  //        0xf000).
   // 3. If none of these rules apply, use the first cmap and hope for
   //    the best (this shouldn't happen).
   unicodeCmap = macRomanCmap = msSymbolCmap = 0xffff;
@@ -91,7 +100,6 @@ TTFontFile::TTFontFile(TTFontEngine *engineA, char *fontFileName,
   }
   i = 0;
   mode = ttFontModeCharCode;
-  charMapOffset = 0;
   if (pdfFontHasEncoding) {
     if (unicodeCmap != 0xffff) {
       i = unicodeCmap;
@@ -114,8 +122,7 @@ TTFontFile::TTFontFile(TTFontEngine *engineA, char *fontFileName,
       mode = ttFontModeCharCode;
     } else if (msSymbolCmap != 0xffff) {
       i = msSymbolCmap;
-      mode = ttFontModeCharCodeOffset;
-      charMapOffset = 0xf000;
+      mode = ttFontModeCharCode;
     }
   }
   TT_Get_CharMap(face, i, &charMap);
@@ -421,11 +428,9 @@ GBool TTFont::getGlyphPixmap(CharCode c, Unicode u) {
     idx = TT_Char_Index(fontFile->charMap, (TT_UShort)u);
     break;
   case ttFontModeCharCode:
-    idx = TT_Char_Index(fontFile->charMap, (TT_UShort)c);
-    break;
-  case ttFontModeCharCodeOffset:
-    idx = TT_Char_Index(fontFile->charMap,
-                       (TT_UShort)(c + fontFile->charMapOffset));
+    if ((idx = TT_Char_Index(fontFile->charMap, (TT_UShort)c)) == 0) {
+      idx = TT_Char_Index(fontFile->charMap, (TT_UShort)(0xf000 + c));
+    }
     break;
   case ttFontModeCodeMap:
     if (c <= 0xff) {
index 96208e21b256a398e1ea1ce5d726a84bfd22e5ae..e4740eadbaaf4331a507a3f93d969857e69eaf1c 100644 (file)
@@ -55,7 +55,6 @@ private:
 enum TTFontIndexMode {
   ttFontModeUnicode,
   ttFontModeCharCode,
-  ttFontModeCharCodeOffset,
   ttFontModeCodeMap,
   ttFontModeCIDToGIDMap
 };
@@ -80,7 +79,6 @@ private:
   TT_Face face;
   TT_CharMap charMap;
   TTFontIndexMode mode;
-  int charMapOffset;
   Guchar *codeMap;
   Gushort *cidToGID;
   int cidToGIDLen;
index 4e9a63a5393bde428aef863786fd1557d5e77546..aeee59cd4f32f984981d915bc9b91b931fc29c67 100644 (file)
@@ -28,6 +28,7 @@
 #include "Error.h"
 #include "GlobalParams.h"
 #include "UnicodeMap.h"
+#include "UnicodeTypeTable.h"
 #include "GfxState.h"
 #include "TextOutputDev.h"
 
 // parameters
 //------------------------------------------------------------------------
 
-// Minium and maximum inter-word spacing (as a fraction of the average
-// character width).
-#define wordMinSpaceWidth 0.3
-#define wordMaxSpaceWidth 2.0
-
-// Default min and max inter-word spacing (when the average character
-// width is unknown).
-#define wordDefMinSpaceWidth 0.2
-#define wordDefMaxSpaceWidth 1.5
-
-// Max difference in x,y coordinates (as a fraction of the font size)
-// allowed for duplicated text (fake boldface, drop shadows) which is
-// to be discarded.
-#define dupMaxDeltaX 0.1
-#define dupMaxDeltaY 0.2
-
-// Min overlap (as a fraction of the font size) required for two
-// lines to be considered vertically overlapping.
-#define lineOverlapSlack 0.5
-
-// Max difference in baseline y coordinates (as a fraction of the font
-// size) allowed for words which are to be grouped into a line, not
-// including sub/superscripts.
-#define lineMaxBaselineDelta 0.1
-
-// Max ratio of font sizes allowed for words which are to be grouped
-// into a line, not including sub/superscripts.
-#define lineMaxFontSizeRatio 1.4
-
-// Min spacing (as a fraction of the font size) allowed between words
-// which are to be grouped into a line.
-#define lineMinDeltaX -0.5
-
-// Minimum vertical overlap (as a fraction of the font size) required
-// for superscript and subscript words.
-#define lineMinSuperscriptOverlap 0.3
-#define lineMinSubscriptOverlap   0.3
-
-// Min/max ratio of font sizes allowed for sub/superscripts compared to
-// the base text.
-#define lineMinSubscriptFontSizeRatio   0.4
-#define lineMaxSubscriptFontSizeRatio   1.01
-#define lineMinSuperscriptFontSizeRatio 0.4
-#define lineMaxSuperscriptFontSizeRatio 1.01
-
-// Max horizontal spacing (as a fraction of the font size) allowed
-// before sub/superscripts.
-#define lineMaxSubscriptDeltaX   0.2
-#define lineMaxSuperscriptDeltaX 0.2
-
-// Maximum vertical spacing (as a fraction of the font size) allowed
-// for lines which are to be grouped into a block.
-#define blkMaxSpacing 2.0
-
-// Max ratio of primary font sizes allowed for lines which are to be
-// grouped into a block.
-#define blkMaxFontSizeRatio 1.3
-
-// Min overlap (as a fraction of the font size) required for two
-// blocks to be considered vertically overlapping.
-#define blkOverlapSlack 0.5
-
-// Max vertical spacing (as a fraction of the font size) allowed
-// between blocks which are 'adjacent' when sorted by reading order.
-#define blkMaxSortSpacing 2.0
-
-// Max vertical offset (as a fraction of the font size) of the top and
-// bottom edges allowed for blocks which are to be grouped into a
-// flow.
-#define flowMaxDeltaY 1.0
+// Each bucket in a text pool includes baselines within a range of
+// this many points.
+#define textPoolStep 4
+
+// Inter-character space width which will cause addChar to break up a
+// text string.
+#define defaultSpaceWidth 0.25
+
+// Max distance between baselines of two lines within a block, as a
+// fraction of the font size.
+#define maxLineSpacingDelta 1.5
+
+// Max difference in primary font sizes on two lines in the same
+// block.  Delta1 is used when examining new lines above and below the
+// current block; delta2 is used when examining text that overlaps the
+// current block; delta3 is used when examining text to the left and
+// right of the current block.
+#define maxBlockFontSizeDelta1 0.05
+#define maxBlockFontSizeDelta2 0.6
+#define maxBlockFontSizeDelta3 0.2
+
+// Max difference in font sizes inside a word.
+#define maxWordFontSizeDelta 0.05
+
+// Maximum distance between baselines of two words on the same line,
+// e.g., distance between subscript or superscript and the primary
+// baseline, as a fraction of the font size.
+#define maxIntraLineDelta 0.5
+
+// Minimum inter-word spacing, as a fraction of the font size.  (Only
+// used for raw ordering.)
+#define minWordSpacing 0.2
+
+// Maximum inter-word spacing, as a fraction of the font size.
+#define maxWordSpacing 1.5
+
+// Minimum spacing between columns, as a fraction of the font size.
+#define minColSpacing 1.0
+
+// Maximum vertical spacing between blocks within a flow, as a
+// multiple of the font size.
+#define maxBlockSpacing 2.5
+
+// Minimum spacing between characters within a word, as a fraction of
+// the font size.
+#define minCharSpacing -0.2
+
+// Maximum spacing between characters within a word, as a fraction of
+// the font size, when there is no obvious extra-wide character
+// spacing.
+#define maxCharSpacing 0.03
+
+// When extra-wide character spacing is detected, the inter-character
+// space threshold is set to the minimum inter-character space
+// multiplied by this constant.
+#define maxWideCharSpacingMul 1.3
+
+// Max difference in primary,secondary coordinates (as a fraction of
+// the font size) allowed for duplicated text (fake boldface, drop
+// shadows) which is to be discarded.
+#define dupMaxPriDelta 0.1
+#define dupMaxSecDelta 0.2
 
 //------------------------------------------------------------------------
 // TextFontInfo
 //------------------------------------------------------------------------
 
 TextFontInfo::TextFontInfo(GfxState *state) {
-  double *textMat;
-  double t1, t2, avgWidth, w;
-  int n, i;
-
   gfxFont = state->getFont();
-  textMat = state->getTextMat();
-  horizScaling = state->getHorizScaling();
-  if ((t1 = fabs(textMat[0])) > 0.01 &&
-      (t2 = fabs(textMat[3])) > 0.01) {
-    horizScaling *= t1 / t2;
-  }
-
-  minSpaceWidth = horizScaling * wordDefMinSpaceWidth;
-  maxSpaceWidth = horizScaling * wordDefMaxSpaceWidth;
-  if (gfxFont && gfxFont->isCIDFont()) {
-    //~ handle 16-bit fonts
-  } else if (gfxFont && gfxFont->getType() != fontType3) {
-    avgWidth = 0;
-    n = 0;
-    for (i = 0; i < 256; ++i) {
-      w = ((Gfx8BitFont *)gfxFont)->getWidth(i);
-      if (w > 0) {
-       avgWidth += w;
-       ++n;
-      }
-    }
-    if (n > 0) {
-      avgWidth /= n;
-      minSpaceWidth = horizScaling * wordMinSpaceWidth * avgWidth;
-      maxSpaceWidth = horizScaling * wordMaxSpaceWidth * avgWidth;
-    }
-  }
-
+#if TEXTOUT_WORD_LIST
+  fontName = (gfxFont && gfxFont->getOrigName())
+                 ? gfxFont->getOrigName()->copy()
+                 : (GString *)NULL;
+#endif
 }
 
 TextFontInfo::~TextFontInfo() {
+#if TEXTOUT_WORD_LIST
+  if (fontName) {
+    delete fontName;
+  }
+#endif
 }
 
 GBool TextFontInfo::matches(GfxState *state) {
-  double *textMat;
-  double t1, t2, h;
-
-  textMat = state->getTextMat();
-  h = state->getHorizScaling();
-  if ((t1 = fabs(textMat[0])) > 0.01 &&
-      (t2 = fabs(textMat[3])) > 0.01) {
-    h *= t1 / t2;
-  }
-  return state->getFont() == gfxFont &&
-         fabs(h - horizScaling) < 0.01;
+  return state->getFont() == gfxFont;
 }
 
 //------------------------------------------------------------------------
 // TextWord
 //------------------------------------------------------------------------
 
-TextWord::TextWord(GfxState *state, double x0, double y0, int charPosA,
-                  TextFontInfo *fontA, double fontSizeA) {
+TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
+                  int charPosA, TextFontInfo *fontA, double fontSizeA) {
   GfxFont *gfxFont;
-  double x, y;
+  double x, y, ascent, descent;
 
+  rot = rotA;
   charPos = charPosA;
   charLen = 0;
   font = fontA;
   fontSize = fontSizeA;
   state->transform(x0, y0, &x, &y);
   if ((gfxFont = font->gfxFont)) {
-    yMin = y - gfxFont->getAscent() * fontSize;
-    yMax = y - gfxFont->getDescent() * fontSize;
+    ascent = gfxFont->getAscent() * fontSize;
+    descent = gfxFont->getDescent() * fontSize;
   } else {
     // this means that the PDF file draws text without a current font,
     // which should never happen
-    yMin = y - 0.95 * fontSize;
-    yMax = y + 0.35 * fontSize;
-  }
-  if (yMin == yMax) {
-    // this is a sanity check for a case that shouldn't happen -- but
-    // if it does happen, we want to avoid dividing by zero later
-    yMin = y;
-    yMax = y + 1;
+    ascent = 0.95 * fontSize;
+    descent = -0.35 * fontSize;
+  }
+  switch (rot) {
+  case 0:
+    yMin = y - ascent;
+    yMax = y - descent;
+    if (yMin == yMax) {
+      // this is a sanity check for a case that shouldn't happen -- but
+      // if it does happen, we want to avoid dividing by zero later
+      yMin = y;
+      yMax = y + 1;
+    }
+    base = y;
+    break;
+  case 1:
+    xMin = x + descent;
+    xMax = x + ascent;
+    if (xMin == xMax) {
+      // this is a sanity check for a case that shouldn't happen -- but
+      // if it does happen, we want to avoid dividing by zero later
+      xMin = x;
+      xMax = x + 1;
+    }
+    base = x;
+    break;
+  case 2:
+    yMin = y + descent;
+    yMax = y + ascent;
+    if (yMin == yMax) {
+      // this is a sanity check for a case that shouldn't happen -- but
+      // if it does happen, we want to avoid dividing by zero later
+      yMin = y;
+      yMax = y + 1;
+    }
+    base = y;
+    break;
+  case 3:
+    xMin = x - ascent;
+    xMax = x - descent;
+    if (xMin == xMax) {
+      // this is a sanity check for a case that shouldn't happen -- but
+      // if it does happen, we want to avoid dividing by zero later
+      xMin = x;
+      xMax = x + 1;
+    }
+    base = x;
+    break;
   }
-  yBase = y;
   text = NULL;
-  xRight = NULL;
+  edge = NULL;
   len = size = 0;
   spaceAfter = gFalse;
   next = NULL;
 
-}
+#if TEXTOUT_WORD_LIST
+  GfxRGB rgb;
 
+  if ((state->getRender() & 3) == 1) {
+    state->getStrokeRGB(&rgb);
+  } else {
+    state->getFillRGB(&rgb);
+  }
+  colorR = rgb.r;
+  colorG = rgb.g;
+  colorB = rgb.b;
+#endif
+}
 
 TextWord::~TextWord() {
   gfree(text);
-  gfree(xRight);
+  gfree(edge);
 }
 
 void TextWord::addChar(GfxState *state, double x, double y,
@@ -217,234 +229,1450 @@ void TextWord::addChar(GfxState *state, double x, double y,
   if (len == size) {
     size += 16;
     text = (Unicode *)grealloc(text, size * sizeof(Unicode));
-    xRight = (double *)grealloc(xRight, size * sizeof(double));
+    edge = (double *)grealloc(edge, (size + 1) * sizeof(double));
   }
   text[len] = u;
-  if (len == 0) {
-    xMin = x;
+  switch (rot) {
+  case 0:
+    if (len == 0) {
+      xMin = x;
+    }
+    edge[len] = x;
+    xMax = edge[len+1] = x + dx;
+    break;
+  case 1:
+    if (len == 0) {
+      yMin = y;
+    }
+    edge[len] = y;
+    yMax = edge[len+1] = y + dy;
+    break;
+  case 2:
+    if (len == 0) {
+      xMax = x;
+    }
+    edge[len] = x;
+    xMin = edge[len+1] = x + dx;
+    break;
+  case 3:
+    if (len == 0) {
+      yMax = y;
+    }
+    edge[len] = y;
+    yMin = edge[len+1] = y + dy;
+    break;
   }
-  xMax = xRight[len] = x + dx;
   ++len;
 }
 
-// Returns true if <this> comes before <word2> in xy order.
-GBool TextWord::xyBefore(TextWord *word2) {
-  return xMin < word2->xMin ||
-        (xMin == word2->xMin && yMin < word2->yMin);
-}
-
-// Merge another word onto the end of this one.
-void TextWord::merge(TextWord *word2) {
+void TextWord::merge(TextWord *word) {
   int i;
 
-  xMax = word2->xMax;
-  if (word2->yMin < yMin) {
-    yMin = word2->yMin;
+  if (word->xMin < xMin) {
+    xMin = word->xMin;
   }
-  if (word2->yMax > yMax) {
-    yMax = word2->yMax;
+  if (word->yMin < yMin) {
+    yMin = word->yMin;
   }
-  if (len + word2->len > size) {
-    size = len + word2->len;
+  if (word->xMax > xMax) {
+    xMax = word->xMax;
+  }
+  if (word->yMax > yMax) {
+    yMax = word->yMax;
+  }
+  if (len + word->len > size) {
+    size = len + word->len;
     text = (Unicode *)grealloc(text, size * sizeof(Unicode));
-    xRight = (double *)grealloc(xRight, size * sizeof(double));
+    edge = (double *)grealloc(edge, (size + 1) * sizeof(double));
+  }
+  for (i = 0; i < word->len; ++i) {
+    text[len + i] = word->text[i];
+    edge[len + i] = word->edge[i];
+  }
+  edge[len + word->len] = word->edge[word->len];
+  len += word->len;
+  charLen += word->charLen;
+}
+
+inline int TextWord::primaryCmp(TextWord *word) {
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    cmp = xMin - word->xMin;
+    break;
+  case 1:
+    cmp = yMin - word->yMin;
+    break;
+  case 2:
+    cmp = word->xMax - xMax;
+    break;
+  case 3:
+    cmp = word->yMax - yMax;
+    break;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+double TextWord::primaryDelta(TextWord *word) {
+  double delta;
+
+  delta = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    delta = word->xMin - xMax;
+    break;
+  case 1:
+    delta = word->yMin - yMax;
+    break;
+  case 2:
+    delta = xMin - word->xMax;
+    break;
+  case 3:
+    delta = yMin - word->yMax;
+    break;
+  }
+  return delta;
+}
+
+int TextWord::cmpYX(const void *p1, const void *p2) {
+  TextWord *word1 = *(TextWord **)p1;
+  TextWord *word2 = *(TextWord **)p2;
+  double cmp;
+
+  cmp = word1->yMin - word2->yMin;
+  if (cmp == 0) {
+    cmp = word1->xMin - word2->xMin;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+#if TEXTOUT_WORD_LIST
+
+GString *TextWord::getText() {
+  GString *s;
+  UnicodeMap *uMap;
+  char buf[8];
+  int n, i;
+
+  s = new GString();
+  if (!(uMap = globalParams->getTextEncoding())) {
+    return s;
   }
-  for (i = 0; i < word2->len; ++i) {
-    text[len + i] = word2->text[i];
-    xRight[len + i] = word2->xRight[i];
+  for (i = 0; i < len; ++i) {
+    n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+    s->append(buf, n);
   }
-  len += word2->len;
-  charLen += word2->charLen;
+  uMap->decRefCnt();
+  return s;
+}
+
+#endif // TEXTOUT_WORD_LIST
+
+//------------------------------------------------------------------------
+// TextPool
+//------------------------------------------------------------------------
+
+TextPool::TextPool() {
+  minBaseIdx = 0;
+  maxBaseIdx = -1;
+  pool = NULL;
+  cursor = NULL;
+  cursorBaseIdx = -1;
+}
+
+TextPool::~TextPool() {
+  int baseIdx;
+  TextWord *word, *word2;
+
+  for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
+    for (word = pool[baseIdx - minBaseIdx]; word; word = word2) {
+      word2 = word->next;
+      delete word;
+    }
+  }
+  gfree(pool);
+}
+
+int TextPool::getBaseIdx(double base) {
+  int baseIdx;
+
+  baseIdx = (int)(base / textPoolStep);
+  if (baseIdx < minBaseIdx) {
+    return minBaseIdx;
+  }
+  if (baseIdx > maxBaseIdx) {
+    return maxBaseIdx;
+  }
+  return baseIdx;
+}
+
+void TextPool::addWord(TextWord *word) {
+  TextWord **newPool;
+  int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx;
+  TextWord *w0, *w1;
+
+  // expand the array if needed
+  wordBaseIdx = (int)(word->base / textPoolStep);
+  if (minBaseIdx > maxBaseIdx) {
+    minBaseIdx = wordBaseIdx - 128;
+    maxBaseIdx = wordBaseIdx + 128;
+    pool = (TextWord **)gmalloc((maxBaseIdx - minBaseIdx + 1) *
+                               sizeof(TextWord *));
+    for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
+      pool[baseIdx - minBaseIdx] = NULL;
+    }
+  } else if (wordBaseIdx < minBaseIdx) {
+    newMinBaseIdx = wordBaseIdx - 128;
+    newPool = (TextWord **)gmalloc((maxBaseIdx - newMinBaseIdx + 1) *
+                                  sizeof(TextWord *));
+    for (baseIdx = newMinBaseIdx; baseIdx < minBaseIdx; ++baseIdx) {
+      newPool[baseIdx - newMinBaseIdx] = NULL;
+    }
+    memcpy(&newPool[minBaseIdx - newMinBaseIdx], pool,
+          (maxBaseIdx - minBaseIdx + 1) * sizeof(TextWord *));
+    gfree(pool);
+    pool = newPool;
+    minBaseIdx = newMinBaseIdx;
+  } else if (wordBaseIdx > maxBaseIdx) {
+    newMaxBaseIdx = wordBaseIdx + 128;
+    pool = (TextWord **)grealloc(pool, (newMaxBaseIdx - minBaseIdx + 1) *
+                                        sizeof(TextWord *));
+    for (baseIdx = maxBaseIdx + 1; baseIdx <= newMaxBaseIdx; ++baseIdx) {
+      pool[baseIdx - minBaseIdx] = NULL;
+    }
+    maxBaseIdx = newMaxBaseIdx;
+  }
+
+  // insert the new word
+  if (cursor && wordBaseIdx == cursorBaseIdx &&
+      word->primaryCmp(cursor) > 0) {
+    w0 = cursor;
+    w1 = cursor->next;
+  } else {
+    w0 = NULL;
+    w1 = pool[wordBaseIdx - minBaseIdx];
+  }
+  for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) ;
+  word->next = w1;
+  if (w0) {
+    w0->next = word;
+  } else {
+    pool[wordBaseIdx - minBaseIdx] = word;
+  }
+  cursor = word;
+  cursorBaseIdx = wordBaseIdx;
 }
 
 //------------------------------------------------------------------------
 // TextLine
 //------------------------------------------------------------------------
 
-TextLine::TextLine() {
-  words = NULL;
+TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) {
+  blk = blkA;
+  rot = rotA;
+  xMin = yMin = 0;
+  xMax = yMax = -1;
+  base = baseA;
+  words = lastWord = NULL;
   text = NULL;
-  xRight = NULL;
+  edge = NULL;
   col = NULL;
   len = 0;
+  convertedLen = 0;
   hyphenated = gFalse;
-  pageNext = NULL;
   next = NULL;
-  flowNext = NULL;
 }
 
 TextLine::~TextLine() {
-  TextWord *w1, *w2;
+  TextWord *word;
 
-  for (w1 = words; w1; w1 = w2) {
-    w2 = w1->next;
-    delete w1;
+  while (words) {
+    word = words;
+    words = words->next;
+    delete word;
   }
   gfree(text);
-  gfree(xRight);
+  gfree(edge);
   gfree(col);
 }
 
-// Returns true if <this> comes before <line2> in yx order, allowing
-// slack for vertically overlapping lines.
-GBool TextLine::yxBefore(TextLine *line2) {
-  double dy;
+void TextLine::addWord(TextWord *word) {
+  if (lastWord) {
+    lastWord->next = word;
+  } else {
+    words = word;
+  }
+  lastWord = word;
+
+  if (xMin > xMax) {
+    xMin = word->xMin;
+    xMax = word->xMax;
+    yMin = word->yMin;
+    yMax = word->yMax;
+  } else {
+    if (word->xMin < xMin) {
+      xMin = word->xMin;
+    }
+    if (word->xMax > xMax) {
+      xMax = word->xMax;
+    }
+    if (word->yMin < yMin) {
+      yMin = word->yMin;
+    }
+    if (word->yMax > yMax) {
+      yMax = word->yMax;
+    }
+  }
+}
 
-  dy = lineOverlapSlack * fontSize;
+double TextLine::primaryDelta(TextLine *line) {
+  double delta;
 
-  // non-overlapping case
-  if (line2->yMin > yMax - dy ||
-      line2->yMax < yMin + dy) {
-    return yMin < line2->yMin ||
-           (yMin == line2->yMin && xMin < line2->xMin);
+  delta = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    delta = line->xMin - xMax;
+    break;
+  case 1:
+    delta = line->yMin - yMax;
+    break;
+  case 2:
+    delta = xMin - line->xMax;
+    break;
+  case 3:
+    delta = yMin - line->yMax;
+    break;
   }
+  return delta;
+}
+
+int TextLine::primaryCmp(TextLine *line) {
+  double cmp;
 
-  // overlapping case
-  return xMin < line2->xMin;
+  cmp = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    cmp = xMin - line->xMin;
+    break;
+  case 1:
+    cmp = yMin - line->yMin;
+    break;
+  case 2:
+    cmp = line->xMax - xMax;
+    break;
+  case 3:
+    cmp = line->yMax - yMax;
+    break;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
 }
 
-// Merge another line's words onto the end of this line.
-void TextLine::merge(TextLine *line2) {
-  int newLen, i;
+int TextLine::secondaryCmp(TextLine *line) {
+  double cmp;
 
-  xMax = line2->xMax;
-  if (line2->yMin < yMin) {
-    yMin = line2->yMin;
+  cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base;
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextLine::cmpYX(TextLine *line) {
+  int cmp;
+
+  if ((cmp = secondaryCmp(line))) {
+    return cmp;
   }
-  if (line2->yMax > yMax) {
-    yMax = line2->yMax;
+  return primaryCmp(line);
+}
+
+int TextLine::cmpXY(const void *p1, const void *p2) {
+  TextLine *line1 = *(TextLine **)p1;
+  TextLine *line2 = *(TextLine **)p2;
+  int cmp;
+
+  if ((cmp = line1->primaryCmp(line2))) {
+    return cmp;
   }
-  xSpaceR = line2->xSpaceR;
-  lastWord->spaceAfter = gTrue;
-  lastWord->next = line2->words;
-  lastWord = line2->lastWord;
-  line2->words = NULL;
-  newLen = len + 1 + line2->len;
-  text = (Unicode *)grealloc(text, newLen * sizeof(Unicode));
-  xRight = (double *)grealloc(xRight, newLen * sizeof(double));
-  text[len] = (Unicode)0x0020;
-  xRight[len] = line2->xMin;
-  for (i = 0; i < line2->len; ++i) {
-    text[len + 1 + i] = line2->text[i];
-    xRight[len + 1 + i] = line2->xRight[i];
+  return line1->secondaryCmp(line2);
+}
+
+void TextLine::coalesce(UnicodeMap *uMap) {
+  TextWord *word0, *word1;
+  double space, delta, minSpace;
+  GBool isUnicode;
+  char buf[8];
+  int i, j;
+
+  if (words->next) {
+
+    // compute the inter-word space threshold
+    if (words->len > 1 || words->next->len > 1) {
+      minSpace = 0;
+    } else {
+      minSpace = words->primaryDelta(words->next);
+      for (word0 = words->next, word1 = word0->next;
+          word1 && minSpace > 0;
+          word0 = word1, word1 = word0->next) {
+       if (word1->len > 1) {
+         minSpace = 0;
+       }
+       delta = word0->primaryDelta(word1);
+       if (delta < minSpace) {
+         minSpace = delta;
+       }
+      }
+    }
+    if (minSpace <= 0) {
+      space = maxCharSpacing * words->fontSize;
+    } else {
+      space = maxWideCharSpacingMul * minSpace;
+    }
+
+    // merge words
+    word0 = words;
+    word1 = words->next;
+    while (word1) {
+      if (word0->primaryDelta(word1) >= space) {
+       word0->spaceAfter = gTrue;
+       word0 = word1;
+       word1 = word1->next;
+      } else if (word0->font == word1->font &&
+                fabs(word0->fontSize - word1->fontSize) <
+                maxWordFontSizeDelta * words->fontSize &&
+                word1->charPos == word0->charPos + word0->charLen) {
+       word0->merge(word1);
+       word0->next = word1->next;
+       delete word1;
+       word1 = word0->next;
+      } else {
+       word0 = word1;
+       word1 = word1->next;
+      }
+    }
   }
-  len = newLen;
-  convertedLen += line2->convertedLen;
-  hyphenated = line2->hyphenated;
+
+  // build the line text
+  isUnicode = uMap ? uMap->isUnicode() : gFalse;
+  len = 0;
+  for (word1 = words; word1; word1 = word1->next) {
+    len += word1->len;
+    if (word1->spaceAfter) {
+      ++len;
+    }
+  }
+  text = (Unicode *)gmalloc(len * sizeof(Unicode));
+  edge = (double *)gmalloc((len + 1) * sizeof(double));
+  i = 0;
+  for (word1 = words; word1; word1 = word1->next) {
+    for (j = 0; j < word1->len; ++j) {
+      text[i] = word1->text[j];
+      edge[i] = word1->edge[j];
+      ++i;
+    }
+    edge[i] = word1->edge[word1->len];
+    if (word1->spaceAfter) {
+      text[i] = (Unicode)0x0020;
+      ++i;
+    }
+  }
+
+  // compute convertedLen and set up the col array
+  col = (int *)gmalloc((len + 1) * sizeof(int));
+  convertedLen = 0;
+  for (i = 0; i < len; ++i) {
+    col[i] = convertedLen;
+    if (isUnicode) {
+      ++convertedLen;
+    } else if (uMap) {
+      convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf));
+    }
+  }
+  col[len] = convertedLen;
+
+  // check for hyphen at end of line
+  //~ need to check for other chars used as hyphens
+  hyphenated = text[len - 1] == (Unicode)'-';
+}
+
+//------------------------------------------------------------------------
+// TextLineFrag
+//------------------------------------------------------------------------
+
+class TextLineFrag {
+public:
+
+  TextLine *line;              // the line object
+  int start, len;              // offset and length of this fragment
+                               //   (in Unicode chars)
+  double xMin, xMax;           // bounding box coordinates
+  double yMin, yMax;
+  double base;                 // baseline virtual coordinate
+  int col;                     // first column
+
+  void init(TextLine *lineA, int startA, int lenA);
+  void computeCoords(GBool oneRot);
+
+  static int cmpYXPrimaryRot(const void *p1, const void *p2);
+  static int cmpYXLineRot(const void *p1, const void *p2);
+  static int cmpXYLineRot(const void *p1, const void *p2);
+};
+
+void TextLineFrag::init(TextLine *lineA, int startA, int lenA) {
+  line = lineA;
+  start = startA;
+  len = lenA;
+  col = line->col[start];
+}
+
+void TextLineFrag::computeCoords(GBool oneRot) {
+  TextBlock *blk;
+  double d0, d1, d2, d3, d4;
+
+  if (oneRot) {
+
+    switch (line->rot) {
+    case 0:
+      xMin = line->edge[start];
+      xMax = line->edge[start + len];
+      yMin = line->yMin;
+      yMax = line->yMax;
+      break;
+    case 1:
+      xMin = line->xMin;
+      xMax = line->xMax;
+      yMin = line->edge[start];
+      yMax = line->edge[start + len];
+      break;
+    case 2:
+      xMin = line->edge[start + len];
+      xMax = line->edge[start];
+      yMin = line->yMin;
+      yMax = line->yMax;
+      break;
+    case 3:
+      xMin = line->xMin;
+      xMax = line->xMax;
+      yMin = line->edge[start + len];
+      yMax = line->edge[start];
+      break;
+    }
+    base = line->base;
+
+  } else {
+
+    if (line->rot == 0 && line->blk->page->primaryRot == 0) {
+
+      xMin = line->edge[start];
+      xMax = line->edge[start + len];
+      yMin = line->yMin;
+      yMax = line->yMax;
+      base = line->base;
+
+    } else {
+
+      blk = line->blk;
+      d0 = line->edge[start];
+      d1 = line->edge[start + len];
+      d2 = d3 = d4 = 0; // make gcc happy
+
+      switch (line->rot) {
+      case 0:
+       d2 = line->yMin;
+       d3 = line->yMax;
+       d4 = line->base;
+       d0 = (d0 - blk->xMin) / (blk->xMax - blk->xMin);
+       d1 = (d1 - blk->xMin) / (blk->xMax - blk->xMin);
+       d2 = (d2 - blk->yMin) / (blk->yMax - blk->yMin);
+       d3 = (d3 - blk->yMin) / (blk->yMax - blk->yMin);
+       d4 = (d4 - blk->yMin) / (blk->yMax - blk->yMin);
+       break;
+      case 1:
+       d2 = line->xMax;
+       d3 = line->xMin;
+       d4 = line->base;
+       d0 = (d0 - blk->yMin) / (blk->yMax - blk->yMin);
+       d1 = (d1 - blk->yMin) / (blk->yMax - blk->yMin);
+       d2 = (blk->xMax - d2) / (blk->xMax - blk->xMin);
+       d3 = (blk->xMax - d3) / (blk->xMax - blk->xMin);
+       d4 = (blk->xMax - d4) / (blk->xMax - blk->xMin);
+       break;
+      case 2:
+       d2 = line->yMax;
+       d3 = line->yMin;
+       d4 = line->base;
+       d0 = (blk->xMax - d0) / (blk->xMax - blk->xMin);
+       d1 = (blk->xMax - d1) / (blk->xMax - blk->xMin);
+       d2 = (blk->yMax - d2) / (blk->yMax - blk->yMin);
+       d3 = (blk->yMax - d3) / (blk->yMax - blk->yMin);
+       d4 = (blk->yMax - d4) / (blk->yMax - blk->yMin);
+       break;
+      case 3:
+       d2 = line->xMin;
+       d3 = line->xMax;
+       d4 = line->base;
+       d0 = (blk->yMax - d0) / (blk->yMax - blk->yMin);
+       d1 = (blk->yMax - d1) / (blk->yMax - blk->yMin);
+       d2 = (d2 - blk->xMin) / (blk->xMax - blk->xMin);
+       d3 = (d3 - blk->xMin) / (blk->xMax - blk->xMin);
+       d4 = (d4 - blk->xMin) / (blk->xMax - blk->xMin);
+       break;
+      }
+
+      switch (line->blk->page->primaryRot) {
+      case 0:
+       xMin = blk->xMin + d0 * (blk->xMax - blk->xMin);
+       xMax = blk->xMin + d1 * (blk->xMax - blk->xMin);
+       yMin = blk->yMin + d2 * (blk->yMax - blk->yMin);
+       yMax = blk->yMin + d3 * (blk->yMax - blk->yMin);
+       base = blk->yMin + base * (blk->yMax - blk->yMin);
+       break;
+      case 1:
+       xMin = blk->xMax - d3 * (blk->xMax - blk->xMin);
+       xMax = blk->xMax - d2 * (blk->xMax - blk->xMin);
+       yMin = blk->yMin + d0 * (blk->yMax - blk->yMin);
+       yMax = blk->yMin + d1 * (blk->yMax - blk->yMin);
+       base = blk->xMax - d4 * (blk->xMax - blk->xMin);
+       break;
+      case 2:
+       xMin = blk->xMax - d1 * (blk->xMax - blk->xMin);
+       xMax = blk->xMax - d0 * (blk->xMax - blk->xMin);
+       yMin = blk->yMax - d3 * (blk->yMax - blk->yMin);
+       yMax = blk->yMax - d2 * (blk->yMax - blk->yMin);
+       base = blk->yMax - d4 * (blk->yMax - blk->yMin);
+       break;
+      case 3:
+       xMin = blk->xMin + d2 * (blk->xMax - blk->xMin);
+       xMax = blk->xMin + d3 * (blk->xMax - blk->xMin);
+       yMin = blk->yMax - d1 * (blk->yMax - blk->yMin);
+       yMax = blk->yMax - d0 * (blk->yMax - blk->yMin);
+       base = blk->xMin + d4 * (blk->xMax - blk->xMin);
+       break;
+      }
+
+    }
+  }
+}
+
+int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) {
+  TextLineFrag *frag1 = (TextLineFrag *)p1;
+  TextLineFrag *frag2 = (TextLineFrag *)p2;
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (frag1->line->blk->page->primaryRot) {
+  case 0:
+    if ((cmp = frag1->yMin - frag2->yMin) == 0) {
+      cmp = frag1->xMin - frag2->xMin;
+    }
+    break;
+  case 1:
+    if ((cmp = frag2->xMax - frag1->xMax) == 0) {
+      cmp = frag1->yMin - frag2->yMin;
+    }
+    break;
+  case 2:
+    if ((cmp = frag2->yMin - frag1->yMin) == 0) {
+      cmp = frag2->xMax - frag1->xMax;
+    }
+    break;
+  case 3:
+    if ((cmp = frag1->xMax - frag2->xMax) == 0) {
+      cmp = frag2->yMax - frag1->yMax;
+    }
+    break;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) {
+  TextLineFrag *frag1 = (TextLineFrag *)p1;
+  TextLineFrag *frag2 = (TextLineFrag *)p2;
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (frag1->line->rot) {
+  case 0:
+    if ((cmp = frag1->yMin - frag2->yMin) == 0) {
+      cmp = frag1->xMin - frag2->xMin;
+    }
+    break;
+  case 1:
+    if ((cmp = frag2->xMax - frag1->xMax) == 0) {
+      cmp = frag1->yMin - frag2->yMin;
+    }
+    break;
+  case 2:
+    if ((cmp = frag2->yMin - frag1->yMin) == 0) {
+      cmp = frag2->xMax - frag1->xMax;
+    }
+    break;
+  case 3:
+    if ((cmp = frag1->xMax - frag2->xMax) == 0) {
+      cmp = frag2->yMax - frag1->yMax;
+    }
+    break;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {
+  TextLineFrag *frag1 = (TextLineFrag *)p1;
+  TextLineFrag *frag2 = (TextLineFrag *)p2;
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (frag1->line->rot) {
+  case 0:
+    if ((cmp = frag1->xMin - frag2->xMin) == 0) {
+      cmp = frag1->yMin - frag2->yMin;
+    }
+    break;
+  case 1:
+    if ((cmp = frag1->yMin - frag2->yMin) == 0) {
+      cmp = frag2->xMax - frag1->xMax;
+    }
+    break;
+  case 2:
+    if ((cmp = frag2->xMax - frag1->xMax) == 0) {
+      cmp = frag2->yMin - frag1->yMin;
+    }
+    break;
+  case 3:
+    if ((cmp = frag2->yMax - frag1->yMax) == 0) {
+      cmp = frag1->xMax - frag2->xMax;
+    }
+    break;
+  }
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
 }
 
 //------------------------------------------------------------------------
 // TextBlock
 //------------------------------------------------------------------------
 
-TextBlock::TextBlock() {
+TextBlock::TextBlock(TextPage *pageA, int rotA) {
+  page = pageA;
+  rot = rotA;
+  xMin = yMin = 0;
+  xMax = yMax = -1;
+  priMin = 0;
+  priMax = page->pageWidth;
+  pool = new TextPool();
   lines = NULL;
+  curLine = NULL;
   next = NULL;
+  stackNext = NULL;
 }
 
 TextBlock::~TextBlock() {
-  TextLine *l1, *l2;
+  TextLine *line;
 
-  for (l1 = lines; l1; l1 = l2) {
-    l2 = l1->next;
-    delete l1;
+  delete pool;
+  while (lines) {
+    line = lines;
+    lines = lines->next;
+    delete line;
   }
 }
 
-// Returns true if <this> comes before <blk2> in xy order, allowing
-// slack for vertically overlapping blocks.
-GBool TextBlock::yxBefore(TextBlock *blk2) {
-  double dy;
+void TextBlock::addWord(TextWord *word) {
+  pool->addWord(word);
+  if (xMin > xMax) {
+    xMin = word->xMin;
+    xMax = word->xMax;
+    yMin = word->yMin;
+    yMax = word->yMax;
+  } else {
+    if (word->xMin < xMin) {
+      xMin = word->xMin;
+    }
+    if (word->xMax > xMax) {
+      xMax = word->xMax;
+    }
+    if (word->yMin < yMin) {
+      yMin = word->yMin;
+    }
+    if (word->yMax > yMax) {
+      yMax = word->yMax;
+    }
+  }
+}
 
-  dy = blkOverlapSlack * lines->fontSize;
+void TextBlock::coalesce(UnicodeMap *uMap) {
+  TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord;
+  TextLine *line, *line0, *line1;
+  int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx;
+  int baseIdx, bestWordBaseIdx, idx0, idx1;
+  double minBase, maxBase;
+  double fontSize, delta, priDelta, secDelta;
+  TextLine **lineArray;
+  GBool found;
+  int col1, col2;
+  int i, j, k;
+
+  // discard duplicated text (fake boldface, drop shadows)
+  for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) {
+    word0 = pool->getPool(idx0);
+    while (word0) {
+      priDelta = dupMaxPriDelta * word0->fontSize;
+      secDelta = dupMaxSecDelta * word0->fontSize;
+      if (rot == 0 || rot == 3) {
+       maxBaseIdx = pool->getBaseIdx(word0->base + secDelta);
+      } else {
+       maxBaseIdx = pool->getBaseIdx(word0->base - secDelta);
+      }
+      found = gFalse;
+      word1 = word2 = NULL; // make gcc happy
+      for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) {
+       if (idx1 == idx0) {
+         word1 = word0;
+         word2 = word0->next;
+       } else {
+         word1 = NULL;
+         word2 = pool->getPool(idx1);
+       }
+       for (; word2; word1 = word2, word2 = word2->next) {
+         if (word2->len == word0->len &&
+             !memcmp(word2->text, word0->text,
+                     word0->len * sizeof(Unicode))) {
+           switch (rot) {
+           case 0:
+           case 2:
+             found = fabs(word0->xMin - word2->xMin) < priDelta &&
+                     fabs(word0->xMax - word2->xMax) < priDelta &&
+                     fabs(word0->yMin - word2->yMin) < secDelta &&
+                     fabs(word0->yMax - word2->yMax) < secDelta;
+             break;
+           case 1:
+           case 3:
+             found = fabs(word0->xMin - word2->xMin) < secDelta &&
+                     fabs(word0->xMax - word2->xMax) < secDelta &&
+                     fabs(word0->yMin - word2->yMin) < priDelta &&
+                     fabs(word0->yMax - word2->yMax) < priDelta;
+             break;
+           }
+         }
+         if (found) {
+           break;
+         }
+       }
+       if (found) {
+         break;
+       }
+      }
+      if (found) {
+       if (word1) {
+         word1->next = word2->next;
+       } else {
+         pool->setPool(idx1, word2->next);
+       }
+       delete word2;
+      } else {
+       word0 = word0->next;
+      }
+    }
+  }
+
+  // build the lines
+  curLine = NULL;
+  poolMinBaseIdx = pool->minBaseIdx;
+  charCount = 0;
+  nLines = 0;
+  while (1) {
 
-  // non-overlapping case
-  if (blk2->yMin > yMax - dy ||
-      blk2->yMax < yMin + dy) {
-    return yMin < blk2->yMin ||
-           (yMin == blk2->yMin && xMin < blk2->xMin);
+    // find the first non-empty line in the pool
+    for (;
+        poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx);
+        ++poolMinBaseIdx) ;
+    if (poolMinBaseIdx > pool->maxBaseIdx) {
+      break;
+    }
+
+    // look for the left-most word in the first four lines of the
+    // pool -- this avoids starting with a superscript word
+    startBaseIdx = poolMinBaseIdx;
+    for (baseIdx = poolMinBaseIdx + 1;
+        baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
+        ++baseIdx) {
+      if (!pool->getPool(baseIdx)) {
+       continue;
+      }
+      if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
+         < 0) {
+       startBaseIdx = baseIdx;
+      }
+    }
+
+    // create a new line
+    word0 = pool->getPool(startBaseIdx);
+    pool->setPool(startBaseIdx, word0->next);
+    word0->next = NULL;
+    line = new TextLine(this, word0->rot, word0->base);
+    line->addWord(word0);
+    lastWord = word0;
+
+    // compute the search range
+    fontSize = word0->fontSize;
+    minBase = word0->base - maxIntraLineDelta * fontSize;
+    maxBase = word0->base + maxIntraLineDelta * fontSize;
+    minBaseIdx = pool->getBaseIdx(minBase);
+    maxBaseIdx = pool->getBaseIdx(maxBase);
+
+    // find the rest of the words in this line
+    while (1) {
+
+      // find the left-most word whose baseline is in the range for
+      // this line
+      bestWordBaseIdx = 0;
+      bestWord0 = bestWord1 = NULL;
+      for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
+       for (word0 = NULL, word1 = pool->getPool(baseIdx);
+            word1;
+            word0 = word1, word1 = word1->next) {
+         if (word1->base >= minBase &&
+             word1->base <= maxBase &&
+             (delta = lastWord->primaryDelta(word1)) >=
+               minCharSpacing * fontSize) {
+           if (delta < maxWordSpacing * fontSize &&
+               (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) {
+             bestWordBaseIdx = baseIdx;
+             bestWord0 = word0;
+             bestWord1 = word1;
+           }
+           break;
+         }
+       }
+      }
+      if (!bestWord1) {
+       break;
+      }
+
+      // remove it from the pool, and add it to the line
+      if (bestWord0) {
+       bestWord0->next = bestWord1->next;
+      } else {
+       pool->setPool(bestWordBaseIdx, bestWord1->next);
+      }
+      bestWord1->next = NULL;
+      line->addWord(bestWord1);
+      lastWord = bestWord1;
+    }
+
+    // add the line
+    if (curLine && line->cmpYX(curLine) > 0) {
+      line0 = curLine;
+      line1 = curLine->next;
+    } else {
+      line0 = NULL;
+      line1 = lines;
+    }
+    for (;
+        line1 && line->cmpYX(line1) > 0;
+        line0 = line1, line1 = line1->next) ;
+    if (line0) {
+      line0->next = line;
+    } else {
+      lines = line;
+    }
+    line->next = line1;
+    curLine = line;
+    line->coalesce(uMap);
+    charCount += line->len;
+    ++nLines;
+  }
+
+  // sort lines into xy order for column assignment
+  lineArray = (TextLine **)gmalloc(nLines * sizeof(TextLine *));
+  for (line = lines, i = 0; line; line = line->next, ++i) {
+    lineArray[i] = line;
   }
+  qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY);
 
-  // overlapping case
-  return xMin < blk2->xMin;
+  // column assignment
+  nColumns = 0;
+  for (i = 0; i < nLines; ++i) {
+    line0 = lineArray[i];
+    col1 = 0;
+    for (j = 0; j < i; ++j) {
+      line1 = lineArray[j];
+      if (line1->primaryDelta(line0) >= 0) {
+       col2 = line1->col[line1->len] + 1;
+      } else {
+       k = 0; // make gcc happy
+       switch (rot) {
+       case 0:
+         for (k = 0;
+              k < line1->len &&
+                line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
+              ++k) ;
+         break;
+       case 1:
+         for (k = 0;
+              k < line1->len &&
+                line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
+              ++k) ;
+         break;
+       case 2:
+         for (k = 0;
+              k < line1->len &&
+                line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
+              ++k) ;
+         break;
+       case 3:
+         for (k = 0;
+              k < line1->len &&
+                line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
+              ++k) ;
+         break;
+       }
+       col2 = line1->col[k];
+      }
+      if (col2 > col1) {
+       col1 = col2;
+      }
+    }
+    for (k = 0; k <= line0->len; ++k) {
+      line0->col[k] += col1;
+    }
+    if (line0->col[line0->len] > nColumns) {
+      nColumns = line0->col[line0->len];
+    }
+  }
+  gfree(lineArray);
 }
 
-// Merge another block's line onto the right of this one.
-void TextBlock::mergeRight(TextBlock *blk2) {
-  lines->merge(blk2->lines);
-  xMax = lines->xMax;
-  yMin = lines->yMin;
-  yMax = lines->yMax;
-  xSpaceR = lines->xSpaceR;
+void TextBlock::updatePriMinMax(TextBlock *blk) {
+  double newPriMin, newPriMax;
+  GBool gotPriMin, gotPriMax;
+
+  gotPriMin = gotPriMax = gFalse;
+  newPriMin = newPriMax = 0; // make gcc happy
+  switch (page->primaryRot) {
+  case 0:
+  case 2:
+    if (blk->yMin < yMax && blk->yMax > yMin) {
+      if (blk->xMin < xMin) {
+       newPriMin = blk->xMax;
+       gotPriMin = gTrue;
+      }
+      if (blk->xMax > xMax) {
+       newPriMax = blk->xMin;
+       gotPriMax = gTrue;
+      }
+    }
+    break;
+  case 1:
+  case 3:
+    if (blk->xMin < xMax && blk->xMax > xMin) {
+      if (blk->yMin < yMin) {
+       newPriMin = blk->yMax;
+       gotPriMin = gTrue;
+      }
+      if (blk->yMax > yMax) {
+       newPriMax = blk->yMin;
+       gotPriMax = gTrue;
+      }
+    }
+    break;
+  }
+  if (gotPriMin) {
+    if (newPriMin > xMin) {
+      newPriMin = xMin;
+    }
+    if (newPriMin > priMin) {
+      priMin = newPriMin;
+    }
+  }
+  if (gotPriMax) {
+    if (newPriMax < xMax) {
+      newPriMax = xMax;
+    }
+    if (newPriMax < priMax) {
+      priMax = newPriMax;
+    }
+  }
 }
 
-// Merge another block's lines onto the bottom of this block.
-void TextBlock::mergeBelow(TextBlock *blk2) {
-  TextLine *line;
+int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) {
+  TextBlock *blk1 = *(TextBlock **)p1;
+  TextBlock *blk2 = *(TextBlock **)p2;
+  double cmp;
 
-  if (blk2->xMin < xMin) {
-    xMin = blk2->xMin;
+  cmp = 0; // make gcc happy
+  switch (blk1->page->primaryRot) {
+  case 0:
+    if ((cmp = blk1->xMin - blk2->xMin) == 0) {
+      cmp = blk1->yMin - blk2->yMin;
+    }
+    break;
+  case 1:
+    if ((cmp = blk1->yMin - blk2->yMin) == 0) {
+      cmp = blk2->xMax - blk1->xMax;
+    }
+    break;
+  case 2:
+    if ((cmp = blk2->xMax - blk1->xMax) == 0) {
+      cmp = blk2->yMin - blk1->yMin;
+    }
+    break;
+  case 3:
+    if ((cmp = blk2->yMax - blk1->yMax) == 0) {
+      cmp = blk1->xMax - blk2->xMax;
+    }
+    break;
   }
-  if (blk2->xMax > xMax) {
-    xMax = blk2->xMax;
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextBlock::cmpYXPrimaryRot(const void *p1, const void *p2) {
+  TextBlock *blk1 = *(TextBlock **)p1;
+  TextBlock *blk2 = *(TextBlock **)p2;
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (blk1->page->primaryRot) {
+  case 0:
+    if ((cmp = blk1->yMin - blk2->yMin) == 0) {
+      cmp = blk1->xMin - blk2->xMin;
+    }
+    break;
+  case 1:
+    if ((cmp = blk2->xMax - blk1->xMax) == 0) {
+      cmp = blk1->yMin - blk2->yMin;
+    }
+    break;
+  case 2:
+    if ((cmp = blk2->yMin - blk1->yMin) == 0) {
+      cmp = blk2->xMax - blk1->xMax;
+    }
+    break;
+  case 3:
+    if ((cmp = blk1->xMax - blk2->xMax) == 0) {
+      cmp = blk2->yMax - blk1->yMax;
+    }
+    break;
   }
-  yMax = blk2->yMax;
-  if (blk2->xSpaceL > xSpaceL) {
-    xSpaceL = blk2->xSpaceL;
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextBlock::primaryCmp(TextBlock *blk) {
+  double cmp;
+
+  cmp = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    cmp = xMin - blk->xMin;
+    break;
+  case 1:
+    cmp = yMin - blk->yMin;
+    break;
+  case 2:
+    cmp = blk->xMax - xMax;
+    break;
+  case 3:
+    cmp = blk->yMax - yMax;
+    break;
   }
-  if (blk2->xSpaceR < xSpaceR) {
-    xSpaceR = blk2->xSpaceR;
+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+double TextBlock::secondaryDelta(TextBlock *blk) {
+  double delta;
+
+  delta = 0; // make gcc happy
+  switch (rot) {
+  case 0:
+    delta = blk->yMin - yMax;
+    break;
+  case 1:
+    delta = xMin - blk->xMax;
+    break;
+  case 2:
+    delta = yMin - blk->yMax;
+    break;
+  case 3:
+    delta = blk->xMin - xMax;
+    break;
   }
-  if (blk2->maxFontSize > maxFontSize) {
-    maxFontSize = blk2->maxFontSize;
+  return delta;
+}
+
+GBool TextBlock::isBelow(TextBlock *blk) {
+  GBool below;
+
+  below = gFalse; // make gcc happy
+  switch (page->primaryRot) {
+  case 0:
+    below = xMin >= blk->priMin && xMax <= blk->priMax &&
+            yMin > blk->yMin;
+    break;
+  case 1:
+    below = yMin >= blk->priMin && yMax <= blk->priMax &&
+            xMax < blk->xMax;
+    break;
+  case 2:
+    below = xMin >= blk->priMin && xMax <= blk->priMax &&
+            yMax < blk->yMax;
+    break;
+  case 3:
+    below = yMin >= blk->priMin && yMax <= blk->priMax &&
+            xMin > blk->xMin;
+    break;
   }
-  for (line = lines; line->next; line = line->next) ;
-  line->next = line->flowNext = blk2->lines;
-  blk2->lines = NULL;
+
+  return below;
 }
 
 //------------------------------------------------------------------------
 // TextFlow
 //------------------------------------------------------------------------
 
-TextFlow::TextFlow() {
-  blocks = NULL;
+TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) {
+  page = pageA;
+  xMin = blk->xMin;
+  xMax = blk->xMax;
+  yMin = blk->yMin;
+  yMax = blk->yMax;
+  priMin = blk->priMin;
+  priMax = blk->priMax;
+  blocks = lastBlk = blk;
   next = NULL;
 }
 
 TextFlow::~TextFlow() {
-  TextBlock *b1, *b2;
+  TextBlock *blk;
 
-  for (b1 = blocks; b1; b1 = b2) {
-    b2 = b1->next;
-    delete b1;
+  while (blocks) {
+    blk = blocks;
+    blocks = blocks->next;
+    delete blk;
+  }
+}
+
+void TextFlow::addBlock(TextBlock *blk) {
+  if (lastBlk) {
+    lastBlk->next = blk;
+  } else {
+    blocks = blk;
+  }
+  lastBlk = blk;
+  if (blk->xMin < xMin) {
+    xMin = blk->xMin;
+  }
+  if (blk->xMax > xMax) {
+    xMax = blk->xMax;
+  }
+  if (blk->yMin < yMin) {
+    yMin = blk->yMin;
+  }
+  if (blk->yMax > yMax) {
+    yMax = blk->yMax;
+  }
+}
+
+GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {
+  GBool fits;
+
+  // lower blocks must use smaller fonts
+  if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) {
+    return gFalse;
+  }
+
+  fits = gFalse; // make gcc happy
+  switch (page->primaryRot) {
+  case 0:
+    fits = blk->xMin >= priMin && blk->xMax <= priMax;
+    break;
+  case 1:
+    fits = blk->yMin >= priMin && blk->yMax <= priMax;
+    break;
+  case 2:
+    fits = blk->xMin >= priMin && blk->xMax <= priMax;
+    break;
+  case 3:
+    fits = blk->yMin >= priMin && blk->yMax <= priMax;
+    break;
   }
+  return fits;
 }
 
+#if TEXTOUT_WORD_LIST
+
+//------------------------------------------------------------------------
+// TextWordList
+//------------------------------------------------------------------------
+
+TextWordList::TextWordList(TextPage *text, GBool physLayout) {
+  TextFlow *flow;
+  TextBlock *blk;
+  TextLine *line;
+  TextWord *word;
+  TextWord **wordArray;
+  int nWords, i;
+
+  words = new GList();
+
+  if (text->rawOrder) {
+    for (word = text->rawWords; word; word = word->next) {
+      words->append(word);
+    }
+
+  } else if (physLayout) {
+    // this is inefficient, but it's also the least useful of these
+    // three cases
+    nWords = 0;
+    for (flow = text->flows; flow; flow = flow->next) {
+      for (blk = flow->blocks; blk; blk = blk->next) {
+       for (line = blk->lines; line; line = line->next) {
+         for (word = line->words; word; word = word->next) {
+           ++nWords;
+         }
+       }
+      }
+    }
+    wordArray = (TextWord **)gmalloc(nWords * sizeof(TextWord *));
+    i = 0;
+    for (flow = text->flows; flow; flow = flow->next) {
+      for (blk = flow->blocks; blk; blk = blk->next) {
+       for (line = blk->lines; line; line = line->next) {
+         for (word = line->words; word; word = word->next) {
+           wordArray[i++] = word;
+         }
+       }
+      }
+    }
+    qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX);
+    for (i = 0; i < nWords; ++i) {
+      words->append(wordArray[i]);
+    }
+    gfree(wordArray);
+
+  } else {
+    for (flow = text->flows; flow; flow = flow->next) {
+      for (blk = flow->blocks; blk; blk = blk->next) {
+       for (line = blk->lines; line; line = line->next) {
+         for (word = line->words; word; word = word->next) {
+           words->append(word);
+         }
+       }
+      }
+    }
+  }
+}
+
+TextWordList::~TextWordList() {
+  delete words;
+}
+
+int TextWordList::getLength() {
+  return words->getLength();
+}
+
+TextWord *TextWordList::get(int idx) {
+  if (idx < 0 || idx >= words->getLength()) {
+    return NULL;
+  }
+  return (TextWord *)words->get(idx);
+}
+
+#endif // TEXTOUT_WORD_LIST
 
 //------------------------------------------------------------------------
 // TextPage
 //------------------------------------------------------------------------
 
-TextPage::TextPage(GBool rawOrderA) {
-  rawOrder = rawOrderA;
+TextPage::TextPage(GBool rawOrderA) {
+  int rot;
+
+  rawOrder = rawOrderA;
+  curWord = NULL;
+  charPos = 0;
+  curFont = NULL;
+  curFontSize = 0;
+  nest = 0;
+  nTinyChars = 0;
+  if (!rawOrder) {
+    for (rot = 0; rot < 4; ++rot) {
+      pools[rot] = new TextPool();
+    }
+  }
+  flows = NULL;
+  blocks = NULL;
+  rawWords = NULL;
+  rawLastWord = NULL;
+  fonts = new GList();
+  lastFindXMin = lastFindYMin = 0;
+  haveLastFind = gFalse;
+}
+
+TextPage::~TextPage() {
+  int rot;
+
+  clear();
+  if (!rawOrder) {
+    for (rot = 0; rot < 4; ++rot) {
+      delete pools[rot];
+    }
+  }
+  delete fonts;
+}
+
+void TextPage::startPage(GfxState *state) {
+  clear();
+  if (state) {
+    pageWidth = state->getPageWidth();
+    pageHeight = state->getPageHeight();
+  } else {
+    pageWidth = pageHeight = 0;
+  }
+}
+
+void TextPage::clear() {
+  int rot;
+  TextFlow *flow;
+  TextWord *word;
+
+  if (curWord) {
+    delete curWord;
+    curWord = NULL;
+  }
+  if (rawOrder) {
+    while (rawWords) {
+      word = rawWords;
+      rawWords = rawWords->next;
+      delete word;
+    }
+  } else {
+    for (rot = 0; rot < 4; ++rot) {
+      delete pools[rot];
+    }
+    while (flows) {
+      flow = flows;
+      flows = flows->next;
+      delete flow;
+    }
+    gfree(blocks);
+  }
+  deleteGList(fonts, TextFontInfo);
+
   curWord = NULL;
   charPos = 0;
-  font = NULL;
-  fontSize = 0;
+  curFont = NULL;
+  curFontSize = 0;
   nest = 0;
   nTinyChars = 0;
-  words = wordPtr = NULL;
-  lines = NULL;
+  if (!rawOrder) {
+    for (rot = 0; rot < 4; ++rot) {
+      pools[rot] = new TextPool();
+    }
+  }
   flows = NULL;
+  blocks = NULL;
+  rawWords = NULL;
+  rawLastWord = NULL;
   fonts = new GList();
 }
 
-TextPage::~TextPage() {
-  clear();
-  delete fonts;
-}
-
 void TextPage::updateFont(GfxState *state) {
   GfxFont *gfxFont;
   double *fm;
@@ -454,22 +1682,22 @@ void TextPage::updateFont(GfxState *state) {
   int i;
 
   // get the font info object
-  font = NULL;
+  curFont = NULL;
   for (i = 0; i < fonts->getLength(); ++i) {
-    font = (TextFontInfo *)fonts->get(i);
-    if (font->matches(state)) {
+    curFont = (TextFontInfo *)fonts->get(i);
+    if (curFont->matches(state)) {
       break;
     }
-    font = NULL;
+    curFont = NULL;
   }
-  if (!font) {
-    font = new TextFontInfo(state);
-    fonts->append(font);
+  if (!curFont) {
+    curFont = new TextFontInfo(state);
+    fonts->append(curFont);
   }
 
   // adjust the font size
   gfxFont = state->getFont();
-  fontSize = state->getTransformedFontSize();
+  curFontSize = state->getTransformedFontSize();
   if (gfxFont && gfxFont->getType() == fontType3) {
     // This is a hack which makes it possible to deal with some Type 3
     // fonts.  The problem is that it's impossible to know what the
@@ -496,24 +1724,28 @@ void TextPage::updateFont(GfxState *state) {
     if (mCode >= 0 &&
        (w = ((Gfx8BitFont *)gfxFont)->getWidth(mCode)) > 0) {
       // 0.6 is a generic average 'm' width -- yes, this is a hack
-      fontSize *= w / 0.6;
+      curFontSize *= w / 0.6;
     } else if (letterCode >= 0 &&
               (w = ((Gfx8BitFont *)gfxFont)->getWidth(letterCode)) > 0) {
       // even more of a hack: 0.5 is a generic letter width
-      fontSize *= w / 0.5;
+      curFontSize *= w / 0.5;
     } else if (anyCode >= 0 &&
               (w = ((Gfx8BitFont *)gfxFont)->getWidth(anyCode)) > 0) {
       // better than nothing: 0.5 is a generic character width
-      fontSize *= w / 0.5;
+      curFontSize *= w / 0.5;
     }
     fm = gfxFont->getFontMatrix();
     if (fm[0] != 0) {
-      fontSize *= fabs(fm[3] / fm[0]);
+      curFontSize *= fabs(fm[3] / fm[0]);
     }
   }
 }
 
 void TextPage::beginWord(GfxState *state, double x0, double y0) {
+  double *txtm, *ctm, *fontm;
+  double m[4], m2[4];
+  int rot;
+
   // This check is needed because Type 3 characters can contain
   // text-drawing operations (when TextPage is being used via
   // XOutputDev rather than TextOutputDev).
@@ -522,7 +1754,31 @@ void TextPage::beginWord(GfxState *state, double x0, double y0) {
     return;
   }
 
-  curWord = new TextWord(state, x0, y0, charPos, font, fontSize);
+  // compute the rotation
+  txtm = state->getTextMat();
+  ctm = state->getCTM();
+  m[0] = txtm[0] * ctm[0] + txtm[1] * ctm[2];
+  m[1] = txtm[0] * ctm[1] + txtm[1] * ctm[3];
+  m[2] = txtm[2] * ctm[0] + txtm[3] * ctm[2];
+  m[3] = txtm[2] * ctm[1] + txtm[3] * ctm[3];
+  if (state->getFont()->getType() == fontType3) {
+    fontm = state->getFont()->getFontMatrix();
+    m2[0] = fontm[0] * m[0] + fontm[1] * m[2];
+    m2[1] = fontm[0] * m[1] + fontm[1] * m[3];
+    m2[2] = fontm[2] * m[0] + fontm[3] * m[2];
+    m2[3] = fontm[2] * m[1] + fontm[3] * m[3];
+    m[0] = m2[0];
+    m[1] = m2[1];
+    m[2] = m2[2];
+    m[3] = m2[3];
+  }
+  if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) {
+    rot = (m[3] < 0) ? 0 : 2;
+  } else {
+    rot = (m[2] > 0) ? 1 : 3;
+  }
+
+  curWord = new TextWord(state, rot, x0, y0, charPos, curFont, curFontSize);
 }
 
 void TextPage::addChar(GfxState *state, double x, double y,
@@ -557,7 +1813,7 @@ void TextPage::addChar(GfxState *state, double x, double y,
   // check the tiny chars limit
   if (!globalParams->getTextKeepTinyChars() &&
       fabs(w1) < 3 && fabs(h1) < 3) {
-    if (++nTinyChars > 20000) {
+    if (++nTinyChars > 50000) {
       return;
     }
   }
@@ -574,16 +1830,26 @@ void TextPage::addChar(GfxState *state, double x, double y,
   // this case, break text into individual chars and let the coalesce
   // function deal with it later
   n = curWord->len;
-  if (n > 0 && x1 - curWord->xRight[n-1] >
-                    curWord->font->minSpaceWidth * curWord->fontSize) {
-    endWord();
-    beginWord(state, x, y);
+  if (n > 0) {
+    switch (curWord->rot) {
+    case 0: sp = x1 - curWord->xMax; break;
+    case 1: sp = y1 - curWord->yMax; break;
+    case 2: sp = curWord->xMin - x1; break;
+    case 3: sp = curWord->yMin - y1; break;
+    }
+    if (sp > defaultSpaceWidth * curWord->fontSize) {
+      endWord();
+      beginWord(state, x, y);
+    }
   }
 
   // page rotation and/or transform matrices can cause text to be
   // drawn in reverse order -- in this case, swap the begin/end
   // coordinates and break text into individual chars
-  if (w1 < 0) {
+  if ((curWord->rot == 0 && w1 < 0) ||
+      (curWord->rot == 1 && h1 < 0) ||
+      (curWord->rot == 2 && w1 > 0) ||
+      (curWord->rot == 3 && h1 > 0)) {
     endWord();
     beginWord(state, x + dx, y + dy);
     x1 += w1;
@@ -620,8 +1886,6 @@ void TextPage::endWord() {
 }
 
 void TextPage::addWord(TextWord *word) {
-  TextWord *p1, *p2;
-
   // throw away zero-length words -- they don't have valid xMin/xMax
   // values, and they're useless anyway
   if (word->len == 0) {
@@ -629,533 +1893,399 @@ void TextPage::addWord(TextWord *word) {
     return;
   }
 
-  // insert word in xy list
   if (rawOrder) {
-    p1 = wordPtr;
-    p2 = NULL;
-  } else {
-    if (wordPtr && wordPtr->xyBefore(word)) {
-      p1 = wordPtr;
-      p2 = wordPtr->next;
+    if (rawLastWord) {
+      rawLastWord->next = word;
     } else {
-      p1 = NULL;
-      p2 = words;
-    }
-    for (; p2; p1 = p2, p2 = p2->next) {
-      if (word->xyBefore(p2)) {
-       break;
-      }
+      rawWords = word;
     }
-  }
-  if (p1) {
-    p1->next = word;
+    rawLastWord = word;
   } else {
-    words = word;
+    pools[word->rot]->addWord(word);
   }
-  word->next = p2;
-  wordPtr = word;
 }
 
 void TextPage::coalesce(GBool physLayout) {
+  UnicodeMap *uMap;
+  TextPool *pool;
   TextWord *word0, *word1, *word2;
-  TextLine *line0, *line1, *line2, *line3, *line4, *lineList;
-  TextBlock *blk0, *blk1, *blk2, *blk3, *blk4, *blk5, *blk6;
-  TextBlock *yxBlocks, *blocks, *blkStack;
-  TextFlow *flow0, *flow1;
-  double sz, xLimit, yLimit;
-  double fit1, fit2, sp1, sp2 = 0.0e+0;
+  TextLine *line;
+  TextBlock *blkList, *blkStack, *blk, *lastBlk, *blk0, *blk1;
+  TextBlock **blkArray;
+  TextFlow *flow, *lastFlow;
+  int rot, poolMinBaseIdx, baseIdx, startBaseIdx;
+  double minBase, maxBase, newMinBase, newMaxBase;
+  double fontSize, colSpace, lineSpace, intraLineSpace, blkSpace;
   GBool found;
-  UnicodeMap *uMap;
-  GBool isUnicode;
-  char buf[8];
-  int col1, col2, d, i, j;
+  int count[4];
+  int lrCount;
+  int firstBlkIdx, nBlocksLeft;
+  int col1, col2;
+  int i, j, n;
 
-#if 0 // for debugging
-  printf("*** initial word list ***\n");
-  for (word0 = words; word0; word0 = word0->next) {
-    printf("word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f: '",
-          word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->yBase);
-    for (i = 0; i < word0->len; ++i) {
-      fputc(word0->text[i] & 0xff, stdout);
-    }
-    printf("'\n");
+  if (rawOrder) {
+    primaryRot = 0;
+    primaryLR = gTrue;
+    return;
   }
-  printf("\n");
-  fflush(stdout);
-#endif
 
-  //----- discard duplicated text (fake boldface, drop shadows)
-
-  word0 = words;
-  while (word0) {
-    sz = word0->fontSize;
-    xLimit = word0->xMin + sz * dupMaxDeltaX;
-    found = gFalse;
-    for (word1 = word0, word2 = word0->next;
-        word2 && word2->xMin < xLimit;
-        word1 = word2, word2 = word2->next) {
-      if (word2->len == word0->len &&
-         !memcmp(word2->text, word0->text, word0->len * sizeof(Unicode)) &&
-         fabs(word2->yMin - word0->yMin) < sz * dupMaxDeltaY &&
-         fabs(word2->yMax - word0->yMax) < sz * dupMaxDeltaY &&
-         fabs(word2->xMax - word0->xMax) < sz * dupMaxDeltaX) {
-       found = gTrue;
-       break;
-      }
-    }
-    if (found) {
-      word1->next = word2->next;
-      delete word2;
-    } else {
-      word0 = word0->next;
-    }
-  }
+  uMap = globalParams->getTextEncoding();
+  blkList = NULL;
+  lastBlk = NULL;
+  nBlocks = 0;
+  primaryRot = -1;
 
 #if 0 // for debugging
-  printf("*** words after removing duplicate text ***\n");
-  for (word0 = words; word0; word0 = word0->next) {
-    printf("word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f: '",
-          word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->yBase);
-    for (i = 0; i < word0->len; ++i) {
-      fputc(word0->text[i] & 0xff, stdout);
-    }
-    printf("'\n");
-  }
-  printf("\n");
-  fflush(stdout);
-#endif
-
-  //----- merge words
-
-  word0 = words;
-  while (word0) {
-    sz = word0->fontSize;
-
-    // look for adjacent text which is part of the same word, and
-    // merge it into this word
-    xLimit = word0->xMax + sz * word0->font->minSpaceWidth;
-    if (rawOrder) {
-      word1 = word0;
-      word2 = word0->next;
-      found = word2 &&
-             word2->xMin < xLimit &&
-             word2->font == word0->font &&
-             fabs(word2->fontSize - sz) < 0.05 &&
-             fabs(word2->yBase - word0->yBase) < 0.05 &&
-             word2->charPos == word0->charPos + word0->charLen;
-    } else {
-      found = gFalse;
-      for (word1 = word0, word2 = word0->next;
-          word2 && word2->xMin < xLimit;
-          word1 = word2, word2 = word2->next) {
-       if (word2->font == word0->font &&
-           fabs(word2->fontSize - sz) < 0.05 &&
-           fabs(word2->yBase - word0->yBase) < 0.05 &&
-           word2->charPos == word0->charPos + word0->charLen) {
-         found = gTrue;
-         break;
+  printf("*** initial words ***\n");
+  for (rot = 0; rot < 4; ++rot) {
+    pool = pools[rot];
+    for (baseIdx = pool->minBaseIdx; baseIdx <= pool->maxBaseIdx; ++baseIdx) {
+      for (word0 = pool->getPool(baseIdx); word0; word0 = word0->next) {
+       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f '",
+              word0->xMin, word0->xMax, word0->yMin, word0->yMax,
+              word0->base, word0->fontSize);
+       for (i = 0; i < word0->len; ++i) {
+         fputc(word0->text[i] & 0xff, stdout);
        }
+       printf("'\n");
       }
     }
-    if (found) {
-      word0->merge(word2);
-      word1->next = word2->next;
-      delete word2;
-      continue;
-    }
-
-    word0 = word0->next;
-  }
-
-#if 0 // for debugging
-  printf("*** after merging words ***\n");
-  for (word0 = words; word0; word0 = word0->next) {
-    printf("word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f: '",
-          word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->yBase);
-    for (i = 0; i < word0->len; ++i) {
-      fputc(word0->text[i] & 0xff, stdout);
-    }
-    printf("'\n");
   }
   printf("\n");
-  fflush(stdout);
 #endif
 
-  //----- assemble words into lines
+  //----- assemble the blocks
 
-  lineList = line0 = NULL;
-  while (words) {
-
-    // remove the first word from the word list
-    word0 = words;
-    words = words->next;
-    word0->next = NULL;
-
-    // find the best line (if any) for the word
-    if (rawOrder) {
-      if (line0 && lineFit(line0, word0, &sp2) >= 0) {
-       line1 = line0;
-       sp1 = sp2;
-      } else {
-       line1 = NULL;
-       sp1 = 0;
-      }
-    } else {
-      line1 = NULL;
-      fit1 = 0;
-      sp1 = 0;
-      for (line2 = lineList; line2; line2 = line2->next) {
-       fit2 = lineFit(line2, word0, &sp2);
-       if (fit2 >= 0 && (!line1 || fit2 < fit1)) {
-         line1 = line2;
-         fit1 = fit2;
-         sp1 = sp2;
-       }
-      }
-    }
-
-    // found a line: append the word
-    if (line1) {
-      word1 = line1->lastWord;
-      word1->next = word0;
-      line1->lastWord = word0;
-      if (word0->xMax > line1->xMax) {
-       line1->xMax = word0->xMax;
-      }
-      if (word0->yMin < line1->yMin) {
-       line1->yMin = word0->yMin;
-      }
-      if (word0->yMax > line1->yMax) {
-       line1->yMax = word0->yMax;
-      }
-      line1->len += word0->len;
-      if (sp1 > line1->fontSize * line1->font->minSpaceWidth) {
-       word1->spaceAfter = gTrue;
-       ++line1->len;
-      }
-
-    // didn't find a line: create a new line
-    } else {
-      line1 = new TextLine();
-      line1->words = line1->lastWord = word0;
-      line1->xMin = word0->xMin;
-      line1->xMax = word0->xMax;
-      line1->yMin = word0->yMin;
-      line1->yMax = word0->yMax;
-      line1->yBase = word0->yBase;
-      line1->font = word0->font;
-      line1->fontSize = word0->fontSize;
-      line1->len = word0->len;
-      if (line0) {
-       line0->next = line1;
-      } else {
-       lineList = line1;
-      }
-      line0 = line1;
-    }
-  }
-
-  // build the line text
-  uMap = globalParams->getTextEncoding();
-  isUnicode = uMap ? uMap->isUnicode() : gFalse;
-
-  for (line1 = lineList; line1; line1 = line1->next) {
-    line1->text = (Unicode *)gmalloc(line1->len * sizeof(Unicode));
-    line1->xRight = (double *)gmalloc(line1->len * sizeof(double));
-    line1->col = (int *)gmalloc(line1->len * sizeof(int));
-    i = 0;
-    for (word1 = line1->words; word1; word1 = word1->next) {
-      for (j = 0; j < word1->len; ++j) {
-       line1->text[i] = word1->text[j];
-       line1->xRight[i] = word1->xRight[j];
-       ++i;
-      }
-      if (word1->spaceAfter && word1->next) {
-       line1->text[i] = (Unicode)0x0020;
-       line1->xRight[i] = word1->next->xMin;
-       ++i;
-      }
-    }
-    line1->convertedLen = 0;
-    for (j = 0; j < line1->len; ++j) {
-      line1->col[j] = line1->convertedLen;
-      if (isUnicode) {
-       ++line1->convertedLen;
-      } else if (uMap) {
-       line1->convertedLen +=
-         uMap->mapUnicode(line1->text[j], buf, sizeof(buf));
-      }
-    }
-
-    // check for hyphen at end of line
-    //~ need to check for other chars used as hyphens
-    if (line1->text[line1->len - 1] == (Unicode)'-') {
-      line1->hyphenated = gTrue;
-    }
+  //~ add an outer loop for writing mode (vertical text)
 
-  }
+  // build blocks for each rotation value
+  for (rot = 0; rot < 4; ++rot) {
+    pool = pools[rot];
+    poolMinBaseIdx = pool->minBaseIdx;
+    count[rot] = 0;
 
-  if (uMap) {
-    uMap->decRefCnt();
-  }
+    // add blocks until no more words are left
+    while (1) {
 
-#if 0 // for debugging
-  printf("*** lines in xy order ***\n");
-  for (line0 = lineList; line0; line0 = line0->next) {
-    printf("[line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f len=%d]\n",
-          line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-          line0->yBase, line0->len);
-    for (word0 = line0->words; word0; word0 = word0->next) {
-      printf("  word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSz=%.2f space=%d: '",
-            word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-            word0->yBase, word0->fontSize, word0->spaceAfter);
-      for (i = 0; i < word0->len; ++i) {
-       fputc(word0->text[i] & 0xff, stdout);
+      // find the first non-empty line in the pool
+      for (;
+          poolMinBaseIdx <= pool->maxBaseIdx &&
+            !pool->getPool(poolMinBaseIdx);
+          ++poolMinBaseIdx) ;
+      if (poolMinBaseIdx > pool->maxBaseIdx) {
+       break;
       }
-      printf("'\n");
-    }
-  }
-  printf("\n");
-  fflush(stdout);
-#endif
-
-  //----- column assignment
 
-  for (line1 = lineList; line1; line1 = line1->next) {
-    col1 = 0;
-    for (line2 = lineList; line2 != line1; line2 = line2->next) {
-      if (line1->xMin >= line2->xMax) {
-       d = (int)((line1->xMin - line2->xMax) /
-                 (line1->font->maxSpaceWidth * line1->fontSize));
-       if (d > 4) {
-         d = 4;
-       }
-       col2 = line2->col[0] + line2->convertedLen + d;
-       if (col2 > col1) {
-         col1 = col2;
+      // look for the left-most word in the first four lines of the
+      // pool -- this avoids starting with a superscript word
+      startBaseIdx = poolMinBaseIdx;
+      for (baseIdx = poolMinBaseIdx + 1;
+          baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
+          ++baseIdx) {
+       if (!pool->getPool(baseIdx)) {
+         continue;
        }
-      } else if (line1->xMin > line2->xMin) {
-       for (i = 0; i < line2->len && line1->xMin >= line2->xRight[i]; ++i) ;
-       col2 = line2->col[i];
-       if (col2 > col1) {
-         col1 = col2;
+       if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
+           < 0) {
+         startBaseIdx = baseIdx;
        }
       }
-    }
-    for (j = 0; j < line1->len; ++j) {
-      line1->col[j] += col1;
-    }
-  }
-
-#if 0 // for debugging
-  printf("*** lines in xy order, after column assignment ***\n");
-  for (line0 = lineList; line0; line0 = line0->next) {
-    printf("[line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f col=%d len=%d]\n",
-          line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-          line0->yBase, line0->col[0], line0->len);
-    for (word0 = line0->words; word0; word0 = word0->next) {
-      printf("  word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSz=%.2f space=%d: '",
-            word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-            word0->yBase, word0->fontSize, word0->spaceAfter);
-      for (i = 0; i < word0->len; ++i) {
-       fputc(word0->text[i] & 0xff, stdout);
-      }
-      printf("'\n");
-    }
-  }
-  printf("\n");
-  fflush(stdout);
-#endif
-
-  //----- assemble lines into blocks
-
-  if (rawOrder) {
-
-    lines = lineList;
-    for (line1 = lines; line1; line1 = line1->next) {
-      line1->xSpaceL = 0;
-      line1->xSpaceR = pageWidth;
-    }
-
-  } else {
 
-    // sort lines into yx order
-    lines = NULL;
-    while (lineList) {
-      line0 = lineList;
-      lineList = lineList->next;
-      for (line1 = NULL, line2 = lines;
-          line2 && !line0->yxBefore(line2);
-          line1 = line2, line2 = line2->next) ;
-      if (line1) {
-       line1->next = line0;
-      } else {
-       lines = line0;
-      }
-      line0->next = line2;
-    }
-
-    // compute whitespace to left and right of each line
-    line0 = lines;
-    for (line1 = lines; line1; line1 = line1->next) {
-
-      // find the first vertically overlapping line
-      for (; line0 && line0->yMax < line1->yMin; line0 = line0->next) ;
-
-      // check each vertically overlapping line -- look for the nearest
-      // on each side
-      line1->xSpaceL = 0;
-      line1->xSpaceR = pageWidth;
-      for (line2 = line0;
-          line2 && line2->yMin < line1->yMax;
-          line2 = line2->next) {
-       if (line2->yMax > line1->yMin) {
-         if (line2->xMax < line1->xMin) {
-           if (line2->xMax > line1->xSpaceL) {
-             line1->xSpaceL = line2->xMax;
-           }
-         } else if (line2->xMin > line1->xMax) {
-           if (line2->xMin < line1->xSpaceR) {
-             line1->xSpaceR = line2->xMin;
+      // create a new block
+      word0 = pool->getPool(startBaseIdx);
+      pool->setPool(startBaseIdx, word0->next);
+      word0->next = NULL;
+      blk = new TextBlock(this, rot);
+      blk->addWord(word0);
+
+      fontSize = word0->fontSize;
+      minBase = maxBase = word0->base;
+      colSpace = minColSpacing * fontSize;
+      lineSpace = maxLineSpacingDelta * fontSize;
+      intraLineSpace = maxIntraLineDelta * fontSize;
+
+      // add words to the block
+      do {
+       found = gFalse;
+
+       // look for words on the line above the current top edge of
+       // the block
+       newMinBase = minBase;
+       for (baseIdx = pool->getBaseIdx(minBase);
+            baseIdx >= pool->getBaseIdx(minBase - lineSpace);
+            --baseIdx) {
+         word0 = NULL;
+         word1 = pool->getPool(baseIdx);
+         while (word1) {
+           if (word1->base < minBase &&
+               word1->base >= minBase - lineSpace &&
+               ((rot == 0 || rot == 2)
+                ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
+                : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
+               fabs(word1->fontSize - fontSize) <
+                 maxBlockFontSizeDelta1 * fontSize) {
+             word2 = word1;
+             if (word0) {
+               word0->next = word1->next;
+             } else {
+               pool->setPool(baseIdx, word1->next);
+             }
+             word1 = word1->next;
+             word2->next = NULL;
+             blk->addWord(word2);
+             found = gTrue;
+             newMinBase = word2->base;
+           } else {
+             word0 = word1;
+             word1 = word1->next;
            }
          }
        }
-      }
-    }
-  } // (!rawOrder)
-
-#if 0 // for debugging
-  printf("*** lines in yx order ***\n");
-  for (line0 = lines; line0; line0 = line0->next) {
-    printf("[line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f xSpaceL=%.2f xSpaceR=%.2f len=%d]\n",
-          line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-          line0->yBase, line0->xSpaceL, line0->xSpaceR, line0->len);
-    for (word0 = line0->words; word0; word0 = word0->next) {
-      printf("  word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSz=%.2f space=%d: '",
-            word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-            word0->yBase, word0->fontSize, word0->spaceAfter);
-      for (i = 0; i < word0->len; ++i) {
-       fputc(word0->text[i] & 0xff, stdout);
-      }
-      printf("'\n");
-    }
-  }
-  printf("\n");
-  fflush(stdout);
-#endif
-
-  lineList = lines;
-  yxBlocks = NULL;
-  blk0 = NULL;
-  while (lineList) {
-
-    // build a new block object
-    line0 = lineList;
-    lineList = lineList->next;
-    line0->next = NULL;
-    blk1 = new TextBlock();
-    blk1->lines = line0;
-    blk1->xMin = line0->xMin;
-    blk1->xMax = line0->xMax;
-    blk1->yMin = line0->yMin;
-    blk1->yMax = line0->yMax;
-    blk1->xSpaceL = line0->xSpaceL;
-    blk1->xSpaceR = line0->xSpaceR;
-    blk1->maxFontSize = line0->fontSize;
-
-    // find subsequent lines in the block
-    while (lineList) {
-
-      // look for the first horizontally overlapping line below this
-      // one
-      yLimit = line0->yMax + blkMaxSpacing * line0->fontSize;
-      line3 = line4 = NULL;
-      if (rawOrder) {
-       if (lineList->yMin < yLimit &&
-           lineList->xMax > blk1->xMin &&
-           lineList->xMin < blk1->xMax) {
-         line3 = NULL;
-         line4 = lineList;
+       minBase = newMinBase;
+
+       // look for words on the line below the current bottom edge of
+       // the block
+       newMaxBase = maxBase;
+       for (baseIdx = pool->getBaseIdx(maxBase);
+            baseIdx <= pool->getBaseIdx(maxBase + lineSpace);
+            ++baseIdx) {
+         word0 = NULL;
+         word1 = pool->getPool(baseIdx);
+         while (word1) {
+           if (word1->base > maxBase &&
+               word1->base <= maxBase + lineSpace &&
+               ((rot == 0 || rot == 2)
+                ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
+                : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
+               fabs(word1->fontSize - fontSize) <
+                 maxBlockFontSizeDelta1 * fontSize) {
+             word2 = word1;
+             if (word0) {
+               word0->next = word1->next;
+             } else {
+               pool->setPool(baseIdx, word1->next);
+             }
+             word1 = word1->next;
+             word2->next = NULL;
+             blk->addWord(word2);
+             found = gTrue;
+             newMaxBase = word2->base;
+           } else {
+             word0 = word1;
+             word1 = word1->next;
+           }
+         }
        }
-      } else {
-       for (line1 = NULL, line2 = lineList;
-            line2 && line2->yMin < yLimit;
-            line1 = line2, line2 = line2->next) {
-         if (line2->xMax > blk1->xMin &&
-             line2->xMin < blk1->xMax) {
-           line3 = line1;
-           line4 = line2;
-           break;
+       maxBase = newMaxBase;
+
+       // look for words that are on lines already in the block, and
+       // that overlap the block horizontally
+       for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+            baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+            ++baseIdx) {
+         word0 = NULL;
+         word1 = pool->getPool(baseIdx);
+         while (word1) {
+           if (word1->base >= minBase - intraLineSpace &&
+               word1->base <= maxBase + intraLineSpace &&
+               ((rot == 0 || rot == 2)
+                ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
+                : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
+               fabs(word1->fontSize - fontSize) <
+                 maxBlockFontSizeDelta2 * fontSize) {
+             word2 = word1;
+             if (word0) {
+               word0->next = word1->next;
+             } else {
+               pool->setPool(baseIdx, word1->next);
+             }
+             word1 = word1->next;
+             word2->next = NULL;
+             blk->addWord(word2);
+             found = gTrue;
+           } else {
+             word0 = word1;
+             word1 = word1->next;
+           }
          }
        }
-      }
 
-      // if there is an overlapping line and it fits in the block, add
-      // it to the block
-      if (line4 && blockFit(blk1, line4)) {
-       if (line3) {
-         line3->next = line4->next;
-       } else {
-         lineList = line4->next;
-       }
-       line0->next = line0->flowNext = line4;
-       line4->next = NULL;
-       if (line4->xMin < blk1->xMin) {
-         blk1->xMin = line4->xMin;
-       } else if (line4->xMax > blk1->xMax) {
-         blk1->xMax = line4->xMax;
+       // only check for outlying words (the next two chunks of code)
+       // if we didn't find anything else
+       if (found) {
+         continue;
        }
-       if (line4->yMax > blk1->yMax) {
-         blk1->yMax = line4->yMax;
+
+       // scan down the left side of the block, looking for words
+       // that are near (but not overlapping) the block; if there are
+       // three or fewer, add them to the block
+       n = 0;
+       for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+            baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+            ++baseIdx) {
+         word1 = pool->getPool(baseIdx);
+         while (word1) {
+           if (word1->base >= minBase - intraLineSpace &&
+               word1->base <= maxBase + intraLineSpace &&
+               ((rot == 0 || rot == 2)
+                ? (word1->xMax <= blk->xMin &&
+                   word1->xMax > blk->xMin - colSpace)
+                : (word1->yMax <= blk->yMin &&
+                   word1->yMax > blk->yMin - colSpace)) &&
+               fabs(word1->fontSize - fontSize) <
+                 maxBlockFontSizeDelta3 * fontSize) {
+             ++n;
+             break;
+           }
+           word1 = word1->next;
+         }
        }
-       if (line4->xSpaceL > blk1->xSpaceL) {
-         blk1->xSpaceL = line4->xSpaceL;
+       if (n > 0 && n <= 3) {
+         for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+              baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+              ++baseIdx) {
+           word0 = NULL;
+           word1 = pool->getPool(baseIdx);
+           while (word1) {
+             if (word1->base >= minBase - intraLineSpace &&
+                 word1->base <= maxBase + intraLineSpace &&
+                 ((rot == 0 || rot == 2)
+                  ? (word1->xMax <= blk->xMin &&
+                     word1->xMax > blk->xMin - colSpace)
+                  : (word1->yMax <= blk->yMin &&
+                     word1->yMax > blk->yMin - colSpace)) &&
+                 fabs(word1->fontSize - fontSize) <
+                   maxBlockFontSizeDelta3 * fontSize) {
+               word2 = word1;
+               if (word0) {
+                 word0->next = word1->next;
+               } else {
+                 pool->setPool(baseIdx, word1->next);
+               }
+               word1 = word1->next;
+               word2->next = NULL;
+               blk->addWord(word2);
+               if (word2->base < minBase) {
+                 minBase = word2->base;
+               } else if (word2->base > maxBase) {
+                 maxBase = word2->base;
+               }
+               found = gTrue;
+               break;
+             } else {
+               word0 = word1;
+               word1 = word1->next;
+             }
+           }
+         }
        }
-       if (line4->xSpaceR < blk1->xSpaceR) {
-         blk1->xSpaceR = line4->xSpaceR;
+
+       // scan down the right side of the block, looking for words
+       // that are near (but not overlapping) the block; if there are
+       // three or fewer, add them to the block
+       n = 0;
+       for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+            baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+            ++baseIdx) {
+         word1 = pool->getPool(baseIdx);
+         while (word1) {
+           if (word1->base >= minBase - intraLineSpace &&
+               word1->base <= maxBase + intraLineSpace &&
+               ((rot == 0 || rot == 2)
+                ? (word1->xMin >= blk->xMax &&
+                   word1->xMin < blk->xMax + colSpace)
+                : (word1->yMin >= blk->yMax &&
+                   word1->yMin < blk->yMax + colSpace)) &&
+               fabs(word1->fontSize - fontSize) <
+                 maxBlockFontSizeDelta3 * fontSize) {
+             ++n;
+             break;
+           }
+           word1 = word1->next;
+         }
        }
-       if (line4->fontSize > blk1->maxFontSize) {
-         blk1->maxFontSize = line4->fontSize;
+       if (n > 0 && n <= 3) {
+         for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+              baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+              ++baseIdx) {
+           word0 = NULL;
+           word1 = pool->getPool(baseIdx);
+           while (word1) {
+             if (word1->base >= minBase - intraLineSpace &&
+                 word1->base <= maxBase + intraLineSpace &&
+                 ((rot == 0 || rot == 2)
+                  ? (word1->xMin >= blk->xMax &&
+                     word1->xMin < blk->xMax + colSpace)
+                  : (word1->yMin >= blk->yMax &&
+                     word1->yMin < blk->yMax + colSpace)) &&
+                 fabs(word1->fontSize - fontSize) <
+                   maxBlockFontSizeDelta3 * fontSize) {
+               word2 = word1;
+               if (word0) {
+                 word0->next = word1->next;
+               } else {
+                 pool->setPool(baseIdx, word1->next);
+               }
+               word1 = word1->next;
+               word2->next = NULL;
+               blk->addWord(word2);
+               if (word2->base < minBase) {
+                 minBase = word2->base;
+               } else if (word2->base > maxBase) {
+                 maxBase = word2->base;
+               }
+               found = gTrue;
+               break;
+             } else {
+               word0 = word1;
+               word1 = word1->next;
+             }
+           }
+         }
        }
-       line0 = line4;
 
-      // otherwise, we're done with this block
+      } while (found);
+
+      //~ need to compute the primary writing mode (horiz/vert) in
+      //~ addition to primary rotation
+
+      // coalesce the block, and add it to the list
+      blk->coalesce(uMap);
+      if (lastBlk) {
+       lastBlk->next = blk;
       } else {
-       break;
+       blkList = blk;
+      }
+      lastBlk = blk;
+      count[rot] += blk->charCount;
+      if (primaryRot < 0 || count[rot] > count[primaryRot]) {
+       primaryRot = rot;
       }
+      ++nBlocks;
     }
+  }
 
-    // insert block on list, in yx order
-    if (rawOrder) {
-      blk2 = blk0;
-      blk3 = NULL;
-      blk0 = blk1;
-    } else {
-      for (blk2 = NULL, blk3 = yxBlocks;
-          blk3 && !blk1->yxBefore(blk3);
-          blk2 = blk3, blk3 = blk3->next) ;
-    }
-    blk1->next = blk3;
-    if (blk2) {
-      blk2->next = blk1;
-    } else {
-      yxBlocks = blk1;
-    }
+#if 0 // for debugging 
+  printf("*** rotation ***\n");
+  for (rot = 0; rot < 4; ++rot) {
+    printf("  %d: %6d\n", rot, count[rot]);
   }
+  printf("  primary rot = %d\n", primaryRot);
+  printf("\n");
+#endif
 
 #if 0 // for debugging
-  printf("*** blocks in yx order ***\n");
-  for (blk0 = yxBlocks; blk0; blk0 = blk0->next) {
-    printf("[block: x=%.2f..%.2f y=%.2f..%.2f]\n",
-          blk0->xMin, blk0->xMax, blk0->yMin, blk0->yMax);
-    for (line0 = blk0->lines; line0; line0 = line0->next) {
-      printf("  [line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f len=%d]\n",
-            line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-            line0->yBase, line0->len);
-      for (word0 = line0->words; word0; word0 = word0->next) {
-       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f space=%d: '",
+  printf("*** blocks ***\n");
+  for (blk = blkList; blk; blk = blk->next) {
+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n",
+          blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax);
+    for (line = blk->lines; line; line = line->next) {
+      printf("  line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n",
+            line->xMin, line->xMax, line->yMin, line->yMax, line->base);
+      for (word0 = line->words; word0; word0 = word0->next) {
+       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
               word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-              word0->yBase, word0->spaceAfter);
+              word0->base, word0->fontSize, word0->spaceAfter);
        for (i = 0; i < word0->len; ++i) {
          fputc(word0->text[i] & 0xff, stdout);
        }
@@ -1164,148 +2294,109 @@ void TextPage::coalesce(GBool physLayout) {
     }
   }
   printf("\n");
-  fflush(stdout);
 #endif
 
-  //----- merge lines and blocks, sort blocks into reading order
-
-  if (rawOrder) {
-    blocks = yxBlocks;
-
-  } else {
-    blocks = NULL;
-    blk0 = NULL;
-    blkStack = NULL;
-    while (yxBlocks) {
-
-      // find the next two blocks:
-      // - if the depth-first traversal stack is empty, take the first
-      //   (upper-left-most) two blocks on the yx-sorted block list
-      // - otherwise, find the two upper-left-most blocks under the top
-      //   block on the stack
-      if (blkStack) {
-       blk3 = blk4 = blk5 = blk6 = NULL;
-       for (blk1 = NULL, blk2 = yxBlocks;
-            blk2;
-            blk1 = blk2, blk2 = blk2->next) {
-         if (blk2->yMin > blkStack->yMin &&
-             blk2->xMax > blkStack->xMin &&
-             blk2->xMin < blkStack->xMax) {
-           if (!blk4 || blk2->yxBefore(blk4)) {
-             blk5 = blk3;
-             blk6 = blk4;
-             blk3 = blk1;
-             blk4 = blk2;
-           } else if (!blk6 || blk2->yxBefore(blk6)) {
-             blk5 = blk1;
-             blk6 = blk2;
-           }
+  // determine the primary direction
+  lrCount = 0;
+  for (blk = blkList; blk; blk = blk->next) {
+    for (line = blk->lines; line; line = line->next) {
+      for (word0 = line->words; word0; word0 = word0->next) {
+       for (i = 0; i < word0->len; ++i) {
+         if (unicodeTypeL(word0->text[i])) {
+           ++lrCount;
+         } else if (unicodeTypeR(word0->text[i])) {
+           --lrCount;
          }
        }
-      } else {
-       blk3 = NULL;
-       blk4 = yxBlocks;
-       blk5 = yxBlocks;
-       blk6 = yxBlocks->next;
       }
+    }
+  }
+  primaryLR = lrCount >= 0;
 
-      // merge case 1:
-      //   |                     |           |
-      //   |   blkStack          |           |   blkStack
-      //   +---------------------+    -->    +--------------
-      //   +------+ +------+                 +-----------+
-      //   | blk4 | | blk6 | ...             | blk4+blk6 |
-      //   +------+ +------+                 +-----------+
-      yLimit = 0; // make gcc happy
-      if (blkStack) {
-       yLimit = blkStack->yMax + blkMaxSpacing * blkStack->lines->fontSize;
-      }
-      if (blkStack && blk4 && blk6 &&
-         !blk4->lines->next && !blk6->lines->next &&
-         lineFit2(blk4->lines, blk6->lines) &&
-         blk4->yMin < yLimit &&
-         blk4->xMin > blkStack->xSpaceL &&
-         blkStack->xMin > blk4->xSpaceL &&
-         blk6->xMax < blkStack->xSpaceR) {
-       blk4->mergeRight(blk6);
-       if (blk5) {
-         blk5->next = blk6->next;
+#if 0 // for debugging
+  printf("*** direction ***\n");
+  printf("lrCount = %d\n", lrCount);
+  printf("primaryLR = %d\n", primaryLR);
+#endif
+
+  //----- column assignment
+
+  // sort blocks into xy order for column assignment
+  blocks = (TextBlock **)gmalloc(nBlocks * sizeof(TextBlock *));
+  for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
+    blocks[i] = blk;
+  }
+  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);
+
+  // column assignment
+  for (i = 0; i < nBlocks; ++i) {
+    blk0 = blocks[i];
+    col1 = 0;
+    for (j = 0; j < i; ++j) {
+      blk1 = blocks[j];
+      col2 = 0; // make gcc happy
+      switch (primaryRot) {
+      case 0:
+       if (blk0->xMin > blk1->xMax) {
+         col2 = blk1->col + blk1->nColumns + 3;
        } else {
-         yxBlocks = blk6->next;
+         col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) /
+                                   (blk1->xMax - blk1->xMin)) *
+                                  blk1->nColumns);
        }
-       delete blk6;
-
-      // merge case 2:
-      //   |                     |           |                   |
-      //   |   blkStack          |           |                   |
-      //   +---------------------+    -->    |   blkStack+blk2   |
-      //   +---------------------+           |                   |
-      //   |   blk4              |           |                   |
-      //   |                     |           |                   |
-      } else if (blkStack && blk4 &&
-                blk4->yMin < yLimit &&
-                blockFit2(blkStack, blk4)) {
-       blkStack->mergeBelow(blk4);
-       if (blk3) {
-         blk3->next = blk4->next;
+       break;
+      case 1:
+       if (blk0->yMin > blk1->yMax) {
+         col2 = blk1->col + blk1->nColumns + 3;
        } else {
-         yxBlocks = blk4->next;
+         col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) /
+                                   (blk1->yMax - blk1->yMin)) *
+                                  blk1->nColumns);
        }
-       delete blk4;
-
-      // if any of:
-      // 1. no block found
-      // 2. non-fully overlapping block found
-      // 3. large vertical gap above the overlapping block
-      // then pop the stack and try again
-      } else if (!blk4 ||
-                (blkStack && (blk4->xMin < blkStack->xSpaceL ||
-                              blk4->xMax > blkStack->xSpaceR ||
-                              blk4->yMin - blkStack->yMax >
-                                blkMaxSortSpacing * blkStack->maxFontSize))) {
-       blkStack = blkStack->stackNext;
-
-      // add a block to the sorted list
-      } else {
-
-       // remove the block from the yx-sorted list
-       if (blk3) {
-         blk3->next = blk4->next;
+       break;
+      case 2:
+       if (blk0->xMax < blk1->xMin) {
+         col2 = blk1->col + blk1->nColumns + 3;
        } else {
-         yxBlocks = blk4->next;
+         col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) /
+                                   (blk1->xMin - blk1->xMax)) *
+                                  blk1->nColumns);
        }
-       blk4->next = NULL;
-
-       // append the block to the reading-order list
-       if (blk0) {
-         blk0->next = blk4;
+       break;
+      case 3:
+       if (blk0->yMax < blk1->yMin) {
+         col2 = blk1->col + blk1->nColumns + 3;
        } else {
-         blocks = blk4;
-       }
-       blk0 = blk4;
-
-       // push the block on the traversal stack
-       if (!physLayout) {
-         blk4->stackNext = blkStack;
-         blkStack = blk4;
+         col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) /
+                                   (blk1->yMin - blk1->yMax)) *
+                                  blk1->nColumns);
        }
+       break;
+      }
+      if (col2 > col1) {
+       col1 = col2;
+      }
+    }
+    blk0->col = col1;
+    for (line = blk0->lines; line; line = line->next) {
+      for (j = 0; j <= line->len; ++j) {
+       line->col[j] += col1;
       }
     }
-  } // (!rawOrder)
+  }
 
 #if 0 // for debugging
-  printf("*** blocks in reading order (after merging) ***\n");
-  for (blk0 = blocks; blk0; blk0 = blk0->next) {
-    printf("[block: x=%.2f..%.2f y=%.2f..%.2f]\n",
-          blk0->xMin, blk0->xMax, blk0->yMin, blk0->yMax);
-    for (line0 = blk0->lines; line0; line0 = line0->next) {
-      printf("  [line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f len=%d]\n",
-            line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-            line0->yBase, line0->len);
-      for (word0 = line0->words; word0; word0 = word0->next) {
-       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f space=%d: '",
+  printf("*** blocks, after column assignment ***\n");
+  for (blk = blkList; blk; blk = blk->next) {
+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f col=%d nCols=%d\n",
+          blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col,
+          blk->nColumns);
+    for (line = blk->lines; line; line = line->next) {
+      printf("  line:\n");
+      for (word0 = line->words; word0; word0 = word0->next) {
+       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
               word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-              word0->yBase, word0->spaceAfter);
+              word0->base, word0->fontSize, word0->spaceAfter);
        for (i = 0; i < word0->len; ++i) {
          fputc(word0->text[i] & 0xff, stdout);
        }
@@ -1314,409 +2405,314 @@ void TextPage::coalesce(GBool physLayout) {
     }
   }
   printf("\n");
-  fflush(stdout);
 #endif
 
-  //----- assemble blocks into flows
-
-  if (rawOrder) {
-
-    // one flow per block
-    flow0 = NULL;
-    while (blocks) {
-      flow1 = new TextFlow();
-      flow1->blocks = blocks;
-      flow1->lines = blocks->lines;
-      flow1->yMin = blocks->yMin;
-      flow1->yMax = blocks->yMax;
-      blocks = blocks->next;
-      flow1->blocks->next = NULL;
-      if (flow0) {
-       flow0->next = flow1;
-      } else {
-       flows = flow1;
-      }
-      flow0 = flow1;
-    }
-
-  } else {
-
-    // compute whitespace above and below each block
-    for (blk0 = blocks; blk0; blk0 = blk0->next) {
-      blk0->ySpaceT = 0;
-      blk0->ySpaceB = pageHeight;
-
-      // check each horizontally overlapping block
-      for (blk1 = blocks; blk1; blk1 = blk1->next) {
-       if (blk1 != blk0 &&
-           blk1->xMin < blk0->xMax &&
-           blk1->xMax > blk0->xMin) {
-         if (blk1->yMax < blk0->yMin) {
-           if (blk1->yMax > blk0->ySpaceT) {
-             blk0->ySpaceT = blk1->yMax;
-           }
-         } else if (blk1->yMin > blk0->yMax) {
-           if (blk1->yMin < blk0->ySpaceB) {
-             blk0->ySpaceB = blk1->yMin;
-           }
-         }
-       }
-      }
-    }
-
-    flow0 = NULL;
-    while (blocks) {
-
-      // build a new flow object
-      flow1 = new TextFlow();
-      flow1->blocks = blocks;
-      flow1->lines = blocks->lines;
-      flow1->yMin = blocks->yMin;
-      flow1->yMax = blocks->yMax;
-      flow1->ySpaceT = blocks->ySpaceT;
-      flow1->ySpaceB = blocks->ySpaceB;
-
-      // find subsequent blocks in the flow
-      for (blk1 = blocks, blk2 = blocks->next;
-          blk2 && flowFit(flow1, blk2);
-          blk1 = blk2, blk2 = blk2->next) {
-       if (blk2->yMin < flow1->yMin) {
-         flow1->yMin = blk2->yMin;
-       }
-       if (blk2->yMax > flow1->yMax) {
-         flow1->yMax = blk2->yMax;
-       }
-       if (blk2->ySpaceT > flow1->ySpaceT) {
-         flow1->ySpaceT = blk2->ySpaceT;
-       }
-       if (blk2->ySpaceB < flow1->ySpaceB) {
-         flow1->ySpaceB = blk2->ySpaceB;
-       }
-       for (line1 = blk1->lines; line1->next; line1 = line1->next) ;
-       line1->flowNext = blk2->lines;
-      }
+  //----- reading order sort
 
-      // chop the block list
-      blocks = blk1->next;
-      blk1->next = NULL;
+  // sort blocks into yx order (in preparation for reading order sort)
+  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);
 
-      // append the flow to the list
-      if (flow0) {
-       flow0->next = flow1;
-      } else {
-       flows = flow1;
+  // compute space on left and right sides of each block
+  for (i = 0; i < nBlocks; ++i) {
+    blk0 = blocks[i];
+    for (j = 0; j < nBlocks; ++j) {
+      blk1 = blocks[j];
+      if (blk1 != blk0) {
+       blk0->updatePriMinMax(blk1);
       }
-      flow0 = flow1;
     }
   }
 
 #if 0 // for debugging
-  printf("*** flows ***\n");
-  for (flow0 = flows; flow0; flow0 = flow0->next) {
-    printf("[flow]\n");
-    for (blk0 = flow0->blocks; blk0; blk0 = blk0->next) {
-      printf("  [block: x=%.2f..%.2f y=%.2f..%.2f ySpaceT=%.2f ySpaceB=%.2f]\n",
-            blk0->xMin, blk0->xMax, blk0->yMin, blk0->yMax,
-            blk0->ySpaceT, blk0->ySpaceB);
-      for (line0 = blk0->lines; line0; line0 = line0->next) {
-       printf("    [line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f len=%d]\n",
-              line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-              line0->yBase, line0->len);
-       for (word0 = line0->words; word0; word0 = word0->next) {
-         printf("      word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f space=%d: '",
-                word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-                word0->yBase, word0->spaceAfter);
-         for (i = 0; i < word0->len; ++i) {
-           fputc(word0->text[i] & 0xff, stdout);
-         }
-         printf("'\n");
+  printf("*** blocks, after yx sort ***\n");
+  for (i = 0; i < nBlocks; ++i) {
+    blk = blocks[i];
+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f space=%.2f..%.2f\n",
+          blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
+          blk->priMin, blk->priMax);
+    for (line = blk->lines; line; line = line->next) {
+      printf("  line:\n");
+      for (word0 = line->words; word0; word0 = word0->next) {
+       printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
+              word0->xMin, word0->xMax, word0->yMin, word0->yMax,
+              word0->base, word0->fontSize, word0->spaceAfter);
+       for (j = 0; j < word0->len; ++j) {
+         fputc(word0->text[j] & 0xff, stdout);
        }
+       printf("'\n");
       }
     }
   }
   printf("\n");
-  fflush(stdout);
 #endif
 
-  //----- sort lines into yx order
-
-  // (the block/line merging process doesn't maintain the full-page
-  // linked list of lines)
+  // build the flows
+  //~ this needs to be adjusted for writing mode (vertical text)
+  //~ this also needs to account for right-to-left column ordering
+  blkArray = (TextBlock **)gmalloc(nBlocks * sizeof(TextBlock *));
+  memcpy(blkArray, blocks, nBlocks * sizeof(TextBlock *));
+  flows = lastFlow = NULL;
+  firstBlkIdx = 0;
+  nBlocksLeft = nBlocks;
+  while (nBlocksLeft > 0) {
+
+    // find the upper-left-most block
+    for (; !blkArray[firstBlkIdx]; ++firstBlkIdx) ;
+    i = firstBlkIdx;
+    blk = blkArray[i];
+    for (j = firstBlkIdx + 1; j < nBlocks; ++j) {
+      blk1 = blkArray[j];
+      if (blk1) {
+       if (blk && blk->secondaryDelta(blk1) > 0) {
+         break;
+       }
+       if (blk1->primaryCmp(blk) < 0) {
+         i = j;
+         blk = blk1;
+       }
+      }
+    }
+    blkArray[i] = NULL;
+    --nBlocksLeft;
+    blk->next = NULL;
 
-  lines = NULL;
-  if (rawOrder) {
-    line0 = NULL;
-    for (flow0 = flows; flow0; flow0 = flow0->next) {
-      for (line1 = flow0->lines; line1; line1 = line1->flowNext) {
-       if (line0) {
-         line0->pageNext = line1;
-       } else {
-         lines = line1;
+    // create a new flow, starting with the upper-left-most block
+    flow = new TextFlow(this, blk);
+    if (lastFlow) {
+      lastFlow->next = flow;
+    } else {
+      flows = flow;
+    }
+    lastFlow = flow;
+    fontSize = blk->lines->words->fontSize;
+
+    // push the upper-left-most block on the stack
+    blk->stackNext = NULL;
+    blkStack = blk;
+
+    // find the other blocks in this flow
+    while (blkStack) {
+
+      // find the upper-left-most block under (but within
+      // maxBlockSpacing of) the top block on the stack
+      blkSpace = maxBlockSpacing * blkStack->lines->words->fontSize;
+      blk = NULL;
+      i = -1;
+      for (j = firstBlkIdx; j < nBlocks; ++j) {
+       blk1 = blkArray[j];
+       if (blk1) {
+         if (blkStack->secondaryDelta(blk1) > blkSpace) {
+           break;
+         }
+         if (blk && blk->secondaryDelta(blk1) > 0) {
+           break;
+         }
+         if (blk1->isBelow(blkStack) &&
+             (!blk || blk1->primaryCmp(blk) < 0)) {
+           i = j;
+           blk = blk1;
+         }
        }
-       line0 = line1;
       }
-    }
-  } else {
-    for (flow0 = flows; flow0; flow0 = flow0->next) {
-      for (line0 = flow0->lines; line0; line0 = line0->flowNext) {
-       for (line1 = NULL, line2 = lines;
-            line2 && !line0->yxBefore(line2);
-            line1 = line2, line2 = line2->pageNext) ;
-       if (line1) {
-         line1->pageNext = line0;
-       } else {
-         lines = line0;
-       }
-       line0->pageNext = line2;
+
+      // if a suitable block was found, add it to the flow and push it
+      // onto the stack
+      if (blk && flow->blockFits(blk, blkStack)) {
+       blkArray[i] = NULL;
+       --nBlocksLeft;
+       blk->next = NULL;
+       flow->addBlock(blk);
+       fontSize = blk->lines->words->fontSize;
+       blk->stackNext = blkStack;
+       blkStack = blk;
+
+      // otherwise (if there is no block under the top block or the
+      // block is not suitable), pop the stack
+      } else {
+       blkStack = blkStack->stackNext;
       }
     }
   }
+  gfree(blkArray);
 
 #if 0 // for debugging
-  printf("*** lines in yx order ***\n");
-  for (line0 = lines; line0; line0 = line0->pageNext) {
-    printf("[line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f xSpaceL=%.2f xSpaceR=%.2f col=%d len=%d]\n",
-          line0->xMin, line0->xMax, line0->yMin, line0->yMax,
-          line0->yBase, line0->xSpaceL, line0->xSpaceR, line0->col[0],
-          line0->len);
-    for (word0 = line0->words; word0; word0 = word0->next) {
-      printf("  word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f space=%d: '",
-            word0->xMin, word0->xMax, word0->yMin, word0->yMax,
-            word0->yBase, word0->spaceAfter);
-      for (i = 0; i < word0->len; ++i) {
-       fputc(word0->text[i] & 0xff, stdout);
+  printf("*** flows ***\n");
+  for (flow = flows; flow; flow = flow->next) {
+    printf("flow: x=%.2f..%.2f y=%.2f..%.2f pri:%.2f..%.2f\n",
+          flow->xMin, flow->xMax, flow->yMin, flow->yMax,
+          flow->priMin, flow->priMax);
+    for (blk = flow->blocks; blk; blk = blk->next) {
+      printf("  block: rot=%d x=%.2f..%.2f y=%.2f..%.2f pri=%.2f..%.2f\n",
+            blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
+            blk->priMin, blk->priMax);
+      for (line = blk->lines; line; line = line->next) {
+       printf("    line:\n");
+       for (word0 = line->words; word0; word0 = word0->next) {
+         printf("      word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
+                word0->xMin, word0->xMax, word0->yMin, word0->yMax,
+                word0->base, word0->fontSize, word0->spaceAfter);
+         for (i = 0; i < word0->len; ++i) {
+           fputc(word0->text[i] & 0xff, stdout);
+         }
+         printf("'\n");
+       }
       }
-      printf("'\n");
     }
   }
   printf("\n");
-  fflush(stdout);
 #endif
-}
 
-// If <word> can be added the end of <line>, return the absolute value
-// of the difference between <line>'s baseline and <word>'s baseline,
-// and set *<space> to the horizontal space between the current last
-// word in <line> and <word>.  A smaller return value indicates a
-// better fit.  Otherwise, return a negative number.
-double TextPage::lineFit(TextLine *line, TextWord *word, double *space) {
-  TextWord *lastWord;
-  double fontSize0, fontSize1;
-  double dx, dxLimit;
-
-  lastWord = line->lastWord;
-  fontSize0 = line->fontSize;
-  fontSize1 = word->fontSize;
-  dx = word->xMin - lastWord->xMax;
-  dxLimit = fontSize0 * lastWord->font->maxSpaceWidth;
-
-  // check inter-word spacing
-  if (dx < fontSize0 * lineMinDeltaX ||
-      dx > dxLimit) {
-    return -1;
-  }
-
-  if (
-      // look for adjacent words with close baselines and close font sizes
-      (fabs(line->yBase - word->yBase) < lineMaxBaselineDelta * fontSize0 &&
-       fontSize0 < lineMaxFontSizeRatio * fontSize1 &&
-       fontSize1 < lineMaxFontSizeRatio * fontSize0) ||
-
-      // look for a superscript
-      (fontSize1 > lineMinSuperscriptFontSizeRatio * fontSize0 &&
-       fontSize1 < lineMaxSuperscriptFontSizeRatio * fontSize0 &&
-       (word->yMax < lastWord->yMax ||
-       word->yBase < lastWord->yBase) &&
-       word->yMax - lastWord->yMin > lineMinSuperscriptOverlap * fontSize0 &&
-       dx < fontSize0 * lineMaxSuperscriptDeltaX) ||
-
-      // look for a subscript
-      (fontSize1 > lineMinSubscriptFontSizeRatio * fontSize0 &&
-       fontSize1 < lineMaxSubscriptFontSizeRatio * fontSize0 &&
-       (word->yMin > lastWord->yMin ||
-       word->yBase > lastWord->yBase) &&
-       line->yMax - word->yMin > lineMinSubscriptOverlap * fontSize0 &&
-       dx < fontSize0 * lineMaxSubscriptDeltaX)) {
-
-    *space = dx;
-    return fabs(word->yBase - line->yBase);
-  }
-
-  return -1;
-}
-
-// Returns true if <line0> and <line1> can be merged into a single
-// line, ignoring max word spacing.
-GBool TextPage::lineFit2(TextLine *line0, TextLine *line1) {
-  double fontSize0, fontSize1;
-  double dx;
-
-  fontSize0 = line0->fontSize;
-  fontSize1 = line1->fontSize;
-  dx = line1->xMin - line0->xMax;
-
-  // check inter-word spacing
-  if (dx < fontSize0 * lineMinDeltaX) {
-    return gFalse;
-  }
-
-  // look for close baselines and close font sizes
-  if (fabs(line0->yBase - line1->yBase) < lineMaxBaselineDelta * fontSize0 &&
-      fontSize0 < lineMaxFontSizeRatio * fontSize1 &&
-      fontSize1 < lineMaxFontSizeRatio * fontSize0) {
-    return gTrue;
+  if (uMap) {
+    uMap->decRefCnt();
   }
-
-  return gFalse;
 }
 
-// Returns true if <line> can be added to <blk>.  Assumes the y
-// coordinates are within range.
-GBool TextPage::blockFit(TextBlock *blk, TextLine *line) {
-  double fontSize0, fontSize1;
-
-  // check edges
-  if (line->xMin < blk->xSpaceL ||
-      line->xMax > blk->xSpaceR ||
-      blk->xMin < line->xSpaceL ||
-      blk->xMax > line->xSpaceR) {
-    return gFalse;
-  }
-
-  // check font sizes
-  fontSize0 = blk->lines->fontSize;
-  fontSize1 = line->fontSize;
-  if (fontSize0 > blkMaxFontSizeRatio * fontSize1 ||
-      fontSize1 > blkMaxFontSizeRatio * fontSize0) {
-    return gFalse;
-  }
-
-  return gTrue;
-}
+GBool TextPage::findText(Unicode *s, int len,
+                        GBool startAtTop, GBool stopAtBottom,
+                        GBool startAtLast, GBool stopAtLast,
+                        double *xMin, double *yMin,
+                        double *xMax, double *yMax) {
+  TextBlock *blk;
+  TextLine *line;
+  Unicode *p;
+  Unicode u1, u2;
+  int m, i, j, k;
+  double xStart, yStart, xStop, yStop;
+  double xMin0, yMin0, xMax0, yMax0;
+  double xMin1, yMin1, xMax1, yMax1;
+  GBool found;
 
-// Returns true if <blk0> and <blk1> can be merged into a single
-// block.  Assumes the y coordinates are within range.
-GBool TextPage::blockFit2(TextBlock *blk0, TextBlock *blk1) {
-  double fontSize0, fontSize1;
+  //~ needs to handle right-to-left text
 
-  // check edges
-  if (blk1->xMin < blk0->xSpaceL ||
-      blk1->xMax > blk0->xSpaceR ||
-      blk0->xMin < blk1->xSpaceL ||
-      blk0->xMax > blk1->xSpaceR) {
+  if (rawOrder) {
     return gFalse;
   }
 
-  // check font sizes
-  fontSize0 = blk0->lines->fontSize;
-  fontSize1 = blk1->lines->fontSize;
-  if (fontSize0 > blkMaxFontSizeRatio * fontSize1 ||
-      fontSize1 > blkMaxFontSizeRatio * fontSize0) {
-    return gFalse;
+  xStart = yStart = xStop = yStop = 0;
+  if (startAtLast && haveLastFind) {
+    xStart = lastFindXMin;
+    yStart = lastFindYMin;
+  } else if (!startAtTop) {
+    xStart = *xMin;
+    yStart = *yMin;
   }
-
-  return gTrue;
-}
-
-// Returns true if <blk> can be added to <flow>.
-GBool TextPage::flowFit(TextFlow *flow, TextBlock *blk) {
-  double dy;
-
-  // check whitespace above and below
-  if (blk->yMin < flow->ySpaceT ||
-      blk->yMax > flow->ySpaceB ||
-      flow->yMin < blk->ySpaceT ||
-      flow->yMax > blk->ySpaceB) {
-    return gFalse;
+  if (stopAtLast && haveLastFind) {
+    xStop = lastFindXMin;
+    yStop = lastFindYMin;
+  } else if (!stopAtBottom) {
+    xStop = *xMax;
+    yStop = *yMax;
   }
 
-  // check that block top edge is within +/- dy of flow top edge,
-  // and that block bottom edge is above flow bottom edge + dy
-  dy = flowMaxDeltaY * flow->blocks->maxFontSize;
-  return blk->yMin > flow->yMin - dy &&
-         blk->yMin < flow->yMin + dy &&
-         blk->yMax < flow->yMax + dy;
-}
-
+  found = gFalse;
+  xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
+  xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
 
-GBool TextPage::findText(Unicode *s, int len,
-                        GBool top, GBool bottom,
-                        double *xMin, double *yMin,
-                        double *xMax, double *yMax) {
-  TextLine *line;
-  Unicode *p;
-  Unicode u1, u2;
-  int m, i, j;
-  double x0, x1, x;
+  for (i = 0; i < nBlocks; ++i) {
+    blk = blocks[i];
 
-  // scan all text on the page
-  for (line = lines; line; line = line->pageNext) {
-
-    // check: above top limit?
-    if (!top && (line->yMax < *yMin ||
-                (line->yMin < *yMin && line->xMax <= *xMin))) {
+    // check: is the block above the top limit?
+    if (!startAtTop && blk->yMax < yStart) {
       continue;
     }
 
-    // check: below bottom limit?
-    if (!bottom && (line->yMin > *yMax ||
-                   (line->yMax > *yMax && line->xMin >= *xMax))) {
-      return gFalse;
+    // check: is the block below the bottom limit?
+    if (!stopAtBottom && blk->yMin > yStop) {
+      break;
     }
 
-    // search each position in this line
-    m = line->len;
-    for (i = 0, p = line->text; i <= m - len; ++i, ++p) {
+    for (line = blk->lines; line; line = line->next) {
 
-      x0 = (i == 0) ? line->xMin : line->xRight[i-1];
-      x1 = line->xRight[i];
-      x = 0.5 * (x0 + x1);
-
-      // check: above top limit?
-      if (!top && line->yMin < *yMin) {
-       if (x < *xMin) {
-         continue;
-       }
+      // check: is the line above the top limit?
+      if (!startAtTop && line->yMin < yStart) {
+       continue;
       }
 
-      // check: below bottom limit?
-      if (!bottom && line->yMax > *yMax) {
-       if (x > *xMax) {
-         return gFalse;
-       }
+      // check: is the line below the bottom limit?
+      if (!stopAtBottom && line->yMin > yStop) {
+       continue;
       }
 
-      // compare the strings
-      for (j = 0; j < len; ++j) {
+      // search each position in this line
+      m = line->len;
+      for (j = 0, p = line->text; j <= m - len; ++j, ++p) {
+
+       // compare the strings
+       for (k = 0; k < len; ++k) {
 #if 1 //~ this lowercases Latin A-Z only -- this will eventually be
       //~ extended to handle other character sets
-       if (p[j] >= 0x41 && p[j] <= 0x5a) {
-         u1 = p[j] + 0x20;
-       } else {
-         u1 = p[j];
-       }
-       if (s[j] >= 0x41 && s[j] <= 0x5a) {
-         u2 = s[j] + 0x20;
-       } else {
-         u2 = s[j];
-       }
+         if (p[k] >= 0x41 && p[k] <= 0x5a) {
+           u1 = p[k] + 0x20;
+         } else {
+           u1 = p[k];
+         }
+         if (s[k] >= 0x41 && s[k] <= 0x5a) {
+           u2 = s[k] + 0x20;
+         } else {
+           u2 = s[k];
+         }
 #endif
-       if (u1 != u2) {
-         break;
+         if (u1 != u2) {
+           break;
+         }
        }
-      }
 
-      // found it
-      if (j == len) {
-       *xMin = x0;
-       *xMax = line->xRight[i + len - 1];
-       *yMin = line->yMin;
-       *yMax = line->yMax;
-       return gTrue;
+       // found it
+       if (k == len) {
+         switch (line->rot) {
+         case 0:
+           xMin1 = line->edge[j];
+           xMax1 = line->edge[j + len];
+           yMin1 = line->yMin;
+           yMax1 = line->yMax;
+           break;
+         case 1:
+           xMin1 = line->xMin;
+           xMax1 = line->xMax;
+           yMin1 = line->edge[j];
+           yMax1 = line->edge[j + len];
+           break;
+         case 2:
+           xMin1 = line->edge[j + len];
+           xMax1 = line->edge[j];
+           yMin1 = line->yMin;
+           yMax1 = line->yMax;
+           break;
+         case 3:
+           xMin1 = line->xMin;
+           xMax1 = line->xMax;
+           yMin1 = line->edge[j + len];
+           yMax1 = line->edge[j];
+           break;
+         }
+         if ((startAtTop ||
+              yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
+             (stopAtBottom ||
+              yMin1 < yStop || (yMin1 == yStop && xMin1 < yStop))) {
+           if (!found || yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
+             xMin0 = xMin1;
+             xMax0 = xMax1;
+             yMin0 = yMin1;
+             yMax0 = yMax1;
+             found = gTrue;
+           }
+         }
+       }
       }
     }
   }
 
+  if (found) {
+    *xMin = xMin0;
+    *xMax = xMax0;
+    *yMin = yMin0;
+    *yMax = yMax0;
+    lastFindXMin = xMin0;
+    lastFindYMin = yMin0;
+    haveLastFind = gTrue;
+    return gTrue;
+  }
+
   return gFalse;
 }
 
@@ -1725,15 +2721,24 @@ GString *TextPage::getText(double xMin, double yMin,
   GString *s;
   UnicodeMap *uMap;
   GBool isUnicode;
-  char space[8], eol[16], buf[8];
-  int spaceLen, eolLen, len;
-  TextLine *line, *prevLine;
-  double x0, x1, y;
-  int firstCol, col, i;
-  GBool multiLine;
+  TextBlock *blk;
+  TextLine *line;
+  TextLineFrag *frags;
+  int nFrags, fragsSize;
+  TextLineFrag *frag;
+  char space[8], eol[16];
+  int spaceLen, eolLen;
+  int lastRot;
+  double x, y;
+  int col, idx0, idx1, i, j;
+  GBool multiLine, oneRot;
 
   s = new GString();
 
+  if (rawOrder) {
+    return s;
+  }
+
   // get the output encoding
   if (!(uMap = globalParams->getTextEncoding())) {
     return s;
@@ -1754,109 +2759,173 @@ GString *TextPage::getText(double xMin, double yMin,
     break;
   }
 
-  // find the leftmost column
-  firstCol = -1;
-  for (line = lines; line; line = line->pageNext) {
-    if (line->yMin > yMax) {
-      break;
-    }
-    if (line->yMax < yMin || 
-       line->xMax < xMin ||
-       line->xMin > xMax) {
-      continue;
-    }
-
-    y = 0.5 * (line->yMin + line->yMax);
-    if (y < yMin || y > yMax) {
-      continue;
-    }
-
-    i = 0;
-    while (i < line->len) {
-      x0 = (i==0) ? line->xMin : line->xRight[i-1];
-      x1 = line->xRight[i];
-      if (0.5 * (x0 + x1) > xMin) {
-       break;
+  //~ writing mode (horiz/vert)
+
+  // collect the line fragments that are in the rectangle
+  fragsSize = 256;
+  frags = (TextLineFrag *)gmalloc(fragsSize * sizeof(TextLineFrag));
+  nFrags = 0;
+  lastRot = -1;
+  oneRot = gTrue;
+  for (i = 0; i < nBlocks; ++i) {
+    blk = blocks[i];
+    if (xMin < blk->xMax && blk->xMin < xMax &&
+       yMin < blk->yMax && blk->yMin < yMax) {
+      for (line = blk->lines; line; line = line->next) {
+       if (xMin < line->xMax && line->xMin < xMax &&
+           yMin < line->yMax && line->yMin < yMax) {
+         idx0 = idx1 = -1;
+         switch (line->rot) {
+         case 0:
+           y = 0.5 * (line->yMin + line->yMax);
+           if (yMin < y && y < yMax) {
+             j = 0;
+             while (j < line->len) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
+                 idx0 = j;
+                 break;
+               }
+               ++j;
+             }
+             j = line->len - 1;
+             while (j >= 0) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
+                 idx1 = j;
+                 break;
+               }
+               --j;
+             }
+           }
+           break;
+         case 1:
+           x = 0.5 * (line->xMin + line->xMax);
+           if (xMin < x && x < xMax) {
+             j = 0;
+             while (j < line->len) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
+                 idx0 = j;
+                 break;
+               }
+               ++j;
+             }
+             j = line->len - 1;
+             while (j >= 0) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
+                 idx1 = j;
+                 break;
+               }
+               --j;
+             }
+           }
+           break;
+         case 2:
+           y = 0.5 * (line->yMin + line->yMax);
+           if (yMin < y && y < yMax) {
+             j = 0;
+             while (j < line->len) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
+                 idx0 = j;
+                 break;
+               }
+               ++j;
+             }
+             j = line->len - 1;
+             while (j >= 0) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
+                 idx1 = j;
+                 break;
+               }
+               --j;
+             }
+           }
+           break;
+         case 3:
+           x = 0.5 * (line->xMin + line->xMax);
+           if (xMin < x && x < xMax) {
+             j = 0;
+             while (j < line->len) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
+                 idx0 = j;
+                 break;
+               }
+               ++j;
+             }
+             j = line->len - 1;
+             while (j >= 0) {
+               if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
+                 idx1 = j;
+                 break;
+               }
+               --j;
+             }
+           }
+           break;
+         }
+         if (idx0 >= 0 && idx1 >= 0) {
+           if (nFrags == fragsSize) {
+             fragsSize *= 2;
+             frags = (TextLineFrag *)
+                         grealloc(frags, fragsSize * sizeof(TextLineFrag));
+           }
+           frags[nFrags].init(line, idx0, idx1 - idx0 + 1);
+           ++nFrags;
+           if (lastRot >= 0 && line->rot != lastRot) {
+             oneRot = gFalse;
+           }
+           lastRot = line->rot;
+         }
+       }
       }
-      ++i;
-    }
-    if (i == line->len) {
-      continue;
-    }
-    col = line->col[i];
-
-    if (firstCol < 0 || col < firstCol) {
-      firstCol = col;
     }
   }
 
-  // extract the text
-  col = firstCol;
-  multiLine = gFalse;
-  prevLine = NULL;
-  for (line = lines; line; line = line->pageNext) {
-    if (line->yMin > yMax) {
-      break;
-    }
-    if (line->yMax < yMin || 
-       line->xMax < xMin ||
-       line->xMin > xMax) {
-      continue;
-    }
-
-    y = 0.5 * (line->yMin + line->yMax);
-    if (y < yMin || y > yMax) {
-      continue;
-    }
-
-    i = 0;
-    while (i < line->len) {
-      x0 = (i==0) ? line->xMin : line->xRight[i-1];
-      x1 = line->xRight[i];
-      if (0.5 * (x0 + x1) > xMin) {
-       break;
-      }
-      ++i;
-    }
-    if (i == line->len) {
-      continue;
-    }
+  // sort the fragments and generate the string
+  if (nFrags > 0) {
 
-    // insert a return
-    if (line->col[i] < col ||
-       (prevLine &&
-        line->yMin >
-          prevLine->yMax - lineOverlapSlack * prevLine->fontSize)) {
-      s->append(eol, eolLen);
-      col = firstCol;
-      multiLine = gTrue;
+    for (i = 0; i < nFrags; ++i) {
+      frags[i].computeCoords(oneRot);
     }
-    prevLine = line;
+    assignColumns(frags, nFrags, oneRot);
 
-    // line this block up with the correct column
-    for (; col < line->col[i]; ++col) {
-      s->append(space, spaceLen);
+    // if all lines in the region have the same rotation, use it;
+    // otherwise, use the page's primary rotation
+    if (oneRot) {
+      qsort(frags, nFrags, sizeof(TextLineFrag),
+           &TextLineFrag::cmpYXLineRot);
+    } else {
+      qsort(frags, nFrags, sizeof(TextLineFrag),
+           &TextLineFrag::cmpYXPrimaryRot);
     }
 
-    // print the portion of the line
-    for (; i < line->len; ++i) {
+    col = 0;
+    multiLine = gFalse;
+    for (i = 0; i < nFrags; ++i) {
+      frag = &frags[i];
+
+      // insert a return
+      if (frag->col < col ||
+         (i > 0 && fabs(frag->base - frags[i-1].base) >
+                     maxIntraLineDelta * frags[i-1].line->words->fontSize)) {
+       s->append(eol, eolLen);
+       col = 0;
+       multiLine = gTrue;
+      }
 
-      x0 = (i==0) ? line->xMin : line->xRight[i-1];
-      x1 = line->xRight[i];
-      if (0.5 * (x0 + x1) > xMax) {
-       break;
+      // column alignment
+      for (; col < frag->col; ++col) {
+       s->append(space, spaceLen);
       }
 
-      len = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
-      s->append(buf, len);
-      col += isUnicode ? 1 : len;
+      // get the fragment text
+      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
     }
-  }
 
-  if (multiLine) {
-    s->append(eol, eolLen);
+    if (multiLine) {
+      s->append(eol, eolLen);
+    }
   }
 
+  gfree(frags);
   uMap->decRefCnt();
 
   return s;
@@ -1865,58 +2934,107 @@ GString *TextPage::getText(double xMin, double yMin,
 GBool TextPage::findCharRange(int pos, int length,
                              double *xMin, double *yMin,
                              double *xMax, double *yMax) {
+  TextBlock *blk;
   TextLine *line;
   TextWord *word;
-  double x;
+  double xMin0, xMax0, yMin0, yMax0;
+  double xMin1, xMax1, yMin1, yMax1;
   GBool first;
-  int i;
+  int i, j0, j1;
+
+  if (rawOrder) {
+    return gFalse;
+  }
 
   //~ this doesn't correctly handle:
   //~ - ranges split across multiple lines (the highlighted region
   //~   is the bounding box of all the parts of the range)
   //~ - cases where characters don't convert one-to-one into Unicode
   first = gTrue;
-  for (line = lines; line; line = line->pageNext) {
-    for (word = line->words; word; word = word->next) {
-      if (pos < word->charPos + word->charLen &&
-         word->charPos < pos + length) {
-       i = pos - word->charPos;
-       if (i < 0) {
-         i = 0;
-       }
-       x = (i == 0) ? word->xMin : word->xRight[i - 1];
-       if (first || x < *xMin) {
-         *xMin = x;
-       }
-       i = pos + length - word->charPos;
-       if (i >= word->len) {
-         i = word->len - 1;
-       }
-       x = word->xRight[i];
-       if (first || x > *xMax) {
-         *xMax = x;
-       }
-       if (first || word->yMin < *yMin) {
-         *yMin = word->yMin;
-       }
-       if (first || word->yMax > *yMax) {
-         *yMax = word->yMax;
+  xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
+  xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
+  for (i = 0; i < nBlocks; ++i) {
+    blk = blocks[i];
+    for (line = blk->lines; line; line = line->next) {
+      for (word = line->words; word; word = word->next) {
+       if (pos < word->charPos + word->charLen &&
+           word->charPos < pos + length) {
+         j0 = pos - word->charPos;
+         if (j0 < 0) {
+           j0 = 0;
+         }
+         j1 = pos + length - 1 - word->charPos;
+         if (j1 >= word->len) {
+           j1 = word->len - 1;
+         }
+         switch (line->rot) {
+         case 0:
+           xMin1 = word->edge[j0];
+           xMax1 = word->edge[j1 + 1];
+           yMin1 = word->yMin;
+           yMax1 = word->yMax;
+           break;
+         case 1:
+           xMin1 = word->xMin;
+           xMax1 = word->xMax;
+           yMin1 = word->edge[j0];
+           yMax1 = word->edge[j1 + 1];
+           break;
+         case 2:
+           xMin1 = word->edge[j1 + 1];
+           xMax1 = word->edge[j0];
+           yMin1 = word->yMin;
+           yMax1 = word->yMax;
+           break;
+         case 3:
+           xMin1 = word->xMin;
+           xMax1 = word->xMax;
+           yMin1 = word->edge[j1 + 1];
+           yMax1 = word->edge[j0];
+           break;
+         }
+         if (first || xMin1 < xMin0) {
+           xMin0 = xMin1;
+         }
+         if (first || xMax1 > xMax0) {
+           xMax0 = xMax1;
+         }
+         if (first || yMin1 < yMin0) {
+           yMin0 = yMin1;
+         }
+         if (first || yMax1 > yMax0) {
+           yMax0 = yMax1;
+         }
+         first = gFalse;
        }
-       first = gFalse;
       }
     }
   }
-  return !first;
+  if (!first) {
+    *xMin = xMin0;
+    *xMax = xMax0;
+    *yMin = yMin0;
+    *yMax = yMax0;
+    return gTrue;
+  }
+  return gFalse;
 }
 
 void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
                    GBool physLayout) {
   UnicodeMap *uMap;
-  char space[8], eol[16], eop[8], buf[8];
-  int spaceLen, eolLen, eopLen, len;
   TextFlow *flow;
+  TextBlock *blk;
   TextLine *line;
-  int col, d, n, i;
+  TextLineFrag *frags;
+  TextWord *word;
+  int nFrags, fragsSize;
+  TextLineFrag *frag;
+  char space[8], eol[16], eop[8];
+  int spaceLen, eolLen, eopLen;
+  GBool pageBreaks;
+  GString *s;
+  int col, i, d, n;
 
   // get the output encoding
   if (!(uMap = globalParams->getTextEncoding())) {
@@ -1937,69 +3055,119 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
     break;
   }
   eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
+  pageBreaks = globalParams->getTextPageBreaks();
 
-  // output the page, maintaining the original physical layout
-  if (physLayout || rawOrder) {
-    col = 0;
-    for (line = lines; line; line = line->pageNext) {
+  //~ writing mode (horiz/vert)
 
-      // line this block up with the correct column
-      if (!rawOrder) {
-       for (; col < line->col[0]; ++col) {
+  // output the page in raw (content stream) order
+  if (rawOrder) {
+
+    for (word = rawWords; word; word = word->next) {
+      s = new GString();
+      dumpFragment(word->text, word->len, uMap, s);
+      (*outputFunc)(outputStream, s->getCString(), s->getLength());
+      delete s;
+      if (word->next &&
+         fabs(word->next->base - word->base) <
+           maxIntraLineDelta * word->fontSize) {
+       if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {
          (*outputFunc)(outputStream, space, spaceLen);
        }
+      } else {
+       (*outputFunc)(outputStream, eol, eolLen);
       }
+    }
 
-      // print the line
-      for (i = 0; i < line->len; ++i) {
-       len = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
-       (*outputFunc)(outputStream, buf, len);
+  // output the page, maintaining the original physical layout
+  } else if (physLayout) {
+
+    // collect the line fragments for the page and sort them
+    fragsSize = 256;
+    frags = (TextLineFrag *)gmalloc(fragsSize * sizeof(TextLineFrag));
+    nFrags = 0;
+    for (i = 0; i < nBlocks; ++i) {
+      blk = blocks[i];
+      for (line = blk->lines; line; line = line->next) {
+       if (nFrags == fragsSize) {
+         fragsSize *= 2;
+         frags = (TextLineFrag *)grealloc(frags,
+                                          fragsSize * sizeof(TextLineFrag));
+       }
+       frags[nFrags].init(line, 0, line->len);
+       frags[nFrags].computeCoords(gTrue);
+       ++nFrags;
+      }
+    }
+    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot);
+
+    // generate output
+    col = 0;
+    for (i = 0; i < nFrags; ++i) {
+      frag = &frags[i];
+
+      // column alignment
+      for (; col < frag->col; ++col) {
+       (*outputFunc)(outputStream, space, spaceLen);
       }
-      col += line->convertedLen;
 
-      // print one or more returns if necessary
-      if (rawOrder ||
-         !line->pageNext ||
-         line->pageNext->col[0] < col ||
-         line->pageNext->yMin >
-           line->yMax - lineOverlapSlack * line->fontSize) {
-
-       // compute number of returns
-       d = 1;
-       if (line->pageNext) {
-         d += (int)((line->pageNext->yMin - line->yMax) /
-                    line->fontSize + 0.5);
-       }
+      // print the line
+      s = new GString();
+      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
+      (*outputFunc)(outputStream, s->getCString(), s->getLength());
+      delete s;
 
-       // various things (weird font matrices) can result in bogus
-       // values here, so do a sanity check
-       if (d < 1) {
+      // print one or more returns if necessary
+      if (i == nFrags - 1 ||
+         frags[i+1].col < col ||
+         fabs(frags[i+1].base - frag->base) >
+           maxIntraLineDelta * frag->line->words->fontSize) {
+       if (i < nFrags - 1) {
+         d = (int)((frags[i+1].base - frag->base) /
+                   frag->line->words->fontSize);
+         if (d < 1) {
+           d = 1;
+         } else if (d > 5) {
+           d = 5;
+         }
+       } else {
          d = 1;
-       } else if (d > 5) {
-         d = 5;
        }
        for (; d > 0; --d) {
          (*outputFunc)(outputStream, eol, eolLen);
        }
-
        col = 0;
       }
     }
 
+    gfree(frags);
+
   // output the page, "undoing" the layout
   } else {
     for (flow = flows; flow; flow = flow->next) {
-      for (line = flow->lines; line; line = line->flowNext) {
-       n = line->len;
-       if (line->flowNext && line->hyphenated) {
-         --n;
-       }
-       for (i = 0; i < n; ++i) {
-         len = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
-         (*outputFunc)(outputStream, buf, len);
-       }
-       if (line->flowNext && !line->hyphenated) {
-         (*outputFunc)(outputStream, space, spaceLen);
+      for (blk = flow->blocks; blk; blk = blk->next) {
+       for (line = blk->lines; line; line = line->next) {
+         n = line->len;
+         if (line->hyphenated && (line->next || blk->next)) {
+           --n;
+         }
+         s = new GString();
+         dumpFragment(line->text, n, uMap, s);
+         (*outputFunc)(outputStream, s->getCString(), s->getLength());
+         delete s;
+         if (!line->hyphenated) {
+           if (line->next) {
+             (*outputFunc)(outputStream, space, spaceLen);
+           } else if (blk->next) {
+             //~ this is a bit of a kludge - we should really do a more
+             //~ intelligent determination of paragraphs
+             if (blk->next->lines->words->fontSize ==
+                 blk->lines->words->fontSize) {
+               (*outputFunc)(outputStream, space, spaceLen);
+             } else {
+               (*outputFunc)(outputStream, eol, eolLen);
+             }
+           }
+         }
        }
       }
       (*outputFunc)(outputStream, eol, eolLen);
@@ -2008,56 +3176,196 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
   }
 
   // end of page
-  (*outputFunc)(outputStream, eop, eopLen);
-  (*outputFunc)(outputStream, eol, eolLen);
+  if (pageBreaks) {
+    (*outputFunc)(outputStream, eop, eopLen);
+    (*outputFunc)(outputStream, eol, eolLen);
+  }
 
   uMap->decRefCnt();
 }
 
-void TextPage::startPage(GfxState *state) {
-  clear();
-  if (state) {
-    pageWidth = state->getPageWidth();
-    pageHeight = state->getPageHeight();
+void TextPage::assignColumns(TextLineFrag *frags, int nFrags, GBool oneRot) {
+  TextLineFrag *frag0, *frag1;
+  int rot, col1, col2, i, j, k;
+
+  // all text in the region has the same rotation -- recompute the
+  // column numbers based only on the text in the region
+  if (oneRot) {
+    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpXYLineRot);
+    rot = frags[0].line->rot;
+    for (i = 0; i < nFrags; ++i) {
+      frag0 = &frags[i];
+      col1 = 0;
+      for (j = 0; j < i; ++j) {
+       frag1 = &frags[j];
+       col2 = 0; // make gcc happy
+       switch (rot) {
+       case 0:
+         if (frag0->xMin >= frag1->xMax) {
+           col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
+                                frag1->line->col[frag1->start]) + 1;
+         } else {
+           for (k = frag1->start;
+                k < frag1->start + frag1->len &&
+                  frag0->xMin >= 0.5 * (frag1->line->edge[k] +
+                                        frag1->line->edge[k+1]);
+                ++k) ;
+           col2 = frag1->col +
+                  frag1->line->col[k] - frag1->line->col[frag1->start];
+         }
+         break;
+       case 1:
+         if (frag0->yMin >= frag1->yMax) {
+           col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
+                                frag1->line->col[frag1->start]) + 1;
+         } else {
+           for (k = frag1->start;
+                k < frag1->start + frag1->len &&
+                  frag0->yMin >= 0.5 * (frag1->line->edge[k] +
+                                        frag1->line->edge[k+1]);
+                ++k) ;
+           col2 = frag1->col +
+                  frag1->line->col[k] - frag1->line->col[frag1->start];
+         }
+         break;
+       case 2:
+         if (frag0->xMax <= frag1->xMin) {
+           col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
+                                frag1->line->col[frag1->start]) + 1;
+         } else {
+           for (k = frag1->start;
+                k < frag1->start + frag1->len &&
+                  frag0->xMax <= 0.5 * (frag1->line->edge[k] +
+                                        frag1->line->edge[k+1]);
+                ++k) ;
+           col2 = frag1->col +
+                  frag1->line->col[k] - frag1->line->col[frag1->start];
+         }
+         break;
+       case 3:
+         if (frag0->yMax <= frag1->yMin) {
+           col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
+                                frag1->line->col[frag1->start]) + 1;
+         } else {
+           for (k = frag1->start;
+                k < frag1->start + frag1->len &&
+                  frag0->yMax <= 0.5 * (frag1->line->edge[k] +
+                                        frag1->line->edge[k+1]);
+                ++k) ;
+           col2 = frag1->col +
+                  frag1->line->col[k] - frag1->line->col[frag1->start];
+         }
+         break;
+       }
+       if (col2 > col1) {
+         col1 = col2;
+       }
+      }
+      frag0->col = col1;
+    }
+
+  // the region includes text at different rotations -- use the
+  // globally assigned column numbers, offset by the minimum column
+  // number (i.e., shift everything over to column 0)
   } else {
-    pageWidth = pageHeight = 0;
+    col1 = frags[0].col;
+    for (i = 1; i < nFrags; ++i) {
+      if (frags[i].col < col1) {
+       col1 = frags[i].col;
+      }
+    }
+    for (i = 0; i < nFrags; ++i) {
+      frags[i].col -= col1;
+    }
   }
 }
 
-void TextPage::clear() {
-  TextWord *w1, *w2;
-  TextFlow *f1, *f2;
+int TextPage::dumpFragment(Unicode *text, int len, UnicodeMap *uMap,
+                          GString *s) {
+  char lre[8], rle[8], popdf[8], buf[8];
+  int lreLen, rleLen, popdfLen, n;
+  int nCols, i, j, k;
+
+  nCols = 0;
+
+  if (uMap->isUnicode()) {
+
+    lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
+    rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
+    popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));
+
+    if (primaryLR) {
+
+      i = 0;
+      while (i < len) {
+       // output a left-to-right section
+       for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ;
+       for (k = i; k < j; ++k) {
+         n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+         s->append(buf, n);
+         ++nCols;
+       }
+       i = j;
+       // output a right-to-left section
+       for (j = i; j < len && !unicodeTypeL(text[j]); ++j) ;
+       if (j > i) {
+         s->append(rle, rleLen);
+         for (k = j - 1; k >= i; --k) {
+           n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+           s->append(buf, n);
+           ++nCols;
+         }
+         s->append(popdf, popdfLen);
+         i = j;
+       }
+      }
+
+    } else {
+
+      s->append(rle, rleLen);
+      i = len - 1;
+      while (i >= 0) {
+       // output a right-to-left section
+       for (j = i; j >= 0 && !unicodeTypeL(text[j]); --j) ;
+       for (k = i; k > j; --k) {
+         n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+         s->append(buf, n);
+         ++nCols;
+       }
+       i = j;
+       // output a left-to-right section
+       for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ;
+       if (j < i) {
+         s->append(lre, lreLen);
+         for (k = j + 1; k <= i; ++k) {
+           n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+           s->append(buf, n);
+           ++nCols;
+         }
+         s->append(popdf, popdfLen);
+         i = j;
+       }
+      }
+      s->append(popdf, popdfLen);
 
-  if (curWord) {
-    delete curWord;
-    curWord = NULL;
-  }
-  if (words) {
-    for (w1 = words; w1; w1 = w2) {
-      w2 = w1->next;
-      delete w1;
     }
-  } else if (flows) {
-    for (f1 = flows; f1; f1 = f2) {
-      f2 = f1->next;
-      delete f1;
+
+  } else {
+    for (i = 0; i < len; ++i) {
+      n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+      s->append(buf, n);
+      nCols += n;
     }
   }
-  deleteGList(fonts, TextFontInfo);
-
-  curWord = NULL;
-  charPos = 0;
-  font = NULL;
-  fontSize = 0;
-  nest = 0;
-  nTinyChars = 0;
-  words = wordPtr = NULL;
-  lines = NULL;
-  flows = NULL;
-  fonts = new GList();
 
+  return nCols;
 }
 
+#if TEXTOUT_WORD_LIST
+TextWordList *TextPage::makeWordList(GBool physLayout) {
+  return new TextWordList(this, physLayout);
+}
+#endif
 
 //------------------------------------------------------------------------
 // TextOutputDev
@@ -2153,10 +3461,12 @@ void TextOutputDev::drawChar(GfxState *state, double x, double y,
 }
 
 GBool TextOutputDev::findText(Unicode *s, int len,
-                             GBool top, GBool bottom,
+                             GBool startAtTop, GBool stopAtBottom,
+                             GBool startAtLast, GBool stopAtLast,
                              double *xMin, double *yMin,
                              double *xMax, double *yMax) {
-  return text->findText(s, len, top, bottom, xMin, yMin, xMax, yMax);
+  return text->findText(s, len, startAtTop, stopAtBottom,
+                       startAtLast, stopAtLast, xMin, yMin, xMax, yMax);
 }
 
 GString *TextOutputDev::getText(double xMin, double yMin,
@@ -2170,4 +3480,8 @@ GBool TextOutputDev::findCharRange(int pos, int length,
   return text->findCharRange(pos, length, xMin, yMin, xMax, yMax);
 }
 
-
+#if TEXTOUT_WORD_LIST
+TextWordList *TextOutputDev::makeWordList() {
+  return text->makeWordList(physLayout);
+}
+#endif
index e0c22c2270277f2ec7020c9345f29a8e9384c64b..b501907b4a257868566c632fe01dee88e4de0179 100644 (file)
@@ -24,12 +24,12 @@ class GString;
 class GList;
 class GfxFont;
 class GfxState;
+class UnicodeMap;
 
 //------------------------------------------------------------------------
 
 typedef void (*TextOutputFunc)(void *stream, char *text, int len);
 
-
 //------------------------------------------------------------------------
 // TextFontInfo
 //------------------------------------------------------------------------
@@ -45,13 +45,9 @@ public:
 private:
 
   GfxFont *gfxFont;
-  double horizScaling;
-
-  double minSpaceWidth;                // min width for inter-word space, as a
-                               //   fraction of the font size
-  double maxSpaceWidth;                // max width for inter-word space, as a
-                               //   fraction of the font size
-
+#if TEXTOUT_WORD_LIST
+  GString *fontName;
+#endif
 
   friend class TextWord;
   friend class TextPage;
@@ -65,9 +61,8 @@ class TextWord {
 public:
 
   // Constructor.
-  TextWord(GfxState *state, double x0, double y0, int charPosA,
-          TextFontInfo *fontA, double fontSize);
-
+  TextWord(GfxState *state, int rotA, double x0, double y0,
+          int charPosA, TextFontInfo *fontA, double fontSize);
 
   // Destructor.
   ~TextWord();
@@ -76,19 +71,44 @@ public:
   void addChar(GfxState *state, double x, double y,
               double dx, double dy, Unicode u);
 
+  // Merge <word> onto the end of <this>.
+  void merge(TextWord *word);
+
+  // Compares <this> to <word>, returning -1 (<), 0 (=), or +1 (>),
+  // based on a primary-axis comparison, e.g., x ordering if rot=0.
+  int primaryCmp(TextWord *word);
+
+  // Return the distance along the primary axis between <this> and
+  // <word>.
+  double primaryDelta(TextWord *word);
+
+  static int cmpYX(const void *p1, const void *p2);
+
+#if TEXTOUT_WORD_LIST
+  int getLength() { return len; }
+  Unicode getChar(int idx) { return text[idx]; }
+  GString *getText();
+  GString *getFontName() { return font->fontName; }
+  void getColor(double *r, double *g, double *b)
+    { *r = colorR; *g = colorG; *b = colorB; }
+  void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
+    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+  int getCharPos() { return charPos; }
+  int getCharLen() { return charLen; }
+#endif
 
 private:
 
-  GBool xyBefore(TextWord *word2);
-  void merge(TextWord *word2);
-
+  int rot;                     // rotation, multiple of 90 degrees
+                               //   (0, 1, 2, or 3)
   double xMin, xMax;           // bounding box x coordinates
   double yMin, yMax;           // bounding box y coordinates
-  double yBase;                        // baseline y coordinate
+  double base;                 // baseline x or y coordinate
   Unicode *text;               // the text
-  double *xRight;              // right-hand x coord of each char
-  int len;                     // length of text and xRight
-  int size;                    // size of text and xRight arrays
+  double *edge;                        // "near" edge x or y coord of each char
+                               //   (plus one extra entry for the last char)
+  int len;                     // length of text and edge arrays
+  int size;                    // size of text and edge arrays
   int charPos;                  // character position (within content stream)
   int charLen;                  // number of content stream characters in
                                 //   this word
@@ -96,11 +116,49 @@ private:
   double fontSize;             // font size
   GBool spaceAfter;            // set if there is a space between this
                                //   word and the next word on the line
-  TextWord *next;              // next word in line (before lines are
-                               //   assembled: next word in xy order)
+  TextWord *next;              // next word in line
 
+#if TEXTOUT_WORD_LIST
+  double colorR,               // word color
+         colorG,
+         colorB;
+#endif
 
+  friend class TextPool;
   friend class TextLine;
+  friend class TextBlock;
+  friend class TextFlow;
+  friend class TextWordList;
+  friend class TextPage;
+};
+
+//------------------------------------------------------------------------
+// TextPool
+//------------------------------------------------------------------------
+
+class TextPool {
+public:
+
+  TextPool();
+  ~TextPool();
+
+  TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; }
+  void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; }
+
+  int getBaseIdx(double base);
+
+  void addWord(TextWord *word);
+
+private:
+
+  int minBaseIdx;              // min baseline bucket index
+  int maxBaseIdx;              // max baseline bucket index
+  TextWord **pool;             // array of linked lists, one for each
+                               //   baseline value (multiple of 4 pts)
+  TextWord *cursor;            // pointer to last-accessed word
+  int cursorBaseIdx;           // baseline bucket index of last-accessed word
+
+  friend class TextBlock;
   friend class TextPage;
 };
 
@@ -111,34 +169,53 @@ private:
 class TextLine {
 public:
 
-  TextLine();
+  TextLine(TextBlock *blkA, int rotA, double baseA);
   ~TextLine();
 
-private:
+  void addWord(TextWord *word);
+
+  // Return the distance along the primary axis between <this> and
+  // <line>.
+  double primaryDelta(TextLine *line);
+
+  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
+  // based on a primary-axis comparison, e.g., x ordering if rot=0.
+  int primaryCmp(TextLine *line);
+
+  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
+  // based on a secondary-axis comparison of the baselines, e.g., y
+  // ordering if rot=0.
+  int secondaryCmp(TextLine *line);
 
-  GBool yxBefore(TextLine *line2);
-  void merge(TextLine *line2);
+  int cmpYX(TextLine *line);
 
+  static int cmpXY(const void *p1, const void *p2);
+
+  void coalesce(UnicodeMap *uMap);
+
+private:
+
+  TextBlock *blk;              // parent block
+  int rot;                     // text rotation
   double xMin, xMax;           // bounding box x coordinates
   double yMin, yMax;           // bounding box y coordinates
-  double yBase;                        // primary baseline y coordinate
-  double xSpaceL, xSpaceR;     // whitespace to left and right of this line
-  TextFontInfo *font;          // primary font
-  double fontSize;             // primary font size
+  double base;                 // baseline x or y coordinate
   TextWord *words;             // words in this line
   TextWord *lastWord;          // last word in this line
   Unicode *text;               // Unicode text of the line, including
                                //   spaces between words
-  double *xRight;              // right-hand x coord of each Unicode char
+  double *edge;                        // "near" edge x or y coord of each char
+                               //   (plus one extra entry for the last char)
   int *col;                    // starting column number of each Unicode char
   int len;                     // number of Unicode chars
   int convertedLen;            // total number of converted characters
   GBool hyphenated;            // set if last char is a hyphen
-  TextLine *pageNext;          // next line on page
   TextLine *next;              // next line in block
-  TextLine *flowNext;          // next line in flow
 
+  friend class TextLineFrag;
   friend class TextBlock;
+  friend class TextFlow;
+  friend class TextWordList;
   friend class TextPage;
 };
 
@@ -149,25 +226,52 @@ private:
 class TextBlock {
 public:
 
-  TextBlock();
+  TextBlock(TextPage *pageA, int rotA);
   ~TextBlock();
 
-private:
+  void addWord(TextWord *word);
+
+  void coalesce(UnicodeMap *uMap);
+
+  // Update this block's priMin and priMax values, looking at <blk>.
+  void updatePriMinMax(TextBlock *blk);
+
+  static int cmpXYPrimaryRot(const void *p1, const void *p2);
 
-  GBool yxBefore(TextBlock *blk2);
-  void mergeRight(TextBlock *blk2);
-  void mergeBelow(TextBlock *blk2);
+  static int cmpYXPrimaryRot(const void *p1, const void *p2);
 
+  int primaryCmp(TextBlock *blk);
+
+  double secondaryDelta(TextBlock *blk);
+
+  // Returns true if <this> is below <blk>, relative to the page's
+  // primary rotation.
+  GBool isBelow(TextBlock *blk);
+
+private:
+
+  TextPage *page;              // the parent page
+  int rot;                     // text rotation
   double xMin, xMax;           // bounding box x coordinates
   double yMin, yMax;           // bounding box y coordinates
-  double xSpaceL, xSpaceR;     // whitespace to left and right of this block
-  double ySpaceT, ySpaceB;     // whitespace above and below this block
-  double maxFontSize;          // max primary font size
-  TextLine *lines;             // lines in block
-  TextBlock *next;             // next block in flow
-  TextBlock *stackNext;                // next block on traversal stack
+  double priMin, priMax;       // whitespace bounding box along primary axis
+
+  TextPool *pool;              // pool of words (used only until lines
+                               //   are built)
+  TextLine *lines;             // linked list of lines
+  TextLine *curLine;           // most recently added line
+  int nLines;                  // number of lines
+  int charCount;               // number of characters in the block
+  int col;                     // starting column
+  int nColumns;                        // number of columns in the block
 
+  TextBlock *next;
+  TextBlock *stackNext;
+
+  friend class TextLine;
+  friend class TextLineFrag;
   friend class TextFlow;
+  friend class TextWordList;
   friend class TextPage;
 };
 
@@ -178,20 +282,61 @@ private:
 class TextFlow {
 public:
 
-  TextFlow();
+  TextFlow(TextPage *pageA, TextBlock *blk);
   ~TextFlow();
 
+  // Add a block to the end of this flow.
+  void addBlock(TextBlock *blk);
+
+  // Returns true if <blk> fits below <prevBlk> in the flow, i.e., (1)
+  // it uses a font no larger than the last block added to the flow,
+  // and (2) it fits within the flow's [priMin, priMax] along the
+  // primary axis.
+  GBool blockFits(TextBlock *blk, TextBlock *prevBlk);
+
 private:
 
+  TextPage *page;              // the parent page
+  double xMin, xMax;           // bounding box x coordinates
   double yMin, yMax;           // bounding box y coordinates
-  double ySpaceT, ySpaceB;     // whitespace above and below this flow
+  double priMin, priMax;       // whitespace bounding box along primary axis
   TextBlock *blocks;           // blocks in flow
-  TextLine *lines;             // lines in flow
-  TextFlow *next;              // next flow on page
+  TextBlock *lastBlk;          // last block in this flow
+  TextFlow *next;
 
+  friend class TextWordList;
   friend class TextPage;
 };
 
+#if TEXTOUT_WORD_LIST
+
+//------------------------------------------------------------------------
+// TextWordList
+//------------------------------------------------------------------------
+
+class TextWordList {
+public:
+
+  // Build a flat word list, in content stream order (if
+  // text->rawOrder is true), physical layout order (if <physLayout>
+  // is true and text->rawOrder is false), or reading order (if both
+  // flags are false).
+  TextWordList(TextPage *text, GBool physLayout);
+
+  ~TextWordList();
+
+  // Return the number of words on the list.
+  int getLength();
+
+  // Return the <idx>th word from the list.
+  TextWord *get(int idx);
+
+private:
+
+  GList *words;
+};
+
+#endif // TEXTOUT_WORD_LIST
 
 //------------------------------------------------------------------------
 // TextPage
@@ -201,15 +346,17 @@ class TextPage {
 public:
 
   // Constructor.
-  TextPage(GBool rawOrder);
+  TextPage(GBool rawOrderA);
 
   // Destructor.
   ~TextPage();
 
+  // Start a new page.
+  void startPage(GfxState *state);
+
   // Update the current font.
   void updateFont(GfxState *state);
 
-
   // Begin a new word.
   void beginWord(GfxState *state, double x0, double y0);
 
@@ -224,17 +371,19 @@ public:
   // Add a word, sorting it into the list of words.
   void addWord(TextWord *word);
 
-
   // Coalesce strings that look like parts of the same line.
   void coalesce(GBool physLayout);
 
-  // Find a string.  If <top> is true, starts looking at top of page;
-  // otherwise starts looking at <xMin>,<yMin>.  If <bottom> is true,
-  // stops looking at bottom of page; otherwise stops looking at
-  // <xMax>,<yMax>.  If found, sets the text bounding rectangle and
-  // returns true; otherwise returns false.
+  // Find a string.  If <startAtTop> is true, starts looking at the
+  // top of the page; else if <startAtLast> is true, starts looking
+  // immediately after the last find result; else starts looking at
+  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
+  // bottom of the page; else if <stopAtLast> is true, stops looking
+  // just before the last find result; else stops looking at
+  // <xMax>,<yMax>.
   GBool findText(Unicode *s, int len,
-                GBool top, GBool bottom,
+                GBool startAtTop, GBool stopAtBottom,
+                GBool startAtLast, GBool stopAtLast,
                 double *xMin, double *yMin,
                 double *xMax, double *yMax);
 
@@ -253,18 +402,19 @@ public:
   void dump(void *outputStream, TextOutputFunc outputFunc,
            GBool physLayout);
 
-  // Start a new page.
-  void startPage(GfxState *state);
-
+#if TEXTOUT_WORD_LIST
+  // Build a flat word list, in content stream order (if
+  // this->rawOrder is true), physical layout order (if <physLayout>
+  // is true and this->rawOrder is false), or reading order (if both
+  // flags are false).
+  TextWordList *makeWordList(GBool physLayout);
+#endif
 
 private:
 
   void clear();
-  double lineFit(TextLine *line, TextWord *word, double *space);
-  GBool lineFit2(TextLine *line0, TextLine *line1);
-  GBool blockFit(TextBlock *blk, TextLine *line);
-  GBool blockFit2(TextBlock *blk0, TextBlock *blk1);
-  GBool flowFit(TextFlow *flow, TextBlock *blk);
+  void assignColumns(TextLineFrag *frags, int nFrags, int rot);
+  int dumpFragment(Unicode *text, int len, UnicodeMap *uMap, GString *s);
 
   GBool rawOrder;              // keep text in content stream order
 
@@ -272,22 +422,34 @@ private:
   TextWord *curWord;           // currently active string
   int charPos;                 // next character position (within content
                                //   stream)
-  TextFontInfo *font;          // current font
-  double fontSize;             // current font size
+  TextFontInfo *curFont;       // current font
+  double curFontSize;          // current font size
   int nest;                    // current nesting level (for Type 3 fonts)
   int nTinyChars;              // number of "tiny" chars seen so far
 
-  TextWord *words;             // words, in xy order (before they're
-                               //   sorted into lines)
-  TextWord *wordPtr;           // cursor for the word list
-
-  TextLine *lines;             // lines, in xy order
-  TextFlow *flows;             // flows, in reading order
+  TextPool *pools[4];          // a "pool" of TextWords for each rotation
+  TextFlow *flows;             // linked list of flows
+  TextBlock **blocks;          // array of blocks, in yx order
+  int nBlocks;                 // number of blocks
+  int primaryRot;              // primary rotation
+  GBool primaryLR;             // primary direction (true means L-to-R,
+                               //   false means R-to-L)
+  TextWord *rawWords;          // list of words, in raw order (only if
+                               //   rawOrder is set)
+  TextWord *rawLastWord;       // last word on rawWords list
 
   GList *fonts;                        // all font info objects used on this
                                //   page [TextFontInfo]
 
+  double lastFindXMin,         // coordinates of the last "find" result
+         lastFindYMin;
+  GBool haveLastFind;
 
+  friend class TextLine;
+  friend class TextLineFrag;
+  friend class TextBlock;
+  friend class TextFlow;
+  friend class TextWordList;
 };
 
 //------------------------------------------------------------------------
@@ -353,17 +515,18 @@ public:
                        double originX, double originY,
                        CharCode c, Unicode *u, int uLen);
 
-  //----- path painting
-
   //----- special access
 
-  // Find a string.  If <top> is true, starts looking at top of page;
-  // otherwise starts looking at <xMin>,<yMin>.  If <bottom> is true,
-  // stops looking at bottom of page; otherwise stops looking at
-  // <xMax>,<yMax>.  If found, sets the text bounding rectangle and
-  // returns true; otherwise returns false.
+  // Find a string.  If <startAtTop> is true, starts looking at the
+  // top of the page; else if <startAtLast> is true, starts looking
+  // immediately after the last find result; else starts looking at
+  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
+  // bottom of the page; else if <stopAtLast> is true, stops looking
+  // just before the last find result; else stops looking at
+  // <xMax>,<yMax>.
   GBool findText(Unicode *s, int len,
-                GBool top, GBool bottom,
+                GBool startAtTop, GBool stopAtBottom,
+                GBool startAtLast, GBool stopAtLast,
                 double *xMin, double *yMin,
                 double *xMax, double *yMax);
 
@@ -378,6 +541,13 @@ public:
                      double *xMin, double *yMin,
                      double *xMax, double *yMax);
 
+#if TEXTOUT_WORD_LIST
+  // Build a flat word list, in content stream order (if
+  // this->rawOrder is true), physical layout order (if
+  // this->physLayout is true and this->rawOrder is false), or reading
+  // order (if both flags are false).
+  TextWordList *makeWordList();
+#endif
 
 private:
 
@@ -390,7 +560,6 @@ private:
                                //   dumping text
   GBool rawOrder;              // keep text in content stream order
   GBool ok;                    // set up ok?
-
 };
 
 #endif
index e6284eb33bd25ccca0ec7a448dc89ff6fb17f286..300d802aa555ee01d706f3acd97fabdcbcd94dc6 100644 (file)
@@ -116,6 +116,9 @@ UnicodeMap::UnicodeMap(GString *encodingNameA) {
   eMaps = NULL;
   eMapsLen = 0;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
@@ -128,6 +131,9 @@ UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
   eMaps = NULL;
   eMapsLen = 0;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
@@ -139,6 +145,9 @@ UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
   eMaps = NULL;
   eMapsLen = 0;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 UnicodeMap::~UnicodeMap() {
@@ -149,14 +158,32 @@ UnicodeMap::~UnicodeMap() {
   if (eMaps) {
     gfree(eMaps);
   }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
 }
 
 void UnicodeMap::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
   ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
 }
 
 void UnicodeMap::decRefCnt() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
@@ -175,29 +202,28 @@ int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
 
   a = 0;
   b = len;
-  if (u < ranges[a].start) {
-    return 0;
-  }
-  // invariant: ranges[a].start <= u < ranges[b].start
-  while (b - a > 1) {
-    m = (a + b) / 2;
-    if (u >= ranges[m].start) {
-      a = m;
-    } else if (u < ranges[m].start) {
-      b = m;
-    }
-  }
-  if (u <= ranges[a].end) {
-    n = ranges[a].nBytes;
-    if (n > bufSize) {
-      return 0;
+  if (u >= ranges[a].start) {
+    // invariant: ranges[a].start <= u < ranges[b].start
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      if (u >= ranges[m].start) {
+       a = m;
+      } else if (u < ranges[m].start) {
+       b = m;
+      }
     }
-    code = ranges[a].code + (u - ranges[a].start);
-    for (i = n - 1; i >= 0; --i) {
-      buf[i] = (char)(code & 0xff);
-      code >>= 8;
+    if (u <= ranges[a].end) {
+      n = ranges[a].nBytes;
+      if (n > bufSize) {
+       return 0;
+      }
+      code = ranges[a].code + (u - ranges[a].start);
+      for (i = n - 1; i >= 0; --i) {
+       buf[i] = (char)(code & 0xff);
+       code >>= 8;
+      }
+      return n;
     }
-    return n;
   }
 
   for (i = 0; i < eMapsLen; ++i) {
index 24de28ce1637cc79a65227169cbe283afae35d4f..6fd4ed24fb0f2c9acdccb6cafecf24def21d84a0 100644 (file)
 #include "gtypes.h"
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 class GString;
 
 //------------------------------------------------------------------------
@@ -91,6 +95,9 @@ private:
   UnicodeMapExt *eMaps;                // (user)
   int eMapsLen;                        // (user)
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
index 2100355af0338a8194e38861452d41f8dffe1e7a..a156b5584dfcb2fcf554ee8ae4e918bae51488f2 100644 (file)
@@ -624,18 +624,20 @@ XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
   xOut = xOutA;
 
 #if HAVE_T1LIB_H
-  t1Engine = NULL;
   t1libControl = t1libControlA;
+  t1Engine = NULL;
+  t1FontFiles = NULL;
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
+  freetypeControl = freetypeControlA;
   ftEngine = NULL;
+  ftFontFiles = NULL;
 #endif
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
-  ttEngine = NULL;
-#endif
-#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
   freetypeControl = freetypeControlA;
+  ttEngine = NULL;
+  ttFontFiles = NULL;
 #endif
 
   clear();
@@ -718,25 +720,37 @@ void XOutputFontCache::delFonts() {
 
 #if HAVE_T1LIB_H
   // delete Type 1 font files
-  deleteGList(t1FontFiles, XOutputT1FontFile);
+  if (t1FontFiles) {
+    deleteGList(t1FontFiles, XOutputT1FontFile);
+    t1FontFiles = NULL;
+  }
   if (t1Engine) {
     delete t1Engine;
+    t1Engine = NULL;
   }
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   // delete FreeType font files
-  deleteGList(ftFontFiles, XOutputFTFontFile);
+  if (ftFontFiles) {
+    deleteGList(ftFontFiles, XOutputFTFontFile);
+    ftFontFiles = NULL;
+  }
   if (ftEngine) {
     delete ftEngine;
+    ftEngine = NULL;
   }
 #endif
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   // delete TrueType fonts
-  deleteGList(ttFontFiles, XOutputTTFontFile);
+  if (ttFontFiles) {
+    deleteGList(ttFontFiles, XOutputTTFontFile);
+    ttFontFiles = NULL;
+  }
   if (ttEngine) {
     delete ttEngine;
+    ttEngine = NULL;
   }
 #endif
 }
@@ -1088,12 +1102,16 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
     if (gfxFont->getType() == fontType1C) {
       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
        fclose(f);
+       unlink(fileName->getCString());
+       delete fileName;
        return NULL;
       }
       ff = new Type1CFontFile(fontBuf, fontLen);
       if (!ff->isOk()) {
        delete ff;
        gfree(fontBuf);
+       unlink(fileName->getCString());
+       delete fileName;
        return NULL;
       }
       ff->convertToType1(outputToFile, f);
@@ -1235,6 +1253,8 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
        gfxFont->getType() == fontCIDType2) {
       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
        fclose(f);
+       unlink(fileName->getCString());
+       delete fileName;
        return NULL;
       }
       ff = new TrueTypeFontFile(fontBuf, fontLen);
@@ -1304,6 +1324,11 @@ XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
   Ref *id;
   FTFontFile *fontFile;
   XOutputFont *font;
+  char *buf;
+  int len;
+  FILE *f;
+  TrueTypeFontFile *ff;
+  Gushort *codeToGID;
 
   // create the FreeType font file
   if (gfxFont->isCIDFont()) {
@@ -1316,10 +1341,27 @@ XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
       fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded);
     }
   } else {
+    if (!(f = fopen(fileName->getCString(), "rb"))) {
+      return NULL;
+    }
+    fseek(f, 0, SEEK_END);
+    len = (int)ftell(f);
+    fseek(f, 0, SEEK_SET);
+    buf = (char *)gmalloc(len);
+    if ((int)fread(buf, 1, len, f) != len) {
+      gfree(buf);
+      fclose(f);
+      return NULL;
+    }
+    fclose(f);
+    ff = new TrueTypeFontFile(buf, len);
+    codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
     fontFile = new FTFontFile(ftEngine, fileName->getCString(),
                              ((Gfx8BitFont *)gfxFont)->getEncoding(),
-                             ((Gfx8BitFont *)gfxFont)->getHasEncoding(),
-                             ((Gfx8BitFont *)gfxFont)->isSymbolic());
+                             codeToGID);
+    gfree(codeToGID);
+    delete ff;
+    gfree(buf);
   }
   if (!fontFile->isOk()) {
     error(-1, "Couldn't create FreeType font from '%s'",
@@ -1784,6 +1826,9 @@ XOutputDev::XOutputDev(Display *displayA, int screenNumA,
   nT3Fonts = 0;
   t3GlyphStack = NULL;
 
+  // no text outline clipping path
+  textClipPath = NULL;
+
   // empty state stack
   save = NULL;
 
@@ -1877,32 +1922,61 @@ void XOutputDev::endPage() {
 }
 
 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
-  double x1, y1, x2, y2, w;
+  double x1, y1, x2, y2;
+  LinkBorderStyle *borderStyle;
   GfxRGB rgb;
+  double *dash;
+  char dashList[20];
+  int dashLength;
   XPoint points[5];
-  int x, y;
+  int x, y, i;
 
-  link->getBorder(&x1, &y1, &x2, &y2, &w);
-  if (w > 0) {
-    rgb.r = 0;
-    rgb.g = 0;
-    rgb.b = 1;
+  link->getRect(&x1, &y1, &x2, &y2);
+  borderStyle = link->getBorderStyle();
+  if (borderStyle->getWidth() > 0) {
+    borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
     XSetForeground(display, strokeGC, findColor(&rgb));
-    XSetLineAttributes(display, strokeGC, xoutRound(w),
-                      LineSolid, CapRound, JoinRound);
-    cvtUserToDev(x1, y1, &x, &y);
-    points[0].x = points[4].x = x;
-    points[0].y = points[4].y = y;
-    cvtUserToDev(x2, y1, &x, &y);
-    points[1].x = x;
-    points[1].y = y;
-    cvtUserToDev(x2, y2, &x, &y);
-    points[2].x = x;
-    points[2].y = y;
-    cvtUserToDev(x1, y2, &x, &y);
-    points[3].x = x;
-    points[3].y = y;
-    XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
+    borderStyle->getDash(&dash, &dashLength);
+    if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
+      if (dashLength > 20) {
+       dashLength = 20;
+      }
+      for (i = 0; i < dashLength; ++i) {
+       if ((dashList[i] = xoutRound(dash[i])) == 0) {
+         dashList[i] = 1;
+       }
+      }
+      XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
+                        LineOnOffDash, CapButt, JoinMiter);
+      XSetDashes(display, strokeGC, 0, dashList, dashLength);
+    } else {
+      XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
+                        LineSolid, CapButt, JoinMiter);
+    }
+    if (borderStyle->getType() == linkBorderUnderlined) {
+      cvtUserToDev(x1, y1, &x, &y);
+      points[0].x = x;
+      points[0].y = y;
+      cvtUserToDev(x2, y1, &x, &y);
+      points[1].x = x;
+      points[1].y = y;
+      XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y,
+               points[1].x, points[1].y);
+    } else {
+      cvtUserToDev(x1, y1, &x, &y);
+      points[0].x = points[4].x = x;
+      points[0].y = points[4].y = y;
+      cvtUserToDev(x2, y1, &x, &y);
+      points[1].x = x;
+      points[1].y = y;
+      cvtUserToDev(x2, y2, &x, &y);
+      points[2].x = x;
+      points[2].y = y;
+      cvtUserToDev(x1, y2, &x, &y);
+      points[3].x = x;
+      points[3].y = y;
+      XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
+    }
   }
 }
 
@@ -2100,7 +2174,8 @@ void XOutputDev::stroke(GfxState *state) {
   int n, size, numPoints, i, j;
 
   // transform points
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
+  n = convertPath(state, state->getPath(),
+                 &points, &size, &numPoints, &lengths, gFalse);
 
   // draw each subpath
   j = 0;
@@ -2143,7 +2218,8 @@ void XOutputDev::doFill(GfxState *state, int rule) {
   XSetFillRule(display, fillGC, rule);
 
   // transform points, build separate polygons
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
+  n = convertPath(state, state->getPath(),
+                 &points, &size, &numPoints, &lengths, gTrue);
 
   // fill them
   j = 0;
@@ -2165,41 +2241,109 @@ void XOutputDev::doFill(GfxState *state, int rule) {
 }
 
 void XOutputDev::clip(GfxState *state) {
-  doClip(state, WindingRule);
+  doClip(state, state->getPath(), WindingRule);
 }
 
 void XOutputDev::eoClip(GfxState *state) {
-  doClip(state, EvenOddRule);
+  doClip(state, state->getPath(), EvenOddRule);
 }
 
-void XOutputDev::doClip(GfxState *state, int rule) {
+void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) {
+  GfxSubpath *subpath;
   Region region, region2;
+  XPoint rect[5];
   XPoint *points;
   int *lengths;
+  double x0, y0, x1, y1, x2, y2, x3, y3;
+  GBool gotRect;
   int n, size, numPoints, i, j;
 
-  // transform points, build separate polygons
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
-
-  // construct union of subpath regions
-  // (XPolygonRegion chokes if there aren't at least three points --
-  // this happens if the PDF file does moveto/closepath/clip, which
-  // sets an empty clipping region)
-  if (lengths[0] > 2) {
-    region = XPolygonRegion(points, lengths[0], rule);
-  } else {
-    region = XCreateRegion();
+  // special case for rectangular clipping paths -- this is a common
+  // case, and we want to make sure not to clip an extra pixel on the
+  // right and bottom edges due to the difference between the PDF and
+  // X rendering models
+  gotRect = gFalse;
+  if (path->getNumSubpaths() == 1) {
+    subpath = path->getSubpath(0);
+    if ((subpath->isClosed() && subpath->getNumPoints() == 5) ||
+       (!subpath->isClosed() && subpath->getNumPoints() == 4)) {
+      state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0);
+      state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1);
+      state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2);
+      state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3);
+      if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 &&
+         fabs(y0-y3) < 1 && fabs(y1-y2) < 1) {
+       if (x0 < x2) {
+         rect[0].x = rect[1].x = rect[4].x = (int)floor(x0);
+         rect[2].x = rect[3].x = (int)floor(x2) + 1;
+       } else {
+         rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1;
+         rect[2].x = rect[3].x = (int)floor(x2);
+       }
+       if (y0 < y1) {
+         rect[0].y = rect[3].y = rect[4].y = (int)floor(y0);
+         rect[1].y = rect[2].y = (int)floor(y1) + 1;
+       } else {
+         rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1;
+         rect[1].y = rect[2].y = (int)floor(y1);
+       }
+       gotRect = gTrue;
+      } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 &&
+                fabs(y0-y1) < 1 && fabs(y2-y3) < 1) {
+       if (x0 < x1) {
+         rect[0].x = rect[3].x = rect[4].x = (int)floor(x0);
+         rect[1].x = rect[2].x = (int)floor(x1) + 1;
+       } else {
+         rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1;
+         rect[1].x = rect[2].x = (int)floor(x1);
+       }
+       if (y0 < y2) {
+         rect[0].y = rect[1].y = rect[4].y = (int)floor(y0);
+         rect[2].y = rect[3].y = (int)floor(y2) + 1;
+       } else {
+         rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1;
+         rect[2].y = rect[3].y = (int)floor(y2);
+       }
+       gotRect = gTrue;
+      }
+    }
   }
-  j = lengths[0] + 1;
-  for (i = 1; i < n; ++i) {
-    if (lengths[i] > 2) {
-      region2 = XPolygonRegion(points + j, lengths[i], rule);
+
+  if (gotRect) {
+    region = XPolygonRegion(rect, 5, EvenOddRule);
+
+  } else {
+    // transform points, build separate polygons
+    n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue);
+
+    // construct union of subpath regions
+    // (XPolygonRegion chokes if there aren't at least three points --
+    // this happens if the PDF file does moveto/closepath/clip, which
+    // sets an empty clipping region)
+    if (lengths[0] > 2) {
+      region = XPolygonRegion(points, lengths[0], rule);
     } else {
-      region2 = XCreateRegion();
+      region = XCreateRegion();
+    }
+    j = lengths[0] + 1;
+    for (i = 1; i < n; ++i) {
+      if (lengths[i] > 2) {
+       region2 = XPolygonRegion(points + j, lengths[i], rule);
+      } else {
+       region2 = XCreateRegion();
+      }
+      XUnionRegion(region2, region, region);
+      XDestroyRegion(region2);
+      j += lengths[i] + 1;
+    }
+
+    // free points and lengths arrays
+    if (points != tmpPoints) {
+      gfree(points);
+    }
+    if (lengths != tmpLengths) {
+      gfree(lengths);
     }
-    XUnionRegion(region2, region, region);
-    XDestroyRegion(region2);
-    j += lengths[i] + 1;
   }
 
   // intersect region with clipping region
@@ -2207,12 +2351,6 @@ void XOutputDev::doClip(GfxState *state, int rule) {
   XDestroyRegion(region);
   XSetRegion(display, strokeGC, clipRegion);
   XSetRegion(display, fillGC, clipRegion);
-
-  // free points and lengths arrays
-  if (points != tmpPoints)
-    gfree(points);
-  if (lengths != tmpLengths)
-    gfree(lengths);
 }
 
 //
@@ -2224,15 +2362,14 @@ void XOutputDev::doClip(GfxState *state, int rule) {
 // Then it connects subaths within a single compound polygon to a single
 // point so that X can fill the polygon (sort of).
 //
-int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
+int XOutputDev::convertPath(GfxState *state, GfxPath *path,
+                           XPoint **points, int *size,
                            int *numPoints, int **lengths, GBool fillHack) {
-  GfxPath *path;
   BoundingRect *rects;
   BoundingRect rect;
   int n, i, ii, j, k, k0;
 
   // get path and number of subpaths
-  path = state->getPath();
   n = path->getNumSubpaths();
 
   // allocate lengths array
@@ -2294,15 +2431,15 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
   // kludge: munge any points that are *way* out of bounds - these can
   // crash certain (buggy) X servers
   for (i = 0; i < *numPoints; ++i) {
-    if ((*points)[i].x < -pixmapW) {
-      (*points)[i].x = -pixmapW;
-    } else if ((*points)[i].x > 2 * pixmapW) {
-      (*points)[i].x = 2 * pixmapW;
+    if ((*points)[i].x < -4 * pixmapW) {
+      (*points)[i].x = -4 * pixmapW;
+    } else if ((*points)[i].x > 4 * pixmapW) {
+      (*points)[i].x = 4 * pixmapW;
     }
     if ((*points)[i].y < -pixmapH) {
-      (*points)[i].y = -pixmapH;
-    } else if ((*points)[i].y > 2 * pixmapH) {
-      (*points)[i].y = 2 * pixmapH;
+      (*points)[i].y = -4 * pixmapH;
+    } else if ((*points)[i].y > 4 * pixmapH) {
+      (*points)[i].y = 4 * pixmapH;
     }
   }
 
@@ -2528,7 +2665,7 @@ void XOutputDev::drawChar(GfxState *state, double x, double y,
 
   // check for invisible text -- this is used by Acrobat Capture
   render = state->getRender();
-  if ((render & 3) == 3) {
+  if (render == 3) {
     return;
   }
 
@@ -2577,11 +2714,22 @@ void XOutputDev::drawChar(GfxState *state, double x, double y,
     }
   }
 
-#if 0 //~ unimplemented: clipping to char path
   // clip
   if (render & 4) {
+    if (font->hasGetCharPath()) {
+      saveCurX = state->getCurX();
+      saveCurY = state->getCurY();
+      font->getCharPath(state, code, u, uLen);
+      state->getPath()->offset(x1, y1);
+      if (textClipPath) {
+       textClipPath->append(state->getPath());
+      } else {
+       textClipPath = state->getPath()->copy();
+      }
+      state->clearPath();
+      state->moveTo(saveCurX, saveCurY);
+    }
   }
-#endif
 }
 
 GBool XOutputDev::beginType3Char(GfxState *state,
@@ -2991,7 +3139,23 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy,
                -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
 }
 
-inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
+void XOutputDev::endTextObject(GfxState *state) {
+  double *ctm;
+  double saveCTM[6];
+
+  if (textClipPath) {
+    ctm = state->getCTM();
+    memcpy(saveCTM, ctm, 6 * sizeof(double));
+    state->setCTM(1, 0, 0, 1, 0, 0);
+    doClip(state, textClipPath, WindingRule);
+    state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
+                 saveCTM[4], saveCTM[5]);
+    delete textClipPath;
+    textClipPath = NULL;
+  }
+}
+
+inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) {
   double gray;
   int r, g, b;
   Gulong pixel;
@@ -3003,30 +3167,26 @@ inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
     pixel = ((Gulong)r << rShift) +
             ((Gulong)g << gShift) +
             ((Gulong)b << bShift);
-    err->r = x->r - (double)r / rMul;
-    err->g = x->g - (double)g / gMul;
-    err->b = x->b - (double)b / bMul;
+    actual->r = (double)r / rMul;
+    actual->g = (double)g / gMul;
+    actual->b = (double)b / bMul;
   } else if (numColors == 1) {
     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
     if (gray < 0.5) {
       pixel = colors[0];
-      err->r = x->r;
-      err->g = x->g;
-      err->b = x->b;
+      actual->r = actual->g = actual->b = 0;
     } else {
       pixel = colors[1];
-      err->r = x->r - 1;
-      err->g = x->g - 1;
-      err->b = x->b - 1;
+      actual->r = actual->g = actual->b = 1;
     }
   } else {
     r = xoutRound(x->r * (numColors - 1));
     g = xoutRound(x->g * (numColors - 1));
     b = xoutRound(x->b * (numColors - 1));
     pixel = colors[(r * numColors + g) * numColors + b];
-    err->r = x->r - (double)r / (numColors - 1);
-    err->g = x->g - (double)g / (numColors - 1); 
-    err->b = x->b - (double)b / (numColors - 1);
+    actual->r = (double)r / (numColors - 1);
+    actual->g = (double)g / (numColors - 1); 
+    actual->b = (double)b / (numColors - 1);
   }
   return pixel;
 }
@@ -3380,14 +3540,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   int yp, yq, yt, yStep, lastYStep;
   int xp, xq, xt, xStep, xSrc;
   GfxRGB *pixBuf;
-  Guchar *alphaBuf;
+  Guchar *pixBuf1, *alphaBuf;
   Guchar pixBuf2[gfxColorMaxComps];
-  GfxRGB color2, err, errRight;
-  GfxRGB *errDown;
+  GfxRGB color2, color3, actual, err, errRight;
+  GfxRGB *errDown0, *errDown1, *errDownTmp;
   double r0, g0, b0, alpha, mul;
   Gulong pix;
   GfxRGB *p;
-  Guchar *q, *p2;
+  Guchar *q, *p1, *p2;
   GBool oneBitMode;
   GfxRGB oneBitRGB[2];
   int x, y, x1, y1, x2, y2;
@@ -3397,6 +3557,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   nComps = colorMap->getNumPixelComps();
   nVals = width * nComps;
   nBits = colorMap->getBits();
+  oneBitMode = nComps == 1 && nBits == 1 && !maskColors;
   dither = nComps > 1 || nBits > 1;
 
   // get CTM, check for singular matrix
@@ -3533,7 +3694,13 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   xq = width % scaledWidth;
 
   // allocate pixel buffer
-  pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
+  if (oneBitMode) {
+    pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
+    pixBuf = NULL;
+  } else {
+    pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
+    pixBuf1 = NULL;
+  }
   if (maskColors) {
     alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
   } else {
@@ -3556,16 +3723,18 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   // allocate error diffusion accumulators
   if (dither) {
-    errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB));
-    for (j = 0; j < bw; ++j) {
-      errDown[j].r = errDown[j].g = errDown[j].b = 0;
+    errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
+    errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
+    for (j = 0; j < scaledWidth + 2; ++j) {
+      errDown0[j].r = errDown0[j].g = errDown0[j].b = 0;
+      errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
     }
   } else {
-    errDown = NULL;
+    errDown0 = errDown1 = NULL;
   }
 
   // optimize the one-bit-deep image case
-  if ((oneBitMode = nComps == 1 && nBits == 1)) {
+  if (oneBitMode) {
     pixBuf2[0] = 0;
     colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
     pixBuf2[0] = 1;
@@ -3582,8 +3751,16 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   for (y = 0; y < scaledHeight; ++y) {
 
-    // initialize error diffusion accumulator
-    errRight.r = errRight.g = errRight.b = 0;
+    // initialize error diffusion accumulators
+    if (dither) {
+      errDownTmp = errDown0;
+      errDown0 = errDown1;
+      errDown1 = errDownTmp;
+      for (j = 0; j < scaledWidth + 2; ++j) {
+       errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
+      }
+      errRight.r = errRight.g = errRight.b = 0;
+    }
 
     // y scale Bresenham
     yStep = yp;
@@ -3596,30 +3773,35 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
     // read row(s) from image
     n = (yp > 0) ? yStep : lastYStep;
     if (n > 0) {
-      p = pixBuf;
-      q = alphaBuf;
-      for (i = 0; i < n; ++i) {
-        p2 = imgStr->getLine();
-        for (j = 0; j < width; ++j) {
-          if (oneBitMode) {
-            *p = oneBitRGB[*p2];
-          } else {
+      if (oneBitMode) {
+       p1 = pixBuf1;
+       for (i = 0; i < n; ++i) {
+         p2 = imgStr->getLine();
+         memcpy(p1, p2, width);
+         p1 += width;
+       }
+      } else {
+       p = pixBuf;
+       q = alphaBuf;
+       for (i = 0; i < n; ++i) {
+         p2 = imgStr->getLine();
+         for (j = 0; j < width; ++j) {
             colorMap->getRGB(p2, p);
-          }
-          ++p;
-          if (q) {
-            *q = 1;
-            for (k = 0; k < nComps; ++k) {
-              if (p2[k] < maskColors[2*k] ||
-                  p2[k] > maskColors[2*k]) {
-                *q = 0;
-                break;
-              }
-            }
-            ++q;
-          }
-          p2 += nComps;
-        }
+           ++p;
+           if (q) {
+             *q = 1;
+             for (k = 0; k < nComps; ++k) {
+               if (p2[k] < maskColors[2*k] ||
+                   p2[k] > maskColors[2*k+1]) {
+                 *q = 0;
+                 break;
+               }
+             }
+             ++q;
+           }
+           p2 += nComps;
+         }
+       }
       }
     }
     lastYStep = yStep;
@@ -3657,60 +3839,94 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
       // x and y scaling operations
       n = yStep > 0 ? yStep : 1;
       m = xStep > 0 ? xStep : 1;
-      p = pixBuf + xSrc;
-      r0 = g0 = b0 = 0;
-      q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
-      alpha = 0;
-      for (i = 0; i < n; ++i) {
-       for (j = 0; j < m; ++j) {
-         r0 += p->r;
-         g0 += p->g;
-         b0 += p->b;
-         ++p;
-         if (q) {
-           alpha += *q++;
+      if (oneBitMode) {
+       p1 = pixBuf1 + xSrc;
+       k = 0;
+       for (i = 0; i < n; ++i) {
+         for (j = 0; j < m; ++j) {
+           k += *p1++;
          }
+         p1 += width - m;
        }
-       p += width - m;
+       mul = (double)k / (double)(n * m);
+       r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r;
+       g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g;
+       b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b;
+       alpha = 0;
+      } else {
+       p = pixBuf + xSrc;
+       q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
+       alpha = 0;
+       r0 = g0 = b0 = 0;
+       for (i = 0; i < n; ++i) {
+         for (j = 0; j < m; ++j) {
+           r0 += p->r;
+           g0 += p->g;
+           b0 += p->b;
+           ++p;
+           if (q) {
+             alpha += *q++;
+           }
+         }
+         p += width - m;
+       }
+       mul = 1 / (double)(n * m);
+       r0 *= mul;
+       g0 *= mul;
+       b0 *= mul;
+       alpha *= mul;
       }
-      mul = 1 / (double)(n * m);
-      r0 *= mul;
-      g0 *= mul;
-      b0 *= mul;
-      alpha *= mul;
 
       // x scale Bresenham
       xSrc += xStep;
 
       // compute pixel
       if (dither) {
-       color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r;
+       color2.r = r0 + errRight.r + errDown0[x + 1].r;
        if (color2.r > 1) {
-         color2.r = 1;
+         color3.r = 1;
        } else if (color2.r < 0) {
-         color2.r = 0;
+         color3.r = 0;
+       } else {
+         color3.r = color2.r;
        }
-       color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g;
+       color2.g = g0 + errRight.g + errDown0[x + 1].g;
        if (color2.g > 1) {
-         color2.g = 1;
+         color3.g = 1;
        } else if (color2.g < 0) {
-         color2.g = 0;
+         color3.g = 0;
+       } else {
+         color3.g = color2.g;
        }
-       color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b;
+       color2.b = b0 + errRight.b + errDown0[x + 1].b;
        if (color2.b > 1) {
-         color2.b = 1;
+         color3.b = 1;
        } else if (color2.b < 0) {
-         color2.b = 0;
+         color3.b = 0;
+       } else {
+         color3.b = color2.b;
        }
-       pix = findColor(&color2, &err);
-       errRight.r = errDown[tx + x2 - bx0].r = err.r / 2;
-       errRight.g = errDown[tx + x2 - bx0].g = err.g / 2;
-       errRight.b = errDown[tx + x2 - bx0].b = err.b / 2;
+       pix = findColor(&color3, &actual);
+       err.r = (color2.r - actual.r) / 16;
+       err.g = (color2.g - actual.g) / 16;
+       err.b = (color2.b - actual.b) / 16;
+       errRight.r = 7 * err.r;
+       errRight.g = 7 * err.g;
+       errRight.b = 7 * err.b;
+       errDown1[x].r += 3 * err.r;
+       errDown1[x].g += 3 * err.g;
+       errDown1[x].b += 3 * err.b;
+       errDown1[x + 1].r += 5 * err.r;
+       errDown1[x + 1].g += 5 * err.g;
+       errDown1[x + 1].b += 5 * err.b;
+       errDown1[x + 2].r = err.r;
+       errDown1[x + 2].g = err.g;
+       errDown1[x + 2].b = err.b;
       } else {
        color2.r = r0;
        color2.g = g0;
        color2.b = b0;
-       pix = findColor(&color2, &err);
+       pix = findColor(&color2, &actual);
       }
 
       // set pixel
@@ -3726,25 +3942,35 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   // free memory
   delete imgStr;
-  gfree(pixBuf);
+  if (oneBitMode) {
+    gfree(pixBuf1);
+  } else {
+    gfree(pixBuf);
+  }
   if (maskColors) {
     gfree(alphaBuf);
   }
   gfree(image->data);
   image->data = NULL;
   XDestroyImage(image);
-  gfree(errDown);
+  gfree(errDown0);
+  gfree(errDown1);
 }
 
-GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom,
-                          int *xMin, int *yMin, int *xMax, int *yMax) {
+GBool XOutputDev::findText(Unicode *s, int len,
+                          GBool startAtTop, GBool stopAtBottom,
+                          GBool startAtLast, GBool stopAtLast,
+                          int *xMin, int *yMin,
+                          int *xMax, int *yMax) {
   double xMin1, yMin1, xMax1, yMax1;
   
   xMin1 = (double)*xMin;
   yMin1 = (double)*yMin;
   xMax1 = (double)*xMax;
   yMax1 = (double)*yMax;
-  if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
+  if (text->findText(s, len, startAtTop, stopAtBottom,
+                    startAtLast, stopAtLast,
+                    &xMin1, &yMin1, &xMax1, &yMax1)) {
     *xMin = xoutRound(xMin1);
     *xMax = xoutRound(xMax1);
     *yMin = xoutRound(yMin1);
index 7a6544dd6fd97aa447862725407972c3b6dc13d9..3efa1dd0b2a9916202cf18ecd34f3a86f57c96df 100644 (file)
@@ -28,6 +28,7 @@ class GString;
 class GList;
 struct GfxRGB;
 class GfxFont;
+class GfxPath;
 class GfxSubpath;
 class TextPage;
 class XOutputFontCache;
@@ -564,6 +565,7 @@ public:
   virtual GBool beginType3Char(GfxState *state,
                               CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state);
+  virtual void endTextObject(GfxState *state);
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
@@ -582,14 +584,19 @@ public:
 
   // Called to indicate that a new PDF document has been loaded.
   void startDoc(XRef *xrefA);
-
-  // Find a string.  If <top> is true, starts looking at <xMin>,<yMin>;
-  // otherwise starts looking at top of page.  If <bottom> is true,
-  // stops looking at <xMax>,<yMax>; otherwise stops looking at bottom
-  // of page.  If found, sets the text bounding rectange and returns
-  // true; otherwise returns false.
-  GBool findText(Unicode *s, int len, GBool top, GBool bottom,
-                int *xMin, int *yMin, int *xMax, int *yMax);
+  // Find a string.  If <startAtTop> is true, starts looking at the
+  // top of the page; else if <startAtLast> is true, starts looking
+  // immediately after the last find result; else starts looking at
+  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
+  // bottom of the page; else if <stopAtLast> is true, stops looking
+  // just before the last find result; else stops looking at
+  // <xMax>,<yMax>.
+  GBool findText(Unicode *s, int len,
+                GBool startAtTop, GBool stopAtBottom,
+                GBool startAtLast, GBool stopAtLast,
+                int *xMin, int *yMin,
+                int *xMax, int *yMax);
 
   // Get the text which is inside the specified rectangle.
   GString *getText(int xMin, int yMin, int xMax, int yMax);
@@ -647,14 +654,16 @@ private:
     t3FontCache[xOutT3FontCacheSize];
   int nT3Fonts;                        // number of valid entries in t3FontCache
   T3GlyphStack *t3GlyphStack;  // Type 3 glyph context stack
+  GfxPath *textClipPath;       // text outline clipping path
   XOutputState *save;          // stack of saved states
 
   TextPage *text;              // text from the current page
 
   void updateLineAttrs(GfxState *state, GBool updateDash);
   void doFill(GfxState *state, int rule);
-  void doClip(GfxState *state, int rule);
-  int convertPath(GfxState *state, XPoint **points, int *size,
+  void doClip(GfxState *state, GfxPath *path, int rule);
+  int convertPath(GfxState *state, GfxPath *path,
+                 XPoint **points, int *size,
                  int *numPoints, int **lengths, GBool fillHack);
   void convertSubpath(GfxState *state, GfxSubpath *subpath,
                      XPoint **points, int *size, int *n);
@@ -665,7 +674,7 @@ private:
   void drawType3Glyph(T3FontCache *t3Font,
                      T3FontCacheTag *tag, Guchar *data,
                      double x, double y, GfxRGB *color);
-  Gulong findColor(GfxRGB *x, GfxRGB *err);
+  Gulong findColor(GfxRGB *x, GfxRGB *actual);
 };
 
 #endif
index 56cb131c00251cd0fd5bf16379f7a2d8de4464ba..eca638df76177c789ce73341f268cb1afd119b3e 100644 (file)
@@ -134,19 +134,22 @@ Guint XRef::readTrailer() {
   // read last xrefSearchSize bytes
   str->setPos(xrefSearchSize, -1);
   for (n = 0; n < xrefSearchSize; ++n) {
-    if ((c = str->getChar()) == EOF)
+    if ((c = str->getChar()) == EOF) {
       break;
+    }
     buf[n] = c;
   }
   buf[n] = '\0';
 
   // find startxref
   for (i = n - 9; i >= 0; --i) {
-    if (!strncmp(&buf[i], "startxref", 9))
+    if (!strncmp(&buf[i], "startxref", 9)) {
       break;
+    }
+  }
+  if (i < 0) {
+    goto err1;
   }
-  if (i < 0)
-    return 0;
   for (p = &buf[i+9]; isspace(*p); ++p) ;
   pos = lastXRefPos = strToUnsigned(p);
 
@@ -154,20 +157,24 @@ Guint XRef::readTrailer() {
   // (NB: we can't just use the trailer dict at the end of the file --
   // this won't work for linearized files.)
   str->setPos(start + pos);
-  for (i = 0; i < 4; ++i)
+  for (i = 0; i < 4; ++i) {
     buf[i] = str->getChar();
-  if (strncmp(buf, "xref", 4))
-    return 0;
+  }
+  if (strncmp(buf, "xref", 4)) {
+    goto err1;
+  }
   pos1 = pos + 4;
   while (1) {
     str->setPos(start + pos1);
     for (i = 0; i < 35; ++i) {
-      if ((c = str->getChar()) == EOF)
-       return 0;
+      if ((c = str->getChar()) == EOF) {
+       goto err1;
+      }
       buf[i] = c;
     }
-    if (!strncmp(buf, "trailer", 7))
+    if (!strncmp(buf, "trailer", 7)) {
       break;
+    }
     p = buf;
     while (isspace(*p)) ++p;
     while ('0' <= *p && *p <= '9') ++p;
@@ -175,8 +182,9 @@ Guint XRef::readTrailer() {
     n = atoi(p);
     while ('0' <= *p && *p <= '9') ++p;
     while (isspace(*p)) ++p;
-    if (p == buf)
-      return 0;
+    if (p == buf) {
+      goto err1;
+    }
     pos1 += (p - buf) + n * 20;
   }
   pos1 += 7;
@@ -189,26 +197,36 @@ Guint XRef::readTrailer() {
   parser->getObj(&trailerDict);
   if (trailerDict.isDict()) {
     trailerDict.dictLookupNF("Size", &obj);
-    if (obj.isInt())
+    if (obj.isInt()) {
       size = obj.getInt();
-    else
-      pos = 0;
+    } else {
+      goto err3;
+    }
     obj.free();
     trailerDict.dictLookupNF("Root", &obj);
     if (obj.isRef()) {
       rootNum = obj.getRefNum();
       rootGen = obj.getRefGen();
     } else {
-      pos = 0;
+      goto err3;
     }
     obj.free();
   } else {
-    pos = 0;
+    goto err2;
   }
   delete parser;
 
   // return first xref position
   return pos;
+
+ err3:
+  obj.free();
+ err2:
+  trailerDict.free();
+  delete parser;
+ err1:
+  size = 0;
+  return 0;
 }
 
 // Read an xref table and the prev pointer from the trailer.
@@ -266,7 +284,7 @@ GBool XRef::readXRef(Guint *pos) {
     // check for buggy PDF files with an incorrect (too small) xref
     // table size
     if (first + n > size) {
-      newSize = size + 256;
+      newSize = first + n;
       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
       for (i = size; i < newSize; ++i) {
        entries[i].offset = 0xffffffff;
@@ -373,12 +391,14 @@ GBool XRef::constructXRef() {
 
     // got trailer dictionary
     if (!strncmp(p, "trailer", 7)) {
+      gotRoot = gFalse;
       obj.initNull();
       parser = new Parser(NULL,
                 new Lexer(NULL,
                   str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
-      if (!trailerDict.isNone())
+      if (!trailerDict.isNone()) {
        trailerDict.free();
+      }
       parser->getObj(&trailerDict);
       if (trailerDict.isDict()) {
        trailerDict.dictLookupNF("Root", &obj);
@@ -389,7 +409,7 @@ GBool XRef::constructXRef() {
        }
        obj.free();
       } else {
-       pos = 0;
+       trailerDict.free();
       }
       delete parser;
 
@@ -457,6 +477,8 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
   GBool encrypted1;
   GBool ret;
 
+  keyLength = 0;
+  encVersion = encRevision = 0;
   ret = gFalse;
 
   permFlags = defPermFlags;
@@ -551,38 +573,34 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
 
 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToChange(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
+#else
   return gTrue;
+#endif
 }
 
 Object *XRef::fetch(int num, int gen, Object *obj) {
index f3a1b488df2be7b3c95a6e02b8a77b93e1498c42..2fcd1a8543e52b397df7f68fc72df55ccdf32c4f 100644 (file)
@@ -40,8 +40,8 @@ static void scanFont(GfxFont *font, PDFDoc *doc);
 
 static int firstPage = 1;
 static int lastPage = 0;
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
 static GBool printHelp = gFalse;
@@ -91,10 +91,10 @@ int main(int argc, char *argv[]) {
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
   if (!ok || argc != 2 || printVersion || printHelp) {
-    fprintf(stderr, "pdfinfo version %s\n", xpdfVersion);
+    fprintf(stderr, "pdffonts version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
     if (!printVersion) {
-      printUsage("pdfinfo", "<PDF-file>", argDesc);
+      printUsage("pdffonts", "<PDF-file>", argDesc);
     }
     goto err0;
   }
@@ -104,12 +104,12 @@ int main(int argc, char *argv[]) {
   globalParams = new GlobalParams(cfgFileName);
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -176,22 +176,33 @@ int main(int argc, char *argv[]) {
 }
 
 static void scanFonts(Dict *resDict, PDFDoc *doc) {
+  Object obj1, obj2, xObjDict, xObj, resObj;
+  Ref r;
   GfxFontDict *gfxFontDict;
   GfxFont *font;
-  Object fontDict, xObjDict, xObj, resObj;
   int i;
 
   // scan the fonts in this resource dictionary
-  resDict->lookup("Font", &fontDict);
-  if (fontDict.isDict()) {
-    gfxFontDict = new GfxFontDict(doc->getXRef(), fontDict.getDict());
+  gfxFontDict = NULL;
+  resDict->lookupNF("Font", &obj1);
+  if (obj1.isRef()) {
+    obj1.fetch(doc->getXRef(), &obj2);
+    if (obj2.isDict()) {
+      r = obj1.getRef();
+      gfxFontDict = new GfxFontDict(doc->getXRef(), &r, obj2.getDict());
+    }
+    obj2.free();
+  } else if (obj1.isDict()) {
+    gfxFontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+  }
+  if (gfxFontDict) {
     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
       font = gfxFontDict->getFont(i);
       scanFont(font, doc);
     }
     delete gfxFontDict;
   }
-  fontDict.free();
+  obj1.free();
 
   // recursively scan any resource dictionaries in objects in this
   // resource dictionary
@@ -228,17 +239,12 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
     }
   }
 
-  // get the original font name -- the GfxFont class munges substitute
-  // Base-14 font names into proper form, so this code grabs the
-  // original name from the font dictionary; also look for a ToUnicode
-  // map
-  name = NULL;
+  // font name
+  name = font->getOrigName();
+
+  // look for a ToUnicode map
   hasToUnicode = gFalse;
   if (doc->getXRef()->fetch(fontRef.num, fontRef.gen, &fontObj)->isDict()) {
-    if (fontObj.dictLookup("BaseFont", &nameObj)->isName()) {
-      name = new GString(nameObj.getName());
-    }
-    nameObj.free();
     hasToUnicode = fontObj.dictLookup("ToUnicode", &toUnicodeObj)->isStream();
     toUnicodeObj.free();
   }
@@ -263,14 +269,11 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
         font->getEmbeddedFontID(&embRef) ? "yes" : "no",
         subset ? "yes" : "no",
         hasToUnicode ? "yes" : "no");
-  if (fontRef.gen == 999999) {
+  if (fontRef.gen >= 100000) {
     printf(" [none]\n");
   } else {
     printf(" %6d %2d\n", fontRef.num, fontRef.gen);
   }
-  if (name) {
-    delete name;
-  }
 
   // add this font to the list
   if (fontsLen == fontsSize) {
index e8a42aaf342a98c17942b2b6f178cb0be2707be7..a661fb5f990560a94b371c10a391e8f27c822be6 100644 (file)
@@ -30,8 +30,8 @@
 static int firstPage = 1;
 static int lastPage = 0;
 static GBool dumpJPEG = gFalse;
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static GBool quiet = gFalse;
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
@@ -96,12 +96,12 @@ int main(int argc, char *argv[]) {
   }
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -134,7 +134,7 @@ int main(int argc, char *argv[]) {
   // write image files
   imgOut = new ImageOutputDev(imgRoot, dumpJPEG);
   if (imgOut->isOk()) {
-    doc->displayPages(imgOut, firstPage, lastPage, 72, 0, gFalse);
+    doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, gFalse);
   }
   delete imgOut;
 
index f856a6d41e24b728354cf903a459baf60b113495..e29e673dd1eb58a3db14fc679977725b8ae0d8b4 100644 (file)
@@ -34,15 +34,21 @@ static void printInfoString(Dict *infoDict, char *key, char *text,
                            UnicodeMap *uMap);
 static void printInfoDate(Dict *infoDict, char *key, char *text);
 
+static int firstPage = 1;
+static int lastPage = 0;
 static GBool printMetadata = gFalse;
 static char textEncName[128] = "";
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
 static GBool printHelp = gFalse;
 
 static ArgDesc argDesc[] = {
+  {"-f",      argInt,      &firstPage,        0,
+   "first page to convert"},
+  {"-l",      argInt,      &lastPage,         0,
+   "last page to convert"},
   {"-meta",   argFlag,     &printMetadata,    0,
    "print the document metadata (XML)"},
   {"-enc",    argString,   textEncName,    sizeof(textEncName),
@@ -77,7 +83,8 @@ int main(int argc, char *argv[]) {
   GString *metadata;
   GBool ok;
   int exitCode;
-  int i;
+  int pg, i;
+  GBool multiPage;
 
   exitCode = 99;
 
@@ -107,12 +114,12 @@ int main(int argc, char *argv[]) {
   }
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -129,29 +136,43 @@ int main(int argc, char *argv[]) {
     goto err2;
   }
 
+  // get page range
+  if (firstPage < 1) {
+    firstPage = 1;
+  }
+  if (lastPage == 0) {
+    multiPage = gFalse;
+    lastPage = 1;
+  } else {
+    multiPage = gTrue;
+  }
+  if (lastPage < 1 || lastPage > doc->getNumPages()) {
+    lastPage = doc->getNumPages();
+  }
+
   // print doc info
   doc->getDocInfo(&info);
   if (info.isDict()) {
-    printInfoString(info.getDict(), "Title",        "Title:        ", uMap);
-    printInfoString(info.getDict(), "Subject",      "Subject:      ", uMap);
-    printInfoString(info.getDict(), "Keywords",     "Keywords:     ", uMap);
-    printInfoString(info.getDict(), "Author",       "Author:       ", uMap);
-    printInfoString(info.getDict(), "Creator",      "Creator:      ", uMap);
-    printInfoString(info.getDict(), "Producer",     "Producer:     ", uMap);
-    printInfoDate(info.getDict(),   "CreationDate", "CreationDate: ");
-    printInfoDate(info.getDict(),   "ModDate",      "ModDate:      ");
+    printInfoString(info.getDict(), "Title",        "Title:          ", uMap);
+    printInfoString(info.getDict(), "Subject",      "Subject:        ", uMap);
+    printInfoString(info.getDict(), "Keywords",     "Keywords:       ", uMap);
+    printInfoString(info.getDict(), "Author",       "Author:         ", uMap);
+    printInfoString(info.getDict(), "Creator",      "Creator:        ", uMap);
+    printInfoString(info.getDict(), "Producer",     "Producer:       ", uMap);
+    printInfoDate(info.getDict(),   "CreationDate", "CreationDate:   ");
+    printInfoDate(info.getDict(),   "ModDate",      "ModDate:        ");
   }
   info.free();
 
   // print tagging info
-  printf("Tagged:       %s\n",
+  printf("Tagged:         %s\n",
         doc->getStructTreeRoot()->isDict() ? "yes" : "no");
 
   // print page count
-  printf("Pages:        %d\n", doc->getNumPages());
+  printf("Pages:          %d\n", doc->getNumPages());
 
   // print encryption info
-  printf("Encrypted:    ");
+  printf("Encrypted:      ");
   if (doc->isEncrypted()) {
     printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
           doc->okToPrint(gTrue) ? "yes" : "no",
@@ -163,16 +184,20 @@ int main(int argc, char *argv[]) {
   }
 
   // print page size
-  if (doc->getNumPages() >= 1) {
-    w = doc->getPageWidth(1);
-    h = doc->getPageHeight(1);
-    printf("Page size:    %g x %g pts", w, h);
+  for (pg = firstPage; pg <= lastPage; ++pg) {
+    w = doc->getPageWidth(pg);
+    h = doc->getPageHeight(pg);
+    if (multiPage) {
+      printf("Page %4d size: %g x %g pts", pg, w, h);
+    } else {
+      printf("Page size:      %g x %g pts", w, h);
+    }
     if ((fabs(w - 612) < 0.1 && fabs(h - 792) < 0.1) ||
        (fabs(w - 792) < 0.1 && fabs(h - 612) < 0.1)) {
       printf(" (letter)");
     } else {
-      hISO = sqrt(sqrt(2)) * 7200 / 2.54;
-      wISO = hISO / sqrt(2);
+      hISO = sqrt(sqrt(2.0)) * 7200 / 2.54;
+      wISO = hISO / sqrt(2.0);
       for (i = 0; i <= 6; ++i) {
        if ((fabs(w - wISO) < 1 && fabs(h - hISO) < 1) ||
            (fabs(w - hISO) < 1 && fabs(h - wISO) < 1)) {
@@ -180,7 +205,7 @@ int main(int argc, char *argv[]) {
          break;
        }
        hISO = wISO;
-       wISO /= sqrt(2);
+       wISO /= sqrt(2.0);
       }
     }
     printf("\n");
@@ -195,22 +220,22 @@ int main(int argc, char *argv[]) {
   if (f) {
 #if HAVE_FSEEKO
     fseeko(f, 0, SEEK_END);
-    printf("File size:    %u bytes\n", (Guint)ftello(f));
+    printf("File size:      %u bytes\n", (Guint)ftello(f));
 #elif HAVE_FSEEK64
     fseek64(f, 0, SEEK_END);
-    printf("File size:    %u bytes\n", (Guint)ftell64(f));
+    printf("File size:      %u bytes\n", (Guint)ftell64(f));
 #else
     fseek(f, 0, SEEK_END);
-    printf("File size:    %d bytes\n", (int)ftell(f));
+    printf("File size:      %d bytes\n", (int)ftell(f));
 #endif
     fclose(f);
   }
 
   // print linearization info
-  printf("Optimized:    %s\n", doc->isLinearized() ? "yes" : "no");
+  printf("Optimized:      %s\n", doc->isLinearized() ? "yes" : "no");
 
   // print PDF version
-  printf("PDF version:  %.1f\n", doc->getPDFVersion());
+  printf("PDF version:    %.1f\n", doc->getPDFVersion());
 
   // print the metadata
   if (printMetadata && (metadata = doc->readMetadata())) {
index 704a3d06d90f56698fa274e7951a571a982a5425..a0d0af5bbec7d6c145d3c3396c8af1d647054349 100644 (file)
@@ -30,8 +30,8 @@
 static int firstPage = 1;
 static int lastPage = 0;
 static int resolution = 150;
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static GBool quiet = gFalse;
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
@@ -96,12 +96,12 @@ int main(int argc, char *argv[]) {
   }
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -127,7 +127,8 @@ int main(int argc, char *argv[]) {
   // write PBM files
   pbmOut = PBMOutputDev::makePBMOutputDev(NULL, pbmRoot);
   pbmOut->startDoc(doc->getXRef());
-  doc->displayPages(pbmOut, firstPage, lastPage, resolution, 0, gFalse);
+  doc->displayPages(pbmOut, firstPage, lastPage,
+                   resolution, resolution, 0, gFalse);
   PBMOutputDev::killPBMOutputDev(pbmOut);
 
   exitCode = 0;
index 2bb2e3b284f0cb30b0975f928cf687a593768966..247e45506c08b13bd612e8b2c27f15777849a758 100644 (file)
@@ -48,8 +48,8 @@ static char paperSize[15] = "";
 static int paperWidth = 0;
 static int paperHeight = 0;
 static GBool duplex = gFalse;
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static GBool quiet = gFalse;
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
@@ -220,12 +220,12 @@ int main(int argc, char *argv[]) {
   }
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -281,7 +281,7 @@ int main(int argc, char *argv[]) {
   psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
                          doc->getCatalog(), firstPage, lastPage, mode);
   if (psOut->isOk()) {
-    doc->displayPages(psOut, firstPage, lastPage, 72, 0, gFalse);
+    doc->displayPages(psOut, firstPage, lastPage, 72, 72, 0, gFalse);
   } else {
     delete psOut;
     exitCode = 2;
index a67f92439990d1249b35d0dff79a5cde54709bdd..c6ad9c03383d89c168acba91203aed89cf460bf9 100644 (file)
@@ -40,45 +40,48 @@ static GBool rawOrder = gFalse;
 static GBool htmlMeta = gFalse;
 static char textEncName[128] = "";
 static char textEOL[16] = "";
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static GBool noPageBreaks = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static GBool quiet = gFalse;
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
 static GBool printHelp = gFalse;
 
 static ArgDesc argDesc[] = {
-  {"-f",      argInt,      &firstPage,     0,
+  {"-f",       argInt,      &firstPage,     0,
    "first page to convert"},
-  {"-l",      argInt,      &lastPage,      0,
+  {"-l",       argInt,      &lastPage,      0,
    "last page to convert"},
-  {"-layout", argFlag,     &physLayout,    0,
+  {"-layout",  argFlag,     &physLayout,    0,
    "maintain original physical layout"},
-  {"-raw",    argFlag,     &rawOrder,      0,
+  {"-raw",     argFlag,     &rawOrder,      0,
    "keep strings in content stream order"},
-  {"-htmlmeta", argFlag,   &htmlMeta,      0,
+  {"-htmlmeta", argFlag,   &htmlMeta,       0,
    "generate a simple HTML file, including the meta information"},
-  {"-enc",    argString,   textEncName,    sizeof(textEncName),
+  {"-enc",     argString,   textEncName,    sizeof(textEncName),
    "output text encoding name"},
-  {"-eol",    argString,   textEOL,        sizeof(textEOL),
+  {"-eol",     argString,   textEOL,        sizeof(textEOL),
    "output end-of-line convention (unix, dos, or mac)"},
-  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
+  {"-nopgbrk", argFlag,     &noPageBreaks,  0,
+   "don't insert page breaks between pages"},
+  {"-opw",     argString,   ownerPassword,  sizeof(ownerPassword),
    "owner password (for encrypted files)"},
-  {"-upw",    argString,   userPassword,   sizeof(userPassword),
+  {"-upw",     argString,   userPassword,   sizeof(userPassword),
    "user password (for encrypted files)"},
-  {"-q",      argFlag,     &quiet,         0,
+  {"-q",       argFlag,     &quiet,         0,
    "don't print any messages or errors"},
-  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
+  {"-cfg",     argString,   cfgFileName,    sizeof(cfgFileName),
    "configuration file to use in place of .xpdfrc"},
-  {"-v",      argFlag,     &printVersion,  0,
+  {"-v",       argFlag,     &printVersion,  0,
    "print copyright and version info"},
-  {"-h",      argFlag,     &printHelp,     0,
+  {"-h",       argFlag,     &printHelp,     0,
    "print usage information"},
-  {"-help",   argFlag,     &printHelp,     0,
+  {"-help",    argFlag,     &printHelp,     0,
    "print usage information"},
-  {"--help",  argFlag,     &printHelp,     0,
+  {"--help",   argFlag,     &printHelp,     0,
    "print usage information"},
-  {"-?",      argFlag,     &printHelp,     0,
+  {"-?",       argFlag,     &printHelp,     0,
    "print usage information"},
   {NULL}
 };
@@ -120,6 +123,9 @@ int main(int argc, char *argv[]) {
       fprintf(stderr, "Bad '-eol' value on command line\n");
     }
   }
+  if (noPageBreaks) {
+    globalParams->setTextPageBreaks(gFalse);
+  }
   if (quiet) {
     globalParams->setErrQuiet(quiet);
   }
@@ -132,12 +138,12 @@ int main(int argc, char *argv[]) {
   }
 
   // open PDF file
-  if (ownerPassword[0]) {
+  if (ownerPassword[0] != '\001') {
     ownerPW = new GString(ownerPassword);
   } else {
     ownerPW = NULL;
   }
-  if (userPassword[0]) {
+  if (userPassword[0] != '\001') {
     userPW = new GString(userPassword);
   } else {
     userPW = NULL;
@@ -228,7 +234,7 @@ int main(int argc, char *argv[]) {
   textOut = new TextOutputDev(textFileName->getCString(),
                              physLayout, rawOrder, htmlMeta);
   if (textOut->isOk()) {
-    doc->displayPages(textOut, firstPage, lastPage, 72, 0, gFalse);
+    doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0, gFalse);
   } else {
     delete textOut;
     exitCode = 2;
index 174debb304bf15124abf15bf10d44ef96979bf8f..4a59bdadfe0d15f00c020c1d8c05d9edb339a9ca 100644 (file)
@@ -22,7 +22,7 @@ $ COMMON_OBJS = "Annot.obj,Array.obj,BuiltinFont.obj," + -
                 "Link.obj,NameToCharCode.obj,Object.obj,Outline.obj,"+ -
                 "OutputDev.obj,Page.obj,Parser.obj,PDFdoc.obj," + -
                 "PDFDocEncoding.obj,PSTokenizer.obj,Stream.obj," + -
-                "UnicodeMap.obj,XRef.obj"
+                "UnicodeMap.obj,UnicodeTypeTable.obj,XRef.obj"
 $ COMMON_LIBS = "[]common.olb/lib,[-.goo]libgoo.olb/lib"
 $!
 $ XPDF_OBJS = "xpdf.obj,FTFont.obj,PSOutputDev.obj," + -
index 73a0fe2a4660fda1da9f83c2221f4e9f95d6430a..bda355e05b589427b9a38f24974424643401746f 100644 (file)
@@ -30,8 +30,8 @@ static int paperHeight = 0;
 static GBool level1 = gFalse;
 static char textEncName[128] = "";
 static char textEOL[16] = "";
-static char ownerPassword[33] = "";
-static char userPassword[33] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
 static GBool fullScreen = gFalse;
 static char remoteName[100] = "xpdf_";
 static GBool doRemoteReload = gFalse;
@@ -59,7 +59,7 @@ static ArgDesc argDesc[] = {
   {"-papercolor", argStringDummy, NULL,           0,
    "color of paper background"},
   {"-z",          argStringDummy, NULL,           0,
-   "initial zoom level (-5..5, page, width)"},
+   "initial zoom level (percent, 'page', 'width')"},
 #if HAVE_T1LIB_H
   {"-t1lib",      argString,      t1libControlStr, sizeof(t1libControlStr),
    "t1lib font rasterizer control: none, plain, low, high"},
@@ -249,10 +249,10 @@ int main(int argc, char *argv[]) {
   app->setFullScreen(fullScreen);
 
   // check for password string(s)
-  ownerPasswordStr = ownerPassword[0] ? new GString(ownerPassword)
-                                      : (GString *)NULL;
-  userPasswordStr = userPassword[0] ? new GString(userPassword)
-                                    : (GString *)NULL;
+  ownerPasswordStr = ownerPassword[0] != '\001' ? new GString(ownerPassword)
+                                                : (GString *)NULL;
+  userPasswordStr = userPassword[0] != '\001' ? new GString(userPassword)
+                                              : (GString *)NULL;
 
   // open the file and run the main loop
   if (destName) {
index eda629c72d2203acb2ef42b5ab9eb3e26a32b759..ef08a7fe493726a9ec63ce25cbbde69e7bfca893 100644 (file)
 //------------------------------------------------------------------------
 
 // xpdf version
-#define xpdfVersion         "2.02"
-#define xpdfVersionNum      2.02
+#define xpdfVersion         "2.03"
+#define xpdfVersionNum      2.03
 #define xpdfMajorVersion    2
-#define xpdfMinorVersion    2
+#define xpdfMinorVersion    3
 #define xpdfMajorVersionStr "2"
-#define xpdfMinorVersionStr "2"
+#define xpdfMinorVersionStr "3"
 
 // supported PDF version
 #define supportedPDFVersionStr "1.4"
@@ -29,7 +29,7 @@
 #define xpdfCopyright "Copyright 1996-2003 Glyph & Cog, LLC"
 
 // Windows resource file stuff
-#define winxpdfVersion "WinXpdf 2.02"
+#define winxpdfVersion "WinXpdf 2.03"
 #define xpdfCopyrightAmp "Copyright 1996-2003 Glyph && Cog, LLC"
 
 //------------------------------------------------------------------------
@@ -82,7 +82,7 @@
 // popen
 //------------------------------------------------------------------------
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__BORLANDC__)
 #define popen _popen
 #define pclose _pclose
 #endif
 #undef CDECL
 #endif
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__BORLANDC__)
 #define CDECL __cdecl
 #else
 #define CDECL