diff options
author | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2019-09-29 10:00:12 +0000 |
---|---|---|
committer | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2019-09-29 10:00:12 +0000 |
commit | 268dff1b06a01bd6be5f1ae08216c0beff08336c (patch) | |
tree | c8f90365823a9711e674704157dff65babb709b9 /Build/source/libs/xpdf/xpdf-src/xpdf | |
parent | ee6ffc65c0eb84ded1fc7d19eeb90a87541f896d (diff) |
xpdf 4.02
git-svn-id: svn://tug.org/texlive/trunk@52203 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf')
46 files changed, 2398 insertions, 595 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc index 1d312e97428..c7f30405483 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc @@ -1542,10 +1542,11 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, fontSize = atof(tok->getCString()); } else { error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string"); - fontSize = 10; + fontSize = 0; if (!daToks) { daToks = new GList(); } + tfPos = daToks->getLength(); daToks->append(new GString("/xpdf_default_font")); daToks->append(new GString("10")); daToks->append(new GString("Tf")); @@ -1583,7 +1584,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, // compute font autosize if (fontSize == 0) { - for (fontSize = 20; fontSize > 1; --fontSize) { + for (fontSize = 10; fontSize > 1; --fontSize) { y = dy - 3; w2 = 0; i = 0; @@ -1596,7 +1597,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, y -= fontSize; } // approximate the descender for the last line - if (y >= 0.33 * fontSize) { + if (y >= 0.33 * fontSize && w <= wMax) { break; } } @@ -1697,6 +1698,9 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, fontSize = w; } fontSize = floor(fontSize); + if (fontSize > 10) { + fontSize = 10; + } if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); @@ -1783,6 +1787,9 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, fontSize = fontSize2; } fontSize = floor(fontSize); + if (fontSize > 10) { + fontSize = 10; + } if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); @@ -1936,6 +1943,9 @@ void AcroFormField::drawListBox(GString **text, GBool *selection, fontSize = fontSize2; } fontSize = floor(fontSize); + if (fontSize > 10) { + fontSize = 10; + } if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc index 850c6b25651..61a1d434abf 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc @@ -845,7 +845,7 @@ GBool Annot::setFillColor(Object *colorObj) { if (!colorObj->isArray()) { return gFalse; } - for (i = 0; i < colorObj->arrayGetLength(); ++i) { + for (i = 0; i < colorObj->arrayGetLength() && i < 4; ++i) { if (colorObj->arrayGet(i, &obj)->isNum()) { color[i] = obj.getNum(); } else { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc index 01dbd110dca..d2f875e45fd 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc @@ -51,6 +51,7 @@ GBool BuiltinFontWidths::getWidth(const char *name, Gushort *width) { return gTrue; } } + *width = 0; return gFalse; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt index 8fb3f2050bb..4c88d1d351f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt @@ -111,7 +111,9 @@ if (HAVE_SPLASH) ${PAPER_LIBRARY} ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS} ${DTYPE_LIBRARY} - ${LCMS_LIBRARY}) + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) else () add_executable(pdftops $<TARGET_OBJECTS:xpdf_objs> @@ -119,7 +121,11 @@ else () PSOutputDev.cc pdftops.cc ) - target_link_libraries(pdftops goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) + target_link_libraries(pdftops goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) endif () install(TARGETS pdftops RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftops.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -131,7 +137,11 @@ add_executable(pdftotext TextOutputDev.cc pdftotext.cc ) -target_link_libraries(pdftotext goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) +target_link_libraries(pdftotext goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdftotext RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftotext.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -143,13 +153,17 @@ if (HAVE_SPLASH AND PNG_FOUND) HTMLGen.cc SplashOutputDev.cc TextOutputDev.cc + WebFont.cc pdftohtml.cc ) target_link_libraries(pdftohtml goo fofi splash ${PAPER_LIBRARY} ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS} ${DTYPE_LIBRARY} - ${LCMS_LIBRARY} ${PNG_LIBRARIES}) + ${LCMS_LIBRARY} + ${PNG_LIBRARIES} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdftohtml RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftohtml.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () @@ -160,7 +174,11 @@ add_executable(pdfinfo $<TARGET_OBJECTS:xpdf_objs> pdfinfo.cc ) -target_link_libraries(pdfinfo goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) +target_link_libraries(pdfinfo goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdfinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfinfo.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -170,7 +188,11 @@ add_executable(pdffonts $<TARGET_OBJECTS:xpdf_objs> pdffonts.cc ) -target_link_libraries(pdffonts goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) +target_link_libraries(pdffonts goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdffonts RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdffonts.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -180,7 +202,11 @@ add_executable(pdfdetach $<TARGET_OBJECTS:xpdf_objs> pdfdetach.cc ) -target_link_libraries(pdfdetach goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) +target_link_libraries(pdfdetach goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdfdetach RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfdetach.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -196,7 +222,9 @@ if (HAVE_SPLASH) ${PAPER_LIBRARY} ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS} ${DTYPE_LIBRARY} - ${LCMS_LIBRARY}) + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdftoppm RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftoppm.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () @@ -213,7 +241,10 @@ if (HAVE_SPLASH AND PNG_FOUND) ${PAPER_LIBRARY} ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS} ${DTYPE_LIBRARY} - ${LCMS_LIBRARY} ${PNG_LIBRARIES}) + ${LCMS_LIBRARY} + ${PNG_LIBRARIES} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdftopng RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftopng.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () @@ -225,7 +256,11 @@ add_executable(pdfimages ImageOutputDev.cc pdfimages.cc ) -target_link_libraries(pdfimages goo fofi ${PAPER_LIBRARY} ${LCMS_LIBRARY}) +target_link_libraries(pdfimages goo fofi + ${PAPER_LIBRARY} + ${LCMS_LIBRARY} + ${FONTCONFIG_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS pdfimages RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfimages.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc index 470807314a6..bec30a8dcfd 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc @@ -89,6 +89,59 @@ EmbeddedFile::~EmbeddedFile() { } //------------------------------------------------------------------------ +// PageLabelNode +//------------------------------------------------------------------------ + +class PageLabelNode { +public: + + PageLabelNode(int firstPageA, Dict *dict); + ~PageLabelNode(); + + int firstPage; // first page number covered by this node + int lastPage; // last page number covered by this node + TextString *prefix; // label prefix (may be empty) + int start; // value of the numeric portion of this + // label for the first page in the range + char style; // page label style +}; + +PageLabelNode::PageLabelNode(int firstPageA, Dict *dict) { + Object prefixObj, styleObj, startObj; + + // convert page index to page number + firstPage = firstPageA + 1; + + // lastPage will be filled in later + lastPage = -1; + + if (dict->lookup("P", &prefixObj)->isString()) { + prefix = new TextString(prefixObj.getString()); + } else { + prefix = new TextString(); + } + prefixObj.free(); + + style = '\0'; + if (dict->lookup("S", &styleObj)->isName()) { + if (strlen(styleObj.getName()) == 1) { + style = styleObj.getName()[0]; + } + } + styleObj.free(); + + start = 1; + if (dict->lookup("St", &startObj)->isInt()) { + start = startObj.getInt(); + } + startObj.free(); +} + +PageLabelNode::~PageLabelNode() { + delete prefix; +} + +//------------------------------------------------------------------------ // Catalog //------------------------------------------------------------------------ @@ -185,6 +238,15 @@ Catalog::Catalog(PDFDoc *docA) { // get the list of embedded files readEmbeddedFileList(catDict.getDict()); + // get the ViewerPreferences object + catDict.dictLookupNF("ViewerPreferences", &viewerPrefs); + + pageLabels = NULL; + if (catDict.dictLookup("PageLabels", &obj)->isDict()) { + readPageLabelTree(&obj); + } + obj.free(); + catDict.free(); return; @@ -229,6 +291,10 @@ Catalog::~Catalog() { if (embeddedFiles) { deleteGList(embeddedFiles, EmbeddedFile); } + if (pageLabels) { + deleteGList(pageLabels, PageLabelNode); + } + viewerPrefs.free(); } Page *Catalog::getPage(int i) { @@ -833,107 +899,45 @@ Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) { return strObj; } -TextString *Catalog::getPageLabel(int pageNum) { - Object catDict, node, pageLabelObj, start, prefix, style; - GString *label, *s; - TextString *ts; - int firstPageIndex, pageRangeNum; - - if (!xref->getCatalog(&catDict)->isDict()) { - catDict.free(); - return NULL; - } +void Catalog::readPageLabelTree(Object *root) { + PageLabelNode *label0, *label1; + int i; - label = NULL; - catDict.dictLookup("PageLabels", &node); - if (findPageLabel(&node, pageNum - 1, &pageLabelObj, &firstPageIndex)) { - if (pageLabelObj.isDict()) { - if (pageLabelObj.dictLookup("P", &prefix)->isString()) { - label = prefix.getString()->copy(); - } else { - label = new GString(); - } - prefix.free(); + pageLabels = new GList(); + readPageLabelTree2(root); - pageRangeNum = pageNum - firstPageIndex; - if (pageLabelObj.dictLookup("St", &start)->isInt()) { - pageRangeNum += start.getInt() - 1; - } - start.free(); - - if (pageLabelObj.dictLookup("S", &style)->isName()) { - if (style.isName("D")) { - label->appendf("{0:d}", pageRangeNum); - } else if (style.isName("R")) { - s = makeRomanNumeral(pageRangeNum, gTrue); - label->append(s); - delete s; - } else if (style.isName("r")) { - s = makeRomanNumeral(pageRangeNum, gFalse); - label->append(s); - delete s; - } else if (style.isName("A")) { - s = makeLetterLabel(pageRangeNum, gTrue); - label->append(s); - delete s; - } else if (style.isName("a")) { - s = makeLetterLabel(pageRangeNum, gFalse); - label->append(s); - delete s; - } - } - style.free(); - } - pageLabelObj.free(); + if (pageLabels->getLength() == 0) { + deleteGList(pageLabels, PageLabelNode); + pageLabels = NULL; + return; } - node.free(); - catDict.free(); - ts = new TextString(label); - delete label; - return ts; + // set lastPage in each node + label0 = (PageLabelNode *)pageLabels->get(0); + for (i = 1; i < pageLabels->getLength(); ++i) { + label1 = (PageLabelNode *)pageLabels->get(i); + label0->lastPage = label1->firstPage - 1; + label0 = label1; + } + label0->lastPage = numPages; } -GBool Catalog::findPageLabel(Object *node, int pageIndex, - Object *pageLabelObj, int *firstPageIndex) { - Object limits, limit, nums, num, kids, kid; +void Catalog::readPageLabelTree2(Object *node) { + Object nums, num, labelObj, kids, kid; int i; if (!node->isDict()) { - return gFalse; - } - - // we only check the lower limit because page labels are organized - // into ranges based on their first page number -- the Nums and Kids - // arrays are searched backward for the same reason - if (node->dictLookup("Limits", &limits)->isArray() && - limits.arrayGetLength() == 2) { - if (limits.arrayGet(0, &limit)->isInt()) { - if (pageIndex < limit.getInt()) { - limit.free(); - limits.free(); - return gFalse; - } - } - limit.free(); + return; } - limits.free(); if (node->dictLookup("Nums", &nums)->isArray()) { - for (i = nums.arrayGetLength() / 2 - 1; i >= 0; --i) { - if (nums.arrayGet(2*i, &num)->isInt()) { - if (num.getInt() <= pageIndex) { - nums.arrayGet(2*i + 1, pageLabelObj); - *firstPageIndex = num.getInt(); - num.free(); - nums.free(); - return gTrue; + for (i = 0; i < nums.arrayGetLength() - 1; i += 2) { + if (nums.arrayGet(i, &num)->isInt()) { + if (nums.arrayGet(i+1, &labelObj)->isDict()) { + pageLabels->append(new PageLabelNode(num.getInt(), + labelObj.getDict())); } - } else { - error(errSyntaxError, -1, "Invalid key in page label number tree"); - num.free(); - nums.free(); - return gFalse; + labelObj.free(); } num.free(); } @@ -941,20 +945,61 @@ GBool Catalog::findPageLabel(Object *node, int pageIndex, nums.free(); if (node->dictLookup("Kids", &kids)->isArray()) { - for (i = kids.arrayGetLength() - 1; i >= 0; --i) { - if (kids.arrayGet(i, &kid)->isDict()) { - if (findPageLabel(&kid, pageIndex, pageLabelObj, firstPageIndex)) { - kid.free(); - kids.free(); - return gTrue; - } - } + for (i = 0; i < kids.arrayGetLength(); ++i) { + kids.arrayGet(i, &kid); + readPageLabelTree2(&kid); kid.free(); } } kids.free(); +} - return gFalse; +TextString *Catalog::getPageLabel(int pageNum) { + PageLabelNode *label; + TextString *ts; + int pageRangeNum; + GString *suffix; + + if (!pageLabels || !(label = findPageLabel(pageNum))) { + return NULL; + } + + ts = new TextString(label->prefix); + + pageRangeNum = label->start + (pageNum - label->firstPage); + + suffix = NULL; + if (label->style == 'D') { + suffix = GString::format("{0:d}", pageRangeNum); + } else if (label->style == 'R') { + suffix = makeRomanNumeral(pageRangeNum, gTrue); + } else if (label->style == 'r') { + suffix = makeRomanNumeral(pageRangeNum, gFalse); + } else if (label->style == 'A') { + suffix = makeLetterLabel(pageRangeNum, gTrue); + } else if (label->style == 'a') { + suffix = makeLetterLabel(pageRangeNum, gFalse); + } + if (suffix) { + ts->append(suffix); + delete suffix; + } + + return ts; +} + +PageLabelNode *Catalog::findPageLabel(int pageNum) { + PageLabelNode *label; + int i; + + //~ this could use a binary search + for (i = 0; i < pageLabels->getLength(); ++i) { + label = (PageLabelNode *)pageLabels->get(i); + if (pageNum >= label->firstPage && pageNum <= label->lastPage) { + return label; + } + } + return NULL; } GString *Catalog::makeRomanNumeral(int num, GBool uppercase) { @@ -1022,3 +1067,131 @@ GString *Catalog::makeLetterLabel(int num, GBool uppercase) { } return s; } + +int Catalog::getPageNumFromPageLabel(TextString *pageLabel) { + PageLabelNode *label; + int pageNum, prefixLength, i, n; + + if (!pageLabels) { + return -1; + } + for (i = 0; i < pageLabels->getLength(); ++i) { + label = (PageLabelNode *)pageLabels->get(i); + prefixLength = label->prefix->getLength(); + if (pageLabel->getLength() < prefixLength || + memcmp(pageLabel->getUnicode(), label->prefix->getUnicode(), + prefixLength * sizeof(Unicode))) { + continue; + } + if (label->style == '\0' && pageLabel->getLength() == prefixLength) { + return label->firstPage; + } + if (!convertPageLabelToInt(pageLabel, prefixLength, label->style, &n)) { + continue; + } + if (n < label->start) { + continue; + } + pageNum = label->firstPage + n - label->start; + if (pageNum <= label->lastPage) { + return pageNum; + } + } + return -1; +} + +// Attempts to convert pageLabel[prefixLength .. end] to an integer, +// following the specified page label style. If successful, sets *n +// and returns true; else returns false. +GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength, + char style, int *n) { + Unicode *u; + Unicode delta; + int len, i; + + len = pageLabel->getLength(); + if (len <= prefixLength) { + return gFalse; + } + u = pageLabel->getUnicode(); + if (style == 'D') { + *n = 0; + for (i = prefixLength; i < len; ++i) { + if (u[i] < (Unicode)'0' || u[i] > (Unicode)'9') { + return gFalse; + } + *n = *n * 10 + (u[i] - (Unicode)'0'); + } + return gTrue; + } else if (style == 'R' || style == 'r') { + delta = style - 'R'; + *n = 0; + i = prefixLength; + while (i < len && u[i] == (Unicode)'M' + delta) { + *n += 1000; + ++i; + } + if (i+1 < len && u[i] == (Unicode)'C' + delta && + u[i+1] == (Unicode)'M' + delta) { + *n += 900; + i += 2; + } else if (i < len && u[i] == (Unicode)'D' + delta) { + *n += 500; + ++i; + } else if (i+1 < len && u[i] == (Unicode)'C' + delta && + u[i+1] == (Unicode)'D' + delta) { + *n += 400; + i += 2; + } + while (i < len && u[i] == (Unicode)'C' + delta) { + *n += 100; + ++i; + } + if (i+1 < len && u[i] == (Unicode)'X' + delta && + u[i+1] == (Unicode)'C' + delta) { + *n += 90; + i += 2; + } else if (i < len && u[i] == (Unicode)'L' + delta) { + *n += 50; + ++i; + } else if (i+1 < len && u[i] == (Unicode)'X' + delta && + u[i+1] == (Unicode)'L' + delta) { + *n += 40; + i += 2; + } + while (i < len && u[i] == (Unicode)'X' + delta) { + *n += 10; + ++i; + } + if (i+1 < len && u[i] == (Unicode)'I' + delta && + u[i+1] == (Unicode)'X' + delta) { + *n += 9; + i += 2; + } else if (i < len && u[i] == (Unicode)'V' + delta) { + *n += 5; + ++i; + } else if (i+1 < len && u[i] == (Unicode)'I' + delta && + u[i+1] == (Unicode)'V' + delta) { + *n += 4; + i += 2; + } + while (i < len && u[i] == (Unicode)'I' + delta) { + *n += 1; + ++i; + } + return i == len; + } else if (style == 'A' || style == 'a') { + if (u[prefixLength] < (Unicode)style || + u[prefixLength] > (Unicode)style + 25) { + return gFalse; + } + for (i = prefixLength + 1; i < len; ++i) { + if (u[i] != u[prefixLength]) { + return gFalse; + } + } + *n = (len - prefixLength - 1) * 26 + (u[i] - (Unicode)style) + 1; + return gTrue; + } + return gFalse; +} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h index 803550d7f96..b775ae9b9b8 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h @@ -29,6 +29,7 @@ class PageAttrs; struct Ref; class LinkDest; class PageTreeNode; +class PageLabelNode; class Form; class TextString; @@ -103,10 +104,20 @@ public: Object *getEmbeddedFileStreamRef(int idx); Object *getEmbeddedFileStreamObj(int idx, Object *strObj); + // Return true if the document has page labels. + GBool hasPageLabels() { return pageLabels != NULL; } + // Get the page label for page number [pageNum]. Returns NULL if // the PDF file doesn't have page labels. TextString *getPageLabel(int pageNum); + // Returns the page number corresponding to [pageLabel]. Returns -1 + // if there is no matching page label, or if the document doesn't + // have page labels. + int getPageNumFromPageLabel(TextString *pageLabel); + + Object *getViewerPreferences() { return &viewerPrefs; } + private: PDFDoc *doc; @@ -129,6 +140,8 @@ private: Form *form; // parsed form Object ocProperties; // OCProperties dictionary GList *embeddedFiles; // embedded file list [EmbeddedFile] + GList *pageLabels; // page labels [PageLabelNode] + Object viewerPrefs; // ViewerPreferences object GBool ok; // true if catalog is valid Object *findDestInTree(Object *tree, GString *name, Object *obj); @@ -141,10 +154,13 @@ private: void readFileAttachmentAnnots(Object *pageNodeRef, char *touchedObjs); void readEmbeddedFile(Object *fileSpec, Object *name1); - GBool findPageLabel(Object *node, int pageIndex, - Object *pageLabelObj, int *firstPageIndex); + void readPageLabelTree(Object *root); + void readPageLabelTree2(Object *node); + PageLabelNode *findPageLabel(int pageNum); GString *makeRomanNumeral(int num, GBool uppercase); GString *makeLetterLabel(int num, GBool uppercase); + GBool convertPageLabelToInt(TextString *pageLabel, int prefixLength, + char style, int *n); }; #endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc index 72cadd9bed1..82f32b9dd93 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc @@ -41,7 +41,8 @@ Function::Function() { Function::~Function() { } -Function *Function::parse(Object *funcObj, int recursion) { +Function *Function::parse(Object *funcObj, int expectedInputs, + int expectedOutputs, int recursion) { Function *func; Dict *dict; int funcType; @@ -57,7 +58,11 @@ Function *Function::parse(Object *funcObj, int recursion) { } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { - return new IdentityFunction(); + if (expectedInputs != expectedOutputs) { + error(errSyntaxError, -1, "Invalid use of identity function"); + return NULL; + } + return new IdentityFunction(expectedInputs); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return NULL; @@ -76,7 +81,8 @@ Function *Function::parse(Object *funcObj, int recursion) { } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { - func = new StitchingFunction(funcObj, dict, recursion); + func = new StitchingFunction(funcObj, dict, expectedInputs, + expectedOutputs, recursion); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { @@ -88,6 +94,14 @@ Function *Function::parse(Object *funcObj, int recursion) { return NULL; } + if (func->getInputSize() != expectedInputs || + (expectedOutputs >= 0 && func->getOutputSize() != expectedOutputs)) { + error(errSyntaxError, -1, + "Incorrect number of function inputs or outputs"); + delete func; + return NULL; + } + return func; } @@ -169,14 +183,12 @@ GBool Function::init(Dict *dict) { // IdentityFunction //------------------------------------------------------------------------ -IdentityFunction::IdentityFunction() { +IdentityFunction::IdentityFunction(int nInputs) { int i; - // fill these in with arbitrary values just in case they get used - // somewhere - m = funcMaxInputs; - n = funcMaxOutputs; - for (i = 0; i < funcMaxInputs; ++i) { + m = n = nInputs; + // domain info shouldn't be used anywhere + for (i = 0; i < nInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } @@ -189,7 +201,7 @@ IdentityFunction::~IdentityFunction() { void IdentityFunction::transform(double *in, double *out) { int i; - for (i = 0; i < funcMaxOutputs; ++i) { + for (i = 0; i < m; ++i) { out[i] = in[i]; } } @@ -347,8 +359,13 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { //----- samples nSamples = n; - for (i = 0; i < m; ++i) + for (i = 0; i < m; ++i) { + if (nSamples > INT_MAX / sampleSize[i]) { + error(errSyntaxError, -1, "Integer overflow in sampled function setup"); + goto err1; + } nSamples *= sampleSize[i]; + } samples = (double *)gmallocn(nSamples, sizeof(double)); buf = 0; bits = 0; @@ -623,6 +640,7 @@ void ExponentialFunction::transform(double *in, double *out) { //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, + int expectedInputs, int expectedOutputs, int recursion) { Object obj1, obj2; int i; @@ -643,7 +661,8 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, } //----- Functions - if (!dict->lookup("Functions", &obj1)->isArray()) { + if (!dict->lookup("Functions", &obj1)->isArray() || + obj1.arrayGetLength() < 1) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); goto err1; @@ -658,11 +677,14 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, } for (i = 0; i < k; ++i) { if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), + expectedInputs, expectedOutputs, recursion + 1))) { goto err2; } - if (funcs[i]->getInputSize() != 1 || - (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { + if (i == 0) { + n = funcs[0]->getOutputSize(); + } + if (funcs[i]->getInputSize() != 1 || funcs[i]->getOutputSize() != n) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); goto err2; @@ -1305,6 +1327,9 @@ int PostScriptFunction::exec(double *stack, int sp0) { if (sp + 1 >= psStackSize) { goto underflow; } + if (stack[sp] == 0) { + goto invalidArg; + } stack[sp + 1] = stack[sp + 1] / stack[sp]; ++sp; break; @@ -1371,7 +1396,11 @@ int PostScriptFunction::exec(double *stack, int sp0) { if (sp + 1 >= psStackSize) { goto underflow; } - stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; + k = (int)stack[sp]; + if (k == 0) { + goto invalidArg; + } + stack[sp + 1] = (int)stack[sp + 1] / k; ++sp; break; case psOpIndex: @@ -1417,7 +1446,11 @@ int PostScriptFunction::exec(double *stack, int sp0) { if (sp + 1 >= psStackSize) { goto underflow; } - stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; + k = (int)stack[sp]; + if (k == 0) { + goto invalidArg; + } + stack[sp + 1] = (int)stack[sp + 1] % k; ++sp; break; case psOpMul: @@ -1468,22 +1501,24 @@ int PostScriptFunction::exec(double *stack, int sp0) { if (nn < 0) { goto invalidArg; } - if (sp + nn > psStackSize) { - goto underflow; - } - if (k >= 0) { - k %= nn; - } else { - k = -k % nn; - if (k) { - k = nn - k; + if (nn > 0) { + if (sp + nn > psStackSize) { + goto underflow; + } + if (k >= 0) { + k %= nn; + } else { + k = -k % nn; + if (k) { + k = nn - k; + } + } + for (i = 0; i < nn; ++i) { + tmp[i] = stack[sp + i]; + } + for (i = 0; i < nn; ++i) { + stack[sp + i] = tmp[(i + k) % nn]; } - } - for (i = 0; i < nn; ++i) { - tmp[i] = stack[sp + i]; - } - for (i = 0; i < nn; ++i) { - stack[sp + i] = tmp[(i + k) % nn]; } break; case psOpRound: diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h index 5cf45169a49..bf1f25e51cc 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h @@ -38,8 +38,11 @@ public: virtual ~Function(); - // Construct a function. Returns NULL if unsuccessful. - static Function *parse(Object *funcObj, int recursion = 0); + // Construct a function, with [expectedInputs] inputs and + // [expectedOutputs] outputs. [expectedOutputs] can be -1 to + // indicate unknown. Returns NULL if unsuccessful. + static Function *parse(Object *funcObj, int expectedInputs, + int expectedOutputs, int recursion = 0); // Initialize the entries common to all function types. GBool init(Dict *dict); @@ -86,9 +89,9 @@ protected: class IdentityFunction: public Function { public: - IdentityFunction(); + IdentityFunction(int nInputs); virtual ~IdentityFunction(); - virtual Function *copy() { return new IdentityFunction(); } + virtual Function *copy() { return new IdentityFunction(m); } virtual int getType() { return -1; } virtual void transform(double *in, double *out); virtual GBool isOk() { return gTrue; } @@ -173,7 +176,8 @@ private: class StitchingFunction: public Function { public: - StitchingFunction(Object *funcObj, Dict *dict, int recursion); + StitchingFunction(Object *funcObj, Dict *dict, int expectedInputs, + int expectedOutputs, int recursion); virtual ~StitchingFunction(); virtual Function *copy() { return new StitchingFunction(this); } virtual int getType() { return 3; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc index 5d34eb9f9ab..7a0c9c6a9a5 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc @@ -919,12 +919,24 @@ void Gfx::opSetFlat(Object args[], int numArgs) { } void Gfx::opSetLineJoin(Object args[], int numArgs) { - state->setLineJoin(args[0].getInt()); + int lineJoin; + + lineJoin = args[0].getInt(); + if (lineJoin < 0 || lineJoin > 2) { + lineJoin = 0; + } + state->setLineJoin(lineJoin); out->updateLineJoin(state); } void Gfx::opSetLineCap(Object args[], int numArgs) { - state->setLineCap(args[0].getInt()); + int lineCap; + + lineCap = args[0].getInt(); + if (lineCap < 0 || lineCap > 2) { + lineCap = 0; + } + state->setLineCap(lineCap); out->updateLineCap(state); } @@ -1041,17 +1053,17 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { // fill/stroke overprint, overprint mode if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { - if (!state->getInCachedT3Char()) { + if (!state->getIgnoreColorOps()) { state->setFillOverprint(obj2.getBool()); out->updateFillOverprint(state); } else { - error(errSyntaxWarning, getPos(), - "Ignoring overprint setting in cached Type 3 character"); + error(errSyntaxWarning, getPos(), "Ignoring overprint setting" + " in uncolored Type 3 char or tiling pattern"); } } obj2.free(); if (obj1.dictLookup("OP", &obj2)->isBool()) { - if (!state->getInCachedT3Char()) { + if (!state->getIgnoreColorOps()) { state->setStrokeOverprint(obj2.getBool()); out->updateStrokeOverprint(state); if (!haveFillOP) { @@ -1059,18 +1071,18 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { out->updateFillOverprint(state); } } else { - error(errSyntaxWarning, getPos(), - "Ignoring overprint setting in cached Type 3 character"); + error(errSyntaxWarning, getPos(), "Ignoring overprint setting" + " in uncolored Type 3 char or tiling pattern"); } } obj2.free(); if (obj1.dictLookup("OPM", &obj2)->isInt()) { - if (!state->getInCachedT3Char()) { + if (!state->getIgnoreColorOps()) { state->setOverprintMode(obj2.getInt()); out->updateOverprintMode(state); } else { - error(errSyntaxWarning, getPos(), - "Ignoring overprint setting in cached Type 3 character"); + error(errSyntaxWarning, getPos(), "Ignoring overprint setting" + " in uncolored Type 3 char or tiling pattern"); } } obj2.free(); @@ -1088,7 +1100,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { obj1.dictLookup("TR", &obj2); } if (!obj2.isNull()) { - if (!state->getInCachedT3Char()) { + if (!state->getIgnoreColorOps()) { if (obj2.isName("Default") || obj2.isName("Identity")) { funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL; @@ -1097,7 +1109,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { } else if (obj2.isArray() && obj2.arrayGetLength() == 4) { for (i = 0; i < 4; ++i) { obj2.arrayGet(i, &obj3); - funcs[i] = Function::parse(&obj3); + funcs[i] = Function::parse(&obj3, 1, 1); obj3.free(); if (!funcs[i]) { break; @@ -1108,7 +1120,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { out->updateTransfer(state); } } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) { - if ((funcs[0] = Function::parse(&obj2))) { + if ((funcs[0] = Function::parse(&obj2, 1, 1))) { funcs[1] = funcs[2] = funcs[3] = NULL; state->setTransfer(funcs); out->updateTransfer(state); @@ -1118,8 +1130,8 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { "Invalid transfer function in ExtGState"); } } else { - error(errSyntaxWarning, getPos(), - "Ignoring transfer function setting in cached Type 3 character"); + error(errSyntaxWarning, getPos(), "Ignoring transfer function setting" + " in uncolored Type 3 char or tiling pattern"); } } obj2.free(); @@ -1129,9 +1141,14 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { if (obj2.isName("None")) { out->clearSoftMask(state); } else if (obj2.isDict()) { - if (obj2.dictLookup("S", &obj3)->isName("Alpha")) { + obj2.dictLookup("S", &obj3); + if (obj3.isName("Alpha")) { alpha = gTrue; - } else { // "Luminosity" + } else if (obj3.isName("Luminosity")) { + alpha = gFalse; + } else { + error(errSyntaxError, getPos(), + "Missing S (subtype) entry in soft mask"); alpha = gFalse; } obj3.free(); @@ -1141,9 +1158,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { obj3.isName("Identity")) { funcs[0] = NULL; } else { - funcs[0] = Function::parse(&obj3); - if (funcs[0]->getInputSize() != 1 || - funcs[0]->getOutputSize() != 1) { + if (!(funcs[0] = Function::parse(&obj3, 1, 1))) { error(errSyntaxError, getPos(), "Invalid transfer function in soft mask in ExtGState"); delete funcs[0]; @@ -1290,9 +1305,9 @@ void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha, void Gfx::opSetRenderingIntent(Object args[], int numArgs) { GfxRenderingIntent ri; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring rendering intent setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting" + " in uncolored Type 3 char or tiling pattern"); return; } ri = parseRenderingIntent(args[0].getName()); @@ -1320,9 +1335,9 @@ GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) { void Gfx::opSetFillGray(Object args[], int numArgs) { GfxColor color; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setFillPattern(NULL); @@ -1336,9 +1351,9 @@ void Gfx::opSetFillGray(Object args[], int numArgs) { void Gfx::opSetStrokeGray(Object args[], int numArgs) { GfxColor color; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setStrokePattern(NULL); @@ -1353,9 +1368,9 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { GfxColor color; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setFillPattern(NULL); @@ -1372,9 +1387,9 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { GfxColor color; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setStrokePattern(NULL); @@ -1391,9 +1406,9 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) { GfxColor color; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setFillPattern(NULL); @@ -1410,9 +1425,9 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { GfxColor color; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setStrokePattern(NULL); @@ -1430,9 +1445,9 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) { GfxColorSpace *colorSpace; GfxColor color; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color space setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color space setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setFillPattern(NULL); @@ -1461,9 +1476,9 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { GfxColorSpace *colorSpace; GfxColor color; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color space setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color space setting" + " in uncolored Type 3 char or tiling pattern"); return; } state->setStrokePattern(NULL); @@ -1491,9 +1506,9 @@ void Gfx::opSetFillColor(Object args[], int numArgs) { GfxColor color; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } if (numArgs != state->getFillColorSpace()->getNComps()) { @@ -1531,9 +1546,9 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) { GfxPattern *pattern; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } if (state->getFillColorSpace()->getMode() == csPattern) { @@ -1585,9 +1600,9 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) { GfxPattern *pattern; int i; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring color setting in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring color setting" + " in uncolored Type 3 char or tiling pattern"); return; } if (state->getStrokeColorSpace()->getMode() == csPattern) { @@ -2068,6 +2083,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, state->setStrokeColor(state->getFillColor()); out->updateFillColor(state); out->updateStrokeColor(state); + state->setIgnoreColorOps(gTrue); } else { state->setFillColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateFillColorSpace(state); @@ -2223,6 +2239,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, if (stroke) { state->clipToStrokePath(); out->clipToStrokePath(state); + state->setFillOverprint(state->getStrokeOverprint()); } else if (!text) { state->clip(); if (eoFill) { @@ -2241,6 +2258,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; if (fabs(det) <= 1e-10) { error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill"); + restoreStateStack(savedState); return; } det = 1 / det; @@ -2338,9 +2356,9 @@ void Gfx::opShFill(Object args[], int numArgs) { GfxState *savedState; double xMin, yMin, xMax, yMax; - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring shaded fill in cached Type 3 character"); + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring shaded fill" + " in uncolored Type 3 char or tiling pattern"); return; } @@ -4123,7 +4141,7 @@ void Gfx::opXObject(Object args[], int numArgs) { ocState = ocSaved; } -void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { +GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { Dict *dict, *maskDict; int width, height; int bits, maskBits; @@ -4145,7 +4163,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { // check for optional content if (!ocState && !inlineImg) { - return; + return gTrue; } // get info from the stream @@ -4309,6 +4327,11 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { if (!colorSpace) { goto err1; } + if (colorSpace->getMode() == csPattern) { + error(errSyntaxError, getPos(), "Image with a Pattern color space"); + delete colorSpace; + goto err1; + } dict->lookup("Decode", &obj1); if (obj1.isNull()) { obj1.free(); @@ -4365,6 +4388,12 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } maskHeight = obj1.getInt(); obj1.free(); + if (maskWidth <= 0 || maskHeight <= 0) { + delete colorMap; + maskObj.free(); + smaskObj.free(); + goto err1; + } maskDict->lookup("BitsPerComponent", &obj1); if (obj1.isNull()) { obj1.free(); @@ -4378,6 +4407,12 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } maskBits = obj1.getInt(); obj1.free(); + if (maskBits < 1 || maskBits > 16) { + delete colorMap; + maskObj.free(); + smaskObj.free(); + goto err1; + } maskDict->lookup("ColorSpace", &obj1); if (obj1.isNull()) { obj1.free(); @@ -4501,6 +4536,12 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } maskHeight = obj1.getInt(); obj1.free(); + if (maskWidth <= 0 || maskHeight <= 0) { + delete colorMap; + maskObj.free(); + smaskObj.free(); + goto err2; + } maskDict->lookup("ImageMask", &obj1); if (obj1.isNull()) { obj1.free(); @@ -4534,10 +4575,10 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } // if drawing is disabled, skip over inline image data - if (state->getInCachedT3Char() || !ocState) { - if (state->getInCachedT3Char()) { - error(errSyntaxWarning, getPos(), - "Ignoring image in cached Type 3 character"); + if (state->getIgnoreColorOps() || !ocState) { + if (state->getIgnoreColorOps()) { + error(errSyntaxWarning, getPos(), "Ignoring image" + " in uncolored Type 3 char or tiling pattern"); } if (inlineImg) { str->reset(); @@ -4582,7 +4623,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } opCounter += i; - return; + return gTrue; err2: obj1.free(); @@ -4594,6 +4635,8 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { state->setRenderingIntent(riSaved); out->updateRenderingIntent(state); } + + return gFalse; } void Gfx::doForm(Object *strRef, Object *str) { @@ -4826,11 +4869,12 @@ void Gfx::opBeginImage(Object args[], int numArgs) { // display the image if (str) { - doImage(NULL, str, gTrue); + if (!doImage(NULL, str, gTrue)) { + delete str; // if we have the stream length, skip to end-of-stream and then // skip 'EI' in the original stream - if (haveLength) { + } else if (haveLength) { while ((c1 = str->getChar()) != EOF) ; delete str; str = parser->getStream(); @@ -4935,7 +4979,7 @@ void Gfx::opSetCharWidth(Object args[], int numArgs) { } void Gfx::opSetCacheDevice(Object args[], int numArgs) { - state->setInCachedT3Char(gTrue); + state->setIgnoreColorOps(gTrue); out->type3D1(state, args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h index 9d175825f97..2159eaaf2e9 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h @@ -330,7 +330,7 @@ private: // XObject operators void opXObject(Object args[], int numArgs); - void doImage(Object *ref, Stream *str, GBool inlineImg); + GBool doImage(Object *ref, Stream *str, GBool inlineImg); void doForm(Object *strRef, Object *str); // in-line image operators diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc index 640cf81db02..b10c81779ff 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc @@ -164,7 +164,8 @@ GfxFontLoc::~GfxFontLoc() { // GfxFont //------------------------------------------------------------------------ -GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { +GfxFont *GfxFont::makeFont(XRef *xref, const char *tagA, + Ref idA, Dict *fontDict) { GString *nameA; Ref embFontIDA; GfxFontType typeA; @@ -197,7 +198,7 @@ GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { return font; } -GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA, +GfxFont::GfxFont(const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA) { ok = gFalse; tag = new GString(tagA); @@ -541,6 +542,7 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) { GfxFontLoc *fontLoc; SysFontType sysFontType; + FoFiIdentifierType fft; GString *path, *base14Name, *substName; PSFontParam16 *psFont16; Object refObj, embFontObj; @@ -635,32 +637,43 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) { //----- system font if (name && (path = globalParams->findSystemFontFile(name, &sysFontType, &fontNum))) { + fontLoc = new GfxFontLoc(); + fontLoc->locType = gfxFontLocExternal; + fontLoc->path = path; + fontLoc->fontNum = fontNum; if (isCIDFont()) { if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { - fontLoc = new GfxFontLoc(); - fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontCIDType2; - fontLoc->path = path; - fontLoc->fontNum = fontNum; return fontLoc; + } else if (sysFontType == sysFontOTF) { + fft = FoFiIdentifier::identifyFile(fontLoc->path->getCString()); + if (fft == fofiIdOpenTypeCFFCID) { + fontLoc->fontType = fontCIDType0COT; + return fontLoc; + } else if (fft == fofiIdTrueType) { + fontLoc->fontType = fontCIDType2; + return fontLoc; + } } } else { if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { - fontLoc = new GfxFontLoc(); - fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontTrueType; - fontLoc->path = path; - fontLoc->fontNum = fontNum; return fontLoc; } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) { - fontLoc = new GfxFontLoc(); - fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontType1; - fontLoc->path = path; return fontLoc; + } else if (sysFontType == sysFontOTF) { + fft = FoFiIdentifier::identifyFile(fontLoc->path->getCString()); + if (fft == fofiIdOpenTypeCFF8Bit) { + fontLoc->fontType = fontType1COT; + return fontLoc; + } else if (fft == fofiIdTrueType) { + fontLoc->fontType = fontTrueTypeOT; + return fontLoc; + } } } - delete path; + delete fontLoc; } if (!isCIDFont()) { @@ -863,7 +876,7 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) { // Gfx8BitFont //------------------------------------------------------------------------ -Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict): GfxFont(tagA, idA, nameA, typeA, embFontIDA) { @@ -1490,6 +1503,33 @@ int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) { return map; } +int *Gfx8BitFont::getCodeToGIDMap(FoFiType1C *ff) { + int *map; + GHash *nameToGID; + int i, gid; + + map = (int *)gmallocn(256, sizeof(int)); + for (i = 0; i < 256; ++i) { + map[i] = 0; + } + + nameToGID = ff->getNameToGIDMap(); + for (i = 0; i < 256; ++i) { + if (!enc[i]) { + continue; + } + gid = nameToGID->lookupInt(enc[i]); + if (gid < 0 || gid >= 65536) { + continue; + } + map[i] = gid; + } + + delete nameToGID; + + return map; +} + Dict *Gfx8BitFont::getCharProcs() { return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL; } @@ -1575,7 +1615,7 @@ GBool Gfx8BitFont::problematicForUnicode() { // GfxCIDFont //------------------------------------------------------------------------ -GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict): GfxFont(tagA, idA, nameA, typeA, embFontIDA) { @@ -2001,6 +2041,13 @@ GString *GfxCIDFont::getCollection() { return cMap ? cMap->getCollection() : (GString *)NULL; } +double GfxCIDFont::getWidth(CID cid) { + double w; + + getHorizontalMetrics(cid, &w); + return w; +} + GBool GfxCIDFont::problematicForUnicode() { GString *nameLC; GBool symbolic; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h index b2d8637a46d..3b63fa69fe0 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h @@ -26,6 +26,7 @@ class Dict; class CMap; class CharCodeToUnicode; class FoFiTrueType; +class FoFiType1C; struct GfxFontCIDWidths; struct Base14FontMapEntry; class FNVHash; @@ -131,9 +132,10 @@ class GfxFont { public: // Build a GfxFont object. - static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict); + static GfxFont *makeFont(XRef *xref, const char *tagA, + Ref idA, Dict *fontDict); - GfxFont(char *tagA, Ref idA, GString *nameA, + GfxFont(const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA); virtual ~GfxFont(); @@ -243,7 +245,7 @@ protected: class Gfx8BitFont: public GfxFont { public: - Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, + Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); virtual ~Gfx8BitFont(); @@ -274,6 +276,10 @@ public: // (This is only useful for TrueType fonts.) int *getCodeToGIDMap(FoFiTrueType *ff); + // Return a char code-to-GID mapping for the provided font file. + // (This is only useful for Type1C fonts.) + int *getCodeToGIDMap(FoFiType1C *ff); + // Return the Type 3 CharProc dictionary, or NULL if none. Dict *getCharProcs(); @@ -311,7 +317,7 @@ private: class GfxCIDFont: public GfxFont { public: - GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, + GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); virtual ~GfxCIDFont(); @@ -331,6 +337,9 @@ public: // Get the collection name (<registry>-<ordering>). GString *getCollection(); + // Return the horizontal width for <cid>. + double getWidth(CID cid); + // Return the CID-to-GID mapping table. These should only be called // if type is fontCIDType2. int *getCIDToGID() { return cidToGID; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc index 9a43608c2fe..60b5f577054 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc @@ -1281,7 +1281,7 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, } obj1.free(); arr->get(3, &obj1); - if (!(funcA = Function::parse(&obj1))) { + if (!(funcA = Function::parse(&obj1, 1, altA->getNComps()))) { goto err4; } obj1.free(); @@ -1476,7 +1476,7 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, } obj1.free(); arr->get(3, &obj1); - if (!(funcA = Function::parse(&obj1))) { + if (!(funcA = Function::parse(&obj1, nCompsA, altA->getNComps()))) { goto err4; } obj1.free(); @@ -2117,14 +2117,14 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!(funcsA[i] = Function::parse(&obj2))) { + if (!(funcsA[i] = Function::parse(&obj2, 2, 1))) { goto err2; } obj2.free(); } } else { nFuncsA = 1; - if (!(funcsA[0] = Function::parse(&obj1))) { + if (!(funcsA[0] = Function::parse(&obj1, 2, -1))) { goto err1; } } @@ -2137,6 +2137,16 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict delete shading; return NULL; } + + for (i = 0; i < shading->nFuncs; ++i) { + if (shading->funcs[i]->getOutputSize() + != shading->getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Invalid function in shading dictionary"); + delete shading; + return NULL; + } + } + return shading; err2: @@ -2274,7 +2284,7 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!(funcsA[i] = Function::parse(&obj2))) { + if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) { obj1.free(); obj2.free(); goto err1; @@ -2283,7 +2293,7 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict } } else { nFuncsA = 1; - if (!(funcsA[0] = Function::parse(&obj1))) { + if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) { obj1.free(); goto err1; } @@ -2307,6 +2317,16 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict delete shading; return NULL; } + + for (i = 0; i < shading->nFuncs; ++i) { + if (shading->funcs[i]->getOutputSize() + != shading->getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Invalid function in shading dictionary"); + delete shading; + return NULL; + } + } + return shading; err1: @@ -2446,7 +2466,7 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!(funcsA[i] = Function::parse(&obj2))) { + if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) { obj1.free(); obj2.free(); goto err1; @@ -2455,7 +2475,7 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict } } else { nFuncsA = 1; - if (!(funcsA[0] = Function::parse(&obj1))) { + if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) { obj1.free(); goto err1; } @@ -2479,6 +2499,16 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict delete shading; return NULL; } + + for (i = 0; i < shading->nFuncs; ++i) { + if (shading->funcs[i]->getOutputSize() + != shading->getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Invalid function in shading dictionary"); + delete shading; + return NULL; + } + } + return shading; err1: @@ -2719,7 +2749,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!(funcsA[i] = Function::parse(&obj2))) { + if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) { obj1.free(); obj2.free(); goto err1; @@ -2728,7 +2758,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( } } else { nFuncsA = 1; - if (!(funcsA[0] = Function::parse(&obj1))) { + if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) { obj1.free(); goto err1; } @@ -2832,6 +2862,16 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( delete shading; return NULL; } + + for (i = 0; i < shading->nFuncs; ++i) { + if (shading->funcs[i]->getOutputSize() + != shading->getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Invalid function in shading dictionary"); + delete shading; + return NULL; + } + } + return shading; err2: @@ -3017,7 +3057,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!(funcsA[i] = Function::parse(&obj2))) { + if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) { obj1.free(); obj2.free(); goto err1; @@ -3026,7 +3066,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, } } else { nFuncsA = 1; - if (!(funcsA[0] = Function::parse(&obj1))) { + if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) { obj1.free(); goto err1; } @@ -3044,6 +3084,12 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, if (!bitBuf->getBits(flagBits, &flag)) { break; } + flag &= 3; + if (flag != 0 && nPatchesA == 0) { + error(errSyntaxError, -1, "Invalid patch in patch mesh shading"); + delete bitBuf; + goto err1; + } if (typeA == 6) { switch (flag) { case 0: nPts = 12; nColors = 4; break; @@ -3445,6 +3491,16 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, delete shading; return NULL; } + + for (i = 0; i < shading->nFuncs; ++i) { + if (shading->funcs[i]->getOutputSize() + != shading->getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Invalid function in shading dictionary"); + delete shading; + return NULL; + } + } + return shading; err2: @@ -4122,7 +4178,7 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox, clipXMax = pageWidth; clipYMax = pageHeight; - inCachedT3Char = gFalse; + ignoreColorOps = gFalse; saved = NULL; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h index 8e5d3b0b7a5..50507040fed 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h @@ -1183,7 +1183,7 @@ public: void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax); double getLineX() { return lineX; } double getLineY() { return lineY; } - GBool getInCachedT3Char() { return inCachedT3Char; } + GBool getIgnoreColorOps() { return ignoreColorOps; } // Is there a current point/path? GBool isCurPt() { return path->isCurPt(); } @@ -1280,8 +1280,9 @@ public: void textShift(double tx, double ty); void shift(double dx, double dy); - // Cached Type 3 char status. - void setInCachedT3Char(GBool in) { inCachedT3Char = in; } + // Ignore color operators (in cached/uncolored Type 3 chars, and + // uncolored tiling patterns). Cached Type 3 char status. + void setIgnoreColorOps(GBool ignore) { ignoreColorOps = ignore; } // Push/pop GfxState on/off stack. GfxState *save(); @@ -1344,7 +1345,9 @@ private: double clipXMin, clipYMin, // bounding box for clip region clipXMax, clipYMax; - GBool inCachedT3Char; // in a cached (uncolored) Type 3 char + GBool ignoreColorOps; // ignore color ops (in cached/uncolored + // Type 3 chars, and uncolored tiling + // patterns) GfxState *saved; // next GfxState on stack diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc index 2205a621322..56bfba8e3cc 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc @@ -17,13 +17,18 @@ #include <string.h> #include <stdio.h> +#include <stdarg.h> #include <ctype.h> +#include <time.h> #ifdef _WIN32 # include <shlobj.h> #endif #if HAVE_PAPER_H #include <paper.h> #endif +#if HAVE_FONTCONFIG +# include <fontconfig/fontconfig.h> +#endif #include "gmem.h" #include "gmempp.h" #include "GString.h" @@ -91,14 +96,14 @@ static struct { {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", "Courier", "Courier Bold Oblique", "Courier-Bold", 0.212557}, {"Courier-Oblique", "n022023l.pfb", "couri.ttf", "Courier", "Courier Oblique", "Courier", 0.212557}, {"Helvetica", "n019003l.pfb", "arial.ttf", "Helvetica", "Helvetica", NULL, 0}, - {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica-Bold", NULL, 0}, + {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica Bold", NULL, 0}, {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", "Helvetica", "Helvetica Bold Oblique", "Helvetica-Bold", 0.212557}, {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", "Helvetica", "Helvetica Oblique", "Helvetica", 0.212557}, {"Symbol", "s050000l.pfb", NULL, "Symbol", "Symbol", NULL, 0}, - {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times-Bold", NULL, 0}, - {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times-BoldItalic", NULL, 0}, - {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times-Italic", NULL, 0}, - {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times-Roman", NULL, 0}, + {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times Bold", NULL, 0}, + {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times Bold Italic", NULL, 0}, + {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times Italic", NULL, 0}, + {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times Roman", NULL, 0}, {"ZapfDingbats", "d050000l.pfb", NULL, "ZapfDingbats", "Zapf Dingbats", NULL, 0}, {NULL} }; @@ -226,6 +231,10 @@ public: void scanWindowsFonts(char *winFontDir); #endif +#if HAVE_FONTCONFIG + void scanFontconfigFonts(); +#endif + private: #ifdef _WIN32 @@ -458,6 +467,8 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { type = sysFontTTC; + } else if (!strcasecmp(path + strlen(path) - 4, ".otf")) { + type = sysFontOTF; } else { type = sysFontTTF; } @@ -465,6 +476,109 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, } #endif +#if HAVE_FONTCONFIG +void SysFontList::scanFontconfigFonts() { + FcPattern *pattern; + FcObjectSet *objSet; + FcFontSet *fontSet; + char *family, *file, *styleLang, *style; + GString *family2; + SysFontType type; + GBool bold, italic; + char c; + int fontNum, i, j, n; + + FcInit(); + + pattern = FcPatternBuild(NULL, + FC_OUTLINE, FcTypeBool, FcTrue, + FC_SCALABLE, FcTypeBool, FcTrue, + NULL); + objSet = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_STYLELANG, + FC_FILE, FC_INDEX, NULL); + fontSet = FcFontList(NULL, pattern, objSet); + FcPatternDestroy(pattern); + FcObjectSetDestroy(objSet); + + if (fontSet) { + for (i = 0; i < fontSet->nfont; ++i) { + + //--- font file, font type + if (FcPatternGetString(fontSet->fonts[i], FC_FILE, 0, + (FcChar8 **)&file) + != FcResultMatch) { + continue; + } + n = (int)strlen(file); + if (n > 4 && !strcasecmp(file + n - 4, ".pfa")) { + type = sysFontPFA; + } else if (n > 4 && !strcasecmp(file + n - 4, ".pfb")) { + type = sysFontPFB; + } else if (n > 4 && !strcasecmp(file + n - 4, ".ttf")) { + type = sysFontTTF; + } else if (n > 4 && !strcasecmp(file + n - 4, ".otf")) { + type = sysFontOTF; + } else { + continue; + } + + //--- font number + if (FcPatternGetInteger(fontSet->fonts[i], FC_INDEX, 0, &fontNum) + != FcResultMatch) { + fontNum = 0; + } + + //--- font family + if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, + (FcChar8 **)&family) + != FcResultMatch) { + continue; + } + + //----- normalize the font name + family2 = new GString(family); + j = 0; + while (j < family2->getLength()) { + c = family2->getChar(j); + if (c == ' ' || c == ',' || c == '-') { + family2->del(j); + } else { + ++j; + } + } + + //--- font style + style = NULL; + for (j = 0; + FcPatternGetString(fontSet->fonts[i], FC_STYLELANG, j, + (FcChar8 **)&styleLang) + == FcResultMatch; + ++j) { + if (!strcmp(styleLang, "en")) { + if (FcPatternGetString(fontSet->fonts[i], FC_STYLE, j, + (FcChar8 **)&style) + != FcResultMatch) { + style = NULL; + } + break; + } + ++j; + } + bold = style && strstr(style, "Bold") != NULL; + italic = style && (strstr(style, "Italic") != NULL || + strstr(style, "Oblique") != NULL); + + fonts->append(new SysFontInfo(family2, bold, italic, + new GString(file), type, fontNum)); + } + + FcFontSetDestroy(fontSet); + } + + FcFini(); +} +#endif // HAVE_FONTCONFIG + //------------------------------------------------------------------------ // KeyBinding //------------------------------------------------------------------------ @@ -619,7 +733,10 @@ GlobalParams::GlobalParams(const char *cfgFileName) { textKeepTinyChars = gTrue; initialZoom = new GString("125"); defaultFitZoom = 0; + initialDisplayMode = new GString("continuous"); + initialToolbarState = gTrue; initialSidebarState = gTrue; + initialSelectMode = new GString("linear"); maxTileWidth = 1500; maxTileHeight = 1500; tileCacheSize = 10; @@ -644,6 +761,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) { paperColor = new GString("#ffffff"); matteColor = new GString("#808080"); fullScreenMatteColor = new GString("#000000"); + reverseVideoInvertImages = gFalse; launchCommand = NULL; movieCommand = NULL; defaultPrinter = NULL; @@ -657,6 +775,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) { tabStateFile = appendToPath(getHomeDir(), ".xpdf.tab-state"); printCommands = gFalse; errQuiet = gFalse; + debugLogFile = NULL; cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); unicodeToUnicodeCache = @@ -787,6 +906,9 @@ void GlobalParams::createDefaultKeyBindings() { xpdfKeyContextAny, "newWindow")); keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl, xpdfKeyContextAny, "closeTabOrQuit")); + keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl, + xpdfKeyContextAny, + "toggleFullScreenMode")); keyBindings->append(new KeyBinding('q', xpdfKeyModCtrl, xpdfKeyContextAny, "quit")); keyBindings->append(new KeyBinding(xpdfKeyCodeTab, xpdfKeyModCtrl, @@ -1022,9 +1144,18 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { parseString("initialZoom", &initialZoom, tokens, fileName, line); } else if (!cmd->cmp("defaultFitZoom")) { parseInteger("defaultFitZoom", &defaultFitZoom, tokens, fileName, line); + } else if (!cmd->cmp("initialDisplayMode")) { + parseString("initialDisplayMode", &initialDisplayMode, + tokens, fileName, line); + } else if (!cmd->cmp("initialToolbarState")) { + parseYesNo("initialToolbarState", &initialToolbarState, + tokens, fileName, line); } else if (!cmd->cmp("initialSidebarState")) { parseYesNo("initialSidebarState", &initialSidebarState, tokens, fileName, line); + } else if (!cmd->cmp("initialSelectMode")) { + parseString("initialSelectMode", &initialSelectMode, + tokens, fileName, line); } else if (!cmd->cmp("maxTileWidth")) { parseInteger("maxTileWidth", &maxTileWidth, tokens, fileName, line); } else if (!cmd->cmp("maxTileHeight")) { @@ -1086,6 +1217,9 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { } else if (!cmd->cmp("fullScreenMatteColor")) { parseString("fullScreenMatteColor", &fullScreenMatteColor, tokens, fileName, line); + } else if (!cmd->cmp("reverseVideoInvertImages")) { + parseYesNo("reverseVideoInvertImages", &reverseVideoInvertImages, + tokens, fileName, line); } else if (!cmd->cmp("launchCommand")) { parseString("launchCommand", &launchCommand, tokens, fileName, line); } else if (!cmd->cmp("movieCommand")) { @@ -1118,6 +1252,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { parseYesNo("printCommands", &printCommands, tokens, fileName, line); } else if (!cmd->cmp("errQuiet")) { parseYesNo("errQuiet", &errQuiet, tokens, fileName, line); + } else if (!cmd->cmp("debugLogFile")) { + parseString("debugLogFile", &debugLogFile, tokens, fileName, line); } else { error(errConfig, -1, "Unknown config file command '{0:t}' ({1:t}:{2:d})", cmd, fileName, line); @@ -1862,6 +1998,8 @@ GlobalParams::~GlobalParams() { deleteGList(psResidentFontsCC, PSFontParam16); delete textEncoding; delete initialZoom; + delete initialDisplayMode; + delete initialSelectMode; if (paperColor) { delete paperColor; } @@ -1943,6 +2081,7 @@ static void getWinFontDir(char *winFontDir) { } } } + FreeLibrary(shell32Lib); } // if something went wrong, or we're on a Terminal Server, try using // %SYSTEMROOT%\Fonts @@ -1958,6 +2097,40 @@ static void getWinFontDir(char *winFontDir) { } #endif +#ifdef __APPLE__ +// Apple dfonts and ttc fonts seem to randomly and interchangeably use +// space and hyphen, and sometimes drop the separator entirely. +static GBool macFontNameMatches(GString *name1, const char *name2) { + const char *p1, *p2; + char c1, c2; + + p1 = name1->getCString(); + p2 = name2; + while (*p1 && *p2) { + c1 = *p1; + c2 = *p2; + if (c2 == ' ') { + // * space or hyphen matches a space in the pattern + // * separators can also be dropped, in which case we move to + // the next char in the pattern + if (c1 == ' ' || c1 == '-') { + ++p1; + } + } else { + if (c1 != c2) { + return gFalse; + } + ++p1; + } + ++p2; + } + if (*p1 || *p2) { + return gFalse; + } + return gTrue; +} +#endif + void GlobalParams::setupBaseFonts(const char *dir) { GString *fontName; GString *fileName; @@ -2036,8 +2209,8 @@ void GlobalParams::setupBaseFonts(const char *dir) { } if (dfontFontNames) { for (k = 0; k < dfontFontNames->getLength(); ++k) { - if (!((GString *)dfontFontNames->get(k)) - ->cmp(displayFontTab[i].macFontName)) { + if (macFontNameMatches((GString *)dfontFontNames->get(k), + displayFontTab[i].macFontName)) { fontNum = k; found = gTrue; break; @@ -2114,6 +2287,9 @@ void GlobalParams::setupBaseFonts(const char *dir) { sysFonts->scanWindowsFonts(winFontDir); } #endif +#if HAVE_FONTCONFIG + sysFonts->scanFontconfigFonts(); +#endif } //------------------------------------------------------------------------ @@ -2653,6 +2829,24 @@ int GlobalParams::getDefaultFitZoom() { return z; } +GString *GlobalParams::getInitialDisplayMode() { + GString *s; + + lockGlobalParams; + s = initialDisplayMode->copy(); + unlockGlobalParams; + return s; +} + +GBool GlobalParams::getInitialToolbarState() { + GBool state; + + lockGlobalParams; + state = initialToolbarState; + unlockGlobalParams; + return state; +} + GBool GlobalParams::getInitialSidebarState() { GBool state; @@ -2662,6 +2856,15 @@ GBool GlobalParams::getInitialSidebarState() { return state; } +GString *GlobalParams::getInitialSelectMode() { + GString *s; + + lockGlobalParams; + s = initialSelectMode->copy(); + unlockGlobalParams; + return s; +} + int GlobalParams::getMaxTileWidth() { int w; @@ -2872,6 +3075,15 @@ GString *GlobalParams::getFullScreenMatteColor() { return s; } +GBool GlobalParams::getReverseVideoInvertImages() { + GBool invert; + + lockGlobalParams; + invert = reverseVideoInvertImages; + unlockGlobalParams; + return invert; +} + GString *GlobalParams::getDefaultPrinter() { GString *s; @@ -2998,6 +3210,51 @@ GBool GlobalParams::getErrQuiet() { return errQuiet; } +GString *GlobalParams::getDebugLogFile() { + return debugLogFile; +} + +void GlobalParams::debugLogPrintf(char *fmt, ...) { + GString *path; + FILE *f; + GBool needClose; + time_t t; + struct tm tm; + va_list args; + + if (!(path = getDebugLogFile())) { + return; + } + needClose = gFalse; + if (!path->cmp("-")) { + f = stdout; + } else if (!path->cmp("+")) { + f = stderr; + } else { + f = fopen(path->getCString(), "a"); + needClose = gTrue; + } + if (!f) { + return; + } + t = time(NULL); +#ifdef _WIN32 + localtime_s(&tm, &t); +#else + localtime_r(&t, &tm); +#endif + fprintf(f, "[%04d-%02d-%02d %02d:%02d:%02d] ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + va_start(args, fmt); + vfprintf(f, fmt, args); + va_end(args); + fflush(f); + if (needClose) { + fclose(f); + } +} + CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) { GString *fileName; CharCodeToUnicode *ctu; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h index 066b420dfa8..ed1fed3152d 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h @@ -55,7 +55,8 @@ enum SysFontType { sysFontPFA, sysFontPFB, sysFontTTF, - sysFontTTC + sysFontTTC, + sysFontOTF }; //------------------------------------------------------------------------ @@ -283,7 +284,10 @@ public: GBool getTextKeepTinyChars(); GString *getInitialZoom(); int getDefaultFitZoom(); + GString *getInitialDisplayMode(); + GBool getInitialToolbarState(); GBool getInitialSidebarState(); + GString *getInitialSelectMode(); int getMaxTileWidth(); int getMaxTileHeight(); int getTileCacheSize(); @@ -308,6 +312,7 @@ public: GString *getPaperColor(); GString *getMatteColor(); GString *getFullScreenMatteColor(); + GBool getReverseVideoInvertImages(); GString *getLaunchCommand() { return launchCommand; } GString *getMovieCommand() { return movieCommand; } GString *getDefaultPrinter(); @@ -322,6 +327,8 @@ public: GString *getTabStateFile(); GBool getPrintCommands(); GBool getErrQuiet(); + GString *getDebugLogFile(); + void debugLogPrintf(char *fmt, ...); CharCodeToUnicode *getCIDToUnicode(GString *collection); CharCodeToUnicode *getUnicodeToUnicode(GString *fontName); @@ -509,8 +516,13 @@ private: GString *initialZoom; // initial zoom level int defaultFitZoom; // default zoom factor if initialZoom is // 'page' or 'width'. + GString *initialDisplayMode; // initial display mode (single, + // continuous, etc.) + GBool initialToolbarState; // initial toolbar state - open (true) + // or closed (false) GBool initialSidebarState; // initial sidebar state - open (true) // or closed (false) + GString *initialSelectMode; // initial selection mode (block or linear) int maxTileWidth; // maximum rasterization tile width int maxTileHeight; // maximum rasterization tile height int tileCacheSize; // number of rasterization tiles in cache @@ -536,6 +548,7 @@ private: GString *paperColor; // paper (page background) color GString *matteColor; // matte (background outside of page) color GString *fullScreenMatteColor; // matte color in full-screen mode + GBool reverseVideoInvertImages; // invert images in reverse video mode GString *launchCommand; // command executed for 'launch' links GString *movieCommand; // command executed for movie annotations GString *defaultPrinter; // default printer (for interactive printing @@ -551,6 +564,7 @@ private: GString *tabStateFile; // path for the tab state save file GBool printCommands; // print the drawing commands GBool errQuiet; // suppress error messages? + GString *debugLogFile; // path for debug log file CharCodeToUnicodeCache *cidToUnicodeCache; CharCodeToUnicodeCache *unicodeToUnicodeCache; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc index d6cbc24680d..036f1116c01 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc @@ -34,9 +34,11 @@ #include "GList.h" #include "SplashBitmap.h" #include "PDFDoc.h" +#include "GfxFont.h" #include "TextOutputDev.h" #include "SplashOutputDev.h" #include "ErrorCodes.h" +#include "WebFont.h" #include "HTMLGen.h" #ifdef _WIN32 @@ -62,11 +64,14 @@ static FontStyleTagInfo fontStyleTags[] = { {"CondensedBold", 13, gTrue, gFalse}, {"CondensedLight", 14, gFalse, gFalse}, {"SemiBold", 8, gTrue, gFalse}, + {"BoldItalicMT", 12, gTrue, gTrue}, {"BoldItalic", 10, gTrue, gTrue}, {"Bold_Italic", 11, gTrue, gTrue}, {"BoldOblique", 11, gTrue, gTrue}, {"Bold_Oblique", 12, gTrue, gTrue}, + {"BoldMT", 6, gTrue, gFalse}, {"Bold", 4, gTrue, gFalse}, + {"ItalicMT", 8, gFalse, gTrue}, {"Italic", 6, gFalse, gTrue}, {"Oblique", 7, gFalse, gTrue}, {"Light", 5, gFalse, gFalse}, @@ -171,6 +176,26 @@ static const char *vertAlignNames[] = { //------------------------------------------------------------------------ +class HTMLGenFontDefn { +public: + + HTMLGenFontDefn(Ref fontIDA, GString *fontFaceA, GString *fontSpecA, + double scaleA) + : fontID(fontIDA), fontFace(fontFaceA), fontSpec(fontSpecA) + , scale(scaleA), used(gFalse) {} + ~HTMLGenFontDefn() { delete fontFace; delete fontSpec; } + GBool match(Ref fontIDA) + { return fontIDA.num == fontID.num && fontIDA.gen == fontID.gen; } + + Ref fontID; + GString *fontFace; // NULL for substituted fonts + GString *fontSpec; + double scale; + GBool used; // set when used (per page) +}; + +//------------------------------------------------------------------------ + //------------------------------------------------------------------------ @@ -184,6 +209,7 @@ HTMLGen::HTMLGen(double backgroundResolutionA) { zoom = 1.0; drawInvisibleText = gTrue; allTextInvisible = gFalse; + extractFontFiles = gFalse; // set up the TextOutputDev textOutControl.mode = textOutReadingOrder; @@ -196,16 +222,27 @@ HTMLGen::HTMLGen(double backgroundResolutionA) { // set up the SplashOutputDev paperColor[0] = paperColor[1] = paperColor[2] = 0xff; splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor); + + fontDefns = NULL; } HTMLGen::~HTMLGen() { delete textOut; delete splashOut; + if (fontDefns) { + deleteGList(fontDefns, HTMLGenFontDefn); + } } void HTMLGen::startDoc(PDFDoc *docA) { doc = docA; splashOut->startDoc(doc->getXRef()); + + if (fontDefns) { + deleteGList(fontDefns, HTMLGenFontDefn); + } + fontDefns = new GList(); + nextFontFaceIdx = 0; } static inline int pr(int (*writeFunc)(void *stream, const char *data, int size), @@ -240,7 +277,7 @@ static void pngWriteFunc(png_structp png, png_bytep data, png_size_t size) { } int HTMLGen::convertPage( - int pg, const char *pngURL, + int pg, const char *pngURL, const char *htmlDir, int (*writeHTML)(void *stream, const char *data, int size), void *htmlStream, int (*writePNG)(void *stream, const char *data, int size), @@ -257,6 +294,7 @@ int HTMLGen::convertPage( TextColumn *col; TextParagraph *par; TextLine *line; + HTMLGenFontDefn *fontDefn; GString *s; double base; int primaryDir, spanDir; @@ -269,7 +307,7 @@ int HTMLGen::convertPage( 0, gFalse, gTrue, gFalse); bitmap = splashOut->getBitmap(); if (!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, - NULL, NULL, NULL)) || + NULL, NULL, NULL)) || !(pngInfo = png_create_info_struct(png))) { return errFileIO; } @@ -314,11 +352,19 @@ int HTMLGen::convertPage( pr(writeHTML, htmlStream, ".txt { white-space:nowrap; }\n"); fonts = text->getFonts(); fontScales = (double *)gmallocn(fonts->getLength(), sizeof(double)); + for (i = 0; i < fontDefns->getLength(); ++i) { + fontDefn = (HTMLGenFontDefn *)fontDefns->get(i); + fontDefn->used = gFalse; + } for (i = 0; i < fonts->getLength(); ++i) { font = (TextFontInfo *)fonts->get(i); - s = getFontDefn(font, &fontScales[i]); - pf(writeHTML, htmlStream, "#f{0:d} {{ {1:t} }}\n", i, s); - delete s; + fontDefn = getFontDefn(font, htmlDir); + if (!fontDefn->used && fontDefn->fontFace) { + pr(writeHTML, htmlStream, fontDefn->fontFace->getCString()); + } + pf(writeHTML, htmlStream, "#f{0:d} {{ {1:t} }}\n", i, fontDefn->fontSpec); + fontScales[i] = fontDefn->scale; + fontDefn->used = gTrue; } pr(writeHTML, htmlStream, "</style>\n"); pr(writeHTML, htmlStream, "</head>\n"); @@ -592,7 +638,112 @@ void HTMLGen::appendUTF8(Unicode u, GString *s) { } } -GString *HTMLGen::getFontDefn(TextFontInfo *font, double *scale) { +HTMLGenFontDefn *HTMLGen::getFontDefn(TextFontInfo *font, + const char *htmlDir) { + Ref id; + HTMLGenFontDefn *fontDefn; + int i; + + // check the existing font defns + id = font->getFontID(); + if (id.num >= 0) { + for (i = 0; i < fontDefns->getLength(); ++i) { + fontDefn = (HTMLGenFontDefn *)fontDefns->get(i); + if (fontDefn->match(id)) { + return fontDefn; + } + } + } + + // try to extract a font file + if (!extractFontFiles || + !(fontDefn = getFontFile(font, htmlDir))) { + + // get a substitute font + fontDefn = getSubstituteFont(font); + } + + fontDefns->append(fontDefn); + return fontDefn; +} + +HTMLGenFontDefn *HTMLGen::getFontFile(TextFontInfo *font, + const char *htmlDir) { + Ref id; + HTMLGenFontDefn *fontDefn; + Object fontObj; + GfxFont *gfxFont; + WebFont *webFont; + GString *fontFile, *fontPath, *fontFace, *fontSpec; + const char *family, *weight, *style; + double scale; + + id = font->getFontID(); + if (id.num < 0) { + return NULL; + } + + doc->getXRef()->fetch(id.num, id.gen, &fontObj); + if (!fontObj.isDict()) { + fontObj.free(); + return NULL; + } + + gfxFont = GfxFont::makeFont(doc->getXRef(), "F", id, fontObj.getDict()); + webFont = new WebFont(gfxFont, doc->getXRef()); + fontDefn = NULL; + + if (webFont->canWriteTTF()) { + fontFile = GString::format("{0:d}.ttf", nextFontFaceIdx); + fontPath = GString::format("{0:s}/{1:t}", htmlDir, fontFile); + if (webFont->writeTTF(fontPath->getCString())) { + fontFace = GString::format("@font-face {{ font-family: ff{0:d}; src: url(\"{1:t}\"); }}\n", + nextFontFaceIdx, fontFile); + getFontDetails(font, &family, &weight, &style, &scale); + fontSpec = GString::format("font-family:ff{0:d},{1:s}; font-weight:{2:s}; font-style:{3:s};", + nextFontFaceIdx, family, weight, style); + ++nextFontFaceIdx; + fontDefn = new HTMLGenFontDefn(id, fontFace, fontSpec, 1.0); + } + delete fontPath; + delete fontFile; + + } else if (webFont->canWriteOTF()) { + fontFile = GString::format("{0:d}.otf", nextFontFaceIdx); + fontPath = GString::format("{0:s}/{1:t}", htmlDir, fontFile); + if (webFont->writeOTF(fontPath->getCString())) { + fontFace = GString::format("@font-face {{ font-family: ff{0:d}; src: url(\"{1:t}\"); }}\n", + nextFontFaceIdx, fontFile); + getFontDetails(font, &family, &weight, &style, &scale); + fontSpec = GString::format("font-family:ff{0:d},{1:s}; font-weight:{2:s}; font-style:{3:s};", + nextFontFaceIdx, family, weight, style); + fontDefn = new HTMLGenFontDefn(id, fontFace, fontSpec, 1.0); + } + delete fontPath; + delete fontFile; + } + + delete webFont; + delete gfxFont; + fontObj.free(); + + return fontDefn; +} + +HTMLGenFontDefn *HTMLGen::getSubstituteFont(TextFontInfo *font) { + const char *family, *weight, *style; + double scale; + GString *fontSpec; + + getFontDetails(font, &family, &weight, &style, &scale); + fontSpec = GString::format("font-family:{0:s}; font-weight:{1:s}; font-style:{2:s};", + family, weight, style); + return new HTMLGenFontDefn(font->getFontID(), NULL, fontSpec, scale); +} + +void HTMLGen::getFontDetails(TextFontInfo *font, const char **family, + const char **weight, const char **style, + double *scale) { GString *fontName; char *fontName2; FontStyleTagInfo *fst; @@ -666,11 +817,7 @@ GString *HTMLGen::getFontDefn(TextFontInfo *font, double *scale) { } } - // generate the CSS markup - return GString::format("font-family:{0:s}; font-weight:{1:s}; font-style:{2:s};", - fixedWidth ? "monospace" - : serif ? "serif" - : "sans-serif", - bold ? "bold" : "normal", - italic ? "italic" : "normal"); + *family = fixedWidth ? "monospace" : serif ? "serif" : "sans-serif"; + *weight = bold ? "bold" : "normal"; + *style = italic ? "italic" : "normal"; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h index e870d8ef842..d09eea3f266 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h @@ -20,6 +20,7 @@ class PDFDoc; class TextOutputDev; class TextFontInfo; class SplashOutputDev; +class HTMLGenFontDefn; //------------------------------------------------------------------------ @@ -46,8 +47,11 @@ public: void setAllTextInvisible(GBool allTextInvisibleA) { allTextInvisible = allTextInvisibleA; } + void setExtractFontFiles(GBool extractFontFilesA) + { extractFontFiles = extractFontFilesA; } + void startDoc(PDFDoc *docA); - int convertPage(int pg, const char *pngURL, + int convertPage(int pg, const char *pngURL, const char *htmlDir, int (*writeHTML)(void *stream, const char *data, int size), void *htmlStream, int (*writePNG)(void *stream, const char *data, int size), @@ -61,20 +65,29 @@ private: int primaryDir, int spanDir, double base, GBool dropCapLine, GString *s); void appendUTF8(Unicode u, GString *s); - GString *getFontDefn(TextFontInfo *font, double *scale); + HTMLGenFontDefn *getFontDefn(TextFontInfo *font, const char *htmlDir); + HTMLGenFontDefn *getFontFile(TextFontInfo *font, const char *htmlDir); + HTMLGenFontDefn *getSubstituteFont(TextFontInfo *font); + void getFontDetails(TextFontInfo *font, const char **family, + const char **weight, const char **style, + double *scale); double backgroundResolution; double zoom; GBool drawInvisibleText; GBool allTextInvisible; + GBool extractFontFiles; PDFDoc *doc; TextOutputDev *textOut; SplashOutputDev *splashOut; - GList *fonts; + GList *fonts; // [TextFontInfo] double *fontScales; + GList *fontDefns; // [HTMLGenFontDefn] + int nextFontFaceIdx; + GBool ok; }; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc index d3c9b6f8c4d..fcb9d6d0e3a 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc @@ -838,6 +838,9 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, } else { y0 = 0; } + if (y > INT_MAX - bitmap->h) { + return; + } if (y + bitmap->h > h) { y1 = h - y; } else { @@ -1202,10 +1205,13 @@ Stream *JBIG2Stream::copy() { } void JBIG2Stream::reset() { - // read the globals stream + GList *t; + + segments = new GList(); globalSegments = new GList(); + + // read the globals stream if (globalsStream.isStream()) { - segments = globalSegments; curStr = globalsStream.getStream(); curStr->reset(); arithDecoder->setStream(curStr); @@ -1213,10 +1219,13 @@ void JBIG2Stream::reset() { mmrDecoder->setStream(curStr); readSegments(); curStr->close(); + // swap the newly read segments list into globalSegments + t = segments; + segments = globalSegments; + globalSegments = t; } // read the main stream - segments = new GList(); curStr = str; curStr->reset(); arithDecoder->setStream(curStr); @@ -1280,7 +1289,8 @@ int JBIG2Stream::getBlock(char *blk, int size) { return n; } -GString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) { +GString *JBIG2Stream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } @@ -1506,7 +1516,7 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, Guint symHeight, symWidth, totalWidth, x, symID; int dh, dw, refAggNum, refDX, refDY, bmSize; GBool ex; - int run, cnt; + int run, prevRun, cnt; Guint i, j, k; symWidths = NULL; @@ -1853,12 +1863,21 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, // exported symbol list i = j = 0; ex = gFalse; + prevRun = 1; while (i < numInputSyms + numNewSyms) { if (huff) { huffDecoder->decodeInt(&run, huffTableA); } else { arithDecoder->decodeInt(&run, iaexStats); } + if (run == 0 && prevRun == 0) { + // this avoids infinite loops with damaged files (consecutive + // zero runs are never useful) + error(errSyntaxError, getPos(), + "Invalid exported symbol list in JBIG2 symbol dictionary"); + delete symbolDict; + goto syntaxError; + } if (i + run > numInputSyms + numNewSyms || (ex && j + run > numExSyms)) { error(errSyntaxError, getPos(), @@ -1874,6 +1893,7 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, i += run; } ex = !ex; + prevRun = run; } if (j != numExSyms) { error(errSyntaxError, getPos(), "Too few symbols in JBIG2 symbol dictionary"); @@ -2143,18 +2163,23 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm, runLengthTab[i].val = i; runLengthTab[i].prefixLen = huffDecoder->readBits(4); runLengthTab[i].rangeLen = 0; + runLengthTab[i].prefix = 0; } runLengthTab[32].val = 0x103; runLengthTab[32].prefixLen = huffDecoder->readBits(4); runLengthTab[32].rangeLen = 2; + runLengthTab[32].prefix = 0; runLengthTab[33].val = 0x203; runLengthTab[33].prefixLen = huffDecoder->readBits(4); runLengthTab[33].rangeLen = 3; + runLengthTab[33].prefix = 0; runLengthTab[34].val = 0x20b; runLengthTab[34].prefixLen = huffDecoder->readBits(4); runLengthTab[34].rangeLen = 7; + runLengthTab[34].prefix = 0; runLengthTab[35].prefixLen = 0; runLengthTab[35].rangeLen = jbig2HuffmanEOT; + runLengthTab[35].prefix = 0; huffDecoder->buildTable(runLengthTab, 35); symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1, sizeof(JBIG2HuffmanTable)); @@ -2170,6 +2195,12 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm, symCodeTab[i++].prefixLen = 0; } } else if (j > 0x100) { + if (i == 0) { + error(errSyntaxError, getPos(), "Invalid code in JBIG2 text region"); + gfree(syms); + gfree(symCodeTab); + return; + } for (j -= 0x100; j && i < numSyms; --j) { symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen; ++i; @@ -2226,8 +2257,8 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm, codeTableError: error(errSyntaxError, getPos(), "Missing code table in JBIG2 text region"); - gfree(codeTables); - delete syms; + delete codeTables; + gfree(syms); return; eofError: diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h index 17181d72038..7aa4d9d8edb 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h @@ -42,7 +42,8 @@ public: virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc index 79a7c51eda9..a5dd6f0db0e 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc @@ -414,18 +414,17 @@ void JPXStream::fillReadBuf() { if (curY >= (img.ySize >> reduction)) { return; } - tileIdx = ((curY - img.yTileOffsetR) / img.yTileSizeR) * img.nXTiles - + (curX - img.xTileOffsetR) / img.xTileSizeR; + tileIdx = (((curY << reduction) - img.yTileOffset) / img.yTileSize) + * img.nXTiles + + ((curX << reduction) - img.xTileOffset) / img.xTileSize; #if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid tileComp = &img.tiles[tileIdx].tileComps[curComp]; #else tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp]; #endif - //~ can curX/curY be less than x/yTileOffsetR? - //~ if yes, we need to use tx = max(0, ....) - tx = jpxFloorDiv((curX - img.xTileOffsetR) % img.xTileSizeR, + tx = jpxFloorDiv(curX - jpxCeilDivPow2(img.tiles[tileIdx].x0, reduction), tileComp->hSep); - ty = jpxFloorDiv((curY - img.yTileOffsetR) % img.yTileSizeR, + ty = jpxFloorDiv(curY - jpxCeilDivPow2(img.tiles[tileIdx].y0, reduction), tileComp->vSep); pix = (int)tileComp->data[ty * tileComp->w + tx]; pixBits = tileComp->prec; @@ -463,7 +462,8 @@ void JPXStream::fillReadBuf() { } while (readBufLen < 8); } -GString *JPXStream::getPSFilter(int psLevel, const char *indent) { +GString *JPXStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } @@ -960,10 +960,6 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) { img.ySizeR = jpxCeilDivPow2(img.ySize, reduction); img.xOffsetR = jpxCeilDivPow2(img.xOffset, reduction); img.yOffsetR = jpxCeilDivPow2(img.yOffset, reduction); - img.xTileSizeR = jpxCeilDivPow2(img.xTileSize, reduction); - img.yTileSizeR = jpxCeilDivPow2(img.yTileSize, reduction); - img.xTileOffsetR = jpxCeilDivPow2(img.xTileOffset, reduction); - img.yTileOffsetR = jpxCeilDivPow2(img.yTileOffset, reduction); img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1) / img.xTileSize; img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1) @@ -1449,8 +1445,7 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) { ok = gTrue; while (1) { if (!readTilePart()) { - ok = gFalse; - break; + return jpxDecodeFatalError; } if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); @@ -1913,6 +1908,7 @@ GBool JPXStream::readTilePart() { tile->res = 0; tile->precinct = 0; tile->layer = 0; + tile->done = gFalse; tile->maxNDecompLevels = 0; for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; @@ -1925,8 +1921,15 @@ GBool JPXStream::readTilePart() { tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep); tileComp->cbW = 1 << tileComp->codeBlockW; tileComp->cbH = 1 << tileComp->codeBlockH; - tileComp->w = jpxCeilDivPow2(tileComp->x1 - tileComp->x0, reduction); - tileComp->h = jpxCeilDivPow2(tileComp->y1 - tileComp->y0, reduction); + tileComp->w = jpxCeilDivPow2(tileComp->x1, reduction) + - jpxCeilDivPow2(tileComp->x0, reduction); + tileComp->h = jpxCeilDivPow2(tileComp->y1, reduction) + - jpxCeilDivPow2(tileComp->y0, reduction); + if (tileComp->w == 0 || tileComp->h == 0) { + error(errSyntaxError, getPos(), + "Invalid tile size or sample separation in JPX stream"); + return gFalse; + } tileComp->data = (int *)gmallocn(tileComp->w * tileComp->h, sizeof(int)); if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { n = tileComp->x1 - tileComp->x0; @@ -1942,13 +1945,20 @@ GBool JPXStream::readTilePart() { resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); + // the JPEG 2000 spec says that packets for empty res levels + // should all be present in the codestream (B.6, B.9, B.10), + // but it appears that encoders drop packets if the res level + // AND the subbands are all completely empty + resLevel->empty = resLevel->x0 == resLevel->x1 || + resLevel->y0 == resLevel->y1; if (r == 0) { resLevel->bx0[0] = resLevel->x0; resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = resLevel->x1; resLevel->by1[0] = resLevel->y1; - resLevel->empty = resLevel->bx0[0] == resLevel->bx1[0] || - resLevel->by0[0] == resLevel->by1[0]; + resLevel->empty = resLevel->empty && + (resLevel->bx0[0] == resLevel->bx1[0] || + resLevel->by0[0] == resLevel->by1[0]); } else { resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); resLevel->by0[0] = resLevel->y0; @@ -1962,7 +1972,8 @@ GBool JPXStream::readTilePart() { resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); - resLevel->empty = (resLevel->bx0[0] == resLevel->bx1[0] || + resLevel->empty = resLevel->empty && + (resLevel->bx0[0] == resLevel->bx1[0] || resLevel->by0[0] == resLevel->by1[0]) && (resLevel->bx0[1] == resLevel->bx1[1] || resLevel->by0[1] == resLevel->by1[1]) && @@ -2117,6 +2128,12 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile = &img.tiles[tileIdx]; + // if the tile is finished, just skip this tile part + if (tile->done) { + bufStr->discardChars(tilePartLen); + return gTrue; + } + // read all packets from this tile-part while (1) { if (tilePartToEOC) { @@ -2373,6 +2390,7 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile->res = 0; if (++tile->layer == tile->nLayers) { tile->layer = 0; + tile->done = gTrue; } } } @@ -2385,6 +2403,7 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; + tile->done = gTrue; } } } @@ -2398,6 +2417,7 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile->comp = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; + tile->done = gTrue; } } } @@ -2411,6 +2431,7 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; + tile->done = gTrue; } } } @@ -2424,6 +2445,7 @@ GBool JPXStream::readTilePartData(Guint tileIdx, tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; + tile->done = gTrue; } } } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h index f1a66f5310d..bc5eac4f5ee 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h @@ -230,10 +230,11 @@ struct JPXTile { // in any component in this tile //----- progression order loop counters - Guint comp; // component - Guint res; // resolution level - Guint precinct; // precinct - Guint layer; // layer + Guint comp; // component + Guint res; // resolution level + Guint precinct; // precinct + Guint layer; // layer + GBool done; // set when this tile is done //----- tile part info Guint nextTilePart; // next expected tile-part @@ -253,9 +254,6 @@ struct JPXImage { yTileOffset; Guint xSizeR, ySizeR; // size of reference grid >> reduction Guint xOffsetR, yOffsetR; // image offset >> reduction - Guint xTileSizeR, yTileSizeR; // size of tiles >> reduction - Guint xTileOffsetR, // offset of first tile >> reduction - yTileOffsetR; Guint nComps; // number of components //----- computed @@ -287,7 +285,8 @@ public: virtual void close(); virtual int getChar(); virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); virtual void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc index 05e9df9f24c..6ff75b0b621 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc @@ -112,7 +112,7 @@ int Lexer::lookChar() { Object *Lexer::getObj(Object *obj) { char *p; int c, c2; - GBool comment, neg, doubleMinus, done; + GBool comment, neg, doubleMinus, done, invalid; int numParen; int xi; double xf, scale; @@ -343,6 +343,7 @@ Object *Lexer::getObj(Object *obj) { p = tokBuf; n = 0; s = NULL; + invalid = gFalse; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (c == '#') { @@ -370,6 +371,9 @@ Object *Lexer::getObj(Object *obj) { goto notEscChar; } getChar(); + if (c == 0) { + invalid = gTrue; + } } notEscChar: // the PDF spec claims that names are limited to 127 chars, but @@ -385,7 +389,13 @@ Object *Lexer::getObj(Object *obj) { s->append((char)c); } } - if (n < tokBufSize) { + if (invalid) { + error(errSyntaxError, getPos(), "Null character in name"); + obj->initError(); + if (s) { + delete s; + } + } else if (n < tokBufSize) { *p = '\0'; obj->initName(tokBuf); } else { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc index 70919f48947..09b5b99759a 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc @@ -39,7 +39,7 @@ PDFCore::PDFCore(SplashColorMode colorMode, int bitmapRowPad, GBool reverseVideo, SplashColorPtr paperColor) { - GString *initialZoom; + GString *initialZoom, *initialDisplayMode; int z, i; doc = NULL; @@ -81,6 +81,19 @@ PDFCore::PDFCore(SplashColorMode colorMode, int bitmapRowPad, state->setZoom(z); } delete initialZoom; + initialDisplayMode = globalParams->getInitialDisplayMode(); + if (!initialDisplayMode->cmp("single")) { + state->setDisplayMode(displaySingle); + } else if (!initialDisplayMode->cmp("sideBySideSingle")) { + state->setDisplayMode(displaySideBySideSingle); + } else if (!initialDisplayMode->cmp("sideBySideContinuous")) { + state->setDisplayMode(displaySideBySideContinuous); + } else if (!initialDisplayMode->cmp("horizontalContinuous")) { + state->setDisplayMode(displayHorizontalContinuous); + } else { + state->setDisplayMode(displayContinuous); + } + delete initialDisplayMode; selectMode = selectModeBlock; selectPage = 0; @@ -615,8 +628,8 @@ void PDFCore::scrollRight(int nCols) { scrollTo(state->getScrollX() + nCols, state->getScrollY()); } -void PDFCore::scrollUp(int nLines) { - scrollTo(state->getScrollX(), state->getScrollY() - nLines); +void PDFCore::scrollUp(int nLines, GBool snapToPage) { + scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage); } void PDFCore::scrollUpPrevPage(int nLines) { @@ -624,12 +637,12 @@ void PDFCore::scrollUpPrevPage(int nLines) { state->getScrollY() == 0) { gotoPrevPage(1, gFalse, gTrue); } else { - scrollUp(nLines); + scrollUp(nLines, gTrue); } } -void PDFCore::scrollDown(int nLines) { - scrollTo(state->getScrollX(), state->getScrollY() + nLines); +void PDFCore::scrollDown(int nLines, GBool snapToPage) { + scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage); } void PDFCore::scrollDownNextPage(int nLines) { @@ -643,7 +656,7 @@ void PDFCore::scrollDownNextPage(int nLines) { scrollDown(nLines); } } else { - scrollDown(nLines); + scrollDown(nLines, gTrue); } } @@ -655,9 +668,35 @@ void PDFCore::scrollPageDown() { scrollDownNextPage(state->getWinH()); } -void PDFCore::scrollTo(int x, int y) { +void PDFCore::scrollTo(int x, int y, GBool snapToPage) { + int next, topPage, topPageY, sy, dy; + startUpdate(); state->setScrollPosition(state->getScrollPage(), x, y); + + if (snapToPage) { + if (state->getDisplayMode() == displayContinuous || + state->getDisplayMode() == displaySideBySideContinuous) { + next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1; + topPage = tileMap->getFirstPage(); + topPageY = tileMap->getPageTopY(topPage); + sy = state->getScrollY(); + dy = sy - topPageY; + // note: dy can be negative here if the inter-page gap is at the + // top of the window + if (-16 < dy && dy < 16) { + state->setScrollPosition(state->getScrollPage(), x, topPageY); + } else if (topPage + next <= doc->getNumPages()) { + topPage += next; + topPageY = tileMap->getPageTopY(topPage); + dy = sy - topPageY; + if (-16 < dy && dy < 0) { + state->setScrollPosition(state->getScrollPage(), x, topPageY); + } + } + } + } + finishUpdate(gTrue, gTrue); } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h index 0fa089ff949..510c01a1a76 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h @@ -135,13 +135,13 @@ public: virtual GBool goBackward(); virtual void scrollLeft(int nCols = 16); virtual void scrollRight(int nCols = 16); - virtual void scrollUp(int nLines = 16); + virtual void scrollUp(int nLines = 16, GBool snapToPage = gFalse); virtual void scrollUpPrevPage(int nLines = 16); - virtual void scrollDown(int nLines = 16); + virtual void scrollDown(int nLines = 16, GBool snapToPage = gFalse); virtual void scrollDownNextPage(int nLines = 16); virtual void scrollPageUp(); virtual void scrollPageDown(); - virtual void scrollTo(int x, int y); + virtual void scrollTo(int x, int y, GBool snapToPage = gFalse); virtual void scrollToLeftEdge(); virtual void scrollToRightEdge(); virtual void scrollToTopEdge(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc index 8bc14eb47ad..a130b164f91 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc @@ -46,6 +46,15 @@ #define headerSearchSize 1024 // read this many bytes at beginning of // file to look for '%PDF' +// Avoid sharing files with child processes on Windows, where sharing +// can cause problems. +#ifdef _WIN32 +# define fopenReadMode "rbN" +# define wfopenReadMode L"rbN" +#else +# define fopenReadMode "rb" +#endif + //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ @@ -75,18 +84,18 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword, // try to open file fileName2 = NULL; #ifdef VMS - if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) { + if (!(file = fopen(fileName1->getCString(), fopenReadMode, "ctx=stm"))) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName1); errCode = errOpenFile; return; } #else - if (!(file = fopen(fileName1->getCString(), "rb"))) { + if (!(file = fopen(fileName1->getCString(), fopenReadMode))) { fileName2 = fileName->copy(); fileName2->lowerCase(); - if (!(file = fopen(fileName2->getCString(), "rb"))) { + if (!(file = fopen(fileName2->getCString(), fopenReadMode))) { fileName2->upperCase(); - if (!(file = fopen(fileName2->getCString(), "rb"))) { + if (!(file = fopen(fileName2->getCString(), fopenReadMode))) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName); delete fileName2; errCode = errOpenFile; @@ -127,9 +136,9 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword, version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { - file = _wfopen(fileNameU, L"rb"); + file = _wfopen(fileNameU, wfopenReadMode); } else { - file = fopen(fileName->getCString(), "rb"); + file = fopen(fileName->getCString(), fopenReadMode); } if (!file) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName); @@ -181,18 +190,17 @@ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword, version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { - file = _wfopen(fileNameU, L"rb"); + file = _wfopen(fileNameU, wfopenReadMode); } else { #endif /* 0 */ - file = fopen(fileName->getCString(), "rb"); + file = fopen(fileName->getCString(), fopenReadMode); #if 0 } #endif /* 0 */ - #elif defined(VMS) - file = fopen(fileName->getCString(), "rb", "ctx=stm"); + file = fopen(fileName->getCString(), fopenReadMode, "ctx=stm"); #else - file = fopen(fileName->getCString(), "rb"); + file = fopen(fileName->getCString(), fopenReadMode); #endif if (!file) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc index c4eef7c24b8..8258395b543 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc @@ -6,6 +6,7 @@ // //======================================================================== +#include <aconf.h> #include "gmempp.h" #include "PDFDocEncoding.h" diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc index 15a2a233990..f39a72e9015 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc @@ -77,6 +77,7 @@ static const char *prolog[] = { " put", " } for", "~123ngs", + "/bdef { bind def } bind def", "/pdfSetup {", " /pdfDuplex exch def", " /setpagedevice where {", @@ -393,9 +394,9 @@ static const char *prolog[] = { "/f { fCol fill } def", "/f* { fCol eofill } def", "% clipping operators", - "/W { clip newpath } def", - "/W* { eoclip newpath } def", - "/Ws { strokepath clip newpath } def", + "/W { clip newpath } bdef", + "/W* { eoclip newpath } bdef", + "/Ws { strokepath clip newpath } bdef", "% text state operators", "/Tc { /pdfCharSpacing exch def } def", "/Tf { dup /pdfFontSize exch def", @@ -985,7 +986,9 @@ public: { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) + { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } @@ -1084,7 +1087,9 @@ public: { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) + { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } @@ -1167,7 +1172,9 @@ public: { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } - virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) + { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } @@ -3841,7 +3848,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask, useLZW = useRLE = gFalse; useCompressed = gFalse; } else { - s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); + s = str->getPSFilter(level < psLevel3 ? 2 : 3, "", gTrue); if (s) { useLZW = useRLE = gFalse; useCompressed = gTrue; @@ -4191,7 +4198,13 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, } sliceX = sliceY = 0; sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); + if (sliceW == 0) { + sliceW = 1; + } sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); + if (sliceH == 0) { + sliceH = 1; + } } nStripes = (int)ceil(((double)sliceW * (double)sliceH) / (double)globalParams->getPSRasterSliceSize()); @@ -5223,6 +5236,7 @@ void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx, // set the pattern if (paintType == 2) { + writePS("fCol\n"); writePS("currentcolor "); } writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns); @@ -6315,7 +6329,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxState *state, useASCII = gFalse; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, - " "); + " ", !inlineImg); if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) || inlineImg || !s) { @@ -6790,7 +6804,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxState *state, maskUseCompressed = gFalse; maskUseASCII = gFalse; } else { - s = maskStr->getPSFilter(3, " "); + s = maskStr->getPSFilter(3, " ", !inlineImg); if (!s) { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; @@ -7018,7 +7032,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxState *state, useCompressed = gFalse; useASCII = gFalse; } else { - s = str->getPSFilter(3, " "); + s = str->getPSFilter(3, " ", !inlineImg); if ((colorMap && level == psLevel3Gray) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; @@ -7557,19 +7571,13 @@ GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) { } obj1.free(); sepCSObj.arrayGet(3, &funcObj); - if (!(func = Function::parse(&funcObj))) { + if (!(func = Function::parse(&funcObj, 1, 4))) { funcObj.free(); sepCSObj.free(); colorants.free(); return NULL; } funcObj.free(); - if (func->getInputSize() != 1 || func->getOutputSize() != 4) { - delete func; - sepCSObj.free(); - colorants.free(); - return NULL; - } sepIn = 1; func->transform(&sepIn, cmyk[i]); delete func; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc index df2e2b289cc..187ef5657b5 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc @@ -28,6 +28,7 @@ #include "BuiltinFont.h" #include "BuiltinFontTables.h" #include "FoFiTrueType.h" +#include "FoFiType1C.h" #include "JPXStream.h" #include "SplashBitmap.h" #include "SplashGlyphBitmap.h" @@ -498,6 +499,7 @@ public: int cacheAssoc; // cache associativity (glyphs per set) Guchar *cacheData; // glyph pixmap cache T3FontCacheTag *cacheTags; // cache tags, i.e., char codes + GBool inUse; // set while this T3 font is in active use }; T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, @@ -539,6 +541,7 @@ T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, for (i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1)); } + inUse = gFalse; } T3FontCache::~T3FontCache() { @@ -615,8 +618,8 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, xref = NULL; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, &screenParams); + colorMode != splashModeMono1, bitmapTopDown, NULL); + splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); splash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); @@ -761,9 +764,10 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { bitmap = NULL; } bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); + colorMode != splashModeMono1, bitmapTopDown, + NULL); } - splash = new Splash(bitmap, vectorAntialias, &screenParams); + splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); splash->setEnablePathSimplification( globalParams->getEnablePathSimplification()); @@ -804,6 +808,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { splash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); splash->clear(paperColor, 0); + reverseVideoInvertImages = globalParams->getReverseVideoInvertImages(); if (startPageCbk) { (*startPageCbk)(startPageCbkData); } @@ -1149,6 +1154,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { SplashFontFile *fontFile; int fontNum; FoFiTrueType *ff; + FoFiType1C *ffT1C; Ref embRef; Object refObj, strObj; #if LOAD_FONTS_FROM_MEM @@ -1166,9 +1172,9 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale; Gushort ww; SplashCoord mat[4]; - char *name; + char *name, *start; Unicode uBuf[8]; - int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, i; + int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, length, i; needFontUpdate = gFalse; font = NULL; @@ -1306,6 +1312,17 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } break; case fontType1C: +#if LOAD_FONTS_FROM_MEM + if ((ffT1C = FoFiType1C::make(fontBuf->getCString(), + fontBuf->getLength()))) { +#else + if ((ffT1C = FoFiType1C::load(fileName->getCString()))) { +#endif + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C); + delete ffT1C; + } else { + codeToGID = NULL; + } if (!(fontFile = fontEngine->loadType1CFont( id, #if LOAD_FONTS_FROM_MEM @@ -1314,6 +1331,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { fileName->getCString(), fileName == tmpFileName, #endif + codeToGID, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1323,6 +1341,21 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { } break; case fontType1COT: + codeToGID = NULL; +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + fontNum, gTrue))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), + fontNum, gTrue))) { +#endif + if (ff->getCFFBlock(&start, &length) && + (ffT1C = FoFiType1C::make(start, length))) { + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C); + delete ffT1C; + } + delete ff; + } if (!(fontFile = fontEngine->loadOpenTypeT1CFont( id, #if LOAD_FONTS_FROM_MEM @@ -1331,6 +1364,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { fileName->getCString(), fileName == tmpFileName, #endif + codeToGID, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() @@ -1559,6 +1593,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { break; default: // this shouldn't happen + delete fontLoc; goto err2; } @@ -1593,8 +1628,8 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { (name[0] >= 'a' && name[0] <= 'z') || (name[0] >= '0' && name[0] <= '9'))) { w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code); - builtinFontSubst[substIdx]->widths->getWidth(name, &ww); - if (w > 0.01 && ww > 10) { + if (builtinFontSubst[substIdx]->widths->getWidth(name, &ww) && + w > 0.01 && ww > 10) { w /= ww * 0.001; if (w < fontScaleMin) { fontScaleMin = w; @@ -1780,7 +1815,10 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, // check for an excessively large tile size tileSize = tileW * tileH; - if (tileSize > 1000000 || tileSize < 0) { + if (tileXMax - tileXMin + 0.5 > (double)INT_MAX || + tileYMax - tileYMin + 0.5 > (double)INT_MAX || + tileW > INT_MAX / tileH || + tileSize > 1000000) { mat1[0] = mat[0]; mat1[1] = mat[1]; mat1[2] = mat[2]; @@ -1932,8 +1970,10 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, origBitmap = bitmap; origSplash = splash; bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad, - colorMode, gTrue, bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen()); + colorMode, gTrue, bitmapTopDown, + origBitmap); + splash = new Splash(bitmap, vectorAntialias, + origSplash->getImageCache(), origSplash->getScreen()); for (i = 0; i < splashMaxColorComps; ++i) { color[i] = 0; } @@ -2085,7 +2125,7 @@ GBool SplashOutputDev::axialShadedFill(GfxState *state, bitmapWidth = ixMax - ixMin; bitmapHeight = iyMax - iyMin; tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, - srcMode, gTrue, gTrue); + srcMode, gTrue, gTrue, bitmap); memset(tBitmap->getAlphaPtr(), 0, (size_t)bitmapWidth * bitmapHeight); nComps = splashColorModeNComps[srcMode]; @@ -2403,7 +2443,7 @@ GBool SplashOutputDev::radialShadedFill(GfxState *state, bitmapWidth = ixMax - ixMin; bitmapHeight = iyMax - iyMin; tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, - srcMode, gTrue, gTrue); + srcMode, gTrue, gTrue, bitmap); memset(tBitmap->getAlphaPtr(), 0, tBitmap->getAlphaRowSize() * bitmapHeight); nComps = splashColorModeNComps[srcMode]; @@ -2908,12 +2948,25 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, if (i >= nT3Fonts) { // create new entry in the font cache - if (nT3Fonts == splashOutT3FontCacheSize) { - delete t3FontCache[nT3Fonts - 1]; + if (nT3Fonts < splashOutT3FontCacheSize) { + for (j = nT3Fonts; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + } else { + for (j = nT3Fonts - 1; j >= 0; --j) { + if (!t3FontCache[j]->inUse) { + break; + } + } + if (j < 0) { + error(errSyntaxError, -1, "Type 3 fonts nested too deeply"); + return gTrue; + } + delete t3FontCache[j]; --nT3Fonts; - } - for (j = nT3Fonts; j > 0; --j) { - t3FontCache[j] = t3FontCache[j - 1]; + for (; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } } ++nT3Fonts; bbox = gfxFont->getFontBBox(); @@ -2985,6 +3038,8 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, } } + t3Font->inUse = gTrue; + // push a new Type 3 glyph record t3gs = new T3GlyphStack(); t3gs->next = t3GlyphStack; @@ -3021,6 +3076,7 @@ void SplashOutputDev::endType3Char(GfxState *state) { } t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; + t3gs->cache->inUse = gFalse; delete t3gs; } @@ -3131,16 +3187,19 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, if (colorMode == splashModeMono1) { colorMode = splashModeMono1; bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono1, gFalse); - splash = new Splash(bitmap, gFalse, t3GlyphStack->origSplash->getScreen()); + splashModeMono1, gFalse, gTrue, bitmap); + splash = new Splash(bitmap, gFalse, + t3GlyphStack->origSplash->getImageCache(), + t3GlyphStack->origSplash->getScreen()); color[0] = 0; splash->clear(color); color[0] = 0xff; } else { colorMode = splashModeMono8; bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono8, gFalse); + splashModeMono8, gFalse, gTrue, bitmap); splash = new Splash(bitmap, vectorAntialias, + t3GlyphStack->origSplash->getImageCache(), t3GlyphStack->origSplash->getScreen()); color[0] = 0x00; splash->clear(color); @@ -3214,6 +3273,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; + GString *imgTag; if (state->getFillColorSpace()->isNonMarking()) { return; @@ -3239,8 +3299,11 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, imgMaskData.height = height; imgMaskData.y = 0; - splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, + imgTag = makeImageTag(ref); + splash->fillImageMask(imgTag, + &imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL, interpolate); + if (inlineImg) { while (imgMaskData.y < height) { imgMaskData.imgStr->getLine(); @@ -3248,6 +3311,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, } } + delete imgTag; delete imgMaskData.imgStr; str->close(); } @@ -3264,6 +3328,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; + GString *imgTag; ctm = state->getCTM(); mat[0] = ctm[0]; @@ -3280,8 +3345,8 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, imgMaskData.height = height; imgMaskData.y = 0; maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, gFalse); - maskSplash = new Splash(maskBitmap, gTrue); + 1, splashModeMono8, gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache()); maskSplash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); maskSplash->setEnablePathSimplification( @@ -3289,8 +3354,10 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, clearMaskRegion(state, maskSplash, 0, 0, 1, 1); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, + imgTag = makeImageTag(ref); + maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData, width, height, mat, gFalse, interpolate); + delete imgTag; delete imgMaskData.imgStr; str->close(); delete maskSplash; @@ -3304,6 +3371,7 @@ struct SplashOutImageData { SplashColorPtr lookup; int *maskColors; SplashColorMode colorMode; + GBool invert; int width, height, y; }; @@ -3312,7 +3380,7 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, SplashOutImageData *imgData = (SplashOutImageData *)data; Guchar *p; SplashColorPtr q, col; - int x; + int n, x; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine())) { @@ -3371,6 +3439,13 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, } } + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + ++imgData->y; return gTrue; } @@ -3381,7 +3456,7 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, Guchar *p0, *p, *aq; SplashColorPtr q, col; Guchar alpha; - int nComps, x, i; + int nComps, x, n, i; if (imgData->y == imgData->height || !(p0 = imgData->imgStr->getLine())) { @@ -3455,6 +3530,13 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, *aq++ = alpha; } + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + ++imgData->y; return gTrue; } @@ -3470,6 +3552,7 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, SplashOutImageData imgData; SplashColorMode srcMode; SplashImageSource src; + GString *imgTag; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK @@ -3500,6 +3583,7 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, imgData.ri = state->getRenderingIntent(); imgData.maskColors = maskColors; imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; imgData.width = width; imgData.height = height; imgData.y = 0; @@ -3559,7 +3643,9 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, srcMode = colorMode; } src = maskColors ? &alphaImageSrc : &imageSrc; - splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse, + imgTag = makeImageTag(ref); + splash->drawImage(imgTag, + src, &imgData, srcMode, maskColors ? gTrue : gFalse, width, height, mat, interpolate); if (inlineImg) { while (imgData.y < height) { @@ -3568,6 +3654,7 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } } + delete imgTag; gfree(imgData.lookup); delete imgData.imgStr; str->close(); @@ -3580,6 +3667,7 @@ struct SplashOutMaskedImageData { SplashBitmap *mask; SplashColorPtr lookup; SplashColorMode colorMode; + GBool invert; int width, height, y; }; @@ -3591,7 +3679,7 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, static Guchar bitToByte[2] = {0x00, 0xff}; Guchar *maskPtr; int maskShift; - int x; + int n, x; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine())) { @@ -3672,6 +3760,13 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, } } + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + ++imgData->y; return gTrue; } @@ -3692,6 +3787,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; + GString *imgTag; SplashColor maskColor; GfxGray gray; GfxRGB rgb; @@ -3741,8 +3837,9 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse); - maskSplash = new Splash(maskBitmap, gFalse); + maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, + gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache()); maskSplash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); maskSplash->setEnablePathSimplification( @@ -3752,7 +3849,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); // use "glyph mode" here to get the correct scaled size - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, + maskSplash->fillImageMask(NULL, &imageMaskSrc, &imgMaskData, maskWidth, maskHeight, mat, gTrue, interpolate); delete imgMaskData.imgStr; maskStr->close(); @@ -3775,6 +3872,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, imgData.ri = state->getRenderingIntent(); imgData.mask = maskBitmap; imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; imgData.width = width; imgData.height = height; imgData.y = 0; @@ -3833,9 +3931,12 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, } else { srcMode = colorMode; } - splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue, + imgTag = makeImageTag(ref); + splash->drawImage(imgTag, + &maskedImageSrc, &imgData, srcMode, gTrue, width, height, mat, interpolate); + delete imgTag; delete maskBitmap; gfree(imgData.lookup); delete imgData.imgStr; @@ -3850,6 +3951,7 @@ struct SplashOutSoftMaskMatteImageData { GfxRenderingIntent ri; Guchar matte[gfxColorMaxComps]; SplashColorMode colorMode; + GBool invert; int width, height, y; }; @@ -3866,7 +3968,7 @@ GBool SplashOutputDev::softMaskMatteImageSrc(void *data, GfxCMYK cmyk; #endif Guchar alpha; - int nComps, x; + int nComps, n, x; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine()) || @@ -3938,6 +4040,13 @@ GBool SplashOutputDev::softMaskMatteImageSrc(void *data, *aq++ = alpha; } + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + ++imgData->y; return gTrue; } @@ -3955,6 +4064,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, SplashOutImageData imgData; SplashOutImageData imgMaskData; SplashOutSoftMaskMatteImageData matteImgData; + GString *imgTag; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; @@ -4044,11 +4154,14 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, n = 1 << 8; } matteImgData.colorMode = colorMode; + matteImgData.invert = reverseVideo && reverseVideoInvertImages; matteImgData.width = width; matteImgData.height = height; matteImgData.y = 0; - splash->drawImage(&softMaskMatteImageSrc, &matteImgData, + imgTag = makeImageTag(ref); + splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData, srcMode, gTrue, width, height, mat, interpolate); + delete imgTag; delete matteImgData.maskStr; delete matteImgData.imgStr; maskStr->close(); @@ -4069,6 +4182,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, imgMaskData.ri = state->getRenderingIntent(); imgMaskData.maskColors = NULL; imgMaskData.colorMode = splashModeMono8; + imgMaskData.invert = gFalse; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; @@ -4080,14 +4194,16 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, imgMaskData.lookup[i] = colToByte(gray); } maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, gFalse); - maskSplash = new Splash(maskBitmap, vectorAntialias); + 1, splashModeMono8, gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, vectorAntialias, + splash->getImageCache()); maskSplash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); maskSplash->setEnablePathSimplification( globalParams->getEnablePathSimplification()); clearMaskRegion(state, maskSplash, 0, 0, 1, 1); - maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse, + maskSplash->drawImage(NULL, + &imageSrc, &imgMaskData, splashModeMono8, gFalse, maskWidth, maskHeight, mat, interpolate); delete imgMaskData.imgStr; maskStr->close(); @@ -4105,6 +4221,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, imgData.ri = state->getRenderingIntent(); imgData.maskColors = NULL; imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; imgData.width = width; imgData.height = height; imgData.y = 0; @@ -4156,10 +4273,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, } } - splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, + imgTag = makeImageTag(ref); + splash->drawImage(imgTag, + &imageSrc, &imgData, srcMode, gFalse, width, height, mat, interpolate); splash->setSoftMask(NULL); + delete imgTag; gfree(imgData.lookup); delete imgData.imgStr; @@ -4168,12 +4288,21 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, } } +GString *SplashOutputDev::makeImageTag(Object *ref) { + if (!ref || !ref->isRef()) { + return NULL; + } + return GString::format("{0:d}_{1:d}", ref->getRefNum(), ref->getRefGen()); +} + void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm, int *width, int *height) { double sw, sh; int reduction; if (str->getKind() == strJPX && + *width >= 256 && + *height >= 256 && *width * *height > 10000000) { sw = (double)*width / (fabs(ctm[0]) + fabs(ctm[1])); sh = (double)*height / (fabs(ctm[2]) + fabs(ctm[3])); @@ -4396,8 +4525,9 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, // create the temporary bitmap bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, - bitmapTopDown); + bitmapTopDown, transpGroup->origBitmap); splash = new Splash(bitmap, vectorAntialias, + transpGroup->origSplash->getImageCache(), transpGroup->origSplash->getScreen()); splash->setMinLineWidth(globalParams->getMinLineWidth()); splash->setStrokeAdjust( @@ -4415,11 +4545,12 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, } if (!isolated && transpGroup->origBitmap->getAlphaPtr() && - transpGroup->origSplash->getInNonIsolatedGroup()) { + transpGroup->origSplash->getInNonIsolatedGroup() && + colorMode != splashModeMono1) { // when drawing a non-isolated group into another non-isolated group, // compute a backdrop bitmap with corrected alpha values backdropBitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, - bitmapTopDown); + bitmapTopDown, transpGroup->origBitmap); transpGroup->origSplash->blitCorrectedAlpha(backdropBitmap, tx, ty, 0, 0, w, h); transpGroup->backdropBitmap = backdropBitmap; @@ -4521,6 +4652,7 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, //~ space is given if (transpGroupStack->blendingColorSpace) { tSplash = new Splash(tBitmap, vectorAntialias, + transpGroupStack->origSplash->getImageCache(), transpGroupStack->origSplash->getScreen()); tSplash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); @@ -4578,7 +4710,7 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, } softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), - 1, splashModeMono8, gFalse); + 1, splashModeMono8, gFalse, gTrue, bitmap); memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5), softMask->getRowSize() * softMask->getHeight()); if (tx < softMask->getWidth() && ty < softMask->getHeight()) { @@ -4660,7 +4792,7 @@ SplashBitmap *SplashOutputDev::takeBitmap() { ret = bitmap; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); + colorMode != splashModeMono1, bitmapTopDown, NULL); return ret; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h index 12e8831a905..2215f883d32 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h @@ -279,6 +279,7 @@ private: static GBool softMaskMatteImageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine); + GString *makeImageTag(Object *ref); void reduceImageResolution(Stream *str, double *mat, int *width, int *height); void clearMaskRegion(GfxState *state, @@ -295,6 +296,7 @@ private: GBool allowAntialias; GBool vectorAntialias; GBool reverseVideo; // reverse video mode + GBool reverseVideoInvertImages; SplashColor paperColor; // paper color SplashScreenParams screenParams; GBool skipHorizText; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc index 4e9a2dc2ff4..a7c13c9eab3 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc @@ -121,7 +121,8 @@ Guint Stream::discardChars(Guint n) { return count; } -GString *Stream::getPSFilter(int psLevel, const char *indent) { +GString *Stream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return new GString(); } @@ -132,15 +133,15 @@ Stream *Stream::addFilters(Object *dict, int recursion) { int i; str = this; - dict->dictLookup("Filter", &obj); + dict->dictLookup("Filter", &obj, recursion); if (obj.isNull()) { obj.free(); - dict->dictLookup("F", &obj); + dict->dictLookup("F", &obj, recursion); } - dict->dictLookup("DecodeParms", ¶ms); + dict->dictLookup("DecodeParms", ¶ms, recursion); if (params.isNull()) { params.free(); - dict->dictLookup("DP", ¶ms); + dict->dictLookup("DP", ¶ms, recursion); } if (obj.isName()) { str = makeFilter(obj.getName(), str, ¶ms, recursion); @@ -1096,13 +1097,14 @@ int ASCIIHexStream::lookChar() { return buf; } -GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { +GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 2) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("/ASCIIHexDecode filter\n"); @@ -1181,13 +1183,14 @@ int ASCII85Stream::lookChar() { return b[index]; } -GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { +GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 2) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("/ASCII85Decode filter\n"); @@ -1408,13 +1411,14 @@ int LZWStream::getCode() { return code; } -GString *LZWStream::getPSFilter(int psLevel, const char *indent) { +GString *LZWStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 2 || pred) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("<< "); @@ -1474,13 +1478,14 @@ int RunLengthStream::getBlock(char *blk, int size) { return n; } -GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { +GString *RunLengthStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 2) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("/RunLengthDecode filter\n"); @@ -2012,7 +2017,7 @@ GBool CCITTFaxStream::readRow() { lookBits(1); eatBits(1); } - if (encoding >= 0) { + if (encoding > 0) { for (i = 0; i < 4; ++i) { code1 = lookBits(12); if (code1 != 0x001) { @@ -2260,14 +2265,15 @@ short CCITTFaxStream::lookBits(int n) { return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n))); } -GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { +GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; char s1[50]; if (psLevel < 2) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("<< "); @@ -2384,6 +2390,11 @@ void DCTStream::reset() { jpeg_start_decompress(&decomp); } +GBool DCTStream::checkSequentialInterleaved() { + //~ this is unimplemented + return gTrue; +} + void DCTStream::close() { // we don't call jpeg_finish_decompress() here because it will report // an error if the full image wasn't read @@ -2754,6 +2765,28 @@ void DCTStream::reset() { } } +GBool DCTStream::checkSequentialInterleaved() { + GBool headerOk; + + str->reset(); + + progressive = interleaved = gFalse; + width = height = 0; + numComps = 0; + numQuantTables = 0; + numDCHuffTables = 0; + numACHuffTables = 0; + gotJFIFMarker = gFalse; + gotAdobeMarker = gFalse; + restartInterval = 0; + + headerOk = readHeader(gTrue); + + FilterStream::close(); + + return headerOk && !progressive && interleaved; +} + void DCTStream::close() { int i; @@ -3755,7 +3788,7 @@ int DCTStream::readBit() { GBool DCTStream::readHeader(GBool frame) { GBool doScan; - int n; + int n, i; int c = 0; // read headers @@ -3850,6 +3883,13 @@ GBool DCTStream::readHeader(GBool frame) { } } + for (i = 0; i < numComps; ++i) { + if (compInfo[i].quantTable >= numQuantTables) { + error(errSyntaxError, getPos(), "Invalid DCT quant table selector"); + return gFalse; + } + } + return gTrue; } @@ -3878,8 +3918,14 @@ GBool DCTStream::readBaselineSOF() { compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { + // a sampling factor of 3 is allowed by the spec, but requires + // messy upsampling, and appears not to be used in practice + if (!(compInfo[i].hSample == 1 || + compInfo[i].hSample == 2 || + compInfo[i].hSample == 4) || + !(compInfo[i].vSample == 1 || + compInfo[i].vSample == 2 || + compInfo[i].vSample == 4)) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } @@ -3917,8 +3963,14 @@ GBool DCTStream::readProgressiveSOF() { compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); - if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || - compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { + // a sampling factor of 3 is allowed by the spec, but requires + // messy upsampling, and appears not to be used in practice + if (!(compInfo[i].hSample == 1 || + compInfo[i].hSample == 2 || + compInfo[i].hSample == 4) || + !(compInfo[i].vSample == 1 || + compInfo[i].vSample == 2 || + compInfo[i].vSample == 4)) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } @@ -4182,13 +4234,19 @@ int DCTStream::read16() { #endif // HAVE_JPEGLIB -GString *DCTStream::getPSFilter(int psLevel, const char *indent) { +GString *DCTStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 2) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { + return NULL; + } + if (okToReadStream && !checkSequentialInterleaved()) { + // PostScript does not allow progressive or interleaved JPEG + delete s; return NULL; } s->append(indent)->append("<< >> /DCTDecode filter\n"); @@ -4982,13 +5040,14 @@ int FlateStream::getBlock(char *blk, int size) { return n; } -GString *FlateStream::getPSFilter(int psLevel, const char *indent) { +GString *FlateStream::getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { GString *s; if (psLevel < 3 || pred) { return NULL; } - if (!(s = str->getPSFilter(psLevel, indent))) { + if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) { return NULL; } s->append(indent)->append("<< >> /FlateDecode filter\n"); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h index c25d46b2cd7..38a8dd3497c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h @@ -117,7 +117,8 @@ public: virtual void setPos(GFileOffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); // Does this stream type potentially contain non-printable chars? virtual GBool isBinary(GBool last = gTrue) = 0; @@ -422,7 +423,8 @@ public: virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -446,7 +448,8 @@ public: virtual int getChar() { int ch = lookChar(); ++index; return ch; } virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -474,7 +477,8 @@ public: virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -520,7 +524,8 @@ public: virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -552,7 +557,8 @@ public: virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -650,12 +656,15 @@ public: virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); Stream *getRawStream() { return str; } private: + GBool checkSequentialInterleaved(); + #if HAVE_JPEGLIB int colorXform; // color transform: -1 = unspecified @@ -785,7 +794,8 @@ public: virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); - virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream); virtual GBool isBinary(GBool last = gTrue); private: @@ -840,7 +850,8 @@ public: virtual int getChar() { return EOF; } virtual int lookChar() { return EOF; } virtual int getBlock(char *blk, int size) { return 0; } - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } }; @@ -859,7 +870,8 @@ public: virtual void reset(); virtual int getChar(); virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue); @@ -885,7 +897,8 @@ public: virtual void reset(); virtual int getChar(); virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue); virtual GBool isEncoder() { return gTrue; } @@ -912,7 +925,8 @@ public: { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } @@ -944,7 +958,8 @@ public: { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } @@ -976,7 +991,8 @@ public: { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } @@ -1012,7 +1028,8 @@ public: virtual void reset(); virtual int getChar(); virtual int lookChar(); - virtual GString *getPSFilter(int psLevel, const char *indent) + virtual GString *getPSFilter(int psLevel, const char *indent, + GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc index 9c226af3f31..4f594384915 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc @@ -16,6 +16,7 @@ #include <stdlib.h> #include <stddef.h> #include <math.h> +#include <limits.h> #include <ctype.h> #ifdef _WIN32 #include <fcntl.h> // for O_BINARY @@ -111,7 +112,7 @@ // In simple layout mode, lines are broken at gaps larger than this // value multiplied by font size. -#define simpleLayoutGapThreshold 0.4 +#define simpleLayoutGapThreshold 0.7 // Table cells (TextColumns) are allowed to overlap by this much // in table layout mode (as a fraction of cell width or height). @@ -407,19 +408,52 @@ void TextBlock::updateBounds(int childIdx) { } //------------------------------------------------------------------------ -// TextGap +// TextGaps //------------------------------------------------------------------------ -class TextGap { +struct TextGap { + double x; // center of gap: x for vertical gaps, + // y for horizontal gaps + double w; // width/height of gap +}; + +class TextGaps { public: - TextGap(double aXY, double aW): xy(aXY), w(aW) {} + TextGaps(); + ~TextGaps(); + void addGap(double x, double w); + int getLength() { return length; } + double getX(int idx) { return gaps[idx].x; } + double getW(int idx) { return gaps[idx].w; } - double xy; // center of gap: x for vertical gaps, - // y for horizontal gaps - double w; // width of gap +private: + + int length; + int size; + TextGap *gaps; }; +TextGaps::TextGaps() { + length = 0; + size = 16; + gaps = (TextGap *)gmallocn(size, sizeof(TextGap)); +} + +TextGaps::~TextGaps() { + gfree(gaps); +} + +void TextGaps::addGap(double x, double w) { + if (length == size) { + size *= 2; + gaps = (TextGap *)greallocn(gaps, size, sizeof(TextGap)); + } + gaps[length].x = x; + gaps[length].w = w; + ++length; +} + //------------------------------------------------------------------------ // TextSuperLine //------------------------------------------------------------------------ @@ -508,6 +542,10 @@ TextOutputControl::TextOutputControl() { discardInvisibleText = gFalse; discardClippedText = gFalse; insertBOM = gFalse; + marginLeft = 0; + marginRight = 0; + marginTop = 0; + marginBottom = 0; } @@ -1136,9 +1174,11 @@ void TextPage::updateFont(GfxState *state) { if (name && name[0] == 'm' && name[1] == '\0') { mCode = code; } - if (letterCode < 0 && name && name[1] == '\0' && + if (letterCode < 0 && + name && ((name[0] >= 'A' && name[0] <= 'Z') || - (name[0] >= 'a' && name[0] <= 'z'))) { + (name[0] >= 'a' && name[0] <= 'z')) && + name[1] == '\0') { letterCode = code; } if (anyCode < 0 && name && @@ -1239,9 +1279,12 @@ void TextPage::addChar(GfxState *state, double x, double y, // throw away chars that aren't inside the page bounds // (and also do a sanity check on the character size) state->transform(x, y, &x1, &y1); - if (x1 + w1 < 0 || x1 > pageWidth || - y1 + h1 < 0 || y1 > pageHeight || - w1 > pageWidth || h1 > pageHeight) { + if (x1 + w1 < control.marginLeft || + x1 > pageWidth - control.marginRight || + y1 + h1 < control.marginTop || + y1 > pageHeight - control.marginBottom || + w1 > pageWidth || + h1 > pageHeight) { charPos += nBytes; return; } @@ -1876,8 +1919,9 @@ void TextPage::writeRaw(void *outputStream, if (fabs(ch2->yMin - ch->yMin) > rawModeLineDelta * ch->fontSize || ch2->xMin - ch->xMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); - } else if (ch2->xMin - ch->xMax > - rawModeWordSpacing * ch->fontSize) { + } else if (ch->spaceAfter || + ch2->xMin - ch->xMax > + rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; @@ -1885,8 +1929,9 @@ void TextPage::writeRaw(void *outputStream, if (fabs(ch->xMax - ch2->xMax) > rawModeLineDelta * ch->fontSize || ch2->yMin - ch->yMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); - } else if (ch2->yMin - ch->yMax > - rawModeWordSpacing * ch->fontSize) { + } else if (ch->spaceAfter || + ch2->yMin - ch->yMax > + rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; @@ -1894,8 +1939,9 @@ void TextPage::writeRaw(void *outputStream, if (fabs(ch->yMax - ch2->yMax) > rawModeLineDelta * ch->fontSize || ch->xMin - ch2->xMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); - } else if (ch->xMin - ch2->xMax > - rawModeWordSpacing * ch->fontSize) { + } else if (ch->spaceAfter || + ch->xMin - ch2->xMax > + rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; @@ -1903,8 +1949,9 @@ void TextPage::writeRaw(void *outputStream, if (fabs(ch2->xMin - ch->xMin) > rawModeLineDelta * ch->fontSize || ch->yMin - ch2->yMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); - } else if (ch->yMin - ch2->yMax > - rawModeWordSpacing * ch->fontSize) { + } else if (ch->spaceAfter || + ch->yMin - ch2->yMax > + rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; @@ -2693,12 +2740,11 @@ TextBlock *TextPage::splitChars(GList *charsA) { TextBlock *TextPage::split(GList *charsA, int rot) { TextBlock *blk; GList *chars2, *chars3; - GList *horizGaps, *vertGaps; - TextGap *gap; + TextGaps *horizGaps, *vertGaps; TextChar *ch; double xMin, yMin, xMax, yMax, avgFontSize; double horizGapSize, vertGapSize, minHorizChunkWidth, minVertChunkWidth; - double nLines, vertGapThreshold, minChunk; + double gap, nLines, vertGapThreshold, minChunk; double largeCharSize; double x0, x1, y0, y1; int nHorizGaps, nVertGaps, nLargeChars; @@ -2707,8 +2753,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { //----- find all horizontal and vertical gaps - horizGaps = new GList(); - vertGaps = new GList(); + horizGaps = new TextGaps(); + vertGaps = new TextGaps(); findGaps(charsA, rot, &xMin, &yMin, &xMax, &yMax, &avgFontSize, horizGaps, vertGaps); @@ -2716,16 +2762,16 @@ TextBlock *TextPage::split(GList *charsA, int rot) { horizGapSize = 0; for (i = 0; i < horizGaps->getLength(); ++i) { - gap = (TextGap *)horizGaps->get(i); - if (gap->w > horizGapSize) { - horizGapSize = gap->w; + gap = horizGaps->getW(i); + if (gap > horizGapSize) { + horizGapSize = gap; } } vertGapSize = 0; for (i = 0; i < vertGaps->getLength(); ++i) { - gap = (TextGap *)vertGaps->get(i); - if (gap->w > vertGapSize) { - vertGapSize = gap->w; + gap = vertGaps->getW(i); + if (gap > vertGapSize) { + vertGapSize = gap; } } @@ -2736,14 +2782,14 @@ TextBlock *TextPage::split(GList *charsA, int rot) { if (horizGaps->getLength() > 0) { y0 = yMin; for (i = 0; i < horizGaps->getLength(); ++i) { - gap = (TextGap *)horizGaps->get(i); - if (gap->w > horizGapSize - splitGapSlack * avgFontSize) { + gap = horizGaps->getW(i); + if (gap > horizGapSize - splitGapSlack * avgFontSize) { ++nHorizGaps; - y1 = gap->xy - 0.5 * gap->w; + y1 = horizGaps->getX(i) - 0.5 * gap; if (y1 - y0 < minHorizChunkWidth) { minHorizChunkWidth = y1 - y0; } - y0 = y1 + gap->w; + y0 = y1 + gap; } } y1 = yMax; @@ -2756,14 +2802,14 @@ TextBlock *TextPage::split(GList *charsA, int rot) { if (vertGaps->getLength() > 0) { x0 = xMin; for (i = 0; i < vertGaps->getLength(); ++i) { - gap = (TextGap *)vertGaps->get(i); - if (gap->w > vertGapSize - splitGapSlack * avgFontSize) { + gap = vertGaps->getW(i); + if (gap > vertGapSize - splitGapSlack * avgFontSize) { ++nVertGaps; - x1 = gap->xy - 0.5 * gap->w; + x1 = vertGaps->getX(i) - 0.5 * gap; if (x1 - x0 < minVertChunkWidth) { minVertChunkWidth = x1 - x0; } - x0 = x1 + gap->w; + x0 = x1 + gap; } } x1 = xMax; @@ -2881,9 +2927,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { printf("vert split xMin=%g yMin=%g xMax=%g yMax=%g small=%d\n", xMin, pageHeight - yMax, xMax, pageHeight - yMin, smallSplit); for (i = 0; i < vertGaps->getLength(); ++i) { - gap = (TextGap *)vertGaps->get(i); - if (gap->w > vertGapSize - splitGapSlack * avgFontSize) { - printf(" x=%g\n", gap->xy); + if (vertGaps->getW(i) > vertGapSize - splitGapSlack * avgFontSize) { + printf(" x=%g\n", vertGaps->getX(i)); } } #endif @@ -2891,9 +2936,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { blk->smallSplit = smallSplit; x0 = xMin - 1; for (i = 0; i < vertGaps->getLength(); ++i) { - gap = (TextGap *)vertGaps->get(i); - if (gap->w > vertGapSize - splitGapSlack * avgFontSize) { - x1 = gap->xy; + if (vertGaps->getW(i) > vertGapSize - splitGapSlack * avgFontSize) { + x1 = vertGaps->getX(i); chars2 = getChars(charsA, x0, yMin - 1, x1, yMax + 1); blk->addChild(split(chars2, rot)); delete chars2; @@ -2910,9 +2954,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { printf("horiz split xMin=%g yMin=%g xMax=%g yMax=%g small=%d\n", xMin, pageHeight - yMax, xMax, pageHeight - yMin, smallSplit); for (i = 0; i < horizGaps->getLength(); ++i) { - gap = (TextGap *)horizGaps->get(i); - if (gap->w > horizGapSize - splitGapSlack * avgFontSize) { - printf(" y=%g\n", pageHeight - gap->xy); + if (horizGaps->getW(i) > horizGapSize - splitGapSlack * avgFontSize) { + printf(" y=%g\n", pageHeight - horizGaps->getX(i)); } } #endif @@ -2920,9 +2963,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { blk->smallSplit = smallSplit; y0 = yMin - 1; for (i = 0; i < horizGaps->getLength(); ++i) { - gap = (TextGap *)horizGaps->get(i); - if (gap->w > horizGapSize - splitGapSlack * avgFontSize) { - y1 = gap->xy; + if (horizGaps->getW(i) > horizGapSize - splitGapSlack * avgFontSize) { + y1 = horizGaps->getX(i); chars2 = getChars(charsA, xMin - 1, y0, xMax + 1, y1); blk->addChild(split(chars2, rot)); delete chars2; @@ -2966,8 +3008,8 @@ TextBlock *TextPage::split(GList *charsA, int rot) { } } - deleteGList(horizGaps, TextGap); - deleteGList(vertGaps, TextGap); + delete horizGaps; + delete vertGaps; tagBlock(blk); @@ -3001,11 +3043,12 @@ void TextPage::findGaps(GList *charsA, int rot, double *xMinOut, double *yMinOut, double *xMaxOut, double *yMaxOut, double *avgFontSizeOut, - GList *horizGaps, GList *vertGaps) { + TextGaps *horizGaps, TextGaps *vertGaps) { TextChar *ch; - int *horizProfile, *vertProfile; + char *horizProfile, *vertProfile; double xMin, yMin, xMax, yMax, w; - double minFontSize, avgFontSize, splitPrecision, ascentAdjust, descentAdjust; + double minFontSize, avgFontSize, splitPrecision, invSplitPrecision; + double ascentAdjust, descentAdjust; int xMinI, yMinI, xMaxI, yMaxI, xMinI2, yMinI2, xMaxI2, yMaxI2; int start, x, y, i; @@ -3037,6 +3080,7 @@ void TextPage::findGaps(GList *charsA, int rot, if (splitPrecision < minSplitPrecision) { splitPrecision = minSplitPrecision; } + invSplitPrecision = 1 / splitPrecision; *xMinOut = xMin; *yMinOut = yMin; *xMaxOut = xMax; @@ -3045,59 +3089,65 @@ void TextPage::findGaps(GList *charsA, int rot, //----- compute the horizontal and vertical profiles + if (xMin * invSplitPrecision < 0.5 * INT_MIN || + xMax * invSplitPrecision > 0.5 * INT_MAX || + yMin * invSplitPrecision < 0.5 * INT_MIN || + xMax * invSplitPrecision > 0.5 * INT_MAX) { + return; + } // add some slack to the array bounds to avoid floating point // precision problems - xMinI = (int)floor(xMin / splitPrecision) - 1; - yMinI = (int)floor(yMin / splitPrecision) - 1; - xMaxI = (int)floor(xMax / splitPrecision) + 1; - yMaxI = (int)floor(yMax / splitPrecision) + 1; - horizProfile = (int *)gmallocn(yMaxI - yMinI + 1, sizeof(int)); - vertProfile = (int *)gmallocn(xMaxI - xMinI + 1, sizeof(int)); - memset(horizProfile, 0, (yMaxI - yMinI + 1) * sizeof(int)); - memset(vertProfile, 0, (xMaxI - xMinI + 1) * sizeof(int)); + xMinI = (int)floor(xMin * invSplitPrecision) - 1; + yMinI = (int)floor(yMin * invSplitPrecision) - 1; + xMaxI = (int)floor(xMax * invSplitPrecision) + 1; + yMaxI = (int)floor(yMax * invSplitPrecision) + 1; + horizProfile = (char *)gmalloc(yMaxI - yMinI + 1); + vertProfile = (char *)gmalloc(xMaxI - xMinI + 1); + memset(horizProfile, 0, yMaxI - yMinI + 1); + memset(vertProfile, 0, xMaxI - xMinI + 1); for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); // yMinI2 and yMaxI2 are adjusted to allow for slightly overlapping lines switch (rot) { case 0: default: - xMinI2 = (int)floor(ch->xMin / splitPrecision); - xMaxI2 = (int)floor(ch->xMax / splitPrecision); + xMinI2 = (int)floor(ch->xMin * invSplitPrecision); + xMaxI2 = (int)floor(ch->xMax * invSplitPrecision); ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin); - yMinI2 = (int)floor((ch->yMin + ascentAdjust) / splitPrecision); + yMinI2 = (int)floor((ch->yMin + ascentAdjust) * invSplitPrecision); descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin); - yMaxI2 = (int)floor((ch->yMax - descentAdjust) / splitPrecision); + yMaxI2 = (int)floor((ch->yMax - descentAdjust) * invSplitPrecision); break; case 1: descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin); - xMinI2 = (int)floor((ch->xMin + descentAdjust) / splitPrecision); + xMinI2 = (int)floor((ch->xMin + descentAdjust) * invSplitPrecision); ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin); - xMaxI2 = (int)floor((ch->xMax - ascentAdjust) / splitPrecision); - yMinI2 = (int)floor(ch->yMin / splitPrecision); - yMaxI2 = (int)floor(ch->yMax / splitPrecision); + xMaxI2 = (int)floor((ch->xMax - ascentAdjust) * invSplitPrecision); + yMinI2 = (int)floor(ch->yMin * invSplitPrecision); + yMaxI2 = (int)floor(ch->yMax * invSplitPrecision); break; case 2: - xMinI2 = (int)floor(ch->xMin / splitPrecision); - xMaxI2 = (int)floor(ch->xMax / splitPrecision); + xMinI2 = (int)floor(ch->xMin * invSplitPrecision); + xMaxI2 = (int)floor(ch->xMax * invSplitPrecision); descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin); - yMinI2 = (int)floor((ch->yMin + descentAdjust) / splitPrecision); + yMinI2 = (int)floor((ch->yMin + descentAdjust) * invSplitPrecision); ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin); - yMaxI2 = (int)floor((ch->yMax - ascentAdjust) / splitPrecision); + yMaxI2 = (int)floor((ch->yMax - ascentAdjust) * invSplitPrecision); break; case 3: ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin); - xMinI2 = (int)floor((ch->xMin + ascentAdjust) / splitPrecision); + xMinI2 = (int)floor((ch->xMin + ascentAdjust) * invSplitPrecision); descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin); - xMaxI2 = (int)floor((ch->xMax - descentAdjust) / splitPrecision); - yMinI2 = (int)floor(ch->yMin / splitPrecision); - yMaxI2 = (int)floor(ch->yMax / splitPrecision); + xMaxI2 = (int)floor((ch->xMax - descentAdjust) * invSplitPrecision); + yMinI2 = (int)floor(ch->yMin * invSplitPrecision); + yMaxI2 = (int)floor(ch->yMax * invSplitPrecision); break; } for (y = yMinI2; y <= yMaxI2; ++y) { - ++horizProfile[y - yMinI]; + horizProfile[y - yMinI] = 1; } for (x = xMinI2; x <= xMaxI2; ++x) { - ++vertProfile[x - xMinI]; + vertProfile[x - xMinI] = 1; } } @@ -3112,8 +3162,7 @@ void TextPage::findGaps(GList *charsA, int rot, } else { if (horizProfile[y + 1 - yMinI]) { w = (y - start) * splitPrecision; - horizGaps->append(new TextGap((start + 1) * splitPrecision + 0.5 * w, - w)); + horizGaps->addGap((start + 1) * splitPrecision + 0.5 * w, w); } } } @@ -3129,8 +3178,7 @@ void TextPage::findGaps(GList *charsA, int rot, } else { if (vertProfile[x + 1 - xMinI]) { w = (x - start) * splitPrecision; - vertGaps->append(new TextGap((start + 1) * splitPrecision + 0.5 * w, - w)); + vertGaps->addGap((start + 1) * splitPrecision + 0.5 * w, w); } } } @@ -3818,18 +3866,27 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { minGap = 0; } - // if spacing is nearly uniform (minGap is close to maxGap), use the - // SpGap/AdjGap values if available, otherwise assume it's a single - // word (technically it could be either "ABC" or "A B C", but it's - // essentially impossible to tell) + // if spacing is nearly uniform (minGap is close to maxGap), there + // are three cases: + // (1) if the SpGap and AdjGap values are both available and + // sensible, use them + // (2) if only the SpGap values are available, meaning that every + // character in the line had a space after it, split after every + // character + // (3) otherwise assume it's a single word (technically it could be + // either "ABC" or "A B C", but it's essentially impossible to + // tell) if (maxGap - minGap < uniformSpacing * avgFontSize) { - if (minAdjGap <= maxAdjGap && - minSpGap <= maxSpGap && - minSpGap - maxAdjGap > 0.01) { - return 0.5 * (maxAdjGap + minSpGap); - } else { - return maxGap + 1; + if (minSpGap <= maxSpGap) { + if (minAdjGap <= maxAdjGap && + minSpGap - maxAdjGap > 0.01) { + return 0.5 * (maxAdjGap + minSpGap); + } else if (minAdjGap > maxAdjGap && + maxSpGap - minSpGap < uniformSpacing * avgFontSize) { + return minSpGap - 1; + } } + return maxGap + 1; // if there is some variation in spacing, but it's small, assume // there are some inter-word spaces @@ -4033,8 +4090,8 @@ void TextPage::assignSimpleLayoutPositions(GList *superLines, UnicodeMap *uMap) { GList *lines; TextLine *line0, *line1; - double xMin; - int px, sp, i, j; + double xMin, xMax; + int px, px2, sp, i, j; // build a list of lines and sort by x lines = new GList(); @@ -4048,21 +4105,25 @@ void TextPage::assignSimpleLayoutPositions(GList *superLines, for (i = 0; i < lines->getLength(); ++i) { line0 = (TextLine *)lines->get(i); computeLinePhysWidth(line0, uMap); - line0->px = (int)((line0->xMin - xMin) / (0.5 * line0->fontSize)); + px = 0; + xMax = xMin; for (j = 0; j < i; ++j) { line1 = (TextLine *)lines->get(j); if (line0->xMin > line1->xMax) { - sp = (int)((line0->xMin - line1->xMax) / - (0.5 * line0->fontSize) + 0.5); - if (sp < 1) { - sp = 1; + if (line1->xMax > xMax) { + xMax = line1->xMax; } - px = line1->px + line1->pw + sp; - if (px > line0->px) { - line0->px = px; + px2 = line1->px + line1->pw; + if (px2 > px) { + px = px2; } } } + sp = (int)((line0->xMin - xMax) / (0.5 * line0->fontSize) + 0.5); + if (sp < 1 && xMax > xMin) { + sp = 1; + } + line0->px = px + sp; } delete lines; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h index 0884ace6b6b..0f00cf5ae7b 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h @@ -26,6 +26,7 @@ class UnicodeRemapping; class TextBlock; class TextChar; +class TextGaps; class TextLink; class TextPage; @@ -69,6 +70,10 @@ public: GBool discardClippedText; // discard all clipped characters GBool insertBOM; // insert a Unicode BOM at the start of // the text output + double marginLeft, // characters outside the margins are + marginRight, // discarded + marginTop, + marginBottom; }; //------------------------------------------------------------------------ @@ -502,7 +507,7 @@ private: double *xMinOut, double *yMinOut, double *xMaxOut, double *yMaxOut, double *avgFontSizeOut, - GList *horizGaps, GList *vertGaps); + TextGaps *horizGaps, TextGaps *vertGaps); void tagBlock(TextBlock *blk); void insertLargeChars(GList *largeChars, TextBlock *blk); void insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc index 431c639a020..70128a4a47f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc @@ -67,7 +67,7 @@ SplashBitmap *TileCompositor::getBitmap(GBool *finished) { } bitmap = new SplashBitmap(state->getWinW(), state->getWinH(), state->getBitmapRowPad(), state->getColorMode(), - gFalse); + gFalse, gTrue, NULL); } clearBitmap(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc index 6db67738d70..d1a5aea2963 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc @@ -592,6 +592,11 @@ GBool TileMap::cvtWindowToDev(int xw, int yw, offsetY = 0; } *pg = findContinuousPage(yw - offsetY + state->getScrollY()); + if (*pg < 1 || *pg > state->getDoc()->getNumPages()) { + *pg = 0; + *xd = *yd = 0; + return gFalse; + } pageW1 = pageW[*pg - 1]; pageH1 = pageH[*pg - 1]; if (maxW < state->getWinW()) { @@ -655,6 +660,11 @@ GBool TileMap::cvtWindowToDev(int xw, int yw, offsetY = 0; } *pg = findSideBySideContinuousPage(yw - offsetY + state->getScrollY()); + if (*pg < 1 || *pg > state->getDoc()->getNumPages()) { + *pg = 0; + *xd = *yd = 0; + return gFalse; + } pageW1 = pageW[*pg - 1]; pageH1 = pageH[*pg - 1]; if (*pg + 1 <= state->getDoc()->getNumPages()) { @@ -693,6 +703,11 @@ GBool TileMap::cvtWindowToDev(int xw, int yw, offsetX = 0; } *pg = findHorizContinuousPage(xw - offsetX + state->getScrollX()); + if (*pg < 1 || *pg > state->getDoc()->getNumPages()) { + *pg = 0; + *xd = *yd = 0; + return gFalse; + } pageW1 = pageW[*pg - 1]; pageH1 = pageH[*pg - 1]; if (maxH < state->getWinH()) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc index 637d36b6d58..9ee01425094 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc @@ -6,6 +6,7 @@ // //======================================================================== +#include <aconf.h> #include "UTF8.h" int mapUTF8(Unicode u, char *buf, int bufSize) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc new file mode 100644 index 00000000000..95f0e983a31 --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc @@ -0,0 +1,384 @@ +//======================================================================== +// +// WebFont.cc +// +// Copyright 2019 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "gmem.h" +#include "gmempp.h" +#include "GHash.h" +#include "FoFiTrueType.h" +#include "FoFiType1C.h" +#include "CharCodeToUnicode.h" +#include "WebFont.h" + +WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) { + GfxFontType type; + + gfxFont = gfxFontA; + fontBuf = NULL; + ffTrueType = NULL; + ffType1C = NULL; + isOpenType = gFalse; + + type = gfxFont->getType(); + if (type == fontTrueType || + type == fontTrueTypeOT || + type == fontCIDType2 || + type == fontCIDType2OT) { + if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { + ffTrueType = FoFiTrueType::make(fontBuf, fontLength, 0); + } + } else if (type == fontType1C || + type == fontCIDType0C) { + if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { + ffType1C = FoFiType1C::make(fontBuf, fontLength); + } + } else if (type == fontType1COT || + type == fontCIDType0COT) { + if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) { + isOpenType = gTrue; + } + } +} + +WebFont::~WebFont() { + delete ffTrueType; + delete ffType1C; + gfree(fontBuf); +} + +GBool WebFont::canWriteTTF() { + return ffTrueType != NULL; +} + +GBool WebFont::canWriteOTF() { + return ffType1C || isOpenType; +} + +static void writeToFile(void *stream, const char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} + +GBool WebFont::writeTTF(const char *fontFilePath) { + FILE *out; + int *codeToGID; + Guchar *cmapTable; + GBool freeCodeToGID; + int nCodes, cmapTableLength; + + if (!ffTrueType) { + return gFalse; + } + if (gfxFont->isCIDFont()) { + codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID(); + nCodes = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + if (!codeToGID) { + nCodes = ffTrueType->getNumGlyphs(); + } + freeCodeToGID = gFalse; + } else { + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffTrueType); + nCodes = 256; + freeCodeToGID = gTrue; + } + cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, &cmapTableLength); + if (freeCodeToGID) { + gfree(codeToGID); + } + if (!cmapTable) { + return gFalse; + } + if (!(out = fopen(fontFilePath, "wb"))) { + gfree(cmapTable); + return gFalse; + } + ffTrueType->writeTTF(writeToFile, out, NULL, NULL, + cmapTable, cmapTableLength); + fclose(out); + gfree(cmapTable); + return gTrue; +} + +GBool WebFont::writeOTF(const char *fontFilePath) { + int *codeToGID; + Gushort *widths; + Guchar *cmapTable; + FILE *out; + int nCodes, nWidths, cmapTableLength; + + if (ffType1C) { + if (gfxFont->getType() == fontType1C) { + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffType1C); + if (!(cmapTable = makeUnicodeCmapTable(codeToGID, 256, + &cmapTableLength))) { + gfree(codeToGID); + return gFalse; + } + widths = makeType1CWidths(codeToGID, 256, &nWidths); + gfree(codeToGID); + } else { // fontCIDType0C + codeToGID = ffType1C->getCIDToGIDMap(&nCodes); + if (!(cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, + &cmapTableLength))) { + gfree(codeToGID); + return gFalse; + } + widths = makeCIDType0CWidths(codeToGID, nCodes, &nWidths); + gfree(codeToGID); + } + if (!(out = fopen(fontFilePath, "wb"))) { + gfree(cmapTable); + gfree(widths); + return gFalse; + } + ffType1C->convertToOpenType(writeToFile, out, + nWidths, widths, + cmapTable, cmapTableLength); + fclose(out); + gfree(cmapTable); + gfree(widths); + + } else if (isOpenType) { + if (!(out = fopen(fontFilePath, "wb"))) { + return gFalse; + } + if (fwrite(fontBuf, 1, fontLength, out) != (Guint)fontLength) { + fclose(out); + return gFalse; + } + fclose(out); + + } else { + return gFalse; + } + + return gTrue; +} + +Gushort *WebFont::makeType1CWidths(int *codeToGID, int nCodes, + int *nWidths) { + Gushort *widths; + Gushort width; + int widthsLen, gid, i; + + widthsLen = ffType1C->getNumGlyphs(); + widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort)); + for (i = 0; i < widthsLen; ++i) { + widths[i] = 0; + } + for (i = 0; i < nCodes; ++i) { + gid = codeToGID[i]; + if (gid < 0 || gid >= widthsLen) { + continue; + } + width = (Gushort)(((Gfx8BitFont *)gfxFont)->getWidth((Guchar)i) + * 1000 + 0.5); + if (width == 0) { + continue; + } + widths[gid] = width; + } + *nWidths = widthsLen; + return widths; +} + +Gushort *WebFont::makeCIDType0CWidths(int *codeToGID, int nCodes, + int *nWidths) { + Gushort *widths; + Gushort width; + int widthsLen, gid, i; + + widthsLen = ffType1C->getNumGlyphs(); + widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort)); + for (i = 0 ; i < widthsLen; ++i) { + widths[i] = 0; + } + for (i = 0; i < nCodes; ++i) { + gid = codeToGID[i]; + if (gid < 0 || gid >= widthsLen) { + continue; + } + width = (Gushort)(((GfxCIDFont *)gfxFont)->getWidth((CID)i) + * 1000 + 0.5); + if (width == 0) { + continue; + } + widths[gid] = width; + } + *nWidths = widthsLen; + return widths; +} + +Guchar *WebFont::makeUnicodeCmapTable(int *codeToGID, int nCodes, + int *unicodeCmapLength) { + int *unicodeToGID; + Guchar *cmapTable; + int unicodeToGIDLength, nMappings, len; + int nSegs, searchRange, entrySelector, rangeShift; + int glyphIdOffset, idRangeOffset; + int start, end, c, i; + + if (!(unicodeToGID = makeUnicodeToGID(codeToGID, nCodes, + &unicodeToGIDLength))) { + return NULL; + } + + // count the valid code-to-glyph mappings, and the sequences of + // consecutive valid mappings + // (note: char code 65535 is used to mark the end of table) + nMappings = 0; + nSegs = 1; // count the last segment, mapping 65535 + for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) { + if (unicodeToGID[c]) { + ++nMappings; + if (c == 0 || !unicodeToGID[c-1]) { + ++nSegs; + } + } + } + + i = 1; + entrySelector = 0; + while (2 * i <= nSegs) { + i *= 2; + ++entrySelector; + } + searchRange = 1 << (entrySelector + 1); + rangeShift = 2 * nSegs - searchRange; + + len = 28 + nSegs * 8 + nMappings * 2; + cmapTable = (Guchar *)gmalloc(len); + + // header + cmapTable[ 0] = 0x00; // table version + cmapTable[ 1] = 0x00; + cmapTable[ 2] = 0x00; // number of cmaps + cmapTable[ 3] = 0x01; + cmapTable[ 4] = 0x00; // platform[0] + cmapTable[ 5] = 0x03; + cmapTable[ 6] = 0x00; // encoding[0] + cmapTable[ 7] = 0x01; + cmapTable[ 8] = 0x00; // offset[0] + cmapTable[ 9] = 0x00; + cmapTable[10] = 0x00; + cmapTable[11] = 0x0c; + + // table info + cmapTable[12] = 0x00; // cmap format + cmapTable[13] = 0x04; + cmapTable[14] = (Guchar)((len - 12) >> 8); // cmap length + cmapTable[15] = (Guchar)(len - 12); + cmapTable[16] = 0x00; // cmap version + cmapTable[17] = 0x00; + cmapTable[18] = (Guchar)(nSegs >> 7); // segCountX2 + cmapTable[19] = (Guchar)(nSegs << 1); + cmapTable[20] = (Guchar)(searchRange >> 8); // searchRange + cmapTable[21] = (Guchar)searchRange; + cmapTable[22] = (Guchar)(entrySelector >> 8); // entrySelector + cmapTable[23] = (Guchar)entrySelector; + cmapTable[24] = (Guchar)(rangeShift >> 8); // rangeShift + cmapTable[25] = (Guchar)rangeShift; + cmapTable[26 + nSegs*2 ] = 0; // reservedPad + cmapTable[26 + nSegs*2 + 1] = 0; + + i = 0; + glyphIdOffset = 28 + nSegs*8; + for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) { + if (unicodeToGID[c]) { + if (c == 0 || !unicodeToGID[c-1]) { + start = c; + cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)(start >> 8); + cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)start; + cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta + cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)0; + idRangeOffset = glyphIdOffset - (28 + nSegs*6 + i*2); + cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)(idRangeOffset >> 8); + cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)idRangeOffset; + } + if (c == 65534 || !unicodeToGID[c+1]) { + end = c; + cmapTable[26 + i*2 ] = (Guchar)(end >> 8); + cmapTable[26 + i*2 + 1] = (Guchar)end; + ++i; + } + cmapTable[glyphIdOffset++] = (Guchar)(unicodeToGID[c] >> 8); + cmapTable[glyphIdOffset++] = (Guchar)unicodeToGID[c]; + } + } + + // last segment maps code 65535 to GID 0 + cmapTable[26 + i*2 ] = (Guchar)0xff; // end + cmapTable[26 + i*2 + 1] = (Guchar)0xff; + cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)0xff; // start + cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)0xff; + cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta + cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)1; + cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)0; // idRangeOffset + cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)0; + + gfree(unicodeToGID); + + *unicodeCmapLength = len; + return cmapTable; +} + +int *WebFont::makeUnicodeToGID(int *codeToGID, int nCodes, + int *unicodeToGIDLength) { + int *unicodeToGID; + CharCodeToUnicode *ctu; + Unicode u[2]; + int len, size, newSize, uLen, c, gid; + + if (gfxFont->isCIDFont()) { + if (!(ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { + return NULL; + } + } else { + ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode(); + } + + len = 0; + size = 256; + unicodeToGID = (int *)gmallocn(size, sizeof(int)); + memset(unicodeToGID, 0, size * sizeof(int)); + for (c = 0; c < nCodes; ++c) { + gid = codeToGID ? codeToGID[c] : c; + if (gid < 0 || gid >= 65536) { + continue; + } + uLen = ctu->mapToUnicode(c, u, 2); + if (uLen != 1) { + continue; + } + if (u[0] >= 65536) { // sanity check + continue; + } + if ((int)u[0] >= size) { + newSize = 2 * size; + while ((int)u[0] >= newSize) { + newSize *= 2; + } + unicodeToGID = (int *)greallocn(unicodeToGID, newSize, sizeof(int)); + memset(unicodeToGID + size, 0, (newSize - size) * sizeof(int)); + size = newSize; + } + unicodeToGID[u[0]] = gid; + if ((int)u[0] >= len) { + len = u[0] + 1; + } + } + + ctu->decRefCnt(); + + *unicodeToGIDLength = len; + return unicodeToGID; +} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h new file mode 100644 index 00000000000..a967e2e490a --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h @@ -0,0 +1,69 @@ +//======================================================================== +// +// WebFont.h +// +// Modify/convert embedded PDF fonts to a form usable by web browsers. +// +// Copyright 2019 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef WEBFONT_H +#define WEBFONT_H + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "gtypes.h" +#include "GfxFont.h" + +class FoFiTrueType; +class FoFiType1C; +class XRef; + +//------------------------------------------------------------------------ + +class WebFont { +public: + + WebFont(GfxFont *gfxFontA, XRef *xref); + + ~WebFont(); + + // Returns true if the font is, or can be converted to, a TrueType + // font. + GBool canWriteTTF(); + + // Returns true if the font is, or can be converted to, an OpenType + // font. + GBool canWriteOTF(); + + // Write a TrueType (.ttf) file to [fontFilePath]. This can only be + // called if canWriteTTF() returns true. Returns true on success. + GBool writeTTF(const char *fontFilePath); + + // Write an OpenType (.otf) file to [fontFilePath]. This can only + // be called if canWriteOTF() returns true. Returns true on + // success. + GBool writeOTF(const char *fontFilePath); + +private: + + Gushort *makeType1CWidths(int *codeToGID, int nCodes, int *nWidths); + Gushort *makeCIDType0CWidths(int *codeToGID, int nCodes, int *nWidths); + Guchar *makeUnicodeCmapTable(int *codeToGID, int nCodes, + int *unicodeCmapLength); + int *makeUnicodeToGID(int *codeToGID, int nCodes, int *unicodeToGIDLength); + + GfxFont *gfxFont; + char *fontBuf; + int fontLength; + FoFiTrueType *ffTrueType; + FoFiType1C *ffType1C; + GBool isOpenType; +}; + +#endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc index 18534401946..3b88270c798 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc @@ -25,6 +25,7 @@ #include "GfxFont.h" #include "Zoox.h" #include "PDF417Barcode.h" +#include "UTF8.h" #include "XFAForm.h" #ifdef _WIN32 @@ -446,7 +447,7 @@ XFAForm *XFAForm::load(PDFDoc *docA, Catalog *catalog, fullNameIdx = new GHash(); xfaForm->scanNode(tmpl, name, fullName, gFalse, NULL, nameCount, nameIdx, fullNameCount, fullNameIdx, - catalog); + NULL, catalog); delete nameCount; delete nameIdx; delete fullNameCount; @@ -504,11 +505,6 @@ XFAForm::~XFAForm() { gfree(pageOffsetY); } -//~ need to handle exclGroup -//~ - fields in an exclGroup may/must(?) not have names -//~ - each field has an items element with the the value when that -//~ field is selected - // Scan <elem>. Constructs the node's name and full name. If <elem> // is a field, creates an XFAFormField; else scans <elem>'s children. void XFAForm::scanNode(ZxElement *elem, @@ -516,7 +512,7 @@ void XFAForm::scanNode(ZxElement *elem, GBool inPageSet, XFATableInfo *tableInfo, GHash *nameCount, GHash *nameIdx, GHash *fullNameCount, GHash *fullNameIdx, - Catalog *catalog) { + GString *exclGroupName, Catalog *catalog) { ZxAttr *attr; GString *name, *fullName, *namePart, *fullNamePart; GHash *childNameCount, *childNameIdx, *childFullNameCount, *childFullNameIdx; @@ -575,7 +571,8 @@ void XFAForm::scanNode(ZxElement *elem, } if (elem->isElement("field")) { - scanField(elem, name, fullName, inPageSet, tableInfo, colSpan, catalog); + scanField(elem, name, fullName, exclGroupName, + inPageSet, tableInfo, colSpan, catalog); } else { scanNonField(elem, name, fullName, inPageSet, tableInfo, colSpan, childNameCount, childNameIdx, @@ -652,7 +649,7 @@ void XFAForm::scanFullNames(ZxElement *elem, GHash *fullNameCount) { } void XFAForm::scanField(ZxElement *elem, GString *name, GString *fullName, - GBool inPageSet, + GString *exclGroupName, GBool inPageSet, XFATableInfo *tableInfo, int colSpan, Catalog *catalog) { double xSubOffset, ySubOffset, columnWidth, rowHeight; @@ -679,6 +676,8 @@ void XFAForm::scanField(ZxElement *elem, GString *name, GString *fullName, } fields->append(new XFAFormField(this, elem, name->copy(), fullName->copy(), + exclGroupName ? exclGroupName->copy() + : (GString *)NULL, curPageNum, curXOffset, curYOffset, columnWidth, rowHeight)); @@ -699,6 +698,7 @@ void XFAForm::scanNonField(ZxElement *elem, GString *name, GString *fullName, ZxNode *child; ZxAttr *attr; PDFRectangle *box; + GString *exclGroupName; double xSubOffset, ySubOffset; int savedPageNum; @@ -744,7 +744,8 @@ void XFAForm::scanNonField(ZxElement *elem, GString *name, GString *fullName, curXOffset += xSubOffset; curYOffset += ySubOffset; - } else if (elem->isElement("area")) { + } else if (elem->isElement("area") || + elem->isElement("exclGroup")) { xSubOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0); ySubOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0); curXOffset += xSubOffset; @@ -780,11 +781,17 @@ void XFAForm::scanNonField(ZxElement *elem, GString *name, GString *fullName, } } + if (elem->isElement("exclGroup")) { + exclGroupName = name; + } else { + exclGroupName = NULL; + } + for (child = elem->getFirstChild(); child; child = child->getNextChild()) { if (child->isElement()) { scanNode((ZxElement *)child, name, fullName, inPageSet, newTableInfo, nameCount, nameIdx, fullNameCount, fullNameIdx, - catalog); + exclGroupName, catalog); } } @@ -871,12 +878,14 @@ FormField *XFAForm::getField(int idx) { XFAFormField::XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, GString *nameA, GString *fullNameA, + GString *exclGroupNameA, int pageNumA, double xOffsetA, double yOffsetA, double columnWidthA, double rowHeightA) { xfaForm = xfaFormA; xml = xmlA; name = nameA; fullName = fullNameA; + exclGroupName = exclGroupNameA; pageNum = pageNumA; xOffset = xOffsetA; yOffset = yOffsetA; @@ -887,6 +896,9 @@ XFAFormField::XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, XFAFormField::~XFAFormField() { delete name; delete fullName; + if (exclGroupName) { + delete exclGroupName; + } } int XFAFormField::getPageNum() { @@ -943,7 +955,9 @@ Unicode *XFAFormField::getValue(int *length) { s = getFieldValue("text"); break; } else if (node->isElement("checkButton")) { - s = getFieldValue("integer"); + if (!(s = getFieldValue("integer"))) { + s = getFieldValue("text"); + } break; } else if (node->isElement("barcode")) { s = getFieldValue("text"); @@ -1921,6 +1935,22 @@ GString *XFAFormField::getFieldValue(const char *valueChildType) { } } } + if (exclGroupName) { + p = exclGroupName->getCString(); + if (xfaForm->xml->getRoot() && !strncmp(p, "form.", 5)) { + if ((datasets = + xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) && + (data = datasets->findFirstChildElement("xfa:data"))) { + elem = findFieldInDatasets(data, p + 5); + if (elem && + elem->getFirstChild() && + elem->getFirstChild()->isCharData() && + ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) { + return ((ZxCharData *)elem->getFirstChild())->getData(); + } + } + } + } // check the <form> element p = fullName->getCString(); @@ -1955,7 +1985,9 @@ GString *XFAFormField::getFieldValue(const char *valueChildType) { ZxElement *XFAFormField::findFieldInDatasets(ZxElement *elem, char *partName) { ZxNode *node; + ZxElement *result; GString *nodeName; + char *next; int curIdx, idx, n; curIdx = 0; @@ -1976,11 +2008,21 @@ ZxElement *XFAFormField::findFieldInDatasets(ZxElement *elem, char *partName) { if (!partName[n]) { return (ZxElement *)node; } else if (partName[n] == '.') { - return findFieldInDatasets((ZxElement *)node, partName + n + 1); + if ((result = findFieldInDatasets((ZxElement *)node, + partName + n + 1))) { + return result; + } + break; } } } } + + // search for an "ancestor match" + if ((next = strchr(partName, '.'))) { + return findFieldInDatasets(elem, next + 1); + } + return NULL; } @@ -2054,14 +2096,26 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, double x, double y, double w, double h, GBool whiteBackground, GfxFontDict *fontDict, GString *appearBuf) { + GString *text2; GfxFont *font; const char *fontTag; GString *s; + Unicode u; double yTop, xx, yy, tw, charWidth, lineHeight; double ascent, descent, rectX, rectY, rectW, rectH, blkH; int nLines, line, i, j, k, c, rectI; - //~ deal with Unicode text (is it UTF-8?) + // convert UTF-8 to Latin1 + //~ this currently drops all non-Latin1 characters + text2 = new GString(); + i = 0; + while (getUTF8(text, &i, &u)) { + if (u <= 0xff) { + text2->append((char)u); + } else { + text2->append('?'); + } + } // find the font if ((font = findFont(fontDict, fontName, bold, italic))) { @@ -2093,8 +2147,8 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, if (vAlign == xfaVAlignBottom || vAlign == xfaVAlignMiddle) { nLines = 0; i = 0; - while (i < text->getLength()) { - getNextLine(text, i, font, fontSize, w, &j, &tw, &k); + while (i < text2->getLength()) { + getNextLine(text2, i, font, fontSize, w, &j, &tw, &k); ++nLines; i = k; } @@ -2114,9 +2168,9 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, // write a series of lines of text line = 0; i = 0; - while (i < text->getLength()) { + while (i < text2->getLength()) { - getNextLine(text, i, font, fontSize, w, &j, &tw, &k); + getNextLine(text2, i, font, fontSize, w, &j, &tw, &k); if (tw > rectW) { rectW = tw; } @@ -2140,7 +2194,7 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy); appearBuf->append('('); for (; i < j; ++i) { - c = text->getChar(i) & 0xff; + c = text2->getChar(i) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append((char)c); @@ -2172,13 +2226,13 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, xx = x; break; case xfaHAlignCenter: - xx = x + (int)(0.5 * (combCells - text->getLength())) * tw; + xx = x + (int)(0.5 * (combCells - text2->getLength())) * tw; break; case xfaHAlignRight: - xx = x + w - text->getLength() * tw; + xx = x + w - text2->getLength() * tw; break; } - rectW = text->getLength() * tw; + rectW = text2->getLength() * tw; switch (vAlign) { case xfaVAlignTop: default: @@ -2195,8 +2249,8 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, rectH = ascent - descent; // write the text string - for (i = 0; i < text->getLength(); ++i) { - c = text->getChar(i) & 0xff; + for (i = 0; i < text2->getLength(); ++i) { + c = text2->getChar(i) & 0xff; if (font && !font->isCIDFont()) { charWidth = fontSize * ((Gfx8BitFont *)font)->getWidth((Guchar)c); appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", @@ -2223,12 +2277,12 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, // compute string width if (font && !font->isCIDFont()) { tw = 0; - for (i = 0; i < text->getLength(); ++i) { - tw += ((Gfx8BitFont *)font)->getWidth(text->getChar(i)); + for (i = 0; i < text2->getLength(); ++i) { + tw += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i)); } } else { // otherwise, make a crude estimate - tw = text->getLength() * 0.5; + tw = text2->getLength() * 0.5; } tw *= fontSize; rectW = tw; @@ -2264,8 +2318,8 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, // write the text string appearBuf->append('('); - for (i = 0; i < text->getLength(); ++i) { - c = text->getChar(i) & 0xff; + for (i = 0; i < text2->getLength(); ++i) { + c = text2->getChar(i) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append((char)c); @@ -2304,6 +2358,8 @@ void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, appearBuf->insert(rectI, s); delete s; } + + delete text2; } // Searches <fontDict> for a font matching(<fontName>, <bold>, @@ -2734,7 +2790,7 @@ GString *XFAFormField::pictureFormatDateTime(GString *value, GString *picture) { u <<= 4; if (c >= '0' && c <= '9') { u += c - '0'; - } else if (c >= 'a' && c <= 'F') { + } else if (c >= 'a' && c <= 'f') { u += c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { u += c - 'A' + 10; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h index 8e0b363386a..2f56e728a98 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h @@ -63,11 +63,11 @@ private: GBool inPageSet, XFATableInfo *tableInfo, GHash *nameCount, GHash *nameIdx, GHash *fullNameCount, GHash *fullNameIdx, - Catalog *catalog); + GString *exclGroupName, Catalog *catalog); void scanNames(ZxElement *elem, GHash *nameCount); void scanFullNames(ZxElement *elem, GHash *fullNameCount); void scanField(ZxElement *elem, GString *name, GString *fullName, - GBool inPageSet, + GString *exclGroupName, GBool inPageSet, XFATableInfo *tableInfo, int colSpan, Catalog *catalog); void scanNonField(ZxElement *elem, GString *name, GString *fullName, @@ -102,7 +102,7 @@ class XFAFormField: public FormField { public: XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, - GString *nameA, GString *fullNameA, + GString *nameA, GString *fullNameA, GString *exclGroupNameA, int pageNumA, double xOffsetA, double yOffsetA, double columnWidthA, double rowHeightA); @@ -166,6 +166,7 @@ private: ZxElement *xml; GString *name; GString *fullName; + GString *exclGroupName; int pageNum; double xOffset, yOffset; double columnWidth; // column width, if this field is in a diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h index f5c566e9696..3dbed2ae93c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h @@ -14,14 +14,14 @@ //------------------------------------------------------------------------ // xpdf version -#define xpdfVersion "4.01.01" -#define xpdfVersionNum 4.01 +#define xpdfVersion "4.02" +#define xpdfVersionNum 4.02 #define xpdfMajorVersion 4 -#define xpdfMinorVersion 1 -#define xpdfUpdateVersion 1 +#define xpdfMinorVersion 2 +#define xpdfUpdateVersion 0 #define xpdfMajorVersionStr "4" -#define xpdfMinorVersionStr "1" -#define xpdfUpdateVersionStr "1" +#define xpdfMinorVersionStr "2" +#define xpdfUpdateVersionStr "0" // supported PDF version #define supportedPDFVersionStr "2.0" @@ -31,7 +31,7 @@ #define xpdfCopyright "Copyright 1996-2019 Glyph & Cog, LLC" // Windows resource file stuff -#define winxpdfVersion "WinXpdf 4.01.01" +#define winxpdfVersion "WinXpdf 4.02" #define xpdfCopyrightAmp "Copyright 1996-2019 Glyph && Cog, LLC" //------------------------------------------------------------------------ diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc index 7df38cc11d7..22298b138c5 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc @@ -159,11 +159,11 @@ int main(int argc, char *argv[]) { // scan the fonts if (showFontLoc || showFontLocPS) { - printf("name type emb sub uni prob object ID location\n"); - printf("------------------------------------ ----------------- --- --- --- ---- --------- --------\n"); + printf("name type emb sub uni prob object ID location\n"); + printf("---------------------------------------------- ----------------- --- --- --- ---- --------- --------\n"); } else { - printf("name type emb sub uni prob object ID\n"); - printf("------------------------------------ ----------------- --- --- --- ---- ---------\n"); + printf("name type emb sub uni prob object ID\n"); + printf("---------------------------------------------- ----------------- --- --- --- ---- ---------\n"); } fonts = NULL; fontsLen = fontsSize = 0; @@ -302,7 +302,7 @@ static void scanFonts(Dict *resDict, PDFDoc *doc) { resDict->lookupNF("ExtGState", &gsDict1); if (checkObject(&gsDict1, &gsDict2) && gsDict2.isDict()) { for (i = 0; i < gsDict2.dictGetLength(); ++i) { - gsDict1.dictGetValNF(i, &gs1); + gsDict2.dictGetValNF(i, &gs1); if (checkObject(&gs1, &gs2) && gs2.isDict()) { gs2.dictLookupNF("SMask", &smask1); if (checkObject(&smask1, &smask2) && smask2.isDict()) { @@ -375,7 +375,7 @@ static void scanFont(GfxFont *font, PDFDoc *doc) { } // print the font info - printf("%-36s %-17s %-3s %-3s %-3s %-4s", + printf("%-46s %-17s %-3s %-3s %-3s %-4s", name ? name->getCString() : "[none]", fontTypeNames[font->getType()], emb ? "yes" : "no", diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc index b458202effb..29095509bed 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc @@ -171,6 +171,7 @@ int main(int argc, char *argv[]) { htmlGen->setZoom(zoom); htmlGen->setDrawInvisibleText(!skipInvisible); htmlGen->setAllTextInvisible(allInvisible); + htmlGen->setExtractFontFiles(gTrue); htmlGen->startDoc(doc); // convert the pages @@ -191,7 +192,7 @@ int main(int argc, char *argv[]) { goto err2; } pngURL = GString::format("page{0:d}.png", pg); - err = htmlGen->convertPage(pg, pngURL->getCString(), + err = htmlGen->convertPage(pg, pngURL->getCString(), htmlDir, &writeToFile, htmlFile, &writeToFile, pngFile); delete pngURL; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc index 720066d32a8..64a0087c7d3 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc @@ -352,6 +352,7 @@ static void writePNGData(png_structp png, SplashBitmap *bitmap) { + static void finishPNG(png_structp *png, png_infop *pngInfo) { if (setjmp(png_jmpbuf(*png))) { exit(2); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc index 0496c6442ff..aa74fb68612 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc @@ -50,6 +50,10 @@ static char textEncName[128] = ""; static char textEOL[16] = ""; static GBool noPageBreaks = gFalse; static GBool insertBOM = gFalse; +static double marginLeft = 0; +static double marginRight = 0; +static double marginTop = 0; +static double marginBottom = 0; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool quiet = gFalse; @@ -88,6 +92,14 @@ static ArgDesc argDesc[] = { "don't insert page breaks between pages"}, {"-bom", argFlag, &insertBOM, 0, "insert a Unicode BOM at the start of the text file"}, + {"-marginl", argFP, &marginLeft, 0, + "left page margin"}, + {"-marginr", argFP, &marginRight, 0, + "right page margin"}, + {"-margint", argFP, &marginTop, 0, + "top page margin"}, + {"-marginb", argFP, &marginBottom, 0, + "bottom page margin"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), @@ -247,6 +259,10 @@ int main(int argc, char *argv[]) { textOutControl.clipText = clipText; textOutControl.discardDiagonalText = discardDiag; textOutControl.insertBOM = insertBOM; + textOutControl.marginLeft = marginLeft; + textOutControl.marginRight = marginRight; + textOutControl.marginTop = marginTop; + textOutControl.marginBottom = marginBottom; textOut = new TextOutputDev(textFileName->getCString(), &textOutControl, gFalse); if (textOut->isOk()) { |