From ad63666daeeda50acc7630132d61fe044634fddd Mon Sep 17 00:00:00 2001 From: Martin Kretzschmar Date: Sun, 16 May 2004 22:45:42 +0000 Subject: Imported Xpdf 2.03 and fixed build. * 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. --- diff --git a/pdf/goo/gfile.cc b/pdf/goo/gfile.cc index b4fb616..11f5cf6 100644 --- a/pdf/goo/gfile.cc +++ b/pdf/goo/gfile.cc @@ -10,13 +10,7 @@ #include -#ifdef WIN32 - extern "C" { -# ifndef _MSC_VER -# include -# endif - } -#else // !WIN32 +#ifndef WIN32 # if defined(MACOS) # include # 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 diff --git a/pdf/xpdf/Annot.cc b/pdf/xpdf/Annot.cc index 20fe24b..42bf849 100644 --- a/pdf/xpdf/Annot.cc +++ b/pdf/xpdf/Annot.cc @@ -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(); } diff --git a/pdf/xpdf/Array.cc b/pdf/xpdf/Array.cc index 27ecbe9..a6c6db1 100644 --- a/pdf/xpdf/Array.cc +++ b/pdf/xpdf/Array.cc @@ -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; diff --git a/pdf/xpdf/BuiltinFontTables.cc b/pdf/xpdf/BuiltinFontTables.cc index 296e564..9c36238 100644 --- a/pdf/xpdf/BuiltinFontTables.cc +++ b/pdf/xpdf/BuiltinFontTables.cc @@ -13,30 +13,40 @@ 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); } diff --git a/pdf/xpdf/CMap.cc b/pdf/xpdf/CMap.cc index c60ce3c..25f3af7 100644 --- a/pdf/xpdf/CMap.cc +++ b/pdf/xpdf/CMap.cc @@ -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; } } diff --git a/pdf/xpdf/CMap.h b/pdf/xpdf/CMap.h index b44f07a..eff2a81 100644 --- a/pdf/xpdf/CMap.h +++ b/pdf/xpdf/CMap.h @@ -18,6 +18,10 @@ #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 }; //------------------------------------------------------------------------ diff --git a/pdf/xpdf/Catalog.cc b/pdf/xpdf/Catalog.cc index ad0821b..c645fd0 100644 --- a/pdf/xpdf/Catalog.cc +++ b/pdf/xpdf/Catalog.cc @@ -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) diff --git a/pdf/xpdf/CharCodeToUnicode.cc b/pdf/xpdf/CharCodeToUnicode.cc index a374b1b..2e2ad47 100644 --- a/pdf/xpdf/CharCodeToUnicode.cc +++ b/pdf/xpdf/CharCodeToUnicode.cc @@ -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(); +} diff --git a/pdf/xpdf/CharCodeToUnicode.h b/pdf/xpdf/CharCodeToUnicode.h index b20f323..605e2bd 100644 --- a/pdf/xpdf/CharCodeToUnicode.h +++ b/pdf/xpdf/CharCodeToUnicode.h @@ -19,6 +19,10 @@ #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 . - // 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 from the file + // specified by . 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 + // . 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. // 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 + // . + void mergeCMap(GString *buf, int nBits); + ~CharCodeToUnicode(); void incRefCnt(); void decRefCnt(); - // Return true if this mapping matches the specified . - GBool match(GString *collectionA); + // Return true if this mapping matches the specified . + GBool match(GString *tagA); + + // Set the mapping for . + 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 . 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 . 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 into the cache, in the most-recently-used position. + void add(CharCodeToUnicode *ctu); private: - CharCodeToUnicode *cache[cidToUnicodeCacheSize]; + CharCodeToUnicode **cache; + int size; }; #endif diff --git a/pdf/xpdf/Decrypt.cc b/pdf/xpdf/Decrypt.cc index dca3762..dab0750 100644 --- a/pdf/xpdf/Decrypt.cc +++ b/pdf/xpdf/Decrypt.cc @@ -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, diff --git a/pdf/xpdf/Dict.cc b/pdf/xpdf/Dict.cc index 9575e4c..6274590 100644 --- a/pdf/xpdf/Dict.cc +++ b/pdf/xpdf/Dict.cc @@ -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; diff --git a/pdf/xpdf/ErrorCodes.h b/pdf/xpdf/ErrorCodes.h index 8de2b01..6eb1435 100644 --- a/pdf/xpdf/ErrorCodes.h +++ b/pdf/xpdf/ErrorCodes.h @@ -23,4 +23,12 @@ #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 diff --git a/pdf/xpdf/FTFont.cc b/pdf/xpdf/FTFont.cc index c360eb7..1a5ecfa 100644 --- a/pdf/xpdf/FTFont.cc +++ b/pdf/xpdf/FTFont.cc @@ -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]; diff --git a/pdf/xpdf/FTFont.h b/pdf/xpdf/FTFont.h index 52bc149..8da5558 100644 --- a/pdf/xpdf/FTFont.h +++ b/pdf/xpdf/FTFont.h @@ -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; diff --git a/pdf/xpdf/FontFile.cc b/pdf/xpdf/FontFile.cc index fde0ae6..0c44422 100644 --- a/pdf/xpdf/FontFile.cc +++ b/pdf/xpdf/FontFile.cc @@ -20,6 +20,7 @@ #include #include #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; diff --git a/pdf/xpdf/FontFile.h b/pdf/xpdf/FontFile.h index a71653c..7aa5ba9 100644 --- a/pdf/xpdf/FontFile.h +++ b/pdf/xpdf/FontFile.h @@ -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 th cmap. + int getCmapPlatform(int i); + + // Return the encoding ID of the th cmap. + int getCmapEncoding(int i); + + // Return the index of the cmap for , . Returns + // -1 if there is no corresponding cmap. + int findCmap(int platform, int enc); + + // Return the GID corresponding to according to the 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, diff --git a/pdf/xpdf/Function.cc b/pdf/xpdf/Function.cc index 28eed87..d9d4a93 100644 --- a/pdf/xpdf/Function.cc +++ b/pdf/xpdf/Function.cc @@ -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<= 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: diff --git a/pdf/xpdf/Gfx.cc b/pdf/xpdf/Gfx.cc index 63896d8..d108208 100644 --- a/pdf/xpdf/Gfx.cc +++ b/pdf/xpdf/Gfx.cc @@ -41,6 +41,12 @@ // 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 @@ -57,6 +63,10 @@ // 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; +} diff --git a/pdf/xpdf/Gfx.h b/pdf/xpdf/Gfx.h index c7aef11..20898ec 100644 --- a/pdf/xpdf/Gfx.h +++ b/pdf/xpdf/Gfx.h @@ -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 diff --git a/pdf/xpdf/GfxFont.cc b/pdf/xpdf/GfxFont.cc index d687588..6f83676 100644 --- a/pdf/xpdf/GfxFont.cc +++ b/pdf/xpdf/GfxFont.cc @@ -17,6 +17,7 @@ #include #include #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()); diff --git a/pdf/xpdf/GfxFont.h b/pdf/xpdf/GfxFont.h index 23371b3..ddd88be 100644 --- a/pdf/xpdf/GfxFont.h +++ b/pdf/xpdf/GfxFont.h @@ -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(); diff --git a/pdf/xpdf/GfxState.cc b/pdf/xpdf/GfxState.cc index 7efd0b9..d202939 100644 --- a/pdf/xpdf/GfxState.cc +++ b/pdf/xpdf/GfxState.cc @@ -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; diff --git a/pdf/xpdf/GfxState.h b/pdf/xpdf/GfxState.h index a0b1d14..d072fd3 100644 --- a/pdf/xpdf/GfxState.h +++ b/pdf/xpdf/GfxState.h @@ -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; @@ -518,17 +522,43 @@ private: }; //------------------------------------------------------------------------ +// 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 //------------------------------------------------------------------------ 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; @@ -548,6 +580,37 @@ private: }; //------------------------------------------------------------------------ +// 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 (, ) 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 to . + void append(GfxPath *path); + + // Add (, ) 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 , - // page box , page rotation , and coordinate system - // specified by . - GfxState(double dpi, PDFRectangle *pageBox, int rotate, - GBool upsideDown); + // Construct a default GfxState, for a device with resolution + // x , page box , page rotation , and + // coordinate system specified by . + 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) diff --git a/pdf/xpdf/GlobalParams.cc b/pdf/xpdf/GlobalParams.cc index c5083b2..9aa54e9 100644 --- a/pdf/xpdf/GlobalParams.cc +++ b/pdf/xpdf/GlobalParams.cc @@ -32,11 +32,19 @@ #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" @@ -46,6 +54,11 @@ //------------------------------------------------------------------------ +#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; } diff --git a/pdf/xpdf/GlobalParams.h b/pdf/xpdf/GlobalParams.h index dee9e25..472beed 100644 --- a/pdf/xpdf/GlobalParams.h +++ b/pdf/xpdf/GlobalParams.h @@ -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 }; diff --git a/pdf/xpdf/Link.cc b/pdf/xpdf/Link.cc index c87f6ce..bfb41b7 100644 --- a/pdf/xpdf/Link.cc +++ b/pdf/xpdf/Link.cc @@ -583,13 +583,42 @@ LinkUnknown::~LinkUnknown() { } //------------------------------------------------------------------------ +// 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; + } } //------------------------------------------------------------------------ diff --git a/pdf/xpdf/Link.h b/pdf/xpdf/Link.h index 20ed450..aa8727f 100644 --- a/pdf/xpdf/Link.h +++ b/pdf/xpdf/Link.h @@ -302,6 +302,42 @@ private: }; //------------------------------------------------------------------------ +// 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? }; diff --git a/pdf/xpdf/Makefile.am b/pdf/xpdf/Makefile.am index 5ff9a34..96d57ec 100644 --- a/pdf/xpdf/Makefile.am +++ b/pdf/xpdf/Makefile.am @@ -97,6 +97,7 @@ libxpdf_a_SOURCES = \ UnicodeMap.cc \ UnicodeMap.h \ UnicodeMapTables.h \ + UnicodeTypeTable.cc \ XRef.cc \ XRef.h diff --git a/pdf/xpdf/OutputDev.h b/pdf/xpdf/OutputDev.h index f050c03..67737af 100644 --- a/pdf/xpdf/OutputDev.h +++ b/pdf/xpdf/OutputDev.h @@ -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, diff --git a/pdf/xpdf/PDFDoc.cc b/pdf/xpdf/PDFDoc.cc index 9108220..c92048b 100644 --- a/pdf/xpdf/PDFDoc.cc +++ b/pdf/xpdf/PDFDoc.cc @@ -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); diff --git a/pdf/xpdf/PDFDoc.h b/pdf/xpdf/PDFDoc.h index ea73282..2d060dc 100644 --- a/pdf/xpdf/PDFDoc.h +++ b/pdf/xpdf/PDFDoc.h @@ -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 , 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 , is in a link. GBool onLink(double x, double y) { return links->onLink(x, y); } diff --git a/pdf/xpdf/PSOutputDev.cc b/pdf/xpdf/PSOutputDev.cc index 7bef193..34d7fdc 100644 --- a/pdf/xpdf/PSOutputDev.cc +++ b/pdf/xpdf/PSOutputDev.cc @@ -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" @@ -48,7 +47,15 @@ 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: diff --git a/pdf/xpdf/PSOutputDev.h b/pdf/xpdf/PSOutputDev.h index f7896cc..06130fb 100644 --- a/pdf/xpdf/PSOutputDev.h +++ b/pdf/xpdf/PSOutputDev.h @@ -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 diff --git a/pdf/xpdf/Page.cc b/pdf/xpdf/Page.cc index c7a1e13..e12e65c 100644 --- a/pdf/xpdf/Page.cc +++ b/pdf/xpdf/Page.cc @@ -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 diff --git a/pdf/xpdf/Page.h b/pdf/xpdf/Page.h index 8b339eb..8fa1ef6 100644 --- a/pdf/xpdf/Page.h +++ b/pdf/xpdf/Page.h @@ -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, diff --git a/pdf/xpdf/Parser.cc b/pdf/xpdf/Parser.cc index deb894a..2f881d4 100644 --- a/pdf/xpdf/Parser.cc +++ b/pdf/xpdf/Parser.cc @@ -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; } diff --git a/pdf/xpdf/Stream.cc b/pdf/xpdf/Stream.cc index 11b51b6..e770b61 100644 --- a/pdf/xpdf/Stream.cc +++ b/pdf/xpdf/Stream.cc @@ -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: // +-----+--------------+-----------------+-- diff --git a/pdf/xpdf/Stream.h b/pdf/xpdf/Stream.h index 31c0a97..0121df1 100644 --- a/pdf/xpdf/Stream.h +++ b/pdf/xpdf/Stream.h @@ -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; } diff --git a/pdf/xpdf/TTFont.cc b/pdf/xpdf/TTFont.cc index fc6d849..c499cf1 100644 --- a/pdf/xpdf/TTFont.cc +++ b/pdf/xpdf/TTFont.cc @@ -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) { diff --git a/pdf/xpdf/TTFont.h b/pdf/xpdf/TTFont.h index 96208e2..e4740ea 100644 --- a/pdf/xpdf/TTFont.h +++ b/pdf/xpdf/TTFont.h @@ -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; diff --git a/pdf/xpdf/TextOutputDev.cc b/pdf/xpdf/TextOutputDev.cc index 4e9a63a..aeee59c 100644 --- a/pdf/xpdf/TextOutputDev.cc +++ b/pdf/xpdf/TextOutputDev.cc @@ -28,6 +28,7 @@ #include "Error.h" #include "GlobalParams.h" #include "UnicodeMap.h" +#include "UnicodeTypeTable.h" #include "GfxState.h" #include "TextOutputDev.h" @@ -40,176 +41,187 @@ // 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 comes before 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 (word->yMin < yMin) { + yMin = word->yMin; } - if (word2->yMax > yMax) { - yMax = word2->yMax; + if (word->xMax > xMax) { + xMax = word->xMax; } - if (len + word2->len > size) { - size = len + word2->len; + 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 comes before 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; + } + } +} + +double TextLine::primaryDelta(TextLine *line) { + double delta; + + 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; + + 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; +} + +int TextLine::secondaryCmp(TextLine *line) { + double cmp; + + cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base; + return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; +} - dy = lineOverlapSlack * fontSize; +int TextLine::cmpYX(TextLine *line) { + int cmp; - // non-overlapping case - if (line2->yMin > yMax - dy || - line2->yMax < yMin + dy) { - return yMin < line2->yMin || - (yMin == line2->yMin && xMin < line2->xMin); + if ((cmp = secondaryCmp(line))) { + return cmp; } + 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; + } + 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; + } + } + } + + // 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; + } - // overlapping case - return xMin < line2->xMin; + } + } } -// Merge another line's words onto the end of this line. -void TextLine::merge(TextLine *line2) { - int newLen, i; +int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) { + TextLineFrag *frag1 = (TextLineFrag *)p1; + TextLineFrag *frag2 = (TextLineFrag *)p2; + double cmp; - xMax = line2->xMax; - if (line2->yMin < yMin) { - yMin = line2->yMin; + 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; } - if (line2->yMax > yMax) { - yMax = line2->yMax; + 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; } - 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 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; } - len = newLen; - convertedLen += line2->convertedLen; - hyphenated = line2->hyphenated; + 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 comes before 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; } - // overlapping case - return xMin < blk2->xMin; + // 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); + + // 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) { + int rot; + rawOrder = rawOrderA; 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(); + 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; + 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(); +} + 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 - - 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)); - } - } + //----- assemble the blocks - // 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; + // 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; } - col2 = line2->col[0] + line2->convertedLen + d; - if (col2 > col1) { - col1 = col2; - } - } 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,118 +2405,152 @@ void TextPage::coalesce(GBool physLayout) { } } printf("\n"); - fflush(stdout); #endif - //----- assemble blocks into flows + //----- reading order sort - if (rawOrder) { + // sort blocks into yx order (in preparation for reading order sort) + qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot); - // 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; + // 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; } + } - } 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; - } - } +#if 0 // for debugging + 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"); +#endif - 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; + // 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 (blk2->ySpaceB < flow1->ySpaceB) { - flow1->ySpaceB = blk2->ySpaceB; + if (blk1->primaryCmp(blk) < 0) { + i = j; + blk = blk1; } - for (line1 = blk1->lines; line1->next; line1 = line1->next) ; - line1->flowNext = blk2->lines; } + } + blkArray[i] = NULL; + --nBlocksLeft; + blk->next = NULL; - // chop the block list - blocks = blk1->next; - blk1->next = NULL; + // 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; + } + } + } - // append the flow to the list - if (flow0) { - flow0->next = flow1; + // 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 { - flows = flow1; + blkStack = blkStack->stackNext; } - flow0 = flow1; } } + gfree(blkArray); #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: '", + 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->yBase, word0->spaceAfter); + word0->base, word0->fontSize, word0->spaceAfter); for (i = 0; i < word0->len; ++i) { fputc(word0->text[i] & 0xff, stdout); } @@ -1435,288 +2560,159 @@ void TextPage::coalesce(GBool physLayout) { } } 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) - - 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; - } - 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 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("'\n"); - } - } - printf("\n"); - fflush(stdout); -#endif -} - -// If can be added the end of , return the absolute value -// of the difference between 's baseline and 's baseline, -// and set * to the horizontal space between the current last -// word in and . 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 and 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 can be added to . 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 and 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 can be added to . -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 + for (i = 0; i < nBlocks; ++i) { + blk = blocks[i]; -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; - - // 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) { - - x0 = (i == 0) ? line->xMin : line->xRight[i-1]; - x1 = line->xRight[i]; - x = 0.5 * (x0 + x1); + for (line = blk->lines; line; line = line->next) { - // 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; - } + // sort the fragments and generate the string + if (nFrags > 0) { - y = 0.5 * (line->yMin + line->yMax); - if (y < yMin || y > yMax) { - continue; + for (i = 0; i < nFrags; ++i) { + frags[i].computeCoords(oneRot); } + assignColumns(frags, nFrags, oneRot); - 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; - } - - // insert a return - if (line->col[i] < col || - (prevLine && - line->yMin > - prevLine->yMax - lineOverlapSlack * prevLine->fontSize)) { - s->append(eol, eolLen); - col = firstCol; - multiLine = gTrue; - } - prevLine = line; - - // 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; } - col += line->convertedLen; + } + qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot); - // 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); - } + // generate output + col = 0; + for (i = 0; i < nFrags; ++i) { + frag = &frags[i]; + + // column alignment + for (; col < frag->col; ++col) { + (*outputFunc)(outputStream, space, spaceLen); + } - // various things (weird font matrices) can result in bogus - // values here, so do a sanity check - if (d < 1) { + // 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; + + // 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 diff --git a/pdf/xpdf/TextOutputDev.h b/pdf/xpdf/TextOutputDev.h index e0c22c2..b501907 100644 --- a/pdf/xpdf/TextOutputDev.h +++ b/pdf/xpdf/TextOutputDev.h @@ -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 onto the end of . + void merge(TextWord *word); + + // Compares to , 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 and + // . + 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 and + // . + double primaryDelta(TextLine *line); + + // Compares to , returning -1 (<), 0 (=), or +1 (>), + // based on a primary-axis comparison, e.g., x ordering if rot=0. + int primaryCmp(TextLine *line); + + // Compares to , 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 . + 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 is below , 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 fits below 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 + // 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 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 is true, starts looking at top of page; - // otherwise starts looking at ,. If is true, - // stops looking at bottom of page; otherwise stops looking at - // ,. If found, sets the text bounding rectangle and - // returns true; otherwise returns false. + // Find a string. If is true, starts looking at the + // top of the page; else if is true, starts looking + // immediately after the last find result; else starts looking at + // ,. If is true, stops looking at the + // bottom of the page; else if is true, stops looking + // just before the last find result; else stops looking at + // ,. 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 + // 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 is true, starts looking at top of page; - // otherwise starts looking at ,. If is true, - // stops looking at bottom of page; otherwise stops looking at - // ,. If found, sets the text bounding rectangle and - // returns true; otherwise returns false. + // Find a string. If is true, starts looking at the + // top of the page; else if is true, starts looking + // immediately after the last find result; else starts looking at + // ,. If is true, stops looking at the + // bottom of the page; else if is true, stops looking + // just before the last find result; else stops looking at + // ,. 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 diff --git a/pdf/xpdf/UnicodeMap.cc b/pdf/xpdf/UnicodeMap.cc index e6284eb..300d802 100644 --- a/pdf/xpdf/UnicodeMap.cc +++ b/pdf/xpdf/UnicodeMap.cc @@ -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) { diff --git a/pdf/xpdf/UnicodeMap.h b/pdf/xpdf/UnicodeMap.h index 24de28c..6fd4ed2 100644 --- a/pdf/xpdf/UnicodeMap.h +++ b/pdf/xpdf/UnicodeMap.h @@ -20,6 +20,10 @@ #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 }; //------------------------------------------------------------------------ diff --git a/pdf/xpdf/XOutputDev.cc b/pdf/xpdf/XOutputDev.cc index 2100355..a156b55 100644 --- a/pdf/xpdf/XOutputDev.cc +++ b/pdf/xpdf/XOutputDev.cc @@ -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); diff --git a/pdf/xpdf/XOutputDev.h b/pdf/xpdf/XOutputDev.h index 7a6544d..3efa1dd 100644 --- a/pdf/xpdf/XOutputDev.h +++ b/pdf/xpdf/XOutputDev.h @@ -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 is true, starts looking at ,; - // otherwise starts looking at top of page. If is true, - // stops looking at ,; 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 is true, starts looking at the + // top of the page; else if is true, starts looking + // immediately after the last find result; else starts looking at + // ,. If is true, stops looking at the + // bottom of the page; else if is true, stops looking + // just before the last find result; else stops looking at + // ,. + 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 diff --git a/pdf/xpdf/XRef.cc b/pdf/xpdf/XRef.cc index 56cb131..eca638d 100644 --- a/pdf/xpdf/XRef.cc +++ b/pdf/xpdf/XRef.cc @@ -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) { diff --git a/pdf/xpdf/pdffonts.cc b/pdf/xpdf/pdffonts.cc index f3a1b48..2fcd1a8 100644 --- a/pdf/xpdf/pdffonts.cc +++ b/pdf/xpdf/pdffonts.cc @@ -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", "", argDesc); + printUsage("pdffonts", "", 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) { diff --git a/pdf/xpdf/pdfimages.cc b/pdf/xpdf/pdfimages.cc index e8a42aa..a661fb5 100644 --- a/pdf/xpdf/pdfimages.cc +++ b/pdf/xpdf/pdfimages.cc @@ -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; diff --git a/pdf/xpdf/pdfinfo.cc b/pdf/xpdf/pdfinfo.cc index f856a6d..e29e673 100644 --- a/pdf/xpdf/pdfinfo.cc +++ b/pdf/xpdf/pdfinfo.cc @@ -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())) { diff --git a/pdf/xpdf/pdftopbm.cc b/pdf/xpdf/pdftopbm.cc index 704a3d0..a0d0af5 100644 --- a/pdf/xpdf/pdftopbm.cc +++ b/pdf/xpdf/pdftopbm.cc @@ -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; diff --git a/pdf/xpdf/pdftops.cc b/pdf/xpdf/pdftops.cc index 2bb2e3b..247e455 100644 --- a/pdf/xpdf/pdftops.cc +++ b/pdf/xpdf/pdftops.cc @@ -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; diff --git a/pdf/xpdf/pdftotext.cc b/pdf/xpdf/pdftotext.cc index a67f924..c6ad9c0 100644 --- a/pdf/xpdf/pdftotext.cc +++ b/pdf/xpdf/pdftotext.cc @@ -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; diff --git a/pdf/xpdf/vms_make.com b/pdf/xpdf/vms_make.com index 174debb..4a59bda 100644 --- a/pdf/xpdf/vms_make.com +++ b/pdf/xpdf/vms_make.com @@ -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," + - diff --git a/pdf/xpdf/xpdf.cc b/pdf/xpdf/xpdf.cc index 73a0fe2..bda355e 100644 --- a/pdf/xpdf/xpdf.cc +++ b/pdf/xpdf/xpdf.cc @@ -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) { diff --git a/pdf/xpdf/xpdfconfig.h b/pdf/xpdf/xpdfconfig.h index eda629c..ef08a7f 100644 --- a/pdf/xpdf/xpdfconfig.h +++ b/pdf/xpdf/xpdfconfig.h @@ -14,12 +14,12 @@ //------------------------------------------------------------------------ // 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 @@ -101,7 +101,7 @@ #undef CDECL #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__BORLANDC__) #define CDECL __cdecl #else #define CDECL -- cgit v0.9.1