diff options
author | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2021-02-01 06:11:18 +0000 |
---|---|---|
committer | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2021-02-01 06:11:18 +0000 |
commit | 4f9de1dfebdc66d367d730252d6f224d37e96ee6 (patch) | |
tree | 3750421fabf3aa8a8e1f4f6d5558d489e9d4fe02 /Build/source/libs/xpdf/xpdf-src/xpdf | |
parent | 9d694205709f79666458dc2ee685767b28bcd9ac (diff) |
xpdf 4.03
git-svn-id: svn://tug.org/texlive/trunk@57588 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf')
69 files changed, 7513 insertions, 5912 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc index c7f30405483..b98ca2f0189 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc @@ -27,6 +27,9 @@ #include "OptionalContent.h" #include "Annot.h" #include "Lexer.h" +#include "XFAScanner.h" +#include "UTF8.h" +#include "PDF417Barcode.h" #include "AcroForm.h" //------------------------------------------------------------------------ @@ -55,6 +58,11 @@ #define acroFormQuadCenter 1 #define acroFormQuadRight 2 +#define acroFormVAlignTop 0 +#define acroFormVAlignMiddle 1 +#define acroFormVAlignMiddleNoDescender 2 +#define acroFormVAlignBottom 3 + #define annotFlagHidden 0x0002 #define annotFlagPrint 0x0004 #define annotFlagNoView 0x0020 @@ -63,6 +71,256 @@ // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle 0.55228475 +// limit recursive field-parent lookups to avoid infinite loops +#define maxFieldObjectDepth 50 + +//------------------------------------------------------------------------ + +// 5 bars + 5 spaces -- each can be wide (1) or narrow (0) +// (there are always exactly 3 wide elements; +// the last space is always narrow) +static Guchar code3Of9Data[128][10] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x10 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24 + { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a + { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d + { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e + { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f + { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30 + { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1' + { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2' + { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3' + { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4' + { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5' + { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6' + { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7' + { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8' + { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9' + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x40 + { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41 + { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B' + { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C' + { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D' + { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E' + { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F' + { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G' + { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H' + { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I' + { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J' + { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K' + { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L' + { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M' + { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N' + { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O' + { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50 + { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q' + { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R' + { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S' + { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T' + { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U' + { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V' + { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W' + { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X' + { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y' + { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z' + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x60 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x70 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +// 3 bars + 3 spaces -- each can be 1, 2, 3, or 4 units wide +static Guchar code128Data[107][6] = { + { 2, 1, 2, 2, 2, 2 }, + { 2, 2, 2, 1, 2, 2 }, + { 2, 2, 2, 2, 2, 1 }, + { 1, 2, 1, 2, 2, 3 }, + { 1, 2, 1, 3, 2, 2 }, + { 1, 3, 1, 2, 2, 2 }, + { 1, 2, 2, 2, 1, 3 }, + { 1, 2, 2, 3, 1, 2 }, + { 1, 3, 2, 2, 1, 2 }, + { 2, 2, 1, 2, 1, 3 }, + { 2, 2, 1, 3, 1, 2 }, + { 2, 3, 1, 2, 1, 2 }, + { 1, 1, 2, 2, 3, 2 }, + { 1, 2, 2, 1, 3, 2 }, + { 1, 2, 2, 2, 3, 1 }, + { 1, 1, 3, 2, 2, 2 }, + { 1, 2, 3, 1, 2, 2 }, + { 1, 2, 3, 2, 2, 1 }, + { 2, 2, 3, 2, 1, 1 }, + { 2, 2, 1, 1, 3, 2 }, + { 2, 2, 1, 2, 3, 1 }, + { 2, 1, 3, 2, 1, 2 }, + { 2, 2, 3, 1, 1, 2 }, + { 3, 1, 2, 1, 3, 1 }, + { 3, 1, 1, 2, 2, 2 }, + { 3, 2, 1, 1, 2, 2 }, + { 3, 2, 1, 2, 2, 1 }, + { 3, 1, 2, 2, 1, 2 }, + { 3, 2, 2, 1, 1, 2 }, + { 3, 2, 2, 2, 1, 1 }, + { 2, 1, 2, 1, 2, 3 }, + { 2, 1, 2, 3, 2, 1 }, + { 2, 3, 2, 1, 2, 1 }, + { 1, 1, 1, 3, 2, 3 }, + { 1, 3, 1, 1, 2, 3 }, + { 1, 3, 1, 3, 2, 1 }, + { 1, 1, 2, 3, 1, 3 }, + { 1, 3, 2, 1, 1, 3 }, + { 1, 3, 2, 3, 1, 1 }, + { 2, 1, 1, 3, 1, 3 }, + { 2, 3, 1, 1, 1, 3 }, + { 2, 3, 1, 3, 1, 1 }, + { 1, 1, 2, 1, 3, 3 }, + { 1, 1, 2, 3, 3, 1 }, + { 1, 3, 2, 1, 3, 1 }, + { 1, 1, 3, 1, 2, 3 }, + { 1, 1, 3, 3, 2, 1 }, + { 1, 3, 3, 1, 2, 1 }, + { 3, 1, 3, 1, 2, 1 }, + { 2, 1, 1, 3, 3, 1 }, + { 2, 3, 1, 1, 3, 1 }, + { 2, 1, 3, 1, 1, 3 }, + { 2, 1, 3, 3, 1, 1 }, + { 2, 1, 3, 1, 3, 1 }, + { 3, 1, 1, 1, 2, 3 }, + { 3, 1, 1, 3, 2, 1 }, + { 3, 3, 1, 1, 2, 1 }, + { 3, 1, 2, 1, 1, 3 }, + { 3, 1, 2, 3, 1, 1 }, + { 3, 3, 2, 1, 1, 1 }, + { 3, 1, 4, 1, 1, 1 }, + { 2, 2, 1, 4, 1, 1 }, + { 4, 3, 1, 1, 1, 1 }, + { 1, 1, 1, 2, 2, 4 }, + { 1, 1, 1, 4, 2, 2 }, + { 1, 2, 1, 1, 2, 4 }, + { 1, 2, 1, 4, 2, 1 }, + { 1, 4, 1, 1, 2, 2 }, + { 1, 4, 1, 2, 2, 1 }, + { 1, 1, 2, 2, 1, 4 }, + { 1, 1, 2, 4, 1, 2 }, + { 1, 2, 2, 1, 1, 4 }, + { 1, 2, 2, 4, 1, 1 }, + { 1, 4, 2, 1, 1, 2 }, + { 1, 4, 2, 2, 1, 1 }, + { 2, 4, 1, 2, 1, 1 }, + { 2, 2, 1, 1, 1, 4 }, + { 4, 1, 3, 1, 1, 1 }, + { 2, 4, 1, 1, 1, 2 }, + { 1, 3, 4, 1, 1, 1 }, + { 1, 1, 1, 2, 4, 2 }, + { 1, 2, 1, 1, 4, 2 }, + { 1, 2, 1, 2, 4, 1 }, + { 1, 1, 4, 2, 1, 2 }, + { 1, 2, 4, 1, 1, 2 }, + { 1, 2, 4, 2, 1, 1 }, + { 4, 1, 1, 2, 1, 2 }, + { 4, 2, 1, 1, 1, 2 }, + { 4, 2, 1, 2, 1, 1 }, + { 2, 1, 2, 1, 4, 1 }, + { 2, 1, 4, 1, 2, 1 }, + { 4, 1, 2, 1, 2, 1 }, + { 1, 1, 1, 1, 4, 3 }, + { 1, 1, 1, 3, 4, 1 }, + { 1, 3, 1, 1, 4, 1 }, + { 1, 1, 4, 1, 1, 3 }, + { 1, 1, 4, 3, 1, 1 }, + { 4, 1, 1, 1, 1, 3 }, + { 4, 1, 1, 3, 1, 1 }, + { 1, 1, 3, 1, 4, 1 }, + { 1, 1, 4, 1, 3, 1 }, + { 3, 1, 1, 1, 4, 1 }, + { 4, 1, 1, 1, 3, 1 }, + { 2, 1, 1, 4, 1, 2 }, // start code A + { 2, 1, 1, 2, 1, 4 }, // start code B + { 2, 1, 1, 2, 3, 2 }, // start code C + { 2, 3, 3, 1, 1, 1 } // stop code (without final bar) +}; + //------------------------------------------------------------------------ // map an annotation ref to a page number @@ -83,13 +341,21 @@ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) { Object acroFormObj2; AcroForm *acroForm; AcroFormField *field; - Object fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2; + Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2; int pageNum, i, j; // this is the normal case: acroFormObj is a dictionary, as expected if (acroFormObjA->isDict()) { acroForm = new AcroForm(docA, acroFormObjA); + if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) { + acroForm->xfaScanner = XFAScanner::load(&xfaObj); + if (!catalog->getNeedsRendering()) { + acroForm->isStaticXFA = gTrue; + } + } + xfaObj.free(); + if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) { acroForm->needAppearances = obj1.getBool(); } @@ -183,17 +449,25 @@ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) { return acroForm; } -AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA): Form(docA) { +AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) { + doc = docA; acroFormObjA->copy(&acroFormObj); needAppearances = gFalse; annotPages = new GList(); fields = new GList(); + xfaScanner = NULL; + isStaticXFA = gFalse; } AcroForm::~AcroForm() { acroFormObj.free(); deleteGList(annotPages, AcroFormAnnotPage); deleteGList(fields, AcroFormField); + delete xfaScanner; +} + +const char *AcroForm::getType() { + return isStaticXFA ? "static XFA" : "AcroForm"; } void AcroForm::buildAnnotPageList(Catalog *catalog) { @@ -296,10 +570,44 @@ int AcroForm::getNumFields() { return fields->getLength(); } -FormField *AcroForm::getField(int idx) { +AcroFormField *AcroForm::getField(int idx) { return (AcroFormField *)fields->get(idx); } +AcroFormField *AcroForm::findField(int pg, double x, double y) { + AcroFormField *field; + double llx, lly, urx, ury; + int i; + + for (i = 0; i < fields->getLength(); ++i) { + field = (AcroFormField *)fields->get(i); + if (field->getPageNum() == pg) { + field->getBBox(&llx, &lly, &urx, &ury); + if (llx <= x && x <= urx && lly <= y && y <= ury) { + return field; + } + } + } + return NULL; +} + +int AcroForm::findFieldIdx(int pg, double x, double y) { + AcroFormField *field; + double llx, lly, urx, ury; + int i; + + for (i = 0; i < fields->getLength(); ++i) { + field = (AcroFormField *)fields->get(i); + if (field->getPageNum() == pg) { + field->getBBox(&llx, &lly, &urx, &ury); + if (llx <= x && x <= urx && lly <= y && y <= ury) { + return i; + } + } + } + return -1; +} + //------------------------------------------------------------------------ // AcroFormField //------------------------------------------------------------------------ @@ -307,11 +615,14 @@ FormField *AcroForm::getField(int idx) { AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { GString *typeStr; TextString *nameA; + GString *xfaName; Guint flagsA; GBool haveFlags, typeFromParentA; Object fieldObjA, parentObj, parentObj2, obj1, obj2; AcroFormFieldType typeA; + XFAField *xfaFieldA; AcroFormField *field; + int depth, i0, i1; fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA); @@ -345,7 +656,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { //----- get info from parent non-terminal fields fieldObjA.dictLookup("Parent", &parentObj); - while (parentObj.isDict()) { + depth = 0; + while (parentObj.isDict() && depth < maxFieldObjectDepth) { if (parentObj.dictLookup("T", &obj1)->isString()) { if (nameA->getLength()) { @@ -373,6 +685,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { parentObj.dictLookup("Parent", &parentObj2); parentObj.free(); parentObj = parentObj2; + + ++depth; } parentObj.free(); @@ -381,6 +695,33 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { goto err1; } + //----- get static XFA info + + xfaFieldA = NULL; + if (acroFormA->xfaScanner) { + // convert field name to UTF-8, and remove segments that start + // with '#' -- to match the XFA field name + xfaName = nameA->toUTF8(); + i0 = 0; + while (i0 < xfaName->getLength()) { + i1 = i0; + while (i1 < xfaName->getLength()) { + if (xfaName->getChar(i1) == '.') { + ++i1; + break; + } + ++i1; + } + if (xfaName->getChar(i0) == '#') { + xfaName->del(i0, i1 - i0); + } else { + i0 = i1; + } + } + xfaFieldA = acroFormA->xfaScanner->findField(xfaName); + delete xfaName; + } + //----- check for a radio button // this is a kludge: if we see a Btn-type field with kids, and the @@ -400,7 +741,9 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { typeA = acroFormFieldCheckbox; } } else if (!typeStr->cmp("Tx")) { - if (flagsA & acroFormFlagFileSelect) { + if (xfaFieldA && xfaFieldA->getBarcodeInfo()) { + typeA = acroFormFieldBarcode; + } else if (flagsA & acroFormFlagFileSelect) { typeA = acroFormFieldFileSelect; } else if (flagsA & acroFormFlagMultiline) { typeA = acroFormFieldMultilineText; @@ -422,7 +765,7 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { delete typeStr; field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA, - typeA, nameA, flagsA, typeFromParentA); + typeA, nameA, flagsA, typeFromParentA, xfaFieldA); fieldObjA.free(); return field; @@ -436,7 +779,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { AcroFormField::AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA, AcroFormFieldType typeA, TextString *nameA, - Guint flagsA, GBool typeFromParentA) { + Guint flagsA, GBool typeFromParentA, + XFAField *xfaFieldA) { acroForm = acroFormA; fieldRefA->copy(&fieldRef); fieldObjA->copy(&fieldObj); @@ -444,6 +788,7 @@ AcroFormField::AcroFormField(AcroForm *acroFormA, name = nameA; flags = flagsA; typeFromParent = typeFromParentA; + xfaField = xfaFieldA; } AcroFormField::~AcroFormField() { @@ -478,6 +823,7 @@ const char *AcroFormField::getType() { case acroFormFieldFileSelect: return "FileSelect"; case acroFormFieldMultilineText: return "MultilineText"; case acroFormFieldText: return "Text"; + case acroFormFieldBarcode: return "Barcode"; case acroFormFieldComboBox: return "ComboBox"; case acroFormFieldListBox: return "ListBox"; case acroFormFieldSignature: return "Signature"; @@ -508,36 +854,47 @@ Unicode *AcroFormField::getValue(int *length) { u = NULL; *length = 0; - fieldLookup("V", &obj1); - if (obj1.isName()) { - s = obj1.getName(); - n = (int)strlen(s); - u = (Unicode *)gmallocn(n, sizeof(Unicode)); - for (i = 0; i < n; ++i) { - u[i] = s[i] & 0xff; - } - *length = n; - } else if (obj1.isString()) { - ts = new TextString(obj1.getString()); - n = ts->getLength(); - u = (Unicode *)gmallocn(n, sizeof(Unicode)); - memcpy(u, ts->getUnicode(), n * sizeof(Unicode)); - *length = n; - delete ts; - } else if (obj1.isDict()) { - obj1.dictLookup("Contents", &obj2); - if (obj2.isString()) { - gs = obj2.getString(); - n = gs->getLength(); + // if this field has a counterpart in the XFA form, take the value + // from the XFA field (NB: an XFA field with no value overrides the + // AcroForm value) + if (xfaField) { + if (xfaField->getValue()) { + u = utf8ToUnicode(xfaField->getValue(), length); + } + + // no XFA form - take the AcroForm value + } else { + fieldLookup("V", &obj1); + if (obj1.isName()) { + s = obj1.getName(); + n = (int)strlen(s); u = (Unicode *)gmallocn(n, sizeof(Unicode)); for (i = 0; i < n; ++i) { - u[i] = gs->getChar(i) & 0xff; + u[i] = s[i] & 0xff; } *length = n; + } else if (obj1.isString()) { + ts = new TextString(obj1.getString()); + n = ts->getLength(); + u = (Unicode *)gmallocn(n, sizeof(Unicode)); + memcpy(u, ts->getUnicode(), n * sizeof(Unicode)); + *length = n; + delete ts; + } else if (obj1.isDict()) { + obj1.dictLookup("Contents", &obj2); + if (obj2.isString()) { + gs = obj2.getString(); + n = gs->getLength(); + u = (Unicode *)gmallocn(n, sizeof(Unicode)); + for (i = 0; i < n; ++i) { + u[i] = gs->getChar(i) & 0xff; + } + *length = n; + } + obj2.free(); } - obj2.free(); + obj1.free(); } - obj1.free(); return u; } @@ -818,6 +1175,8 @@ void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing, render = gFalse; if (acroForm->needAppearances) { render = gTrue; + } else if (xfaField && xfaField->getValue()) { + render = gTrue; } else { if (!annotObj->dictLookup("AP", &obj1)->isDict()) { render = gTrue; @@ -889,9 +1248,17 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, double borderWidth; double *borderDash; GString *appearanceState; - int borderDashLength, rot, quadding, comb, nOptions, topIdx, i, j; + int borderDashLength, rot, quadding, vAlign, comb, nOptions, topIdx, i; appearBuf = new GString(); +#if 0 //~debug + appearBuf->appendf("1 1 0 rg 0 0 {0:.4f} {1:.4f} re f\n", + xMax - xMin, yMax - yMin); +#endif +#if 0 //~debug + appearBuf->appendf("1 1 0 RG 0 0 {0:.4f} {1:.4f} re s\n", + xMax - xMin, yMax - yMin); +#endif // get the appearance characteristics (MK) dictionary if (annot->lookup("MK", &mkObj)->isDict()) { @@ -1141,6 +1508,9 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, asObj.free(); apObj.free(); + int valueLength; + Unicode *value = getValue(&valueLength); + // draw the field contents if (ftObj.isName("Btn")) { caption = NULL; @@ -1153,12 +1523,13 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, // radio button if (flags & acroFormFlagRadio) { //~ Acrobat doesn't draw a caption if there is no AP dict (?) - if (fieldLookup("V", &obj1) - ->isName(appearanceState->getCString())) { + if (value && unicodeStringEqual(value, valueLength, + appearanceState->getCString())) { if (caption) { - drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, - gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth, - appearBuf); + drawText(caption, da, fontDict, gFalse, 0, + acroFormQuadCenter, acroFormVAlignMiddleNoDescender, + gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin, + borderWidth, gFalse, appearBuf); } else { if (mkDict) { if (mkDict->lookup("BC", &obj2)->isArray() && @@ -1173,55 +1544,112 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, } } } - obj1.free(); // pushbutton } else if (flags & acroFormFlagPushbutton) { if (caption) { - drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, - gFalse, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth, - appearBuf); + drawText(caption, da, fontDict, gFalse, 0, + acroFormQuadCenter, acroFormVAlignMiddle, + gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin, + borderWidth, gFalse, appearBuf); } // checkbox } else { - fieldLookup("V", &obj1); - if (obj1.isName() && !(obj1.isName("Off") || - obj1.isName("No") || - obj1.isName(""))) { + if (value && !(unicodeStringEqual(value, valueLength, "Off") || + unicodeStringEqual(value, valueLength, "No") || + unicodeStringEqual(value, valueLength, "0") || + valueLength == 0)) { if (!caption) { caption = new GString("3"); // ZapfDingbats checkmark } - drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, - gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth, - appearBuf); + drawText(caption, da, fontDict, gFalse, 0, + acroFormQuadCenter, acroFormVAlignMiddleNoDescender, + gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin, + borderWidth, gFalse, appearBuf); } - obj1.free(); } if (caption) { delete caption; } } else if (ftObj.isName("Tx")) { - //~ value strings can be Unicode - fieldLookup("V", &obj1); - if (obj1.isString()) { - if (fieldLookup("Q", &obj2)->isInt()) { - quadding = obj2.getInt(); + XFAFieldBarcodeInfo *barcodeInfo = xfaField ? xfaField->getBarcodeInfo() + : (XFAFieldBarcodeInfo *)NULL; + if (value) { + //~ value strings can be Unicode + GString *valueLatin1 = unicodeToLatin1(value, valueLength); + if (barcodeInfo) { + drawBarcode(valueLatin1, da, fontDict, rot, xMin, yMin, xMax, yMax, + barcodeInfo, appearBuf); } else { - quadding = acroFormQuadLeft; - } - obj2.free(); - comb = 0; - if (flags & acroFormFlagComb) { - if (fieldLookup("MaxLen", &obj2)->isInt()) { - comb = obj2.getInt(); + if (fieldLookup("Q", &obj2)->isInt()) { + quadding = obj2.getInt(); + } else { + quadding = acroFormQuadLeft; } obj2.free(); + vAlign = (flags & acroFormFlagMultiline) ? acroFormVAlignTop + : acroFormVAlignMiddle; + XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo() + : (XFAFieldLayoutInfo *)NULL; + if (layoutInfo) { + switch (layoutInfo->hAlign) { + case xfaFieldLayoutHAlignLeft: + default: + quadding = acroFormQuadLeft; + break; + case xfaFieldLayoutHAlignCenter: + quadding = acroFormQuadCenter; + break; + case xfaFieldLayoutHAlignRight: + quadding = acroFormQuadRight; + break; + } + switch (layoutInfo->vAlign) { + case xfaFieldLayoutVAlignTop: + default: + vAlign = acroFormVAlignTop; + break; + case xfaFieldLayoutVAlignMiddle: + vAlign = acroFormVAlignMiddle; + break; + case xfaFieldLayoutVAlignBottom: + vAlign = acroFormVAlignBottom; + break; + } + } + comb = 0; + if (flags & acroFormFlagComb) { + if (fieldLookup("MaxLen", &obj2)->isInt()) { + comb = obj2.getInt(); + } + obj2.free(); + } + XFAFieldPictureInfo *pictureInfo = + xfaField ? xfaField->getPictureInfo() + : (XFAFieldPictureInfo *)NULL; + GString *value2 = valueLatin1; + if (pictureInfo) { + switch (pictureInfo->subtype) { + case xfaFieldPictureDateTime: + value2 = pictureFormatDateTime(valueLatin1, pictureInfo->format); + break; + case xfaFieldPictureNumeric: + value2 = pictureFormatNumber(valueLatin1, pictureInfo->format); + break; + case xfaFieldPictureText: + value2 = pictureFormatText(valueLatin1, pictureInfo->format); + break; + } + } + drawText(value2, da, fontDict, + flags & acroFormFlagMultiline, comb, quadding, vAlign, + gTrue, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin, + borderWidth, gFalse, appearBuf); + if (value2 != valueLatin1) { + delete value2; + } } - drawText(obj1.getString(), da, fontDict, - flags & acroFormFlagMultiline, comb, quadding, - gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth, - appearBuf); + delete valueLatin1; } - obj1.free(); } else if (ftObj.isName("Ch")) { //~ value/option strings can be Unicode if (fieldLookup("Q", &obj1)->isInt()) { @@ -1230,10 +1658,39 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, quadding = acroFormQuadLeft; } obj1.free(); + vAlign = acroFormVAlignMiddle; + XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo() + : (XFAFieldLayoutInfo *)NULL; + if (layoutInfo) { + switch (layoutInfo->hAlign) { + case xfaFieldLayoutHAlignLeft: + default: + quadding = acroFormQuadLeft; + break; + case xfaFieldLayoutHAlignCenter: + quadding = acroFormQuadCenter; + break; + case xfaFieldLayoutHAlignRight: + quadding = acroFormQuadRight; + break; + } + switch (layoutInfo->vAlign) { + case xfaFieldLayoutVAlignTop: + default: + vAlign = acroFormVAlignTop; + break; + case xfaFieldLayoutVAlignMiddle: + vAlign = acroFormVAlignMiddle; + break; + case xfaFieldLayoutVAlignBottom: + vAlign = acroFormVAlignBottom; + break; + } + } // combo box if (flags & acroFormFlagCombo) { - if (fieldLookup("V", &obj1)->isString()) { - val = obj1.getString()->copy(); + if (value) { + val = unicodeToLatin1(value, valueLength); if (fieldObj.dictLookup("Opt", &obj2)->isArray()) { for (i = 0, done = false; i < obj2.arrayGetLength() && !done; ++i) { obj2.arrayGet(i, &obj3); @@ -1254,12 +1711,12 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, } obj2.free(); drawText(val, da, fontDict, - gFalse, 0, quadding, gTrue, gFalse, rot, - xMin, yMin, xMax, yMax, borderWidth, appearBuf); + gFalse, 0, quadding, vAlign, gTrue, gFalse, rot, + 0, 0, xMax - xMin, yMax - yMin, borderWidth, + gFalse, appearBuf); delete val; //~ Acrobat draws a popup icon on the right side } - obj1.free(); // list box } else { if (fieldObj.dictLookup("Opt", &obj1)->isArray()) { @@ -1285,27 +1742,15 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, // get the selected option(s) selection = (GBool *)gmallocn(nOptions, sizeof(GBool)); //~ need to use the I field in addition to the V field - fieldLookup("V", &obj2); for (i = 0; i < nOptions; ++i) { - selection[i] = gFalse; - if (obj2.isString()) { - if (!obj2.getString()->cmp(text[i])) { - selection[i] = gTrue; - } - } else if (obj2.isArray()) { - for (j = 0; j < obj2.arrayGetLength(); ++j) { - if (obj2.arrayGet(j, &obj3)->isString() && - !obj3.getString()->cmp(text[i])) { - selection[i] = gTrue; - } - obj3.free(); - } - } + selection[i] = unicodeStringEqual(value, valueLength, text[i]); } - obj2.free(); // get the top index if (fieldObj.dictLookup("TI", &obj2)->isInt()) { topIdx = obj2.getInt(); + if (topIdx < 0 || topIdx >= nOptions) { + topIdx = 0; + } } else { topIdx = 0; } @@ -1334,14 +1779,16 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, delete da; } da = new GString("/Helv 10 Tf 1 0 0 rg"); - drawText(caption, da, fontDict, - gFalse, 0, acroFormQuadLeft, gFalse, gFalse, rot, - xMin, yMin, xMax, yMax, borderWidth, appearBuf); + drawText(caption, da, fontDict, gFalse, 0, + acroFormQuadLeft, acroFormVAlignMiddle, gFalse, gFalse, rot, + 0, 0, xMax - xMin, yMax - yMin, borderWidth, gFalse, appearBuf); delete caption; } else { error(errSyntaxError, -1, "Unknown field type"); } + gfree(value); + delete appearanceState; if (da) { delete da; @@ -1457,17 +1904,20 @@ void AcroFormField::setColor(Array *a, GBool fill, int adjust, // Draw the variable text or caption for a field. void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, - GBool multiline, int comb, int quadding, + GBool multiline, int comb, + int quadding, int vAlign, GBool txField, GBool forceZapfDingbats, int rot, - double xMin, double yMin, double xMax, double yMax, - double border, GString *appearBuf) { + double x, double y, double width, double height, + double border, GBool whiteBackground, + GString *appearBuf) { GString *text2; GList *daToks; GString *tok; GfxFont *font; double dx, dy; - double fontSize, fontSize2, x, xPrev, y, w, w2, wMax; - int tfPos, tmPos, i, j, k, c; + double fontSize, fontSize2, topBorder, xx, xPrev, yy, w, wMax; + double offset, offset2, charWidth, ascent, descent; + int tfPos, tmPos, nLines, i, j, k, c; //~ if there is no MK entry, this should use the existing content stream, //~ and only replace the marked content portion of it @@ -1501,7 +1951,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, if (da) { daToks = tokenize(da); for (i = 2; i < daToks->getLength(); ++i) { - if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) { + if (!((GString *)daToks->get(i))->cmp("Tf")) { tfPos = i - 2; } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) { tmPos = i - 6; @@ -1558,23 +2008,21 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, } appearBuf->append("q\n"); if (rot == 90) { - appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin); - dx = yMax - yMin; - dy = xMax - xMin; + appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", width); + dx = height; + dy = width; } else if (rot == 180) { - appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", - xMax - xMin, yMax - yMin); - dx = xMax - yMax; - dy = yMax - yMin; + appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", width, height); + dx = width; + dy = height; } else if (rot == 270) { - appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin); - dx = yMax - yMin; - dy = xMax - xMin; + appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", height); + dx = height; + dy = width; } else { // assume rot == 0 - dx = xMax - xMin; - dy = yMax - yMin; + dx = width; + dy = height; } - appearBuf->append("BT\n"); // multi-line text if (multiline) { @@ -1582,22 +2030,29 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, wMax = dx - 2 * border - 4; +#if 1 //~tmp + // this is a kludge that appears to match Adobe's behavior + if (height > 15) { + topBorder = 5; + } else { + topBorder = 2; + } +#else + topBorder = 5; +#endif + // compute font autosize if (fontSize == 0) { for (fontSize = 10; fontSize > 1; --fontSize) { - y = dy - 3; - w2 = 0; + yy = dy - topBorder; i = 0; while (i < text2->getLength()) { getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); - if (w > w2) { - w2 = w; - } i = k; - y -= fontSize; + yy -= fontSize; } // approximate the descender for the last line - if (y >= 0.33 * fontSize && w <= wMax) { + if (yy >= 0.25 * fontSize && w <= wMax) { break; } } @@ -1609,22 +2064,54 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, } // starting y coordinate - // (note: each line of text starts with a Td operator that moves - // down a line) - if (dy > fontSize + 6) { - y = dy - 3; + nLines = 0; + i = 0; + while (i < text2->getLength()) { + getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); + i = k; + ++nLines; + } + if (font) { + ascent = font->getDeclaredAscent() * fontSize; + descent = font->getDescent() * fontSize; } else { - y = 0.5 * dy - 0.4 * fontSize + fontSize; + ascent = 0.75 * fontSize; + descent = -0.25 * fontSize; } + switch (vAlign) { + case acroFormVAlignTop: + default: + yy = dy - ascent - topBorder; + break; + case acroFormVAlignMiddle: + yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize - descent; + break; + case acroFormVAlignMiddleNoDescender: + yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize; + break; + case acroFormVAlignBottom: + yy = (nLines - 1) * fontSize - descent; + break; + } + // if the field is shorter than a line of text, Acrobat positions + // the text relative to the bottom edge + if (dy < fontSize + topBorder) { + yy = 2 - descent; + } + // each line of text starts with a Td operator that moves down a + // line -- so move up a line here + yy += fontSize; + + appearBuf->append("BT\n"); // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); - tok->append('0'); + tok->appendf("{0:.4f}", x); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); - tok->appendf("{0:.4f}", y); + tok->appendf("{0:.4f}", y + yy); } // write the DA string @@ -1636,7 +2123,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, // write the font matrix (if not part of the DA string) if (tmPos < 0) { - appearBuf->appendf("1 0 0 1 0 {0:.4f} Tm\n", y); + appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y + yy); } // write a series of lines of text @@ -1650,18 +2137,18 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, switch (quadding) { case acroFormQuadLeft: default: - x = border + 2; + xx = border + 2; break; case acroFormQuadCenter: - x = (dx - w) / 2; + xx = (dx - w) / 2; break; case acroFormQuadRight: - x = dx - border - 2 - w; + xx = dx - border - 2 - w; break; } // draw the line - appearBuf->appendf("{0:.4f} {1:.4f} Td\n", x - xPrev, -fontSize); + appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize); appearBuf->append('('); for (; i < j; ++i) { c = text2->getChar(i) & 0xff; @@ -1678,9 +2165,11 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, // next line i = k; - xPrev = x; + xPrev = xx; } + appearBuf->append("ET\n"); + // single-line text } else { //~ replace newlines with spaces? - what does Acrobat do? @@ -1689,7 +2178,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, if (comb > 0) { // compute comb spacing - w = (dx - 2 * border) / comb; + w = dx / comb; // compute font autosize if (fontSize == 0) { @@ -1712,25 +2201,48 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, switch (quadding) { case acroFormQuadLeft: default: - x = border + 2; + xx = 0; break; case acroFormQuadCenter: - x = border + 2 + 0.5 * (comb - text2->getLength()) * w; + xx = ((comb - text2->getLength()) / 2) * w; break; case acroFormQuadRight: - x = border + 2 + (comb - text2->getLength()) * w; + xx = (comb - text2->getLength()) * w; + break; + } + if (font) { + ascent = font->getDeclaredAscent() * fontSize; + descent = font->getDescent() * fontSize; + } else { + ascent = 0.75 * fontSize; + descent = -0.25 * fontSize; + } + switch (vAlign) { + case acroFormVAlignTop: + default: + yy = dy - ascent; + break; + case acroFormVAlignMiddle: + yy = 0.5 * (dy - ascent - descent); + break; + case acroFormVAlignMiddleNoDescender: + yy = 0.5 * (dy - ascent); + break; + case acroFormVAlignBottom: + yy = -descent; break; } - y = 0.5 * dy - 0.4 * fontSize; + + appearBuf->append("BT\n"); // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); - tok->appendf("{0:.4f}", x); + tok->appendf("{0:.4f}", x + xx); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); - tok->appendf("{0:.4f}", y); + tok->appendf("{0:.4f}", y + yy); } // write the DA string @@ -1742,29 +2254,35 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, // write the font matrix (if not part of the DA string) if (tmPos < 0) { - appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y); + appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy); } // write the text string - //~ this should center (instead of left-justify) each character within - //~ its comb cell + offset = 0; for (i = 0; i < text2->getLength(); ++i) { - if (i > 0) { - appearBuf->appendf("{0:.4f} 0 Td\n", w); - } - appearBuf->append('('); c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append((char)c); - } else if (c < 0x20 || c >= 0x80) { - appearBuf->appendf("{0:.4f} 0 Td\n", w); + if (c >= 0x20 && c < 0x80) { + if (font && !font->isCIDFont()) { + charWidth = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize; + } else { + // otherwise, make a crude estimate + charWidth = 0.5 * fontSize; + } + offset2 = 0.5 * (w - charWidth); + appearBuf->appendf("{0:.4f} 0 Td\n", offset + offset2); + if (c == '(' || c == ')' || c == '\\') { + appearBuf->appendf("(\\{0:c}) Tj\n", c); + } else { + appearBuf->appendf("({0:c}) Tj\n", c); + } + offset = w - offset2; } else { - appearBuf->append((char)c); + offset += w; } - appearBuf->append(") Tj\n"); } + appearBuf->append("ET\n"); + // regular (non-comb) formatting } else { @@ -1802,25 +2320,54 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, switch (quadding) { case acroFormQuadLeft: default: - x = border + 2; + xx = border + 2; break; case acroFormQuadCenter: - x = (dx - w) / 2; + xx = (dx - w) / 2; break; case acroFormQuadRight: - x = dx - border - 2 - w; + xx = dx - border - 2 - w; break; } - y = 0.5 * dy - 0.4 * fontSize; + if (font) { + ascent = font->getDeclaredAscent() * fontSize; + descent = font->getDescent() * fontSize; + } else { + ascent = 0.75 * fontSize; + descent = -0.25 * fontSize; + } + switch (vAlign) { + case acroFormVAlignTop: + default: + yy = dy - ascent; + break; + case acroFormVAlignMiddle: + yy = 0.5 * (dy - ascent - descent); + break; + case acroFormVAlignMiddleNoDescender: + yy = 0.5 * (dy - ascent); + break; + case acroFormVAlignBottom: + yy = -descent; + break; + } + + if (whiteBackground) { + appearBuf->appendf("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n", + xx - 0.25 * fontSize, yy - 0.35 * fontSize, + w + 0.5 * fontSize, 1.2 * fontSize); + } + + appearBuf->append("BT\n"); // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); - tok->appendf("{0:.4f}", x); + tok->appendf("{0:.4f}", x + xx); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); - tok->appendf("{0:.4f}", y); + tok->appendf("{0:.4f}", y + yy); } // write the DA string @@ -1832,7 +2379,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, // write the font matrix (if not part of the DA string) if (tmPos < 0) { - appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y); + appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy); } // write the text string @@ -1850,10 +2397,11 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, } appearBuf->append(") Tj\n"); } + + appearBuf->append("ET\n"); } // cleanup - appearBuf->append("ET\n"); appearBuf->append("Q\n"); if (txField) { appearBuf->append("EMC\n"); @@ -2197,6 +2745,203 @@ void AcroFormField::drawCircleBottomRight(double cx, double cy, double r, appearBuf->append("S\n"); } +void AcroFormField::drawBarcode(GString *value, GString *da, + GfxFontDict *fontDict, int rot, + double xMin, double yMin, + double xMax, double yMax, + XFAFieldBarcodeInfo *barcodeInfo, + GString *appearBuf) { + //--- handle rotation + double w, h; + appearBuf->append("q\n"); + switch (rot) { + case 0: + default: + w = xMax - xMin; + h = yMax - yMin; + break; + case 90: + appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin); + w = yMax - yMin; + h = xMax - xMin; + break; + case 180: + appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin); + w = yMax - yMin; + h = xMax - xMin; + break; + case 270: + appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin); + w = yMax - yMin; + h = xMax - xMin; + break; + } + + //--- get the font size + double fontSize = 0.2 * h; + if (da) { + GList *daToks = tokenize(da); + for (int i = 2; i < daToks->getLength(); ++i) { + if (!((GString *)daToks->get(i))->cmp("Tf")) { + fontSize = atof(((GString *)daToks->get(i - 1))->getCString()); + break; + } + } + deleteGList(daToks, GString); + } + + //--- compute the embedded text type position + GBool doText = gTrue; + double yText = 0; + int vAlign = acroFormVAlignTop; + double yBarcode = 0; + double hBarcode = 0; + GBool whiteBackground = gFalse; + //~ this uses an estimate of the font baseline position + if (barcodeInfo->textLocation && + !barcodeInfo->textLocation->cmp("above")) { + yText = h; + vAlign = acroFormVAlignTop; + yBarcode = 0; + hBarcode = h - fontSize; + } else if (barcodeInfo->textLocation && + !barcodeInfo->textLocation->cmp("belowEmbedded")) { + yText = 0; + vAlign = acroFormVAlignBottom; + yBarcode = 0; + hBarcode = h; + whiteBackground = gTrue; + } else if (barcodeInfo->textLocation && + !barcodeInfo->textLocation->cmp("aboveEmbedded")) { + yText = h; + vAlign = acroFormVAlignTop; + yBarcode = 0; + hBarcode = h; + whiteBackground = gTrue; + } else if (barcodeInfo->textLocation && + !barcodeInfo->textLocation->cmp("none")) { + doText = gFalse; + } else { // default is "below" + yText = 0; + vAlign = acroFormVAlignBottom; + yBarcode = fontSize; + hBarcode = h - fontSize; + } + double wText = w; + + //--- remove extraneous start/stop chars + GString *value2 = value->copy(); + if (!barcodeInfo->barcodeType->cmp("code3Of9")) { + if (value2->getLength() >= 1 && value2->getChar(0) == '*') { + value2->del(0); + } + if (value2->getLength() >= 1 && + value2->getChar(value2->getLength() - 1) == '*') { + value2->del(value2->getLength() - 1); + } + } + + //--- draw the bar code + if (!barcodeInfo->barcodeType->cmp("code3Of9")) { + if (!barcodeInfo->dataLength) { + error(errSyntaxError, -1, + "Missing 'dataLength' attribute in barcode field"); + goto err; + } + appearBuf->append("0 g\n"); + double wNarrow = w / ((7 + 3 * barcodeInfo->wideNarrowRatio) + * (barcodeInfo->dataLength + 2)); + double xx = 0; + for (int i = -1; i <= value2->getLength(); ++i) { + int c; + if (i < 0 || i >= value2->getLength()) { + c = '*'; + } else { + c = value2->getChar(i) & 0x7f; + } + for (int j = 0; j < 10; j += 2) { + appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", + xx, yBarcode, + (code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio + : 1) * wNarrow, + hBarcode); + xx += ((code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio : 1) + + (code3Of9Data[c][j+1] ? barcodeInfo->wideNarrowRatio : 1)) + * wNarrow; + } + } + // center the text on the drawn barcode (not the max length barcode) + wText = (value2->getLength() + 2) * (7 + 3 * barcodeInfo->wideNarrowRatio) + * wNarrow; + } else if (!barcodeInfo->barcodeType->cmp("code128B")) { + if (!barcodeInfo->dataLength) { + error(errSyntaxError, -1, + "Missing 'dataLength' attribute in barcode field"); + goto err; + } + appearBuf->append("0 g\n"); + double wNarrow = w / (11 * (barcodeInfo->dataLength + 3) + 2); + double xx = 0; + int checksum = 0; + for (int i = -1; i <= value2->getLength() + 1; ++i) { + int c; + if (i == -1) { + // start code B + c = 104; + checksum += c; + } else if (i == value2->getLength()) { + // checksum + c = checksum % 103; + } else if (i == value2->getLength() + 1) { + // stop code + c = 106; + } else { + c = value2->getChar(i) & 0xff; + if (c >= 32 && c <= 127) { + c -= 32; + } else { + c = 0; + } + checksum += (i + 1) * c; + } + for (int j = 0; j < 6; j += 2) { + appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", + xx, yBarcode, + code128Data[c][j] * wNarrow, hBarcode); + xx += (code128Data[c][j] + code128Data[c][j+1]) * wNarrow; + } + } + // final bar of the stop code + appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", + xx, yBarcode, 2 * wNarrow, hBarcode); + // center the text on the drawn barcode (not the max length barcode) + wText = (11 * (value2->getLength() + 3) + 2) * wNarrow; + } else if (!barcodeInfo->barcodeType->cmp("pdf417")) { + drawPDF417Barcode(w, h, barcodeInfo->moduleWidth, + barcodeInfo->moduleHeight, + barcodeInfo->errorCorrectionLevel, + value2, appearBuf); + doText = gFalse; + } else { + error(errSyntaxError, -1, + "Unimplemented barcode type '{0:t}' in barcode field", + barcodeInfo->barcodeType); + } + //~ add other barcode types here + + //--- draw the embedded text + if (doText) { + drawText(value2, da, fontDict, gFalse, 0, acroFormQuadCenter, + vAlign, gFalse, gFalse, 0, 0, yText, wText, yText + fontSize, + 0, whiteBackground, appearBuf); + } + + appearBuf->append("Q\n"); + + err: + delete value2; +} + GList *AcroFormField::tokenize(GString *s) { GList *toks; int i, j; @@ -2373,19 +3118,769 @@ Object *AcroFormField::fieldLookup(const char *key, Object *obj) { } Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) { - Object parent; + Object parent, parent2; + int depth; if (!dict->lookup(key, obj)->isNull()) { return obj; } obj->free(); - if (dict->lookup("Parent", &parent)->isDict()) { - fieldLookup(parent.getDict(), key, obj); - } else { - // some fields don't specify a parent, so we check the AcroForm - // dictionary just in case - acroForm->acroFormObj.dictLookup(key, obj); + + dict->lookup("Parent", &parent)->isDict(); + depth = 0; + while (parent.isDict() && depth < maxFieldObjectDepth) { + if (!parent.dictLookup(key, obj)->isNull()) { + parent.free(); + return obj; + } + obj->free(); + parent.dictLookup("Parent", &parent2); + parent.free(); + parent = parent2; + ++depth; } parent.free(); + + // some fields don't specify a parent, so we check the AcroForm + // dictionary just in case + acroForm->acroFormObj.dictLookup(key, obj); return obj; } + +Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) { + int n = 0; + int i = 0; + Unicode u; + while (getUTF8(s, &i, &u)) { + ++n; + } + Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode)); + n = 0; + i = 0; + while (getUTF8(s, &i, &uVec[n])) { + ++n; + } + *unicodeLength = n; + return uVec; +} + +GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) { + GString *s = new GString(); + for (int i = 0; i < unicodeLength; ++i) { + if (u[i] <= 0xff) { + s->append((char)u[i]); + } + } + return s; +} + +GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength, + GString *s) { + if (s->getLength() != unicodeLength) { + return gFalse; + } + for (int i = 0; i < unicodeLength; ++i) { + if ((s->getChar(i) & 0xff) != u[i]) { + return gFalse; + } + } + return gTrue; +} + +GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength, + const char *s) { + for (int i = 0; i < unicodeLength; ++i) { + if (!s[i] || (s[i] & 0xff) != u[i]) { + return gFalse; + } + } + return gTrue; +} + +//------------------------------------------------------------------------ +// 'picture' formatting +//------------------------------------------------------------------------ + +class PictureNode { +public: + virtual ~PictureNode() {} + virtual GBool isLiteral() { return gFalse; } + virtual GBool isSign() { return gFalse; } + virtual GBool isDigit() { return gFalse; } + virtual GBool isDecPt() { return gFalse; } + virtual GBool isSeparator() { return gFalse; } + virtual GBool isYear() { return gFalse; } + virtual GBool isMonth() { return gFalse; } + virtual GBool isDay() { return gFalse; } + virtual GBool isHour() { return gFalse; } + virtual GBool isMinute() { return gFalse; } + virtual GBool isSecond() { return gFalse; } + virtual GBool isChar() { return gFalse; } +}; + +class PictureLiteral: public PictureNode { +public: + PictureLiteral(GString *sA) { s = sA; } + virtual ~PictureLiteral() { delete s; } + virtual GBool isLiteral() { return gTrue; } + GString *s; +}; + +class PictureSign: public PictureNode { +public: + PictureSign(char cA) { c = cA; } + virtual GBool isSign() { return gTrue; } + char c; +}; + +class PictureDigit: public PictureNode { +public: + PictureDigit(char cA) { c = cA; pos = 0; } + virtual GBool isDigit() { return gTrue; } + char c; + int pos; +}; + +class PictureDecPt: public PictureNode { +public: + PictureDecPt() { } + virtual GBool isDecPt() { return gTrue; } +}; + +class PictureSeparator: public PictureNode { +public: + PictureSeparator() { } + virtual GBool isSeparator() { return gTrue; } +}; + +class PictureYear: public PictureNode { +public: + PictureYear(int nDigitsA) { nDigits = nDigitsA; } + virtual GBool isYear() { return gTrue; } + int nDigits; +}; + +class PictureMonth: public PictureNode { +public: + PictureMonth(int nDigitsA) { nDigits = nDigitsA; } + virtual GBool isMonth() { return gTrue; } + int nDigits; +}; + +class PictureDay: public PictureNode { +public: + PictureDay(int nDigitsA) { nDigits = nDigitsA; } + virtual GBool isDay() { return gTrue; } + int nDigits; +}; + +class PictureHour: public PictureNode { +public: + PictureHour(GBool is24HourA, int nDigitsA) + { is24Hour = is24HourA; nDigits = nDigitsA; } + virtual GBool isHour() { return gTrue; } + GBool is24Hour; + int nDigits; +}; + +class PictureMinute: public PictureNode { +public: + PictureMinute(int nDigitsA) { nDigits = nDigitsA; } + virtual GBool isMinute() { return gTrue; } + int nDigits; +}; + +class PictureSecond: public PictureNode { +public: + PictureSecond(int nDigitsA) { nDigits = nDigitsA; } + virtual GBool isSecond() { return gTrue; } + int nDigits; +}; + +class PictureChar: public PictureNode { +public: + PictureChar() {} + virtual GBool isChar() { return gTrue; } +}; + +GString *AcroFormField::pictureFormatDateTime(GString *value, + GString *picture) { + GList *pic; + PictureNode *node; + GString *ret, *s; + char c; + int year, month, day, hour, min, sec; + int len, picStart, picEnd, u, n, i, j; + + len = value->getLength(); + if (len == 0) { + return value->copy(); + } + + //--- parse the value + + // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)? + // where: + // - the '-'s and ':'s are optional + // - the 'T' is literal + // - we're ignoring optional time zone info at the end + // (if the value is not in this canonical format, we just punt and + // return the value string) + //~ another option would be to parse the value following the + //~ <ui><picture> element + year = month = day = hour = min = sec = 0; + i = 0; + if (!(i + 4 <= len && isValidInt(value, i, 4))) { + return value->copy(); + } + year = convertInt(value, i, 4); + i += 4; + if (i < len && value->getChar(i) == '-') { + ++i; + } + if (i + 2 <= len && isValidInt(value, i, 2)) { + month = convertInt(value, i, 2); + i += 2; + if (i < len && value->getChar(i) == '-') { + ++i; + } + if (i + 2 <= len && isValidInt(value, i, 2)) { + day = convertInt(value, i, 2); + i += 2; + } + } + if (i < len) { + if (value->getChar(i) != 'T') { + return value->copy(); + } + ++i; + if (!(i + 2 <= len && isValidInt(value, i, 2))) { + return value->copy(); + } + hour = convertInt(value, i, 2); + i += 2; + if (i < len && value->getChar(i) == ':') { + ++i; + } + if (i + 2 <= len && isValidInt(value, i, 2)) { + min = convertInt(value, i, 2); + i += 2; + if (i < len && value->getChar(i) == ':') { + ++i; + } + if (i + 2 <= len && isValidInt(value, i, 2)) { + sec = convertInt(value, i, 2); + i += 2; + } + } + } + if (i < len) { + return value->copy(); + } + + //--- skip the category and locale in the picture + + picStart = 0; + picEnd = picture->getLength(); + for (i = 0; i < picture->getLength(); ++i) { + c = picture->getChar(i); + if (c == '{') { + picStart = i + 1; + for (picEnd = picStart; + picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; + ++picEnd) ; + break; + } else if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '(' || + c == ')')) { + break; + } + } + + //--- parse the picture + + pic = new GList(); + i = picStart; + while (i < picEnd) { + c = picture->getChar(i); + ++i; + if (c == '\'') { + s = new GString(); + while (i < picEnd) { + c = picture->getChar(i); + if (c == '\'') { + ++i; + if (i < picEnd && picture->getChar(i) == '\'') { + s->append('\''); + ++i; + } else { + break; + } + } else if (c == '\\') { + ++i; + if (i == picEnd) { + break; + } + c = picture->getChar(i); + ++i; + if (c == 'u' && i+4 <= picEnd) { + u = 0; + for (j = 0; j < 4; ++j, ++i) { + c = picture->getChar(i); + u <<= 4; + if (c >= '0' && c <= '9') { + u += c - '0'; + } else if (c >= 'a' && c <= 'f') { + u += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + u += c - 'A' + 10; + } + } + //~ this should convert to UTF-8 (?) + if (u <= 0xff) { + s->append((char)u); + } + } else { + s->append(c); + } + } else { + s->append(c); + } + } + pic->append(new PictureLiteral(s)); + } else if (c == ',' || c == '-' || c == ':' || + c == '/' || c == '.' || c == ' ') { + s = new GString(); + s->append(c); + pic->append(new PictureLiteral(s)); + } else if (c == 'Y') { + for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ; + pic->append(new PictureYear(n)); + } else if (c == 'M') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ; + pic->append(new PictureMonth(n)); + } else if (c == 'D') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ; + pic->append(new PictureDay(n)); + } else if (c == 'h') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ; + pic->append(new PictureHour(gFalse, n)); + } else if (c == 'H') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ; + pic->append(new PictureHour(gTrue, n)); + } else if (c == 'M') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ; + pic->append(new PictureMinute(n)); + } else if (c == 'S') { + for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ; + pic->append(new PictureSecond(n)); + } + } + + //--- generate formatted text + + ret = new GString(); + for (i = 0; i < pic->getLength(); ++i) { + node = (PictureNode *)pic->get(i); + if (node->isLiteral()) { + ret->append(((PictureLiteral *)node)->s); + } else if (node->isYear()) { + if (((PictureYear *)node)->nDigits == 2) { + if (year >= 1930 && year < 2030) { + ret->appendf("{0:02d}", year % 100); + } else { + ret->append("??"); + } + } else { + ret->appendf("{0:04d}", year); + } + } else if (node->isMonth()) { + if (((PictureMonth *)node)->nDigits == 1) { + ret->appendf("{0:d}", month); + } else { + ret->appendf("{0:02d}", month); + } + } else if (node->isDay()) { + if (((PictureDay *)node)->nDigits == 1) { + ret->appendf("{0:d}", day); + } else { + ret->appendf("{0:02d}", day); + } + } else if (node->isHour()) { + if (((PictureHour *)node)->is24Hour) { + n = hour; + } else { + n = hour % 12; + if (n == 0) { + n = 12; + } + } + if (((PictureHour *)node)->nDigits == 1) { + ret->appendf("{0:d}", n); + } else { + ret->appendf("{0:02d}", n); + } + } else if (node->isMinute()) { + if (((PictureMinute *)node)->nDigits == 1) { + ret->appendf("{0:d}", min); + } else { + ret->appendf("{0:02d}", min); + } + } else if (node->isSecond()) { + if (((PictureSecond *)node)->nDigits == 1) { + ret->appendf("{0:d}", sec); + } else { + ret->appendf("{0:02d}", sec); + } + } + } + deleteGList(pic, PictureNode); + + return ret; +} + +GString *AcroFormField::pictureFormatNumber(GString *value, GString *picture) { + GList *pic; + PictureNode *node; + GString *ret, *s; + GBool neg, haveDigits; + char c; + int start, decPt, trailingZero, len; + int picStart, picEnd, u, pos, i, j; + + len = value->getLength(); + if (len == 0) { + return value->copy(); + } + + //--- parse the value + + // -nnnn.nnnn0000 + // ^ ^ ^ ^ + // | | | +-- len + // | | +------ trailingZero + // | +----------- decPt + // +--------------- start + start = 0; + neg = gFalse; + if (value->getChar(start) == '-') { + neg = gTrue; + ++start; + } else if (value->getChar(start) == '+') { + ++start; + } + for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ; + for (trailingZero = len; + trailingZero > decPt && value->getChar(trailingZero - 1) == '0'; + --trailingZero) ; + + //--- skip the category and locale in the picture + + picStart = 0; + picEnd = picture->getLength(); + for (i = 0; i < picture->getLength(); ++i) { + c = picture->getChar(i); + if (c == '{') { + picStart = i + 1; + for (picEnd = picStart; + picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; + ++picEnd) ; + break; + } else if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '(' || + c == ')')) { + break; + } + } + + //--- parse the picture + + pic = new GList(); + i = picStart; + while (i < picEnd) { + c = picture->getChar(i); + ++i; + if (c == '\'') { + s = new GString(); + while (i < picEnd) { + c = picture->getChar(i); + if (c == '\'') { + ++i; + if (i < picEnd && picture->getChar(i) == '\'') { + s->append('\''); + ++i; + } else { + break; + } + } else if (c == '\\') { + ++i; + if (i == picEnd) { + break; + } + c = picture->getChar(i); + ++i; + if (c == 'u' && i+4 <= picEnd) { + u = 0; + for (j = 0; j < 4; ++j, ++i) { + c = picture->getChar(i); + u <<= 4; + if (c >= '0' && c <= '9') { + u += c - '0'; + } else if (c >= 'a' && c <= 'F') { + u += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + u += c - 'A' + 10; + } + } + //~ this should convert to UTF-8 (?) + if (u <= 0xff) { + s->append((char)u); + } + } else { + s->append(c); + } + } else { + s->append(c); + ++i; + } + } + pic->append(new PictureLiteral(s)); + } else if (c == '-' || c == ':' || c == '/' || c == ' ') { + s = new GString(); + s->append(c); + pic->append(new PictureLiteral(s)); + } else if (c == 's' || c == 'S') { + pic->append(new PictureSign(c)); + } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') { + pic->append(new PictureDigit(c)); + } else if (c == '.') { + pic->append(new PictureDecPt()); + } else if (c == ',') { + pic->append(new PictureSeparator()); + } + } + for (i = 0; i < pic->getLength(); ++i) { + node = (PictureNode *)pic->get(i); + if (node->isDecPt()) { + break; + } + } + pos = 0; + for (j = i - 1; j >= 0; --j) { + node = (PictureNode *)pic->get(j); + if (node->isDigit()) { + ((PictureDigit *)node)->pos = pos; + ++pos; + } + } + pos = -1; + for (j = i + 1; j < pic->getLength(); ++j) { + node = (PictureNode *)pic->get(j); + if (node->isDigit()) { + ((PictureDigit *)node)->pos = pos; + --pos; + } + } + + //--- generate formatted text + + ret = new GString(); + haveDigits = gFalse; + for (i = 0; i < pic->getLength(); ++i) { + node = (PictureNode *)pic->get(i); + if (node->isLiteral()) { + ret->append(((PictureLiteral *)node)->s); + } else if (node->isSign()) { + if (((PictureSign *)node)->c == 'S') { + ret->append(neg ? '-' : ' '); + } else { + if (neg) { + ret->append('-'); + } + } + } else if (node->isDigit()) { + pos = ((PictureDigit *)node)->pos; + c = ((PictureDigit *)node)->c; + if (pos >= 0 && pos < decPt - start) { + ret->append(value->getChar(decPt - 1 - pos)); + haveDigits = gTrue; + } else if (pos < 0 && -pos <= trailingZero - decPt - 1) { + ret->append(value->getChar(decPt - pos)); + haveDigits = gTrue; + } else if (c == '8' && + pos < 0 && + -pos <= len - decPt - 1) { + ret->append('0'); + haveDigits = gTrue; + } else if (c == '9') { + ret->append('0'); + haveDigits = gTrue; + } else if (c == 'Z' && pos >= 0) { + ret->append(' '); + } + } else if (node->isDecPt()) { + if (!(i+1 < pic->getLength() && + ((PictureNode *)pic->get(i+1))->isDigit() && + ((PictureDigit *)pic->get(i+1))->c == 'z') || + trailingZero > decPt + 1) { + ret->append('.'); + } + } else if (node->isSeparator()) { + if (haveDigits) { + ret->append(','); + } + } + } + deleteGList(pic, PictureNode); + + return ret; +} + +GString *AcroFormField::pictureFormatText(GString *value, GString *picture) { + GList *pic; + PictureNode *node; + GString *ret, *s; + char c; + int len, picStart, picEnd, u, i, j; + + len = value->getLength(); + if (len == 0) { + return value->copy(); + } + + //--- skip the category and locale in the picture + + picStart = 0; + picEnd = picture->getLength(); + for (i = 0; i < picture->getLength(); ++i) { + c = picture->getChar(i); + if (c == '{') { + picStart = i + 1; + for (picEnd = picStart; + picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; + ++picEnd) ; + break; + } else if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '(' || + c == ')')) { + break; + } + } + + //--- parse the picture + + pic = new GList(); + i = picStart; + while (i < picEnd) { + c = picture->getChar(i); + ++i; + if (c == '\'') { + s = new GString(); + while (i < picEnd) { + c = picture->getChar(i); + if (c == '\'') { + ++i; + if (i < picEnd && picture->getChar(i) == '\'') { + s->append('\''); + ++i; + } else { + break; + } + } else if (c == '\\') { + ++i; + if (i == picEnd) { + break; + } + c = picture->getChar(i); + ++i; + if (c == 'u' && i+4 <= picEnd) { + u = 0; + for (j = 0; j < 4; ++j, ++i) { + c = picture->getChar(i); + u <<= 4; + if (c >= '0' && c <= '9') { + u += c - '0'; + } else if (c >= 'a' && c <= 'F') { + u += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + u += c - 'A' + 10; + } + } + //~ this should convert to UTF-8 (?) + if (u <= 0xff) { + s->append((char)u); + } + } else { + s->append(c); + } + } else { + s->append(c); + ++i; + } + } + pic->append(new PictureLiteral(s)); + } else if (c == ',' || c == '-' || c == ':' || + c == '/' || c == '.' || c == ' ') { + s = new GString(); + s->append(c); + pic->append(new PictureLiteral(s)); + } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') { + pic->append(new PictureChar()); + } + } + + //--- generate formatted text + + ret = new GString(); + j = 0; + for (i = 0; i < pic->getLength(); ++i) { + node = (PictureNode *)pic->get(i); + if (node->isLiteral()) { + ret->append(((PictureLiteral *)node)->s); + } else if (node->isChar()) { + // if there are more chars in the picture than in the value, + // Adobe renders the value as-is, without picture formatting + if (j >= value->getLength()) { + delete ret; + ret = value->copy(); + break; + } + ret->append(value->getChar(j)); + ++j; + } + } + deleteGList(pic, PictureNode); + + return ret; +} + +GBool AcroFormField::isValidInt(GString *s, int start, int len) { + int i; + + for (i = 0; i < len; ++i) { + if (!(start + i < s->getLength() && + s->getChar(start + i) >= '0' && + s->getChar(start + i) <= '9')) { + return gFalse; + } + } + return gTrue; +} + +int AcroFormField::convertInt(GString *s, int start, int len) { + char c; + int x, i; + + x = 0; + for (i = 0; i < len && start + i < s->getLength(); ++i) { + c = s->getChar(start + i); + if (c < '0' || c > '9') { + break; + } + x = x * 10 + (c - '0'); + } + return x; +} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h index a92979a3e2f..7678d46dbc8 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h @@ -15,27 +15,32 @@ #pragma interface #endif -#include "Form.h" - class TextString; +class Gfx; class GfxFont; class GfxFontDict; +class AcroFormField; +class XFAScanner; +class XFAField; +class XFAFieldBarcodeInfo; //------------------------------------------------------------------------ -class AcroForm: public Form { +class AcroForm { public: static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA); - virtual ~AcroForm(); + ~AcroForm(); - virtual const char *getType() { return "AcroForm"; } + const char *getType(); - virtual void draw(int pageNum, Gfx *gfx, GBool printing); + void draw(int pageNum, Gfx *gfx, GBool printing); - virtual int getNumFields(); - virtual FormField *getField(int idx); + int getNumFields(); + AcroFormField *getField(int idx); + AcroFormField *findField(int pg, double x, double y); + int findFieldIdx(int pg, double x, double y); private: @@ -44,10 +49,13 @@ private: int lookupAnnotPage(Object *annotRef); void scanField(Object *fieldRef); + PDFDoc *doc; Object acroFormObj; GBool needAppearances; GList *annotPages; // [AcroFormAnnotPage] GList *fields; // [AcroFormField] + XFAScanner *xfaScanner; + GBool isStaticXFA; friend class AcroFormField; }; @@ -61,28 +69,29 @@ enum AcroFormFieldType { acroFormFieldFileSelect, acroFormFieldMultilineText, acroFormFieldText, + acroFormFieldBarcode, acroFormFieldComboBox, acroFormFieldListBox, acroFormFieldSignature }; -class AcroFormField: public FormField { +class AcroFormField { public: static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA); - virtual ~AcroFormField(); + ~AcroFormField(); - virtual int getPageNum(); - virtual const char *getType(); - virtual Unicode *getName(int *length); - virtual Unicode *getValue(int *length); - virtual void getBBox(double *llx, double *lly, double *urx, double *ury); - virtual void getFont(Ref *fontID, double *fontSize); - virtual void getColor(double *red, double *green, double *blue); + int getPageNum(); + const char *getType(); + Unicode *getName(int *length); + Unicode *getValue(int *length); + void getBBox(double *llx, double *lly, double *urx, double *ury); + void getFont(Ref *fontID, double *fontSize); + void getColor(double *red, double *green, double *blue); int getMaxLen(); - virtual Object *getResources(Object *res); + Object *getResources(Object *res); AcroFormFieldType getAcroFormFieldType() { return type; } Object *getFieldRef(Object *ref); @@ -94,7 +103,7 @@ private: AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA, AcroFormFieldType typeA, TextString *nameA, - Guint flagsA, GBool typeFromParentA); + Guint flagsA, GBool typeFromParentA, XFAField *xfaFieldA); Ref findFontName(char *fontTag); void draw(int pageNum, Gfx *gfx, GBool printing); void drawAnnot(int pageNum, Gfx *gfx, GBool printing, @@ -107,10 +116,10 @@ private: double xMax, double yMax); void setColor(Array *a, GBool fill, int adjust, GString *appearBuf); void drawText(GString *text, GString *da, GfxFontDict *fontDict, - GBool multiline, int comb, int quadding, + GBool multiline, int comb, int quadding, int vAlign, GBool txField, GBool forceZapfDingbats, int rot, - double xMin, double yMin, double xMax, double yMax, - double border, GString *appearBuf); + double x, double y, double width, double height, + double border, GBool whiteBackground, GString *appearBuf); void drawListBox(GString **text, GBool *selection, int nOptions, int topIdx, GString *da, GfxFontDict *fontDict, @@ -126,12 +135,24 @@ private: GString *appearBuf); void drawCircleBottomRight(double cx, double cy, double r, GString *appearBuf); + void drawBarcode(GString *value, GString *da, GfxFontDict *fontDict, int rot, + double xMin, double yMin, double xMax, double yMax, + XFAFieldBarcodeInfo *barcodeInfo, GString *appearBuf); GList *tokenize(GString *s); Object *getAnnotObj(Object *annotObj); Object *getAnnotResources(Dict *annot, Object *res); void buildDefaultResourceDict(Object *dr); Object *fieldLookup(const char *key, Object *obj); Object *fieldLookup(Dict *dict, const char *key, Object *obj); + Unicode *utf8ToUnicode(GString *s, int *unicodeLength); + GString *unicodeToLatin1(Unicode *u, int unicodeLength); + GBool unicodeStringEqual(Unicode *u, int unicodeLength, GString *s); + GBool unicodeStringEqual(Unicode *u, int unicodeLength, const char *s); + GString *pictureFormatDateTime(GString *value, GString *picture); + GString *pictureFormatNumber(GString *value, GString *picture); + GString *pictureFormatText(GString *value, GString *picture); + GBool isValidInt(GString *s, int start, int len); + int convertInt(GString *s, int start, int len); AcroForm *acroForm; Object fieldRef; @@ -140,6 +161,7 @@ private: TextString *name; Guint flags; GBool typeFromParent; + XFAField *xfaField; friend class AcroForm; }; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc index 61a1d434abf..d7229a2fe86 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc @@ -25,7 +25,7 @@ #include "Lexer.h" #include "PDFDoc.h" #include "OptionalContent.h" -#include "Form.h" +#include "AcroForm.h" #include "BuiltinFontTables.h" #include "FontEncodingTables.h" #include "Annot.h" diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt index 4c88d1d351f..7b515d1d036 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt @@ -38,7 +38,6 @@ add_library(xpdf_objs OBJECT Dict.cc Error.cc FontEncodingTables.cc - Form.cc Function.cc Gfx.cc GfxFont.cc @@ -67,7 +66,7 @@ add_library(xpdf_objs OBJECT UnicodeRemapping.cc UnicodeTypeTable.cc UTF8.cc - XFAForm.cc + XFAScanner.cc XRef.cc Zoox.cc ) @@ -88,6 +87,7 @@ if ((QT4_FOUND OR Qt5Widgets_FOUND) PDFCore.cc PreScanOutputDev.cc PSOutputDev.cc + ShadingImage.cc SplashOutputDev.cc TextOutputDev.cc TileCache.cc @@ -104,6 +104,7 @@ if (HAVE_SPLASH) $<TARGET_OBJECTS:xpdf_objs> PreScanOutputDev.cc PSOutputDev.cc + ShadingImage.cc SplashOutputDev.cc pdftops.cc ) @@ -151,6 +152,7 @@ if (HAVE_SPLASH AND PNG_FOUND) add_executable(pdftohtml $<TARGET_OBJECTS:xpdf_objs> HTMLGen.cc + ShadingImage.cc SplashOutputDev.cc TextOutputDev.cc WebFont.cc @@ -215,6 +217,7 @@ install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfdetach.1 DESTINATION ${CMAKE_INSTALL_ if (HAVE_SPLASH) add_executable(pdftoppm $<TARGET_OBJECTS:xpdf_objs> + ShadingImage.cc SplashOutputDev.cc pdftoppm.cc ) @@ -234,6 +237,7 @@ endif () if (HAVE_SPLASH AND PNG_FOUND) add_executable(pdftopng $<TARGET_OBJECTS:xpdf_objs> + ShadingImage.cc SplashOutputDev.cc pdftopng.cc ) diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc index bec30a8dcfd..6ea8fa89ff4 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc @@ -28,7 +28,7 @@ #include "Page.h" #include "Error.h" #include "Link.h" -#include "Form.h" +#include "AcroForm.h" #include "TextString.h" #include "Catalog.h" @@ -159,6 +159,7 @@ Catalog::Catalog(PDFDoc *docA) { baseURI = NULL; form = NULL; embeddedFiles = NULL; + pageLabels = NULL; #if MULTITHREADED gInitMutex(&pageMutex); #endif @@ -222,7 +223,7 @@ Catalog::Catalog(PDFDoc *docA) { catDict.dictLookup("AcroForm", &acroForm); // get the NeedsRendering flag - // NB: Form::load() uses this value + // NB: AcroForm::load() uses this value needsRendering = catDict.dictLookup("NeedsRendering", &obj)->isBool() && obj.getBool(); obj.free(); @@ -230,7 +231,7 @@ Catalog::Catalog(PDFDoc *docA) { // create the Form // (if acroForm is a null object, this will still create an AcroForm // if there are unattached Widget-type annots) - form = Form::load(doc, this, &acroForm); + form = AcroForm::load(doc, this, &acroForm); // get the OCProperties dictionary catDict.dictLookup("OCProperties", &ocProperties); @@ -241,7 +242,6 @@ Catalog::Catalog(PDFDoc *docA) { // get the ViewerPreferences object catDict.dictLookupNF("ViewerPreferences", &viewerPrefs); - pageLabels = NULL; if (catDict.dictLookup("PageLabels", &obj)->isDict()) { readPageLabelTree(&obj); } @@ -1190,7 +1190,7 @@ GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength, return gFalse; } } - *n = (len - prefixLength - 1) * 26 + (u[i] - (Unicode)style) + 1; + *n = (len - prefixLength - 1) * 26 + (u[prefixLength] - (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 b775ae9b9b8..0acdd2d1574 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h @@ -30,7 +30,7 @@ struct Ref; class LinkDest; class PageTreeNode; class PageLabelNode; -class Form; +class AcroForm; class TextString; //------------------------------------------------------------------------ @@ -88,7 +88,7 @@ public: Object *getAcroForm() { return &acroForm; } - Form *getForm() { return form; } + AcroForm *getForm() { return form; } GBool getNeedsRendering() { return needsRendering; } @@ -137,7 +137,7 @@ private: Object outline; // outline dictionary Object acroForm; // AcroForm dictionary GBool needsRendering; // NeedsRendering flag - Form *form; // parsed form + AcroForm *form; // parsed form Object ocProperties; // OCProperties dictionary GList *embeddedFiles; // embedded file list [EmbeddedFile] GList *pageLabels; // page labels [PageLabelNode] diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc index 0d064cff379..5eec7a9fc6c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc @@ -470,14 +470,24 @@ GBool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) { CharCode oldLen, i; +#if 1 //~utf16 + Unicode u[maxUnicodeString]; + int uLen, j; +#else Unicode u; int j; +#endif if (code > 0xffffff) { // This is an arbitrary limit to avoid integer overflow issues. // (I've seen CMaps with mappings for <ffffffff>.) return; } +#if 1 //~utf16 + if ((uLen = parseUTF16String(uStr, n, u)) == 0) { + return; + } +#endif if (code >= mapLen) { oldLen = mapLen; mapLen = mapLen ? 2 * mapLen : 256; @@ -489,6 +499,25 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, map[i] = 0; } } +#if 1 //~utf16 + if (uLen == 1) { + map[code] = u[0] + offset; + } else { + if (sMapLen >= sMapSize) { + sMapSize = sMapSize + 16; + sMap = (CharCodeToUnicodeString *) + greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString)); + } + map[code] = 0; + sMap[sMapLen].c = code; + for (j = 0; j < uLen; ++j) { + sMap[sMapLen].u[j] = u[j]; + } + sMap[sMapLen].u[uLen - 1] += offset; + sMap[sMapLen].len = uLen; + ++sMapLen; + } +#else //~utf16 if (n <= 4) { if (!parseHex(uStr, n, &u)) { error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); @@ -515,6 +544,36 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset; ++sMapLen; } +#endif //~utf16 +} + +// Convert a UTF-16BE hex string into a sequence of up to +// maxUnicodeString Unicode chars. +int CharCodeToUnicode::parseUTF16String(char *uStr, int n, Unicode *uOut) { + int i = 0; + int uLen = 0; + while (i < n) { + Unicode u; + int j = n; + if (j - i > 4) { + j = i + 4; + } + if (!parseHex(uStr + i, j - i, &u)) { + error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); + return 0; + } + // look for a UTF-16 pair + if (uLen > 0 && uOut[uLen-1] >= 0xd800 && uOut[uLen-1] <= 0xdbff && + u >= 0xdc00 && u <= 0xdfff) { + uOut[uLen-1] = 0x10000 + ((uOut[uLen-1] & 0x03ff) << 10) + (u & 0x03ff); + } else { + if (uLen < maxUnicodeString) { + uOut[uLen++] = u; + } + } + i = j; + } + return uLen; } void CharCodeToUnicode::addMappingInt(CharCode code, Unicode u) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h index 5d7bea5b057..c390a8c5c50 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h @@ -80,6 +80,7 @@ private: GBool parseCMap1(int (*getCharFunc)(void *), void *data, int nBits); void addMapping(CharCode code, char *uStr, int n, int offset); + int parseUTF16String(char *uStr, int n, Unicode *uOut); void addMappingInt(CharCode code, Unicode u); CharCodeToUnicode(); CharCodeToUnicode(GString *tagA); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc deleted file mode 100644 index 681401ad5de..00000000000 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc +++ /dev/null @@ -1,99 +0,0 @@ -//======================================================================== -// -// Form.cc -// -// Copyright 2012 Glyph & Cog, LLC -// -//======================================================================== - -#include <aconf.h> - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include "gmempp.h" -#include "GlobalParams.h" -#include "Error.h" -#include "Object.h" -#include "PDFDoc.h" -#include "AcroForm.h" -#include "XFAForm.h" -#include "Form.h" - -//------------------------------------------------------------------------ -// Form -//------------------------------------------------------------------------ - -Form *Form::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj) { - Form *form; - Object xfaObj; - - if (acroFormObj->isDict()) { - //~ temporary: create an XFAForm only for XFAF, not for dynamic XFA - acroFormObj->dictLookup("XFA", &xfaObj); - if (globalParams->getEnableXFA() && - !xfaObj.isNull() && - !catalog->getNeedsRendering()) { - form = XFAForm::load(docA, catalog, acroFormObj, &xfaObj); - xfaObj.free(); - return form; - } - xfaObj.free(); - } - - // if acroFormObj is a null object, this will still create an - // AcroForm if there are unattached Widget-type annots - return AcroForm::load(docA, catalog, acroFormObj); -} - -Form::Form(PDFDoc *docA) { - doc = docA; -} - -Form::~Form() { -} - -FormField *Form::findField(int pg, double x, double y) { - FormField *field; - double llx, lly, urx, ury; - int i; - - for (i = 0; i < getNumFields(); ++i) { - field = getField(i); - if (field->getPageNum() == pg) { - field->getBBox(&llx, &lly, &urx, &ury); - if (llx <= x && x <= urx && lly <= y && y <= ury) { - return field; - } - } - } - return NULL; -} - -int Form::findFieldIdx(int pg, double x, double y) { - FormField *field; - double llx, lly, urx, ury; - int i; - - for (i = 0; i < getNumFields(); ++i) { - field = getField(i); - if (field->getPageNum() == pg) { - field->getBBox(&llx, &lly, &urx, &ury); - if (llx <= x && x <= urx && lly <= y && y <= ury) { - return i; - } - } - } - return -1; -} - -//------------------------------------------------------------------------ -// FormField -//------------------------------------------------------------------------ - -FormField::FormField() { -} - -FormField::~FormField() { -} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h deleted file mode 100644 index 0e851fe16e0..00000000000 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h +++ /dev/null @@ -1,77 +0,0 @@ -//======================================================================== -// -// Form.h -// -// Copyright 2012 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef FORM_H -#define FORM_H - -#include <aconf.h> - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "gtypes.h" - -class Gfx; -class FormField; - -//------------------------------------------------------------------------ - -class Form { -public: - - static Form *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj); - - virtual ~Form(); - - virtual const char *getType() = 0; - - virtual void draw(int pageNum, Gfx *gfx, GBool printing) = 0; - - virtual int getNumFields() = 0; - virtual FormField *getField(int idx) = 0; - - FormField *findField(int pg, double x, double y); - int findFieldIdx(int pg, double x, double y); - -protected: - - Form(PDFDoc *docA); - - PDFDoc *doc; -}; - -//------------------------------------------------------------------------ - -class FormField { -public: - - FormField(); - virtual ~FormField(); - - virtual int getPageNum() = 0; - virtual const char *getType() = 0; - - // Return the field name. This never returns NULL. - virtual Unicode *getName(int *length) = 0; - - // Return the field value. This returns NULL if the field does not - // have a value. - virtual Unicode *getValue(int *length) = 0; - - virtual void getBBox(double *llx, double *lly, double *urx, double *ury) = 0; - virtual void getFont(Ref *fontID, double *fontSize) = 0; - virtual void getColor(double *red, double *green, double *blue) = 0; - - // Return the resource dictionaries used to draw this field. The - // returned object must be either a dictionary or an array of - // dictonaries. - virtual Object *getResources(Object *res) = 0; -}; - -#endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc index 7a0c9c6a9a5..ed98d9d165f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc @@ -21,6 +21,7 @@ #include "gmempp.h" #include "GString.h" #include "GList.h" +#include "Trace.h" #include "GlobalParams.h" #include "CharTypes.h" #include "Object.h" @@ -146,7 +147,15 @@ Operator Gfx::opTab[] = { &Gfx::opSetStrokeRGBColor}, {"S", 0, {tchkNone}, &Gfx::opStroke}, - {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum}, + {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum}, &Gfx::opSetStrokeColor}, {"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, @@ -240,7 +249,15 @@ Operator Gfx::opTab[] = { &Gfx::opSetRenderingIntent}, {"s", 0, {tchkNone}, &Gfx::opCloseStroke}, - {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum}, + {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum, tchkNum, tchkNum, tchkNum, + tchkNum}, &Gfx::opSetFillColor}, {"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, @@ -277,6 +294,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) { Ref r; if (resDict) { + valid = gTrue; // build font dictionary fonts = NULL; @@ -312,6 +330,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) { resDict->lookup("Properties", &propsDict); } else { + valid = gFalse; fonts = NULL; xObjDict.initNull(); colorSpaceDict.initNull(); @@ -394,7 +413,8 @@ GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) { return gFalse; } -void GfxResources::lookupColorSpace(const char *name, Object *obj) { +void GfxResources::lookupColorSpace(const char *name, Object *obj, + GBool inherit) { GfxResources *resPtr; //~ should also test for G, RGB, and CMYK - but only in inline images (?) @@ -411,6 +431,9 @@ void GfxResources::lookupColorSpace(const char *name, Object *obj) { } obj->free(); } + if (!inherit && valid) { + break; + } } obj->initNull(); } @@ -692,7 +715,7 @@ void Gfx::go(GBool topLevel) { aborted = gFalse; errCount = 0; numArgs = 0; - parser->getObj(&obj); + getContentObj(&obj); while (!obj.isEOF()) { // check for an abort @@ -748,7 +771,7 @@ void Gfx::go(GBool topLevel) { } // grab the next object - parser->getObj(&obj); + getContentObj(&obj); } obj.free(); @@ -772,6 +795,15 @@ void Gfx::go(GBool topLevel) { } } +void Gfx::getContentObj(Object *obj) { + parser->getObj(obj); + if (obj->isRef()) { + error(errSyntaxError, getPos(), "Indirect reference in content stream"); + obj->free(); + obj->initError(); + } +} + // Returns true if successful, false on error. GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) { Operator *op; @@ -807,10 +839,9 @@ GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) { } } else { if (numArgs > -op->numArgs) { - error(errSyntaxError, getPos(), + error(errSyntaxWarning, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); - return gFalse; } } for (i = 0; i < numArgs; ++i) { @@ -951,14 +982,11 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) { } void Gfx::opSetExtGState(Object args[], int numArgs) { - Object obj1, obj2, obj3, objRef3, obj4, obj5; + Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj; Object args2[2]; GfxBlendMode mode; GBool haveFillOP; Function *funcs[4]; - GfxColor backdropColor; - GBool haveBackdropColor; - GfxColorSpace *blendingColorSpace; GBool alpha, knockout; double opac; int i; @@ -1167,48 +1195,20 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { } } obj3.free(); - if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) { - for (i = 0; i < gfxColorMaxComps; ++i) { - backdropColor.c[i] = 0; - } - for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) { - obj3.arrayGet(i, &obj4); - if (obj4.isNum()) { - backdropColor.c[i] = dblToCol(obj4.getNum()); - } - obj4.free(); - } - } - obj3.free(); + obj2.dictLookup("BC", &backdropColorObj); if (obj2.dictLookup("G", &obj3)->isStream()) { if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) { - blendingColorSpace = NULL; knockout = gFalse; - if (!obj4.dictLookup("CS", &obj5)->isNull()) { - blendingColorSpace = GfxColorSpace::parse(&obj5 - ); - } - obj5.free(); if (obj4.dictLookup("K", &obj5)->isBool()) { knockout = obj5.getBool(); } obj5.free(); - if (!haveBackdropColor) { - if (blendingColorSpace) { - blendingColorSpace->getDefaultColor(&backdropColor); - } else { - //~ need to get the parent or default color space (?) - for (i = 0; i < gfxColorMaxComps; ++i) { - backdropColor.c[i] = 0; - } - } - } obj2.dictLookupNF("G", &objRef3); // it doesn't make sense for softmasks to be non-isolated, // because they're blended with a backdrop color, rather // than the original backdrop - doSoftMask(&obj3, &objRef3, alpha, blendingColorSpace, - gTrue, knockout, funcs[0], &backdropColor); + doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0], + &backdropColorObj); objRef3.free(); if (funcs[0]) { delete funcs[0]; @@ -1223,6 +1223,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { "Invalid soft mask in ExtGState - missing group"); } obj3.free(); + backdropColorObj.free(); } else if (!obj2.isNull()) { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState"); } @@ -1233,9 +1234,8 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { } void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha, - GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, - Function *transferFunc, GfxColor *backdropColor) { + Function *transferFunc, Object *backdropColorObj) { Dict *dict, *resDict; double m[6], bbox[4]; Object obj1, obj2; @@ -1291,14 +1291,10 @@ void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha, // draw it ++formDepth; - drawForm(strRef, resDict, m, bbox, gTrue, gTrue, - blendingColorSpace, isolated, knockout, - alpha, transferFunc, backdropColor); + drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout, + alpha, transferFunc, backdropColorObj); --formDepth; - if (blendingColorSpace) { - delete blendingColorSpace; - } obj1.free(); } @@ -1502,6 +1498,10 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { } } +// Technically, per the PDF spec, the 'sc' operator can only be used +// with device, CIE, and Indexed color spaces. Some buggy PDF +// generators use it for DeviceN, so we also allow that (same as +// 'scn', minus the optional pattern name argument). void Gfx::opSetFillColor(Object args[], int numArgs) { GfxColor color; int i; @@ -1524,6 +1524,10 @@ void Gfx::opSetFillColor(Object args[], int numArgs) { out->updateFillColor(state); } +// Technically, per the PDF spec, the 'SC' operator can only be used +// with device, CIE, and Indexed color spaces. Some buggy PDF +// generators use it for DeviceN, so we also allow that (same as +// 'SCN', minus the optional pattern name argument). void Gfx::opSetStrokeColor(Object args[], int numArgs) { GfxColor color; int i; @@ -1998,6 +2002,7 @@ void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, state->lineTo(0, 1); state->closePath(); doPatternFill(gTrue); + state->clearPath(); restoreState(); } @@ -2318,34 +2323,8 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, state->clearPath(); } -#if 1 //~tmp: turn off anti-aliasing temporarily - out->setInShading(gTrue); -#endif - - // do shading type-specific operations - switch (shading->getType()) { - case 1: - doFunctionShFill((GfxFunctionShading *)shading); - break; - case 2: - doAxialShFill((GfxAxialShading *)shading); - break; - case 3: - doRadialShFill((GfxRadialShading *)shading); - break; - case 4: - case 5: - doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); - break; - case 6: - case 7: - doPatchMeshShFill((GfxPatchMeshShading *)shading); - break; - } - -#if 1 //~tmp: turn off anti-aliasing temporarily - out->setInShading(gFalse); -#endif + // perform the fill + doShFill(shading); // restore graphics state restoreStateStack(savedState); @@ -2395,9 +2374,19 @@ void Gfx::opShFill(Object args[], int numArgs) { state->setFillColorSpace(shading->getColorSpace()->copy()); out->updateFillColorSpace(state); -#if 1 //~tmp: turn off anti-aliasing temporarily - out->setInShading(gTrue); -#endif + // perform the fill + doShFill(shading); + + // restore graphics state + restoreStateStack(savedState); + + delete shading; +} + +void Gfx::doShFill(GfxShading *shading) { + if (out->shadedFill(state, shading)) { + return; + } // do shading type-specific operations switch (shading->getType()) { @@ -2419,26 +2408,12 @@ void Gfx::opShFill(Object args[], int numArgs) { doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } - -#if 1 //~tmp: turn off anti-aliasing temporarily - out->setInShading(gFalse); -#endif - - // restore graphics state - restoreStateStack(savedState); - - delete shading; } void Gfx::doFunctionShFill(GfxFunctionShading *shading) { double x0, y0, x1, y1; GfxColor colors[4]; - if (out->useShadedFills() && - out->functionShadedFill(state, shading)) { - return; - } - shading->getDomain(&x0, &y0, &x1, &y1); shading->getColor(x0, y0, &colors[0]); shading->getColor(x0, y1, &colors[1]); @@ -2567,11 +2542,6 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { GfxColor colors[axialSplits]; int abortCheckCounter, nComps, i, j, k; - if (out->useShadedFills() && - out->axialShadedFill(state, shading)) { - return; - } - // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); @@ -2872,11 +2842,6 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) { double theta, alpha, angle, t; int abortCheckCounter, ia, ib, k, n; - if (out->useShadedFills() && - out->radialShadedFill(state, shading)) { - return; - } - // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); @@ -3515,8 +3480,8 @@ void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) { } for (i = 0; i < shading->getNComps(); ++i) { patch00.color[0][0][i] = patch->color[0][0][i]; - patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + - patch->color[0][1][i]); + patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + + patch->color[0][1][i]); patch01.color[0][0][i] = patch00.color[0][1][i]; patch01.color[0][1][i] = patch->color[0][1][i]; patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] + @@ -3905,6 +3870,19 @@ void Gfx::doShowText(GString *s) { //~ the CTM concat values here are wrong (but never used) out->updateCTM(state, 1, 0, 0, 1, 0, 0); state->transformDelta(dx, dy, &ddx, &ddy); +#if 0 + // The PDF spec says: "the graphics state shall be inherited + // from the environment of the text-showing operator that caused + // the [Type 3] glyph description to be invoked". However, + // Acrobat apparently copies the fill color to the stroke color. + // It looks like Ghostscript does the same. (This is only + // relevant for uncached Type 3 glyphs.) Uncomment this block + // to make Xpdf mimic Adobe (in violation of the PDF spec). + state->setStrokeColorSpace(state->getFillColorSpace()->copy()); + out->updateStrokeColorSpace(state); + state->setStrokeColor(state->getFillColor()); + out->updateStrokeColor(state); +#endif if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, code, u, uLen)) { ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef); @@ -4149,7 +4127,7 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { GBool mask, invert; GfxColorSpace *colorSpace, *maskColorSpace; GfxImageColorMap *colorMap, *maskColorMap; - Object maskObj, smaskObj; + Object maskObj, smaskObj, maskRef; GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte; int maskColors[2*gfxColorMaxComps]; int maskWidth, maskHeight; @@ -4591,15 +4569,20 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { // draw it } else { if (haveSoftMask) { + dict->lookupNF("Mask", &maskRef); out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskColorMap, + &maskRef, maskStr, maskWidth, maskHeight, + maskColorMap, haveMatte ? matte : (double *)NULL, interpolate); + maskRef.free(); delete maskColorMap; } else if (haveExplicitMask) { + dict->lookupNF("Mask", &maskRef); out->drawMaskedImage(state, ref, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskInvert, - interpolate); + &maskRef, maskStr, maskWidth, maskHeight, + maskInvert, interpolate); + maskRef.free(); } else { out->drawImage(state, ref, str, width, height, colorMap, haveColorKeyMask ? maskColors : (int *)NULL, inlineImg, @@ -4642,7 +4625,6 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { void Gfx::doForm(Object *strRef, Object *str) { Dict *dict; GBool transpGroup, isolated, knockout; - GfxColorSpace *blendingColorSpace; Object matrixObj, bboxObj; double m[6], bbox[4]; Object resObj; @@ -4705,15 +4687,9 @@ void Gfx::doForm(Object *strRef, Object *str) { // check for a transparency group transpGroup = isolated = knockout = gFalse; - blendingColorSpace = NULL; if (dict->lookup("Group", &obj1)->isDict()) { if (obj1.dictLookup("S", &obj2)->isName("Transparency")) { transpGroup = gTrue; - if (!obj1.dictLookup("CS", &obj3)->isNull()) { - blendingColorSpace = GfxColorSpace::parse(&obj3 - ); - } - obj3.free(); if (obj1.dictLookup("I", &obj3)->isBool()) { isolated = obj3.getBool(); } @@ -4729,25 +4705,23 @@ void Gfx::doForm(Object *strRef, Object *str) { // draw it ++formDepth; - drawForm(strRef, resDict, m, bbox, - transpGroup, gFalse, blendingColorSpace, isolated, knockout); + drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout); --formDepth; - if (blendingColorSpace) { - delete blendingColorSpace; - } resObj.free(); } void Gfx::drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox, GBool transpGroup, GBool softMask, - GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool alpha, Function *transferFunc, - GfxColor *backdropColor) { + Object *backdropColorObj) { Parser *oldParser; GfxState *savedState; + GfxColorSpace *blendingColorSpace; + GfxColor backdropColor; + Object strObj, groupAttrsObj, csObj, obj1; double oldBaseMatrix[6]; int i; @@ -4779,7 +4753,23 @@ void Gfx::drawForm(Object *strRef, Dict *resDict, out->clip(state); state->clearPath(); + blendingColorSpace = NULL; if (softMask || transpGroup) { + // get the blending color space + // NB: this must be done AFTER pushing the resource dictionary, + // so that any Default*** color spaces are available + strRef->fetch(xref, &strObj); + if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) { + if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) { + blendingColorSpace = GfxColorSpace::parse(&csObj + ); + } + csObj.free(); + } + groupAttrsObj.free(); + strObj.free(); + + traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group"); if (state->getBlendMode() != gfxBlendNormal) { state->setBlendMode(gfxBlendNormal); out->updateBlendMode(state); @@ -4831,9 +4821,32 @@ void Gfx::drawForm(Object *strRef, Dict *resDict, popResources(); if (softMask) { - out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor); + for (i = 0; i < gfxColorMaxComps; ++i) { + backdropColor.c[i] = 0; + } + if (backdropColorObj->isArray()) { + for (i = 0; + i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps; + ++i) { + backdropColorObj->arrayGet(i, &obj1); + if (obj1.isNum()) { + backdropColor.c[i] = dblToCol(obj1.getNum()); + } + obj1.free(); + } + } else if (blendingColorSpace) { + blendingColorSpace->getDefaultColor(&backdropColor); + } + //~ else: need to get the parent or default color space (?) + out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor); + traceEnd(oldBaseMatrix, "end soft mask"); } else if (transpGroup) { out->paintTransparencyGroup(state, bbox); + traceEnd(oldBaseMatrix, "end t-group"); + } + + if (blendingColorSpace) { + delete blendingColorSpace; } return; @@ -4911,7 +4924,7 @@ Stream *Gfx::buildImageStream(GBool *haveLength) { // build dictionary dict.initDict(xref); - parser->getObj(&obj); + getContentObj(&obj); while (!obj.isCmd("ID") && !obj.isEOF()) { if (!obj.isName()) { error(errSyntaxError, getPos(), @@ -4920,14 +4933,19 @@ Stream *Gfx::buildImageStream(GBool *haveLength) { } else { key = copyString(obj.getName()); obj.free(); - parser->getObj(&obj); - if (obj.isEOF() || obj.isError()) { + getContentObj(&obj); + if (obj.isEOF()) { gfree(key); break; } - dict.dictAdd(key, &obj); + if (obj.isError()) { + gfree(key); + obj.free(); + } else { + dict.dictAdd(key, &obj); + } } - parser->getObj(&obj); + getContentObj(&obj); } if (obj.isEOF()) { error(errSyntaxError, getPos(), "End of file in inline image"); @@ -5108,7 +5126,7 @@ void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle, // get the form bounding box dict->lookup("BBox", &bboxObj); - if (!bboxObj.isArray()) { + if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) { error(errSyntaxError, getPos(), "Bad form bounding box"); bboxObj.free(); str.free(); @@ -5116,7 +5134,11 @@ void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle, } for (i = 0; i < 4; ++i) { bboxObj.arrayGet(i, &obj1); - bbox[i] = obj1.getNum(); + if (obj1.isNum()) { + bbox[i] = obj1.getNum(); + } else { + bbox[i] = 0; + } obj1.free(); } bboxObj.free(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h index 2159eaaf2e9..4625ac289d0 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h @@ -76,7 +76,7 @@ public: GfxFont *lookupFontByRef(Ref ref); GBool lookupXObject(const char *name, Object *obj); GBool lookupXObjectNF(const char *name, Object *obj); - void lookupColorSpace(const char *name, Object *obj); + void lookupColorSpace(const char *name, Object *obj, GBool inherit = gTrue); GfxPattern *lookupPattern(const char *name ); GfxShading *lookupShading(const char *name @@ -88,6 +88,7 @@ public: private: + GBool valid; GfxFontDict *fonts; Object xObjDict; Object colorSpaceDict; @@ -164,10 +165,9 @@ public: void drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox, GBool transpGroup = gFalse, GBool softMask = gFalse, - GfxColorSpace *blendingColorSpace = NULL, GBool isolated = gFalse, GBool knockout = gFalse, GBool alpha = gFalse, Function *transferFunc = NULL, - GfxColor *backdropColor = NULL); + Object *backdropColorObj = NULL); // Take all of the content stream stack entries from <oldGfx>. This // is useful when creating a new Gfx object to handle a pattern, @@ -214,6 +214,7 @@ private: GBool checkForContentStreamLoop(Object *ref); void go(GBool topLevel); + void getContentObj(Object *obj); GBool execOp(Object *cmd, Object args[], int numArgs); Operator *findOp(char *name); GBool checkArg(Object *arg, TchkType type); @@ -231,9 +232,8 @@ private: void opSetLineWidth(Object args[], int numArgs); void opSetExtGState(Object args[], int numArgs); void doSoftMask(Object *str, Object *strRef, GBool alpha, - GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, - Function *transferFunc, GfxColor *backdropColor); + Function *transferFunc, Object *backdropColorObj); void opSetRenderingIntent(Object args[], int numArgs); GfxRenderingIntent parseRenderingIntent(const char *name); @@ -280,6 +280,7 @@ private: void doShadingPatternFill(GfxShadingPattern *sPat, GBool stroke, GBool eoFill, GBool text); void opShFill(Object args[], int numArgs); + void doShFill(GfxShading *shading); void doFunctionShFill(GfxFunctionShading *shading); void doFunctionShFill1(GfxFunctionShading *shading, double x0, double y0, diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc index b10c81779ff..f224ff75b5f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc @@ -467,6 +467,9 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { } else { t2 = 0; } + if (t != 0 && t < 1.9) { + declaredAscent = t; + } // if both Ascent and CapHeight are set, use the smaller one // (because the most common problem is that Ascent is too large) if (t2 != 0 && (t == 0 || t2 < t)) { @@ -851,19 +854,22 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) { } str = obj2.getStream(); - size = 0; - buf = NULL; + size = 4096; + buf = (char *)gmalloc(size); + *len = 0; str->reset(); do { - if (size > INT_MAX - 4096) { - error(errSyntaxError, -1, "Embedded font file is too large"); - break; + if (*len > size - 4096) { + if (size > INT_MAX / 2) { + error(errSyntaxError, -1, "Embedded font file is too large"); + break; + } + size *= 2; + buf = (char *)grealloc(buf, size); } - buf = (char *)grealloc(buf, size + 4096); - n = str->getBlock(buf + size, 4096); - size += n; + n = str->getBlock(buf + *len, 4096); + *len += n; } while (n == 4096); - *len = size; str->close(); obj2.free(); @@ -947,6 +953,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, missingWidth = builtinFont->missingWidth; ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; + declaredAscent = ascent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; @@ -955,6 +962,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, missingWidth = 0; ascent = 0.75; descent = -0.25; + declaredAscent = ascent; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; } @@ -966,6 +974,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, if (builtinFont) { ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; + declaredAscent = ascent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; @@ -1459,13 +1468,20 @@ int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) { } // reverse map the char names through MacRomanEncoding, then map the - // char codes through the cmap + // char codes through the cmap; fall back on Unicode if that doesn't + // work if (useMacRoman) { for (i = 0; i < 256; ++i) { if ((charName = enc[i])) { if ((code = globalParams->getMacRomanCharCode(charName))) { map[i] = ff->mapCodeToGID(cmap, code); + } else if (unicodeCmap >= 0 && + (u = globalParams->mapNameToUnicode(charName))) { + map[i] = ff->mapCodeToGID(unicodeCmap, u); } + } else if (unicodeCmap >= 0 && + (n = ctu->mapToUnicode((CharCode)i, &u, 1))) { + map[i] = ff->mapCodeToGID(cmap, u); } else { map[i] = -1; } @@ -1631,6 +1647,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA, missingWidth = 0; ascent = 0.95; descent = -0.35; + declaredAscent = ascent; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; collection = NULL; cMap = NULL; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h index 3b63fa69fe0..ded34bc1ee7 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h @@ -185,6 +185,7 @@ public: // Return the ascent and descent values. double getAscent() { return ascent; } double getDescent() { return descent; } + double getDeclaredAscent() { return declaredAscent; } // Return the writing mode (0=horizontal, 1=vertical). virtual int getWMode() { return 0; } @@ -234,6 +235,7 @@ protected: double missingWidth; // "default" width double ascent; // max height above baseline double descent; // max depth below baseline + double declaredAscent; // ascent value, before munging GBool hasToUnicode; // true if the font has a ToUnicode map GBool ok; }; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc index 60b5f577054..5e9991e1b97 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc @@ -101,6 +101,7 @@ static const char *gfxColorSpaceModeNames[] = { GfxColorSpace::GfxColorSpace() { overprintMask = 0x0f; + defaultColorSpace = gFalse; } GfxColorSpace::~GfxColorSpace() { @@ -2683,6 +2684,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( "Missing or invalid BitsPerCoordinate in shading dictionary"); goto err2; } + if (coordBits <= 0 || coordBits > 32) { + error(errSyntaxError, -1, + "Invalid BitsPerCoordinate in shading dictionary"); + goto err2; + } obj1.free(); if (dict->lookup("BitsPerComponent", &obj1)->isInt()) { compBits = obj1.getInt(); @@ -2691,6 +2697,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( "Missing or invalid BitsPerComponent in shading dictionary"); goto err2; } + if (compBits <= 0 || compBits > 16) { + error(errSyntaxError, -1, + "Invalid BitsPerComponent in shading dictionary"); + goto err2; + } obj1.free(); flagBits = vertsPerRow = 0; // make gcc happy if (typeA == 4) { @@ -2701,6 +2712,10 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( "Missing or invalid BitsPerFlag in shading dictionary"); goto err2; } + if (flagBits < 2 || flagBits > 8) { + error(errSyntaxError, -1, "Invalid BitsPerFlag in shading dictionary"); + goto err2; + } obj1.free(); } else { if (dict->lookup("VerticesPerRow", &obj1)->isInt()) { @@ -2711,6 +2726,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( goto err2; } obj1.free(); + if (vertsPerRow < 2) { + error(errSyntaxError, -1, + "Invalid VerticesPerRow in shading dictionary"); + goto err2; + } } if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() >= 6) { @@ -2911,6 +2931,34 @@ void GfxGouraudTriangleShading::getTriangle( } } +void GfxGouraudTriangleShading::getBBox(double *xMin, double *yMin, + double *xMax, double *yMax) { + double xxMin = 0; + double yyMin = 0; + double xxMax = 0; + double yyMax = 0; + if (nVertices > 0) { + xxMin = xxMax = vertices[0].x; + yyMin = yyMax = vertices[0].y; + } + for (int i = 1; i < nVertices; ++i) { + if (vertices[i].x < xxMin) { + xxMin = vertices[i].x; + } else if (vertices[i].x > xxMax) { + xxMax = vertices[i].x; + } + if (vertices[i].y < yyMin) { + yyMin = vertices[i].y; + } else if (vertices[i].y > yyMax) { + yyMax = vertices[i].y; + } + } + *xMin = xxMin; + *yMin = yyMin; + *xMax = xxMax; + *yMax = yyMax; +} + void GfxGouraudTriangleShading::getColor(double *in, GfxColor *out) { double c[gfxColorMaxComps]; int i; @@ -2996,6 +3044,10 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, Object obj1, obj2; int i, j; + nPatchesA = 0; + patchesA = NULL; + patchesSize = 0; + if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) { coordBits = obj1.getInt(); } else { @@ -3003,6 +3055,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, "Missing or invalid BitsPerCoordinate in shading dictionary"); goto err2; } + if (coordBits <= 0 || coordBits > 32) { + error(errSyntaxError, -1, + "Invalid BitsPerCoordinate in shading dictionary"); + goto err2; + } obj1.free(); if (dict->lookup("BitsPerComponent", &obj1)->isInt()) { compBits = obj1.getInt(); @@ -3011,6 +3068,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, "Missing or invalid BitsPerComponent in shading dictionary"); goto err2; } + if (compBits <= 0 || compBits > 16) { + error(errSyntaxError, -1, + "Invalid BitsPerComponent in shading dictionary"); + goto err2; + } obj1.free(); if (dict->lookup("BitsPerFlag", &obj1)->isInt()) { flagBits = obj1.getInt(); @@ -3019,6 +3081,10 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, "Missing or invalid BitsPerFlag in shading dictionary"); goto err2; } + if (flagBits < 2 || flagBits > 8) { + error(errSyntaxError, -1, "Invalid BitsPerFlag in shading dictionary"); + goto err2; + } obj1.free(); if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() >= 6) { @@ -3076,9 +3142,6 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, } obj1.free(); - nPatchesA = 0; - patchesA = NULL; - patchesSize = 0; bitBuf = new GfxShadingBitBuf(str); while (1) { if (!bitBuf->getBits(flagBits, &flag)) { @@ -3506,6 +3569,9 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, err2: obj1.free(); err1: + if (patchesA) { + gfree(patchesA); + } return NULL; } @@ -3513,6 +3579,38 @@ GfxShading *GfxPatchMeshShading::copy() { return new GfxPatchMeshShading(this); } +void GfxPatchMeshShading::getBBox(double *xMin, double *yMin, + double *xMax, double *yMax) { + double xxMin = 0; + double yyMin = 0; + double xxMax = 0; + double yyMax = 0; + if (nPatches > 0) { + xxMin = patches[0].x[0][0]; + yyMin = patches[0].y[0][0]; + } + for (int i = 0; i < nPatches; ++i) { + for (int j = 0; j < 4; ++j) { + for (int k = 0; k < 4; ++k) { + if (patches[i].x[j][k] < xxMin) { + xxMin = patches[i].x[j][k]; + } else if (patches[i].x[j][k] > xxMax) { + xxMax = patches[i].x[j][k]; + } + if (patches[i].y[j][k] < yyMin) { + yyMin = patches[i].y[j][k]; + } else if (patches[i].y[j][k] > yyMax) { + yyMax = patches[i].y[j][k]; + } + } + } + } + *xMin = xxMin; + *yMin = yyMin; + *xMax = xxMax; + *yMax = yyMax; +} + void GfxPatchMeshShading::getColor(double *in, GfxColor *out) { double c[gfxColorMaxComps]; int i; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h index 50507040fed..f9c0cfcad6e 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h @@ -207,6 +207,11 @@ public: // Return the color space's overprint mask. Guint getOverprintMask() { return overprintMask; } + // Return true if this color space object is the result of + // substituting a DefaultGray/RGB/CMYK color space for + // DeviceGray/RGB/CMYK. + GBool isDefaultColorSpace() { return defaultColorSpace; } + // Return the number of color space modes static int getNumColorSpaceModes(); @@ -216,6 +221,7 @@ public: protected: Guint overprintMask; + GBool defaultColorSpace; }; //------------------------------------------------------------------------ @@ -877,6 +883,7 @@ public: void getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2); + void getBBox(double *xMin, double *yMin, double *xMax, double *yMax); void getColor(double *in, GfxColor *out); private: @@ -916,6 +923,7 @@ public: int getNComps() { return nComps; } int getNPatches() { return nPatches; } GfxPatch *getPatch(int i) { return &patches[i]; } + void getBBox(double *xMin, double *yMin, double *xMax, double *yMax); void getColor(double *in, GfxColor *out); private: diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc index 56bfba8e3cc..5ff932479d1 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc @@ -178,24 +178,21 @@ class SysFontInfo { public: GString *name; - GBool bold; - GBool italic; GString *path; SysFontType type; int fontNum; // for TrueType collections - SysFontInfo(GString *nameA, GBool boldA, GBool italicA, - GString *pathA, SysFontType typeA, int fontNumA); + SysFontInfo(GString *nameA, GString *pathA, SysFontType typeA, int fontNumA); ~SysFontInfo(); - GBool match(SysFontInfo *fi); - GBool match(GString *nameA, GBool boldA, GBool italicA); + GString *mungeName1(GString *in); + GString *mungeName2(GString *in); + void mungeName3(GString *name, GBool *bold, GBool *italic); + int match(GString *nameA); }; -SysFontInfo::SysFontInfo(GString *nameA, GBool boldA, GBool italicA, - GString *pathA, SysFontType typeA, int fontNumA) { +SysFontInfo::SysFontInfo(GString *nameA, GString *pathA, + SysFontType typeA, int fontNumA) { name = nameA; - bold = boldA; - italic = italicA; path = pathA; type = typeA; fontNum = fontNumA; @@ -206,14 +203,150 @@ SysFontInfo::~SysFontInfo() { delete path; } -GBool SysFontInfo::match(SysFontInfo *fi) { - return !strcasecmp(name->getCString(), fi->name->getCString()) && - bold == fi->bold && italic == fi->italic; +// Remove space/comma/dash/underscore chars. +// Uppercase the name. +GString *SysFontInfo::mungeName1(GString *in) { + GString *out = new GString(); + for (char *p = in->getCString(); *p; ++p) { + if (*p == ' ' || *p == ',' || *p == '-' || *p == '_') { + // skip + } else if (*p >= 'a' && *p <= 'z') { + out->append((char)(*p & 0xdf)); + } else { + out->append(*p); + } + } + return out; +} + +// Remove trailing "Identity-H"/"Identity-V" from the name. +// Split the name into tokens at space/comma/dash/underscore. +// Remove trailing "MT" or "BT" from tokens. +// Remove trailing "PS" and "WGL4" from tokens. +// Uppercase each token. +// Concatenate tokens (dropping the space/comma/dash chars). +GString *SysFontInfo::mungeName2(GString *in) { + GString *out = new GString(); + char *p0 = in->getCString(); + while (*p0) { + if (!strcmp(p0, "Identity-H") || !strcmp(p0, "Identity-V")) { + break; + } + char *p1; + for (p1 = p0 + 1; + *p1 && *p1 != ' ' && *p1 != ',' && *p1 != '-' && *p1 != '_'; + ++p1) ; + char *p2 = p1; + if (p2 - p0 >= 2 && (p2[-2] == 'B' || p2[-2] == 'M') && p2[-1] == 'T') { + p2 -= 2; + } + if (p2 - p0 >= 2 && p2[-2] == 'P' && p2[-1] == 'S') { + p2 -= 2; + } + if (p2 - p0 >= 4 && + p2[-4] == 'W' && p2[-3] == 'G' && p2[-2] == 'L' && p2[-1] == '4') { + p2 -= 4; + } + for (; p0 < p2; ++p0) { + if (*p0 >= 'a' && *p0 <= 'z') { + out->append((char)(*p0 & 0xdf)); + } else { + out->append(*p0); + } + } + for (p0 = p1; *p0 == ' ' || *p0 == ',' || *p0 == '-' || *p0 == '_'; ++p0) ; + } + return out; } -GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) { - return !strcasecmp(name->getCString(), nameA->getCString()) && - bold == boldA && italic == italicA; +// Remove trailing bold/italic/regular/roman tags from the name. +// (Note: the names have already been uppercased by mungeName1/2.) +void SysFontInfo::mungeName3(GString *name, GBool *bold, GBool *italic) { + *bold = gFalse; + *italic = gFalse; + int n = name->getLength(); + while (1) { + if (n >= 4 && !strcmp(name->getCString() + n - 4, "BOLD")) { + name->del(n - 4, 4); + n -= 4; + *bold = gTrue; + } else if (n >= 6 && !strcmp(name->getCString() + n - 6, "ITALIC")) { + name->del(n - 6, 6); + n -= 6; + *italic = gTrue; + } else if (n >= 7 && !strcmp(name->getCString() + n - 7, "REGULAR")) { + name->del(n - 7, 7); + n -= 7; + } else if (n >= 5 && !strcmp(name->getCString() + n - 5, "ROMAN")) { + name->del(n - 5, 5); + n -= 5; + } else { + break; + } + } +} + +// Returns a score indicating how well this font matches [nameA]. A +// higher score is better. Zero indicates a non-match. +int SysFontInfo::match(GString *nameA) { + // fast fail: check if the first two letters match + if (strncasecmp(name->getCString(), nameA->getCString(), 2)) { + return 0; + } + + GString *pdfName1 = mungeName1(nameA); + GString *sysName1 = mungeName1(name); + if (!pdfName1->cmp(sysName1)) { + delete pdfName1; + delete sysName1; + return 8; + } + + GString *pdfName2 = mungeName2(nameA); + GString *sysName2 = mungeName2(name); + if (!pdfName2->cmp(sysName2)) { + delete pdfName1; + delete sysName1; + delete pdfName2; + delete sysName2; + return 7; + } + + GBool pdfBold1, pdfItalic1, sysBold1, sysItalic1; + mungeName3(pdfName1, &pdfBold1, &pdfItalic1); + mungeName3(sysName1, &sysBold1, &sysItalic1); + int eq1 = !pdfName1->cmp(sysName1); + + GBool pdfBold2, pdfItalic2, sysBold2, sysItalic2; + mungeName3(pdfName2, &pdfBold2, &pdfItalic2); + mungeName3(sysName2, &sysBold2, &sysItalic2); + int eq2 =!pdfName2->cmp(sysName2); + + delete pdfName1; + delete sysName1; + delete pdfName2; + delete sysName2; + + if (eq1 && pdfBold1 == sysBold1 && pdfItalic1 == sysItalic1) { + return 6; + } + if (eq2 && pdfBold2 == sysBold2 && pdfItalic2 == sysItalic2) { + return 5; + } + if (eq1 && pdfItalic1 == sysItalic1) { + return 4; + } + if (eq2 && pdfItalic2 == sysItalic2) { + return 3; + } + if (eq1) { + return 2; + } + if (eq2) { + return 1; + } + + return 0; } //------------------------------------------------------------------------ @@ -254,102 +387,17 @@ SysFontList::~SysFontList() { } SysFontInfo *SysFontList::find(GString *name) { - GString *name2; - GBool bold, italic; - SysFontInfo *fi; - char c; - int n, i; - - name2 = name->copy(); - - // remove space, comma, dash chars - i = 0; - while (i < name2->getLength()) { - c = name2->getChar(i); - if (c == ' ' || c == ',' || c == '-') { - name2->del(i); - } else { - ++i; + SysFontInfo *match = NULL; + int score = 0; + for (int i = 0; i < fonts->getLength(); ++i) { + SysFontInfo *fi = (SysFontInfo *)fonts->get(i); + int s = fi->match(name); + if (s > score) { + match = fi; + score = s; } } - n = name2->getLength(); - - // font names like "Arial-BoldMT,Bold" are occasionally used, - // so run this loop twice - bold = italic = gFalse; - for (i = 0; i < 2; ++i) { - - // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) - if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) { - name2->del(n - 2, 2); - n -= 2; - } - - // look for "Regular" - if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) { - name2->del(n - 7, 7); - n -= 7; - } - - // look for "Italic" - if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) { - name2->del(n - 6, 6); - italic = gTrue; - n -= 6; - } - - // look for "Bold" - if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) { - name2->del(n - 4, 4); - bold = gTrue; - n -= 4; - } - } - - // remove trailing "PS" - if (n > 2 && !strcmp(name2->getCString() + n - 2, "PS")) { - name2->del(n - 2, 2); - n -= 2; - } - - // remove trailing "IdentityH" - if (n > 9 && !strcmp(name2->getCString() + n - 9, "IdentityH")) { - name2->del(n - 9, 9); - n -= 9; - } - - // search for the font - fi = NULL; - for (i = 0; i < fonts->getLength(); ++i) { - fi = (SysFontInfo *)fonts->get(i); - if (fi->match(name2, bold, italic)) { - break; - } - fi = NULL; - } - if (!fi && bold) { - // try ignoring the bold flag - for (i = 0; i < fonts->getLength(); ++i) { - fi = (SysFontInfo *)fonts->get(i); - if (fi->match(name2, gFalse, italic)) { - break; - } - fi = NULL; - } - } - if (!fi && (bold || italic)) { - // try ignoring the bold and italic flags - for (i = 0; i < fonts->getLength(); ++i) { - fi = (SysFontInfo *)fonts->get(i); - if (fi->match(name2, gFalse, gFalse)) { - break; - } - fi = NULL; - } - } - - delete name2; - return fi; + return match; } #ifdef _WIN32 @@ -421,50 +469,15 @@ void SysFontList::scanWindowsFonts(char *winFontDir) { SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, char *path) { - int n; - GBool bold, italic; - GString *s; - char c; - int i; - SysFontType type; - - n = (int)strlen(name); - bold = italic = gFalse; + int n = (int)strlen(name); - // remove trailing ' (TrueType)' - if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) { + // remove trailing ' (TrueType)' or ' (OpenType)' + if (n > 11 && (!strncmp(name + n - 11, " (TrueType)", 11) || + !strncmp(name + n - 11, " (OpenType)", 11))) { n -= 11; } - // remove trailing ' Italic' - if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) { - n -= 7; - italic = gTrue; - } - - // remove trailing ' Bold' - if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) { - n -= 5; - bold = gTrue; - } - - // remove trailing ' Regular' - if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) { - n -= 8; - } - - //----- normalize the font name - s = new GString(name, n); - i = 0; - while (i < s->getLength()) { - c = s->getChar(i); - if (c == ' ' || c == ',' || c == '-') { - s->del(i); - } else { - ++i; - } - } - + SysFontType type; if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { type = sysFontTTC; } else if (!strcasecmp(path + strlen(path) - 4, ".otf")) { @@ -472,31 +485,32 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, } else { type = sysFontTTF; } - return new SysFontInfo(s, bold, italic, new GString(path), type, fontNum); + + return new SysFontInfo(new GString(name, n), new GString(path), + type, fontNum); } -#endif +#endif // _WIN32 #if HAVE_FONTCONFIG void SysFontList::scanFontconfigFonts() { + FcConfig *cfg; FcPattern *pattern; FcObjectSet *objSet; FcFontSet *fontSet; - char *family, *file, *styleLang, *style; - GString *family2; + char *name, *file; SysFontType type; - GBool bold, italic; - char c; - int fontNum, i, j, n; + int fontNum, i, n; - FcInit(); + if (!(cfg = FcInitLoadConfigAndFonts())) { + return; + } 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); + objSet = FcObjectSetBuild(FC_FULLNAME, FC_FILE, FC_INDEX, NULL); + fontSet = FcFontList(cfg, pattern, objSet); FcPatternDestroy(pattern); FcObjectSetDestroy(objSet); @@ -528,54 +542,21 @@ void SysFontList::scanFontconfigFonts() { fontNum = 0; } - //--- font family - if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, - (FcChar8 **)&family) + //--- font name + if (FcPatternGetString(fontSet->fonts[i], FC_FULLNAME, 0, + (FcChar8 **)&name) != 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)); + fonts->append(new SysFontInfo(new GString(name), new GString(file), + type, fontNum)); } FcFontSetDestroy(fontSet); } - FcFini(); + FcConfigDestroy(cfg); } #endif // HAVE_FONTCONFIG @@ -663,6 +644,8 @@ GlobalParams::GlobalParams(const char *cfgFileName) { #else baseDir = appendToPath(getHomeDir(), ".xpdf"); #endif + configFileVars = new GHash(gTrue); + setDataDirVar(); nameToUnicode = new NameToCharCode(); cidToUnicodes = new GHash(gTrue); unicodeToUnicodes = new GHash(gTrue); @@ -736,6 +719,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) { initialDisplayMode = new GString("continuous"); initialToolbarState = gTrue; initialSidebarState = gTrue; + initialSidebarWidth = 0; initialSelectMode = new GString("linear"); maxTileWidth = 1500; maxTileHeight = 1500; @@ -761,6 +745,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) { paperColor = new GString("#ffffff"); matteColor = new GString("#808080"); fullScreenMatteColor = new GString("#000000"); + selectionColor = new GString("#8080ff"); reverseVideoInvertImages = gFalse; launchCommand = NULL; movieCommand = NULL; @@ -769,7 +754,6 @@ GlobalParams::GlobalParams(const char *cfgFileName) { mapUnknownCharNames = gFalse; mapExtTrueTypeFontsViaUnicode = gTrue; droppedFonts = new GHash(gTrue); - enableXFA = gTrue; createDefaultKeyBindings(); popupMenuCmds = new GList(); tabStateFile = appendToPath(getHomeDir(), ".xpdf.tab-state"); @@ -847,14 +831,51 @@ GlobalParams::GlobalParams(const char *cfgFileName) { } } +void GlobalParams::setDataDirVar() { + GString *dir; + +#if defined(XPDFRC_DATADIR) + dir = new GString(XPDFRC_DATADIR); +#elif defined(_WIN32) + wchar_t buf[512]; + DWORD n = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(wchar_t)); + if (n <= 0 || n >= sizeof(buf)) { + // error or path too long for buffer - just use the current dir + buf[0] = L'\0'; + } + GString *path = fileNameToUTF8(buf); + dir = grabPath(path->getCString()); + delete path; + appendToPath(dir, "data"); +#else + //~ may be useful to allow the options of using the install dir + //~ and/or the user's home dir (?) + dir = new GString("./data"); +#endif + + configFileVars->add(new GString("DATADIR"), dir); +} + void GlobalParams::createDefaultKeyBindings() { keyBindings = new GList(); //----- mouse buttons keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone, xpdfKeyContextAny, "startSelection")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModShift, + xpdfKeyContextAny, + "startExtendedSelection")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone, xpdfKeyContextAny, "endSelection")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModShift, + xpdfKeyContextAny, + "endSelection")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMouseDoubleClick1, + xpdfKeyModNone, xpdfKeyContextAny, + "selectWord")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMouseTripleClick1, + xpdfKeyModNone, xpdfKeyContextAny, + "selectLine")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick1, xpdfKeyModNone, xpdfKeyContextAny, "followLinkNoSel")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick2, xpdfKeyModNone, @@ -876,6 +897,10 @@ void GlobalParams::createDefaultKeyBindings() { xpdfKeyContextAny, "scrollLeft(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress7, xpdfKeyModNone, xpdfKeyContextAny, "scrollRight(16)")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModCtrl, + xpdfKeyContextAny, "zoomIn")); + keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModCtrl, + xpdfKeyContextAny, "zoomOut")); //----- control keys keyBindings->append(new KeyBinding('o', xpdfKeyModCtrl, @@ -1002,26 +1027,10 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) { void GlobalParams::parseLine(char *buf, GString *fileName, int line) { GList *tokens; GString *cmd, *incFile; - char *p1, *p2; FILE *f2; // break the line into tokens - tokens = new GList(); - p1 = buf; - while (*p1) { - for (; *p1 && isspace(*p1); ++p1) ; - if (!*p1) { - break; - } - if (*p1 == '"' || *p1 == '\'') { - for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ; - ++p1; - } else { - for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ; - } - tokens->append(new GString(p1, (int)(p2 - p1))); - p1 = *p2 ? p2 + 1 : p2; - } + tokens = parseLineTokens(buf, fileName, line); // parse the line if (tokens->getLength() > 0 && @@ -1153,6 +1162,9 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { } else if (!cmd->cmp("initialSidebarState")) { parseYesNo("initialSidebarState", &initialSidebarState, tokens, fileName, line); + } else if (!cmd->cmp("initialSidebarWidth")) { + parseInteger("initialSidebarWidth", &initialSidebarWidth, + tokens, fileName, line); } else if (!cmd->cmp("initialSelectMode")) { parseString("initialSelectMode", &initialSelectMode, tokens, fileName, line); @@ -1217,6 +1229,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { } else if (!cmd->cmp("fullScreenMatteColor")) { parseString("fullScreenMatteColor", &fullScreenMatteColor, tokens, fileName, line); + } else if (!cmd->cmp("selectionColor")) { + parseString("selectionColor", &selectionColor, tokens, fileName, line); } else if (!cmd->cmp("reverseVideoInvertImages")) { parseYesNo("reverseVideoInvertImages", &reverseVideoInvertImages, tokens, fileName, line); @@ -1238,8 +1252,6 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { tokens, fileName, line); } else if (!cmd->cmp("dropFont")) { parseDropFont(tokens, fileName, line); - } else if (!cmd->cmp("enableXFA")) { - parseYesNo("enableXFA", &enableXFA, tokens, fileName, line); } else if (!cmd->cmp("bind")) { parseBind(tokens, fileName, line); } else if (!cmd->cmp("unbind")) { @@ -1269,6 +1281,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) { error(errConfig, -1, "The config file format has changed since Xpdf 0.9x"); + } else if (!cmd->cmp("enableXFA")) { + error(errConfig, -1, "The enableXFA option is no longer used"); } } } @@ -1276,6 +1290,71 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) { deleteGList(tokens, GString); } +// Split a line into a sequence of tokens. Tokens are separated by +// whitespace. Each token is one of: +// - unquoted string, which can contain any char other than +// whitespace, and which cannot start with a single quote, double +// quote, or at-double-quote (xxxx) +// - single-quoted string, which can contain any char other than the +// single quote ('xxxx') +// - double-quoted string, which can contain any char other than the +// double quote ("xxxx") +// - at-double-quoted string, which can contain variables and escape +// chars (@"xxxx") +// - variables look like ${name} +// - special chars (${}") can be escaped with %, e.g., +// @"foo%"bar", @"foo%$bar", @"foo%%bar" +GList *GlobalParams::parseLineTokens(char *buf, GString *fileName, int line) { + GList *tokens = new GList(); + char *p1 = buf; + while (*p1) { + for (; *p1 && isspace(*p1); ++p1) ; + if (!*p1) { + break; + } + if (*p1 == '"' || *p1 == '\'') { + char *p2; + for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ; + ++p1; + tokens->append(new GString(p1, (int)(p2 - p1))); + p1 = *p2 ? p2 + 1 : p2; + } else if (*p1 == '@' && p1[1] == '"') { + GString *token = new GString(); + char *p2 = p1 + 2; + while (*p2 && *p2 != '"') { + if (*p2 == '%' && p2[1]) { + token->append(p2[1]); + p2 += 2; + } else if (*p2 == '$' && p2[1] == '{') { + p2 += 2; + char *p3; + for (p3 = p2; *p3 && *p3 != '}'; ++p3) ; + GString *varName = new GString(p2, (int)(p3 - p2)); + GString *varValue = (GString *)configFileVars->lookup(varName); + if (varValue) { + token->append(varValue); + } else { + error(errConfig, -1, "Unknown config file variable '%t'", varName); + } + delete varName; + p2 = *p3 ? p3 + 1 : p3; + } else { + token->append(*p2); + ++p2; + } + } + tokens->append(token); + p1 = *p2 ? p2 + 1 : p2; + } else { + char *p2; + for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ; + tokens->append(new GString(p1, (int)(p2 - p1))); + p1 = p2; + } + } + return tokens; +} + void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName, int line) { GString *name; @@ -1738,6 +1817,8 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr, *code = xpdfKeyCodeEnter; } else if (!strcmp(p0, "backspace")) { *code = xpdfKeyCodeBackspace; + } else if (!strcmp(p0, "esc")) { + *code = xpdfKeyCodeEsc; } else if (!strcmp(p0, "insert")) { *code = xpdfKeyCodeInsert; } else if (!strcmp(p0, "delete")) { @@ -1758,8 +1839,6 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr, *code = xpdfKeyCodeUp; } else if (!strcmp(p0, "down")) { *code = xpdfKeyCodeDown; - } else if (!strcmp(p0, "esc")) { - *code = xpdfKeyCodeEsc; } else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) { *code = xpdfKeyCodeF1 + (p0[1] - '1'); } else if (p0[0] == 'f' && @@ -1782,6 +1861,16 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr, (!p0[11] || (p0[11] >= '0' && p0[11] <= '9' && !p0[12])) && (btn = atoi(p0 + 10)) >= 1 && btn <= 32) { *code = xpdfKeyCodeMouseClick1 + btn - 1; + } else if (!strncmp(p0, "mouseDoubleClick", 16) && + p0[16] >= '0' && p0[16] <= '9' && + (!p0[17] || (p0[17] >= '0' && p0[17] <= '9' && !p0[18])) && + (btn = atoi(p0 + 16)) >= 1 && btn <= 32) { + *code = xpdfKeyCodeMouseDoubleClick1 + btn - 1; + } else if (!strncmp(p0, "mouseTripleClick", 16) && + p0[16] >= '0' && p0[16] <= '9' && + (!p0[17] || (p0[17] >= '0' && p0[17] <= '9' && !p0[18])) && + (btn = atoi(p0 + 16)) >= 1 && btn <= 32) { + *code = xpdfKeyCodeMouseTripleClick1 + btn - 1; } else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) { *code = (int)*p0; } else { @@ -1981,6 +2070,7 @@ GlobalParams::~GlobalParams() { delete macRomanReverseMap; delete baseDir; + deleteGHash(configFileVars, GString); delete nameToUnicode; deleteGHash(cidToUnicodes, GString); deleteGHash(unicodeToUnicodes, GString); @@ -2009,6 +2099,9 @@ GlobalParams::~GlobalParams() { if (fullScreenMatteColor) { delete fullScreenMatteColor; } + if (selectionColor) { + delete selectionColor; + } if (launchCommand) { delete launchCommand; } @@ -2784,6 +2877,26 @@ GString *GlobalParams::getTextEncodingName() { return s; } +GList *GlobalParams::getAvailableTextEncodings() { + GList *list; // [GString] + GHashIter *iter; + GString *key; + void *val; + + list = new GList(); + lockGlobalParams; + residentUnicodeMaps->startIter(&iter); + while (residentUnicodeMaps->getNext(&iter, &key, &val)) { + list->append(key->copy()); + } + unicodeMaps->startIter(&iter); + while (unicodeMaps->getNext(&iter, &key, &val)) { + list->append(key->copy()); + } + unlockGlobalParams; + return list; +} + EndOfLineKind GlobalParams::getTextEOL() { EndOfLineKind eol; @@ -2856,6 +2969,15 @@ GBool GlobalParams::getInitialSidebarState() { return state; } +int GlobalParams::getInitialSidebarWidth() { + int w; + + lockGlobalParams; + w = initialSidebarWidth; + unlockGlobalParams; + return w; +} + GString *GlobalParams::getInitialSelectMode() { GString *s; @@ -3075,6 +3197,15 @@ GString *GlobalParams::getFullScreenMatteColor() { return s; } +GString *GlobalParams::getSelectionColor() { + GString *s; + + lockGlobalParams; + s = selectionColor->copy(); + unlockGlobalParams; + return s; +} + GBool GlobalParams::getReverseVideoInvertImages() { GBool invert; @@ -3129,15 +3260,6 @@ GBool GlobalParams::isDroppedFont(const char *fontName) { return isDropped; } -GBool GlobalParams::getEnableXFA() { - GBool enable; - - lockGlobalParams; - enable = enableXFA; - unlockGlobalParams; - return enable; -} - GList *GlobalParams::getKeyBinding(int code, int mods, int context) { KeyBinding *binding; GList *cmds; @@ -3164,6 +3286,10 @@ GList *GlobalParams::getKeyBinding(int code, int mods, int context) { return cmds; } +GList *GlobalParams::getAllKeyBindings() { + return keyBindings; +} + int GlobalParams::getNumPopupMenuCmds() { int n; @@ -3214,7 +3340,7 @@ GString *GlobalParams::getDebugLogFile() { return debugLogFile; } -void GlobalParams::debugLogPrintf(char *fmt, ...) { +void GlobalParams::debugLogPrintf(const char *fmt, ...) { GString *path; FILE *f; GBool needClose; @@ -3620,12 +3746,6 @@ void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) { unlockGlobalParams; } -void GlobalParams::setEnableXFA(GBool enable) { - lockGlobalParams; - enableXFA = enable; - unlockGlobalParams; -} - void GlobalParams::setTabStateFile(char *tabStateFileA) { lockGlobalParams; delete tabStateFile; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h index ed1fed3152d..2823ea59421 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h @@ -134,50 +134,68 @@ public: ~KeyBinding(); }; -#define xpdfKeyCodeTab 0x1000 -#define xpdfKeyCodeReturn 0x1001 -#define xpdfKeyCodeEnter 0x1002 -#define xpdfKeyCodeBackspace 0x1003 -#define xpdfKeyCodeEsc 0x1004 -#define xpdfKeyCodeInsert 0x1005 -#define xpdfKeyCodeDelete 0x1006 -#define xpdfKeyCodeHome 0x1007 -#define xpdfKeyCodeEnd 0x1008 -#define xpdfKeyCodePgUp 0x1009 -#define xpdfKeyCodePgDn 0x100a -#define xpdfKeyCodeLeft 0x100b -#define xpdfKeyCodeRight 0x100c -#define xpdfKeyCodeUp 0x100d -#define xpdfKeyCodeDown 0x100e -#define xpdfKeyCodeF1 0x1100 -#define xpdfKeyCodeF35 0x1122 -#define xpdfKeyCodeMousePress1 0x2001 -#define xpdfKeyCodeMousePress2 0x2002 -#define xpdfKeyCodeMousePress3 0x2003 -#define xpdfKeyCodeMousePress4 0x2004 -#define xpdfKeyCodeMousePress5 0x2005 -#define xpdfKeyCodeMousePress6 0x2006 -#define xpdfKeyCodeMousePress7 0x2007 +#define xpdfKeyCodeTab 0x1000 +#define xpdfKeyCodeReturn 0x1001 +#define xpdfKeyCodeEnter 0x1002 +#define xpdfKeyCodeBackspace 0x1003 +#define xpdfKeyCodeEsc 0x1004 +#define xpdfKeyCodeInsert 0x1005 +#define xpdfKeyCodeDelete 0x1006 +#define xpdfKeyCodeHome 0x1007 +#define xpdfKeyCodeEnd 0x1008 +#define xpdfKeyCodePgUp 0x1009 +#define xpdfKeyCodePgDn 0x100a +#define xpdfKeyCodeLeft 0x100b +#define xpdfKeyCodeRight 0x100c +#define xpdfKeyCodeUp 0x100d +#define xpdfKeyCodeDown 0x100e +#define xpdfKeyCodeF1 0x1100 +#define xpdfKeyCodeF35 0x1122 +#define xpdfKeyCodeMousePress1 0x2001 +#define xpdfKeyCodeMousePress2 0x2002 +#define xpdfKeyCodeMousePress3 0x2003 +#define xpdfKeyCodeMousePress4 0x2004 +#define xpdfKeyCodeMousePress5 0x2005 +#define xpdfKeyCodeMousePress6 0x2006 +#define xpdfKeyCodeMousePress7 0x2007 // ... -#define xpdfKeyCodeMousePress32 0x2020 -#define xpdfKeyCodeMouseRelease1 0x2101 -#define xpdfKeyCodeMouseRelease2 0x2102 -#define xpdfKeyCodeMouseRelease3 0x2103 -#define xpdfKeyCodeMouseRelease4 0x2104 -#define xpdfKeyCodeMouseRelease5 0x2105 -#define xpdfKeyCodeMouseRelease6 0x2106 -#define xpdfKeyCodeMouseRelease7 0x2107 +#define xpdfKeyCodeMousePress32 0x2020 +#define xpdfKeyCodeMouseRelease1 0x2101 +#define xpdfKeyCodeMouseRelease2 0x2102 +#define xpdfKeyCodeMouseRelease3 0x2103 +#define xpdfKeyCodeMouseRelease4 0x2104 +#define xpdfKeyCodeMouseRelease5 0x2105 +#define xpdfKeyCodeMouseRelease6 0x2106 +#define xpdfKeyCodeMouseRelease7 0x2107 // ... -#define xpdfKeyCodeMouseRelease32 0x2120 -#define xpdfKeyCodeMouseClick1 0x2201 -#define xpdfKeyCodeMouseClick2 0x2202 -#define xpdfKeyCodeMouseClick3 0x2203 -#define xpdfKeyCodeMouseClick4 0x2204 -#define xpdfKeyCodeMouseClick5 0x2205 -#define xpdfKeyCodeMouseClick6 0x2206 -#define xpdfKeyCodeMouseClick7 0x2207 +#define xpdfKeyCodeMouseRelease32 0x2120 +#define xpdfKeyCodeMouseClick1 0x2201 +#define xpdfKeyCodeMouseClick2 0x2202 +#define xpdfKeyCodeMouseClick3 0x2203 +#define xpdfKeyCodeMouseClick4 0x2204 +#define xpdfKeyCodeMouseClick5 0x2205 +#define xpdfKeyCodeMouseClick6 0x2206 +#define xpdfKeyCodeMouseClick7 0x2207 // ... -#define xpdfKeyCodeMouseClick32 0x2220 +#define xpdfKeyCodeMouseClick32 0x2220 +#define xpdfKeyCodeMouseDoubleClick1 0x2301 +#define xpdfKeyCodeMouseDoubleClick2 0x2302 +#define xpdfKeyCodeMouseDoubleClick3 0x2303 +#define xpdfKeyCodeMouseDoubleClick4 0x2304 +#define xpdfKeyCodeMouseDoubleClick5 0x2305 +#define xpdfKeyCodeMouseDoubleClick6 0x2306 +#define xpdfKeyCodeMouseDoubleClick7 0x2307 +// ... +#define xpdfKeyCodeMouseDoubleClick32 0x2320 +#define xpdfKeyCodeMouseTripleClick1 0x2401 +#define xpdfKeyCodeMouseTripleClick2 0x2402 +#define xpdfKeyCodeMouseTripleClick3 0x2403 +#define xpdfKeyCodeMouseTripleClick4 0x2404 +#define xpdfKeyCodeMouseTripleClick5 0x2405 +#define xpdfKeyCodeMouseTripleClick6 0x2406 +#define xpdfKeyCodeMouseTripleClick7 0x2407 +// ... +#define xpdfKeyCodeMouseTripleClick32 0x2420 #define xpdfKeyModNone 0 #define xpdfKeyModShift (1 << 0) #define xpdfKeyModCtrl (1 << 1) @@ -279,6 +297,7 @@ public: GBool getPSAlwaysRasterize(); GBool getPSNeverRasterize(); GString *getTextEncodingName(); + GList *getAvailableTextEncodings(); EndOfLineKind getTextEOL(); GBool getTextPageBreaks(); GBool getTextKeepTinyChars(); @@ -287,6 +306,7 @@ public: GString *getInitialDisplayMode(); GBool getInitialToolbarState(); GBool getInitialSidebarState(); + int getInitialSidebarWidth(); GString *getInitialSelectMode(); int getMaxTileWidth(); int getMaxTileHeight(); @@ -312,6 +332,7 @@ public: GString *getPaperColor(); GString *getMatteColor(); GString *getFullScreenMatteColor(); + GString *getSelectionColor(); GBool getReverseVideoInvertImages(); GString *getLaunchCommand() { return launchCommand; } GString *getMovieCommand() { return movieCommand; } @@ -320,15 +341,15 @@ public: GBool getMapUnknownCharNames(); GBool getMapExtTrueTypeFontsViaUnicode(); GBool isDroppedFont(const char *fontName); - GBool getEnableXFA(); GList *getKeyBinding(int code, int mods, int context); + GList *getAllKeyBindings(); int getNumPopupMenuCmds(); PopupMenuCmd *getPopupMenuCmd(int idx); GString *getTabStateFile(); GBool getPrintCommands(); GBool getErrQuiet(); GString *getDebugLogFile(); - void debugLogPrintf(char *fmt, ...); + void debugLogPrintf(const char *fmt, ...); CharCodeToUnicode *getCIDToUnicode(GString *collection); CharCodeToUnicode *getUnicodeToUnicode(GString *fontName); @@ -378,7 +399,6 @@ public: void setMapNumericCharNames(GBool map); void setMapUnknownCharNames(GBool map); void setMapExtTrueTypeFontsViaUnicode(GBool map); - void setEnableXFA(GBool enable); void setTabStateFile(char *tabStateFileA); void setPrintCommands(GBool printCommandsA); void setErrQuiet(GBool errQuietA); @@ -392,8 +412,10 @@ public: private: + void setDataDirVar(); void createDefaultKeyBindings(); void parseFile(GString *fileName, FILE *f); + GList *parseLineTokens(char *buf, GString *fileName, int line); void parseNameToUnicode(GList *tokens, GString *fileName, int line); void parseCIDToUnicode(GList *tokens, GString *fileName, int line); void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line); @@ -438,9 +460,14 @@ private: NameToCharCode * // mapping from char name to macRomanReverseMap; // MacRomanEncoding index - //----- user-modifiable settings + //----- meta settings GString *baseDir; // base directory - for plugins, etc. + GHash *configFileVars; // variables for use in the config file + // [GString] + + //----- user-modifiable settings + NameToCharCode * // mapping from char name to Unicode nameToUnicode; GHash *cidToUnicodes; // files for mappings from char collections @@ -522,6 +549,7 @@ private: // or closed (false) GBool initialSidebarState; // initial sidebar state - open (true) // or closed (false) + int initialSidebarWidth; // initial sidebar width GString *initialSelectMode; // initial selection mode (block or linear) int maxTileWidth; // maximum rasterization tile width int maxTileHeight; // maximum rasterization tile height @@ -548,6 +576,7 @@ private: GString *paperColor; // paper (page background) color GString *matteColor; // matte (background outside of page) color GString *fullScreenMatteColor; // matte color in full-screen mode + GString *selectionColor; // selection color GBool reverseVideoInvertImages; // invert images in reverse video mode GString *launchCommand; // command executed for 'launch' links GString *movieCommand; // command executed for movie annotations @@ -558,7 +587,6 @@ private: GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode // for external TrueType fonts? GHash *droppedFonts; // dropped fonts [int] - GBool enableXFA; // enable XFA form rendering GList *keyBindings; // key & mouse button bindings [KeyBinding] GList *popupMenuCmds; // popup menu commands [PopupMenuCmd] GString *tabStateFile; // path for the tab state save file diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc index 036f1116c01..6f51ad85190 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc @@ -214,6 +214,7 @@ HTMLGen::HTMLGen(double backgroundResolutionA) { // set up the TextOutputDev textOutControl.mode = textOutReadingOrder; textOutControl.html = gTrue; + textOutControl.splitRotatedWords = gTrue; textOut = new TextOutputDev(NULL, &textOutControl, gFalse); if (!textOut->isOk()) { ok = gFalse; @@ -492,7 +493,7 @@ void HTMLGen::appendSpans(GList *words, int firstWordIdx, int lastWordIdx, (spanDir >= 0) ? wordIdx <= lastWordIdx : wordIdx >= lastWordIdx; wordIdx += spanDir) { word1 = (TextWord *)words->get(wordIdx); - invisible = allTextInvisible || word1->isInvisible(); + invisible = allTextInvisible || word1->isInvisible() || word1->isRotated(); if (!drawInvisibleText && invisible) { continue; } @@ -513,6 +514,7 @@ void HTMLGen::appendSpans(GList *words, int firstWordIdx, int lastWordIdx, word1->getFontInfo() != word0->getFontInfo() || word1->getFontSize() != word0->getFontSize() || word1->isInvisible() != word0->isInvisible() || + word1->isRotated() != word0->isRotated() || vertAlign1 != vertAlign0 || r1 != r0 || g1 != g0 || b1 != b0) { if (word0) { @@ -717,6 +719,7 @@ HTMLGenFontDefn *HTMLGen::getFontFile(TextFontInfo *font, 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; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc index 1fb32be0b58..5a694d7b665 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc @@ -29,7 +29,6 @@ ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA, GBool dumpRawA, GBool listA) { fileRoot = copyString(fileRootA); - fileName = (char *)gmalloc((int)strlen(fileRoot) + 30); dumpJPEG = dumpJPEGA; dumpRaw = dumpRawA; list = listA; @@ -39,7 +38,6 @@ ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA, } ImageOutputDev::~ImageOutputDev() { - gfree(fileName); gfree(fileRoot); } @@ -60,6 +58,7 @@ void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { + GString *fileName; FILE *f; char buf[4096]; int size, n, i; @@ -68,11 +67,12 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, if (dumpRaw && !inlineImg) { // open the image file - sprintf(fileName, "%s-%04d.%s", - fileRoot, imgNum, getRawFileExtension(str)); + fileName = GString::format("{0:s}-{1:04d}.{2:s}", + fileRoot, imgNum, getRawFileExtension(str)); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } @@ -92,10 +92,11 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, } else if (dumpJPEG && str->getKind() == strDCT && !inlineImg) { // open the image file - sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } @@ -115,10 +116,11 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, } else { // open the image file and write the PBM header - sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } fprintf(f, "P4\n"); @@ -144,8 +146,10 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, } if (list) { - writeImageInfo(width, height, state, NULL); + writeImageInfo(fileName, width, height, state, NULL); } + + delete fileName; } void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, @@ -154,6 +158,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int *maskColors, GBool inlineImg, GBool interpolate) { GfxColorSpaceMode csMode; + GString *fileName; FILE *f; ImageStream *imgStr; Guchar *p; @@ -173,11 +178,12 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, if (dumpRaw && !inlineImg) { // open the image file - sprintf(fileName, "%s-%04d.%s", - fileRoot, imgNum, getRawFileExtension(str)); + fileName = GString::format("{0:s}-{1:04d}.{2:s}", + fileRoot, imgNum, getRawFileExtension(str)); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } @@ -200,10 +206,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, !inlineImg) { // open the image file - sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } @@ -224,10 +231,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, colorMap->getBits() == 1) { // open the image file and write the PBM header - sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } fprintf(f, "P4\n"); @@ -259,10 +267,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, (csMode == csDeviceGray || csMode == csCalGray)) { // open the image file and write the PGM header - sprintf(fileName, "%s-%04d.pgm", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.pgm", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } fprintf(f, "P5\n"); @@ -300,10 +309,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } else { // open the image file and write the PPM header - sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum); + fileName = GString::format("{0:s}-{1:04d}.ppm", fileRoot, imgNum); ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + if (!(f = openFile(fileName->getCString(), "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fileName); + delete fileName; return; } fprintf(f, "P6\n"); @@ -343,19 +353,21 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } if (list) { - writeImageInfo(width, height, state, colorMap); + writeImageInfo(fileName, width, height, state, colorMap); } + + delete fileName; } void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { drawImage(state, ref, str, width, height, colorMap, NULL, gFalse, interpolate); - drawImageMask(state, ref, maskStr, maskWidth, maskHeight, maskInvert, + drawImageMask(state, maskRef, maskStr, maskWidth, maskHeight, maskInvert, gFalse, interpolate); } @@ -363,13 +375,13 @@ void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate) { drawImage(state, ref, str, width, height, colorMap, NULL, gFalse, interpolate); - drawImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, + drawImage(state, maskRef, maskStr, maskWidth, maskHeight, maskColorMap, NULL, gFalse, interpolate); } @@ -401,7 +413,8 @@ const char *ImageOutputDev::getRawFileExtension(Stream *str) { } } -void ImageOutputDev::writeImageInfo(int width, int height, GfxState *state, +void ImageOutputDev::writeImageInfo(GString *fileName, + int width, int height, GfxState *state, GfxImageColorMap *colorMap) { const char *mode; double hdpi, vdpi, x0, y0, x1, y1; @@ -433,7 +446,7 @@ void ImageOutputDev::writeImageInfo(int width, int height, GfxState *state, } printf("%s: page=%d width=%d height=%d hdpi=%.2f vdpi=%.2f %s%s bpc=%d\n", - fileName, curPageNum, width, height, hdpi, vdpi, + fileName->getCString(), curPageNum, width, height, hdpi, vdpi, mode ? "colorspace=" : "mask", mode ? mode : "", bpc); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h index 3cdae45da66..5a72925aee9 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h @@ -85,12 +85,13 @@ public: virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate); @@ -99,11 +100,11 @@ private: Stream *getRawStream(Stream *str); const char *getRawFileExtension(Stream *str); - void writeImageInfo(int width, int height, GfxState *state, + void writeImageInfo(GString *fileName, + int width, int height, GfxState *state, GfxImageColorMap *colorMap); char *fileRoot; // root of output file names - char *fileName; // buffer for output file names GBool dumpJPEG; // set to dump native JPEG files GBool dumpRaw; // set to dump raw PDF-native image files GBool list; // set to write image info to stdout diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc index fcb9d6d0e3a..4c4e5183026 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc @@ -1304,7 +1304,6 @@ void JBIG2Stream::readSegments() { Guint *refSegs; int c1, c2, c3; Guint i; - GBool done; done = gFalse; while (!done && readULong(&segNum)) { @@ -1723,17 +1722,18 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, "Bad delta-height value in JBIG2 symbol dictionary"); goto syntaxError; } - // sanity check to avoid extremely long run-times with damaged streams - if (dh > 1000000) { - error(errSyntaxError, getPos(), - "Bogus delta-height value in JBIG2 symbol dictionary"); - goto syntaxError; - } symHeight += dh; symWidth = 0; totalWidth = 0; j = i; + // sanity check to avoid extremely long run-times with damaged streams + if (symHeight > 100000) { + error(errSyntaxError, getPos(), + "Bogus symbol height value in JBIG2 symbol dictionary"); + goto syntaxError; + } + // read the symbols in this height class while (1) { @@ -1760,6 +1760,13 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, goto syntaxError; } + // sanity check to avoid extremely long run-times with damaged streams + if (symWidth > 100000) { + error(errSyntaxError, getPos(), + "Bogus symbol width value in JBIG2 symbol dictionary"); + goto syntaxError; + } + // using a collective bitmap, so don't read a bitmap here if (huff && !refAgg) { symWidths[i] = symWidth; @@ -1977,6 +1984,15 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm, error(errSyntaxError, getPos(), "Bad size in JBIG2 text region segment"); return; } + // sanity check: if the w/h/x/y values are way out of range, it likely + // indicates a damaged JBIG2 stream + if (w / 10 > pageW || h / 10 > pageH || + x / 10 > pageW || y / 10 > pageH) { + error(errSyntaxError, getPos(), + "Bad size or position in JBIG2 text region segment"); + done = gTrue; + return; + } extCombOp = segInfoFlags & 7; // rest of the text region header @@ -2557,6 +2573,15 @@ void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm, !readUByte(&segInfoFlags)) { goto eofError; } + // sanity check: if the w/h/x/y values are way out of range, it likely + // indicates a damaged JBIG2 stream + if (w / 10 > pageW || h / 10 > pageH || + x / 10 > pageW || y / 10 > pageH) { + error(errSyntaxError, getPos(), + "Bad size or position in JBIG2 halftone region segment"); + done = gTrue; + return; + } extCombOp = segInfoFlags & 7; // rest of the halftone region header @@ -2720,6 +2745,15 @@ void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm, "Bad bitmap size in JBIG2 generic region segment"); return; } + // sanity check: if the w/h/x/y values are way out of range, it likely + // indicates a damaged JBIG2 stream + if (w / 10 > pageW || h / 10 > pageH || + x / 10 > pageW || y / 10 > pageH) { + error(errSyntaxError, getPos(), + "Bad size or position in JBIG2 generic region segment"); + done = gTrue; + return; + } extCombOp = segInfoFlags & 7; // rest of the generic region segment header @@ -3623,6 +3657,15 @@ void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm, "Bad size in JBIG2 generic refinement region segment"); return; } + // sanity check: if the w/h/x/y values are way out of range, it likely + // indicates a damaged JBIG2 stream + if (w / 10 > pageW || h / 10 > pageH || + x / 10 > pageW || y / 10 > pageH) { + error(errSyntaxError, getPos(), + "Bad size or position in JBIG2 generic refinement region segment"); + done = gTrue; + return; + } extCombOp = segInfoFlags & 7; // rest of the generic refinement region segment header diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h index 7aa4d9d8edb..cca722b4bfc 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h @@ -128,6 +128,7 @@ private: Guchar *dataPtr; Guchar *dataEnd; Guint byteCounter; + GBool done; JArithmeticDecoder *arithDecoder; JArithmeticDecoderStats *genericRegionStats; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc index a5dd6f0db0e..d9c295bb12c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc @@ -20,7 +20,6 @@ #include "JPXStream.h" //~ to do: -// - precincts // - ROI // - progression order changes // - packed packet headers @@ -28,7 +27,6 @@ // - make sure all needed JP2/JPX subboxes are parsed (readBoxes) // - can we assume that QCC segments must come after the QCD segment? // - handle tilePartToEOC in readTilePartData -// - progression orders 2, 3, and 4 // - in coefficient decoding (readCodeBlockData): // - selective arithmetic coding bypass // (this also affects reading the cb->dataLen array) @@ -325,7 +323,7 @@ void JPXStream::close() { for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; if (resLevel->precincts) { - for (pre = 0; pre < 1; ++pre) { + for (pre = 0; pre < resLevel->nPrecincts; ++pre) { precinct = &resLevel->precincts[pre]; if (precinct->subbands) { for (sb = 0; sb < (Guint)(r == 0 ? 1 : 3); ++sb) { @@ -350,13 +348,13 @@ void JPXStream::close() { gfree(precinct->subbands); } } - gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts); + gfree(resLevel->precincts); } } - gfree(img.tiles[i].tileComps[comp].resLevels); + gfree(tileComp->resLevels); } } - gfree(img.tiles[i].tileComps); + gfree(tile->tileComps); } } gfree(img.tiles); @@ -1036,13 +1034,6 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return jpxDecodeFatalError; } -#if 1 //~ progression orders 2-4 are unimplemented - if (progOrder >= 2) { - error(errUnimplemented, -1, - "JPX progression order {0:d} is unimplemented", - progOrder); - } -#endif codeBlockW += 2; codeBlockH += 2; for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { @@ -1445,7 +1436,8 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) { ok = gTrue; while (1) { if (!readTilePart()) { - return jpxDecodeFatalError; + ok = gFalse; + break; } if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); @@ -1497,8 +1489,11 @@ GBool JPXStream::readTilePart() { Guint style, progOrder, nLayers, multiComp, nDecompLevels; Guint codeBlockW, codeBlockH, codeBlockStyle, transform; Guint precinctSize, qStyle; - Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen; - Guint i, j, k, cbX, cbY, r, pre, sb, cbi, cbj; + Guint px0, py0, px1, py1; + Guint preCol0, preCol1, preRow0, preRow1, preCol, preRow; + Guint cbCol0, cbCol1, cbRow0, cbRow1, cbX, cbY; + Guint n, nSBs, nx, ny, comp, segLen; + Guint i, j, k, r, pre, sb, cbi, cbj; int segType, level; // process the SOT marker segment @@ -1534,6 +1529,10 @@ GBool JPXStream::readTilePart() { switch (segType) { case 0x52: // COD - coding style default cover(34); + if (tilePartIdx != 0) { + error(errSyntaxError, getPos(), "Extraneous JPX COD marker segment"); + return gFalse; + } if (!readUByte(&style) || !readUByte(&progOrder) || !readUWord(&nLayers) || @@ -1553,13 +1552,6 @@ GBool JPXStream::readTilePart() { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return gFalse; } -#if 1 //~ progression orders 2-4 are unimplemented - if (img.tiles[tileIdx].progOrder >= 2) { - error(errUnimplemented, -1, - "JPX progression order {0:d} is unimplemented", - img.tiles[tileIdx].progOrder); - } -#endif codeBlockW += 2; codeBlockH += 2; img.tiles[tileIdx].progOrder = progOrder; @@ -1607,6 +1599,10 @@ GBool JPXStream::readTilePart() { break; case 0x53: // COC - coding style component cover(35); + if (tilePartIdx != 0) { + error(errSyntaxError, getPos(), "Extraneous JPX COC marker segment"); + return gFalse; + } if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || @@ -1623,7 +1619,7 @@ GBool JPXStream::readTilePart() { nDecompLevels > 31 || codeBlockW > 8 || codeBlockH > 8) { - error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); + error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[comp].style = @@ -1659,6 +1655,10 @@ GBool JPXStream::readTilePart() { break; case 0x5c: // QCD - quantization default cover(36); + if (tilePartIdx != 0) { + error(errSyntaxError, getPos(), "Extraneous JPX QCD marker segment"); + return gFalse; + } if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; @@ -1726,6 +1726,10 @@ GBool JPXStream::readTilePart() { break; case 0x5d: // QCC - quantization component cover(37); + if (tilePartIdx != 0) { + error(errSyntaxError, getPos(), "Extraneous JPX QCC marker segment"); + return gFalse; + } if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || @@ -1786,6 +1790,10 @@ GBool JPXStream::readTilePart() { break; case 0x5e: // RGN - region of interest cover(38); + if (tilePartIdx != 0) { + error(errSyntaxError, getPos(), "Extraneous JPX RGN marker segment"); + return gFalse; + } #if 1 //~ ROI is unimplemented error(errUnimplemented, -1, "Got a JPX RGN segment"); if (segLen > 2 && @@ -1889,7 +1897,6 @@ GBool JPXStream::readTilePart() { //----- initialize the tile, precincts, and code-blocks if (tilePartIdx == 0) { tile = &img.tiles[tileIdx]; - tile->init = gTrue; i = tileIdx / img.nXTiles; j = tileIdx % img.nXTiles; if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) { @@ -1919,8 +1926,6 @@ GBool JPXStream::readTilePart() { tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep); tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep); tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep); - tileComp->cbW = 1 << tileComp->codeBlockW; - tileComp->cbH = 1 << tileComp->codeBlockH; tileComp->w = jpxCeilDivPow2(tileComp->x1, reduction) - jpxCeilDivPow2(tileComp->x0, reduction); tileComp->h = jpxCeilDivPow2(tileComp->y1, reduction) @@ -1939,12 +1944,26 @@ GBool JPXStream::readTilePart() { tileComp->buf = (int *)gmallocn(n + 8, sizeof(int)); for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; - k = r == 0 ? tileComp->nDecompLevels - : tileComp->nDecompLevels - r + 1; - resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k); - resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); - resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); - resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); + resLevel->x0 = jpxCeilDivPow2(tileComp->x0, + tileComp->nDecompLevels - r); + resLevel->y0 = jpxCeilDivPow2(tileComp->y0, + tileComp->nDecompLevels - r); + resLevel->x1 = jpxCeilDivPow2(tileComp->x1, + tileComp->nDecompLevels - r); + resLevel->y1 = jpxCeilDivPow2(tileComp->y1, + tileComp->nDecompLevels - r); + resLevel->codeBlockW = r == 0 ? resLevel->precinctWidth + : resLevel->precinctWidth - 1; + if (resLevel->codeBlockW > tileComp->codeBlockW) { + resLevel->codeBlockW = tileComp->codeBlockW; + } + resLevel->cbW = 1 << resLevel->codeBlockW; + resLevel->codeBlockH = r == 0 ? resLevel->precinctHeight + : resLevel->precinctHeight - 1; + if (resLevel->codeBlockH > tileComp->codeBlockH) { + resLevel->codeBlockH = tileComp->codeBlockH; + } + resLevel->cbH = 1 << resLevel->codeBlockH; // 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 @@ -1952,6 +1971,7 @@ GBool JPXStream::readTilePart() { resLevel->empty = resLevel->x0 == resLevel->x1 || resLevel->y0 == resLevel->y1; if (r == 0) { + nSBs = 1; resLevel->bx0[0] = resLevel->x0; resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = resLevel->x1; @@ -1960,18 +1980,19 @@ GBool JPXStream::readTilePart() { (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; - resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); - resLevel->by1[0] = resLevel->y1; - resLevel->bx0[1] = resLevel->x0; - resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); - resLevel->bx1[1] = resLevel->x1; - resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); - resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); - 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); + nSBs = 3; + resLevel->bx0[0] = jpxCeilDivPow2(resLevel->x0 - 1, 1); + resLevel->by0[0] = jpxCeilDivPow2(resLevel->y0, 1); + resLevel->bx1[0] = jpxCeilDivPow2(resLevel->x1 - 1, 1); + resLevel->by1[0] = jpxCeilDivPow2(resLevel->y1, 1); + resLevel->bx0[1] = jpxCeilDivPow2(resLevel->x0, 1); + resLevel->by0[1] = jpxCeilDivPow2(resLevel->y0 - 1, 1); + resLevel->bx1[1] = jpxCeilDivPow2(resLevel->x1, 1); + resLevel->by1[1] = jpxCeilDivPow2(resLevel->y1 - 1, 1); + resLevel->bx0[2] = jpxCeilDivPow2(resLevel->x0 - 1, 1); + resLevel->by0[2] = jpxCeilDivPow2(resLevel->y0 - 1, 1); + resLevel->bx1[2] = jpxCeilDivPow2(resLevel->x1 - 1, 1); + resLevel->by1[2] = jpxCeilDivPow2(resLevel->y1 - 1, 1); resLevel->empty = resLevel->empty && (resLevel->bx0[0] == resLevel->bx1[0] || resLevel->by0[0] == resLevel->by1[0]) && @@ -1980,135 +2001,155 @@ GBool JPXStream::readTilePart() { (resLevel->bx0[2] == resLevel->bx1[2] || resLevel->by0[2] == resLevel->by1[2]); } - resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct)); - for (pre = 0; pre < 1; ++pre) { + preCol0 = jpxFloorDivPow2(resLevel->x0, resLevel->precinctWidth); + preCol1 = jpxCeilDivPow2(resLevel->x1, resLevel->precinctWidth); + preRow0 = jpxFloorDivPow2(resLevel->y0, resLevel->precinctHeight); + preRow1 = jpxCeilDivPow2(resLevel->y1, resLevel->precinctHeight); + resLevel->nPrecincts = (preCol1 - preCol0) * (preRow1 - preRow0); + resLevel->precincts = (JPXPrecinct *)gmallocn(resLevel->nPrecincts, + sizeof(JPXPrecinct)); + for (pre = 0; pre < resLevel->nPrecincts; ++pre) { resLevel->precincts[pre].subbands = NULL; } - for (pre = 0; pre < 1; ++pre) { - precinct = &resLevel->precincts[pre]; - precinct->x0 = resLevel->x0; - precinct->y0 = resLevel->y0; - precinct->x1 = resLevel->x1; - precinct->y1 = resLevel->y1; - nSBs = r == 0 ? 1 : 3; - precinct->subbands = - (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband)); - for (sb = 0; sb < nSBs; ++sb) { - precinct->subbands[sb].inclusion = NULL; - precinct->subbands[sb].zeroBitPlane = NULL; - precinct->subbands[sb].cbs = NULL; - } - for (sb = 0; sb < nSBs; ++sb) { - subband = &precinct->subbands[sb]; - subband->x0 = resLevel->bx0[sb]; - subband->y0 = resLevel->by0[sb]; - subband->x1 = resLevel->bx1[sb]; - subband->y1 = resLevel->by1[sb]; - subband->nXCBs = jpxCeilDivPow2(subband->x1, - tileComp->codeBlockW) - - jpxFloorDivPow2(subband->x0, - tileComp->codeBlockW); - subband->nYCBs = jpxCeilDivPow2(subband->y1, - tileComp->codeBlockH) - - jpxFloorDivPow2(subband->y0, - tileComp->codeBlockH); - n = subband->nXCBs > subband->nYCBs ? subband->nXCBs - : subband->nYCBs; - for (subband->maxTTLevel = 0, --n; - n; - ++subband->maxTTLevel, n >>= 1) ; - n = 0; - for (level = subband->maxTTLevel; level >= 0; --level) { - nx = jpxCeilDivPow2(subband->nXCBs, level); - ny = jpxCeilDivPow2(subband->nYCBs, level); - n += nx * ny; + precinct = resLevel->precincts; + for (preRow = preRow0; preRow < preRow1; ++preRow) { + for (preCol = preCol0; preCol < preCol1; ++preCol) { + precinct->subbands = + (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband)); + for (sb = 0; sb < nSBs; ++sb) { + precinct->subbands[sb].inclusion = NULL; + precinct->subbands[sb].zeroBitPlane = NULL; + precinct->subbands[sb].cbs = NULL; } - subband->inclusion = - (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); - subband->zeroBitPlane = - (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); - for (k = 0; k < n; ++k) { - subband->inclusion[k].finished = gFalse; - subband->inclusion[k].val = 0; - subband->zeroBitPlane[k].finished = gFalse; - subband->zeroBitPlane[k].val = 0; - } - subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs * - subband->nYCBs, - sizeof(JPXCodeBlock)); - for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { - subband->cbs[k].dataLen = NULL; - subband->cbs[k].touched = NULL; - subband->cbs[k].arithDecoder = NULL; - subband->cbs[k].stats = NULL; - } - sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); - sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); - if (r == 0) { // (NL)LL - sbCoeffs = tileComp->data; - } else if (sb == 0) { // (NL-r+1)HL - sbCoeffs = tileComp->data - + resLevel->bx1[1] - resLevel->bx0[1]; - } else if (sb == 1) { // (NL-r+1)LH - sbCoeffs = tileComp->data - + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w; - } else { // (NL-r+1)HH - sbCoeffs = tileComp->data - + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w - + resLevel->bx1[1] - resLevel->bx0[1]; - } - cb = subband->cbs; - for (cbY = 0; cbY < subband->nYCBs; ++cbY) { - for (cbX = 0; cbX < subband->nXCBs; ++cbX) { - cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW; - cb->x1 = cb->x0 + tileComp->cbW; - if (subband->x0 > cb->x0) { - cb->x0 = subband->x0; - } - if (subband->x1 < cb->x1) { - cb->x1 = subband->x1; - } - cb->y0 = (sby0 + cbY) << tileComp->codeBlockH; - cb->y1 = cb->y0 + tileComp->cbH; - if (subband->y0 > cb->y0) { - cb->y0 = subband->y0; - } - if (subband->y1 < cb->y1) { - cb->y1 = subband->y1; - } - cb->seen = gFalse; - cb->lBlock = 3; - cb->nextPass = jpxPassCleanup; - cb->nZeroBitPlanes = 0; - cb->dataLenSize = 1; - cb->dataLen = (Guint *)gmalloc(sizeof(Guint)); - if (r <= tileComp->nDecompLevels - reduction) { - cb->coeffs = sbCoeffs - + (cb->y0 - subband->y0) * tileComp->w - + (cb->x0 - subband->x0); - cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW - + tileComp->codeBlockH)); - cb->len = 0; - for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) { - for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) { - cb->coeffs[cbj * tileComp->w + cbi] = 0; + for (sb = 0; sb < nSBs; ++sb) { + subband = &precinct->subbands[sb]; + if (r == 0) { + px0 = preCol << resLevel->precinctWidth; + px1 = (preCol + 1) << resLevel->precinctWidth; + py0 = preRow << resLevel->precinctHeight; + py1 = (preRow + 1) << resLevel->precinctHeight; + } else { + px0 = preCol << (resLevel->precinctWidth - 1); + px1 = (preCol + 1) << (resLevel->precinctWidth - 1); + py0 = preRow << (resLevel->precinctHeight - 1); + py1 = (preRow + 1) << (resLevel->precinctHeight - 1); + } + if (px0 < resLevel->bx0[sb]) { + px0 = resLevel->bx0[sb]; + } + if (px1 > resLevel->bx1[sb]) { + px1 = resLevel->bx1[sb]; + } + if (py0 < resLevel->by0[sb]) { + py0 = resLevel->by0[sb]; + } + if (py1 > resLevel->by1[sb]) { + py1 = resLevel->by1[sb]; + } + if (r == 0) { // (NL)LL + sbCoeffs = tileComp->data; + } else if (sb == 0) { // (NL-r+1)HL + sbCoeffs = tileComp->data + + resLevel->bx1[1] - resLevel->bx0[1]; + } else if (sb == 1) { // (NL-r+1)LH + sbCoeffs = tileComp->data + + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w; + } else { // (NL-r+1)HH + sbCoeffs = tileComp->data + + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w + + (resLevel->bx1[1] - resLevel->bx0[1]); + } + cbCol0 = jpxFloorDivPow2(px0, resLevel->codeBlockW); + cbCol1 = jpxCeilDivPow2(px1, resLevel->codeBlockW); + cbRow0 = jpxFloorDivPow2(py0, resLevel->codeBlockH); + cbRow1 = jpxCeilDivPow2(py1, resLevel->codeBlockH); + subband->nXCBs = cbCol1 - cbCol0; + subband->nYCBs = cbRow1 - cbRow0; + n = subband->nXCBs > subband->nYCBs ? subband->nXCBs + : subband->nYCBs; + for (subband->maxTTLevel = 0, --n; + n; + ++subband->maxTTLevel, n >>= 1) ; + n = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + n += nx * ny; + } + subband->inclusion = + (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); + subband->zeroBitPlane = + (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); + for (k = 0; k < n; ++k) { + subband->inclusion[k].finished = gFalse; + subband->inclusion[k].val = 0; + subband->zeroBitPlane[k].finished = gFalse; + subband->zeroBitPlane[k].val = 0; + } + subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs * + subband->nYCBs, + sizeof(JPXCodeBlock)); + for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { + subband->cbs[k].dataLen = NULL; + subband->cbs[k].touched = NULL; + subband->cbs[k].arithDecoder = NULL; + subband->cbs[k].stats = NULL; + } + cb = subband->cbs; + for (cbY = cbRow0; cbY < cbRow1; ++cbY) { + for (cbX = cbCol0; cbX < cbCol1; ++cbX) { + cb->x0 = cbX << resLevel->codeBlockW; + cb->x1 = cb->x0 + resLevel->cbW; + if (cb->x0 < px0) { + cb->x0 = px0; + } + if (cb->x1 > px1) { + cb->x1 = px1; + } + cb->y0 = cbY << resLevel->codeBlockH; + cb->y1 = cb->y0 + resLevel->cbH; + if (cb->y0 < py0) { + cb->y0 = py0; + } + if (cb->y1 > py1) { + cb->y1 = py1; + } + cb->seen = gFalse; + cb->lBlock = 3; + cb->nextPass = jpxPassCleanup; + cb->nZeroBitPlanes = 0; + cb->dataLenSize = 1; + cb->dataLen = (Guint *)gmalloc(sizeof(Guint)); + if (r <= tileComp->nDecompLevels - reduction) { + cb->coeffs = sbCoeffs + + (cb->y0 - resLevel->by0[sb]) * tileComp->w + + (cb->x0 - resLevel->bx0[sb]); + cb->touched = (char *)gmalloc(1 << (resLevel->codeBlockW + + resLevel->codeBlockH)); + cb->len = 0; + for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) { + for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) { + cb->coeffs[cbj * tileComp->w + cbi] = 0; + } } + memset(cb->touched, 0, + ((size_t)1 << (resLevel->codeBlockW + + resLevel->codeBlockH))); + } else { + cb->coeffs = NULL; + cb->touched = NULL; + cb->len = 0; } - memset(cb->touched, 0, - ((size_t)1 << (tileComp->codeBlockW - + tileComp->codeBlockH))); - } else { - cb->coeffs = NULL; - cb->touched = NULL; - cb->len = 0; + ++cb; } - ++cb; } } + ++precinct; } } } } + tile->init = gTrue; } return readTilePartData(tileIdx, tilePartLen, tilePartToEOC); @@ -2384,68 +2425,87 @@ GBool JPXStream::readTilePartData(Guint tileIdx, switch (tile->progOrder) { case 0: // layer, resolution level, component, precinct cover(58); - if (++tile->comp == img.nComps) { - tile->comp = 0; - if (++tile->res == tile->maxNDecompLevels + 1) { - tile->res = 0; - if (++tile->layer == tile->nLayers) { - tile->layer = 0; - tile->done = gTrue; + resLevel = &tile->tileComps[tile->comp].resLevels[tile->res]; + if (++tile->precinct == resLevel->nPrecincts) { + tile->precinct = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + tile->done = gTrue; + } } } } break; case 1: // resolution level, layer, component, precinct cover(59); - if (++tile->comp == img.nComps) { - tile->comp = 0; - if (++tile->layer == tile->nLayers) { - tile->layer = 0; - if (++tile->res == tile->maxNDecompLevels + 1) { - tile->res = 0; - tile->done = gTrue; + resLevel = &tile->tileComps[tile->comp].resLevels[tile->res]; + if (++tile->precinct == resLevel->nPrecincts) { + tile->precinct = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + tile->done = gTrue; + } } } } break; case 2: // resolution level, precinct, component, layer - //~ this isn't correct -- see B.12.1.3 cover(60); + //~ this is incorrect if there are subsampled components (?) if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->comp == img.nComps) { tile->comp = 0; - if (++tile->res == tile->maxNDecompLevels + 1) { - tile->res = 0; - tile->done = gTrue; + resLevel = &tile->tileComps[tile->comp].resLevels[tile->res]; + if (++tile->precinct == resLevel->nPrecincts) { + tile->precinct = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + tile->done = gTrue; + } } } } break; case 3: // precinct, component, resolution level, layer - //~ this isn't correct -- see B.12.1.4 cover(61); + //~ this is incorrect if there are subsampled components (?) if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; - tile->done = gTrue; + resLevel = &tile->tileComps[tile->comp].resLevels[tile->res]; + if (++tile->precinct == resLevel->nPrecincts) { + tile->precinct = 0; + tile->done = gTrue; + } } } } break; case 4: // component, precinct, resolution level, layer - //~ this isn't correct -- see B.12.1.5 cover(62); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; - if (++tile->comp == img.nComps) { - tile->comp = 0; - tile->done = gTrue; + resLevel = &tile->tileComps[tile->comp].resLevels[tile->res]; + if (++tile->precinct == resLevel->nPrecincts) { + tile->precinct = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + tile->done = gTrue; + } } } } @@ -2515,13 +2575,13 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, - touched0 += 4 << tileComp->codeBlockW) { + touched0 += 4 << resLevel->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0+y1 < cb->y1; - ++y1, coeff += tileComp->w, touched += tileComp->cbW) { + ++y1, coeff += tileComp->w, touched += resLevel->cbW) { if (!*coeff) { horiz = vert = diag = 0; horizSign = vertSign = 2; @@ -2590,13 +2650,13 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, - touched0 += 4 << tileComp->codeBlockW) { + touched0 += 4 << resLevel->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0+y1 < cb->y1; - ++y1, coeff += tileComp->w, touched += tileComp->cbW) { + ++y1, coeff += tileComp->w, touched += resLevel->cbW) { if (*coeff && !*touched) { if (*coeff == 1 || *coeff == -1) { all = 0; @@ -2651,16 +2711,16 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, - touched0 += 4 << tileComp->codeBlockW) { + touched0 += 4 << resLevel->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { y1 = 0; if (y0 + 3 < cb->y1 && !(*touched1) && - !(touched1[tileComp->cbW]) && - !(touched1[2 * tileComp->cbW]) && - !(touched1[3 * tileComp->cbW]) && + !(touched1[resLevel->cbW]) && + !(touched1[2 * resLevel->cbW]) && + !(touched1[3 * resLevel->cbW]) && (x == cb->x0 || y0 == cb->y0 || !coeff1[-(int)tileComp->w - 1]) && (y0 == cb->y0 || @@ -2702,9 +2762,9 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, } } for (coeff = &coeff1[y1 * tileComp->w], - touched = &touched1[y1 << tileComp->codeBlockW]; + touched = &touched1[y1 << resLevel->codeBlockW]; y1 < 4 && y0 + y1 < cb->y1; - ++y1, coeff += tileComp->w, touched += tileComp->cbW) { + ++y1, coeff += tileComp->w, touched += resLevel->cbW) { if (!*touched) { horiz = vert = diag = 0; horizSign = vertSign = 2; @@ -2814,15 +2874,13 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) { int shift2; double mu; int val; - Guint r, cbX, cbY, x, y; + Guint r, pre, cbX, cbY, x, y; cover(68); //----- (NL)LL subband (resolution level 0) resLevel = &tileComp->resLevels[0]; - precinct = &resLevel->precincts[0]; - subband = &precinct->subbands[0]; // i-quant parameters qStyle = tileComp->quantStyle & 0x1f; @@ -2843,44 +2901,48 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) { } // do fixed point adjustment and dequantization on (NL)LL - cb = subband->cbs; - for (cbY = 0; cbY < subband->nYCBs; ++cbY) { - for (cbX = 0; cbX < subband->nXCBs; ++cbX) { - for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; - y < cb->y1; - ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { - for (x = cb->x0, coeff = coeff0, touched = touched0; - x < cb->x1; - ++x, ++coeff, ++touched) { - val = *coeff; - if (val != 0) { - shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); - if (shift2 > 0) { - cover(94); - if (val < 0) { - val = (val << shift2) - (1 << (shift2 - 1)); + for (pre = 0; pre < resLevel->nPrecincts; ++pre) { + precinct = &resLevel->precincts[pre]; + subband = &precinct->subbands[0]; + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; + y < cb->y1; + ++y, coeff0 += tileComp->w, touched0 += resLevel->cbW) { + for (x = cb->x0, coeff = coeff0, touched = touched0; + x < cb->x1; + ++x, ++coeff, ++touched) { + val = *coeff; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); + if (shift2 > 0) { + cover(94); + if (val < 0) { + val = (val << shift2) - (1 << (shift2 - 1)); + } else { + val = (val << shift2) + (1 << (shift2 - 1)); + } } else { - val = (val << shift2) + (1 << (shift2 - 1)); + cover(95); + val >>= -shift2; } - } else { - cover(95); - val >>= -shift2; - } - if (qStyle == 0) { - cover(96); - if (tileComp->transform == 0) { - cover(97); - val &= -1 << (fracBits - tileComp->prec); + if (qStyle == 0) { + cover(96); + if (tileComp->transform == 0) { + cover(97); + val &= -1 << (fracBits - tileComp->prec); + } + } else { + cover(98); + val = (int)((double)val * mu); } - } else { - cover(98); - val = (int)((double)val * mu); } + *coeff = val; } - *coeff = val; } + ++cb; } - ++cb; } } @@ -2914,11 +2976,10 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, int val; int *dataPtr, *bufPtr; Guint nx1, nx2, ny1, ny2, offset; - Guint x, y, sb, cbX, cbY; + Guint x, y, sb, pre, cbX, cbY; qStyle = tileComp->quantStyle & 0x1f; guard = (tileComp->quantStyle >> 5) & 7; - precinct = &resLevel->precincts[0]; //----- compute subband bounds @@ -2931,10 +2992,10 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, // | LH | HH | <- ny1 // +----+----+ // <- ny2 - nx1 = precinct->subbands[1].x1 - precinct->subbands[1].x0; - nx2 = nx1 + precinct->subbands[0].x1 - precinct->subbands[0].x0; - ny1 = precinct->subbands[0].y1 - precinct->subbands[0].y0; - ny2 = ny1 + precinct->subbands[1].y1 - precinct->subbands[1].y0; + nx1 = resLevel->bx1[1] - resLevel->bx0[1]; + nx2 = nx1 + resLevel->bx1[0] - resLevel->bx0[0]; + ny1 = resLevel->by1[0] - resLevel->by0[0]; + ny2 = ny1 + resLevel->by1[1] - resLevel->by0[1]; if (nx2 == 0 || ny2 == 0) { return; } @@ -2965,44 +3026,48 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, } // fixed point adjustment and dequantization - subband = &precinct->subbands[sb]; - cb = subband->cbs; - for (cbY = 0; cbY < subband->nYCBs; ++cbY) { - for (cbX = 0; cbX < subband->nXCBs; ++cbX) { - for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; - y < cb->y1; - ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { - for (x = cb->x0, coeff = coeff0, touched = touched0; - x < cb->x1; - ++x, ++coeff, ++touched) { - val = *coeff; - if (val != 0) { - shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); - if (shift2 > 0) { - cover(74); - if (val < 0) { - val = (val << shift2) - (1 << (shift2 - 1)); + + for (pre = 0; pre < resLevel->nPrecincts; ++pre) { + precinct = &resLevel->precincts[pre]; + subband = &precinct->subbands[sb]; + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; + y < cb->y1; + ++y, coeff0 += tileComp->w, touched0 += resLevel->cbW) { + for (x = cb->x0, coeff = coeff0, touched = touched0; + x < cb->x1; + ++x, ++coeff, ++touched) { + val = *coeff; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); + if (shift2 > 0) { + cover(74); + if (val < 0) { + val = (val << shift2) - (1 << (shift2 - 1)); + } else { + val = (val << shift2) + (1 << (shift2 - 1)); + } } else { - val = (val << shift2) + (1 << (shift2 - 1)); + cover(75); + val >>= -shift2; } - } else { - cover(75); - val >>= -shift2; - } - if (qStyle == 0) { - cover(76); - if (tileComp->transform == 0) { - val &= -1 << (fracBits - tileComp->prec); + if (qStyle == 0) { + cover(76); + if (tileComp->transform == 0) { + val &= -1 << (fracBits - tileComp->prec); + } + } else { + cover(77); + val = (int)((double)val * mu); } - } else { - cover(77); - val = (int)((double)val * mu); } + *coeff = val; } - *coeff = val; } + ++cb; } - ++cb; } } } @@ -3010,13 +3075,9 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, //----- inverse transform // horizontal (row) transforms - if (r == tileComp->nDecompLevels) { - offset = 3 + (tileComp->x0 & 1); - } else { - offset = 3 + (tileComp->resLevels[r+1].x0 & 1); - } + offset = 3 + (resLevel->x0 & 1); for (y = 0, dataPtr = tileComp->data; y < ny2; ++y, dataPtr += tileComp->w) { - if (precinct->subbands[0].x0 == precinct->subbands[1].x0) { + if (resLevel->bx0[0] == resLevel->bx0[1]) { // fetch LL/LH for (x = 0, bufPtr = tileComp->buf + offset; x < nx1; @@ -3050,13 +3111,9 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, } // vertical (column) transforms - if (r == tileComp->nDecompLevels) { - offset = 3 + (tileComp->y0 & 1); - } else { - offset = 3 + (tileComp->resLevels[r+1].y0 & 1); - } + offset = 3 + (resLevel->y0 & 1); for (x = 0, dataPtr = tileComp->data; x < nx2; ++x, ++dataPtr) { - if (precinct->subbands[1].y0 == precinct->subbands[0].y0) { + if (resLevel->by0[0] == resLevel->by0[1]) { // fetch LL/HL for (y = 0, bufPtr = tileComp->buf + offset; y < ny1; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h index bc5eac4f5ee..510826cd162 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h @@ -135,7 +135,6 @@ struct JPXCodeBlock { struct JPXSubband { //----- computed - Guint x0, y0, x1, y1; // bounds Guint nXCBs, nYCBs; // number of code-blocks in the x and y // directions @@ -152,9 +151,6 @@ struct JPXSubband { //------------------------------------------------------------------------ struct JPXPrecinct { - //----- computed - Guint x0, y0, x1, y1; // bounds of the precinct - //----- children JPXSubband *subbands; // the subbands }; @@ -165,12 +161,18 @@ struct JPXResLevel { //----- from the COD and COC segments (main and tile) Guint precinctWidth; // log2(precinct width) Guint precinctHeight; // log2(precinct height) + Guint nPrecincts; //----- computed - Guint x0, y0, x1, y1; // bounds of the tile-comp (for this res level) + Guint x0, y0, x1, y1; // bounds of this tile-comp at this res level Guint bx0[3], by0[3], // subband bounds bx1[3], by1[3]; - GBool empty; // true if all subbands are zero width or height + Guint codeBlockW; // log2(code-block width) + Guint codeBlockH; // log2(code-block height) + Guint cbW; // code-block width + Guint cbH; // code-block height + GBool empty; // true if all subbands and precincts are + // zero width or height //---- children JPXPrecinct *precincts; // the precincts @@ -201,8 +203,6 @@ struct JPXTileComp { //----- computed Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction - Guint cbW; // code-block width - Guint cbH; // code-block height //----- image data int *data; // the decoded image data diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc index 6ff75b0b621..0c74dbb04ea 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc @@ -237,11 +237,6 @@ Object *Lexer::getObj(Object *obj) { switch (c = getChar()) { case EOF: -#if 0 - // This breaks some PDF files, e.g., ones from Photoshop. - case '\r': - case '\n': -#endif error(errSyntaxError, getPos(), "Unterminated string"); done = gTrue; break; @@ -259,6 +254,16 @@ Object *Lexer::getObj(Object *obj) { } break; + case '\r': + // The PDF spec says that any literal end-of-line sequence + // (LF, CR, CR+LF) is translated to a single LF char. + c = lookChar(); + if (c == '\n') { + getChar(); + } + c2 = '\n'; + break; + case '\\': switch (c = getChar()) { case 'n': diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc index c223074677f..898fbc99e2b 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc @@ -45,11 +45,12 @@ const char *objTypeNames[numObjTypes] = { #ifdef DEBUG_MEM #if MULTITHREADED GAtomicCounter Object::numAlloc[numObjTypes] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #else long Object::numAlloc[numObjTypes] = -#endif {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #endif +#endif // DEBUG_MEM Object *Object::initArray(XRef *xref) { initObj(objArray); @@ -229,13 +230,15 @@ void Object::memCheck(FILE *f) { long t; t = 0; - for (i = 0; i < numObjTypes; ++i) + for (i = 0; i < numObjTypes; ++i) { t += numAlloc[i]; + } if (t > 0) { fprintf(f, "Allocated objects:\n"); for (i = 0; i < numObjTypes; ++i) { - if (numAlloc[i] > 0) + if (numAlloc[i] > 0) { fprintf(f, " %-20s: %6ld\n", objTypeNames[i], numAlloc[i]); + } } } #endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc index 6188856e8aa..a0fbb1b06c2 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc @@ -172,6 +172,7 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) { int policy; Ref ref; Object obj2, obj3, obj4, obj5; + GBool gotOCG; int i; if (obj->isNull()) { @@ -224,34 +225,40 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) { obj2.free(); return gFalse; } + gotOCG = gFalse; for (i = 0; i < obj4.arrayGetLength(); ++i) { obj4.arrayGetNF(i, &obj5); if (obj5.isRef()) { ref = obj5.getRef(); - if (!(ocg = findOCG(&ref))) { - obj5.free(); - obj4.free(); - obj3.free(); - obj2.free(); - return gFalse; - } - switch (policy) { - case ocPolicyAllOn: - *visible = *visible && ocg->getState(); - break; - case ocPolicyAnyOn: - *visible = *visible || ocg->getState(); - break; - case ocPolicyAnyOff: - *visible = *visible || !ocg->getState(); - break; - case ocPolicyAllOff: - *visible = *visible && !ocg->getState(); - break; + if ((ocg = findOCG(&ref))) { + gotOCG = gTrue; + switch (policy) { + case ocPolicyAllOn: + *visible = *visible && ocg->getState(); + break; + case ocPolicyAnyOn: + *visible = *visible || ocg->getState(); + break; + case ocPolicyAnyOff: + *visible = *visible || !ocg->getState(); + break; + case ocPolicyAllOff: + *visible = *visible && !ocg->getState(); + break; + } } } obj5.free(); } + if (!gotOCG) { + // if the "OCGs" array is "empty or contains references only + // to null or deleted objects", this OCMD doesn't have any + // effect + obj4.free(); + obj3.free(); + obj2.free(); + return gFalse; + } obj4.free(); } obj3.free(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc index adec372117d..f2be88edfdc 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc @@ -113,7 +113,7 @@ void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str, void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { drawImage(state, ref, str, width, height, colorMap, NULL, gFalse, @@ -123,7 +123,7 @@ void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h index f53dab16a23..6fe7eeedb4e 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h @@ -24,9 +24,7 @@ class GfxState; struct GfxColor; class GfxColorSpace; class GfxImageColorMap; -class GfxFunctionShading; -class GfxAxialShading; -class GfxRadialShading; +class GfxShading; class Stream; class Links; class Link; @@ -61,11 +59,6 @@ public: // operations. virtual GBool useTilingPatternFill() { return gFalse; } - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - virtual GBool useShadedFills() { return gFalse; } - // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual GBool useDrawForm() { return gFalse; } @@ -166,12 +159,7 @@ public: double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) {} - virtual GBool functionShadedFill(GfxState *state, - GfxFunctionShading *shading) - { return gFalse; } - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading) - { return gFalse; } - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading) + virtual GBool shadedFill(GfxState *state, GfxShading *shading) { return gFalse; } //----- path clipping @@ -212,12 +200,13 @@ public: virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate); @@ -253,10 +242,6 @@ public: //----- links virtual void processLink(Link *link) {} -#if 1 //~tmp: turn off anti-aliasing temporarily - virtual void setInShading(GBool sh) {} -#endif - private: double defCTM[6]; // default coordinate transform matrix diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc index 09b5b99759a..877bcb88255 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc @@ -26,7 +26,7 @@ #include "PDFDoc.h" #include "Link.h" #include "Annot.h" -#include "Form.h" +#include "AcroForm.h" #include "OptionalContent.h" #include "TileMap.h" #include "TileCache.h" @@ -260,9 +260,15 @@ void PDFCore::displayPage(int page, GBool scrollToTop, if (page <= 0 || page > doc->getNumPages()) { return; } - if (scrollToTop || - state->getDisplayMode() == displayContinuous || - state->getDisplayMode() == displaySideBySideContinuous) { + if (!scrollToTop && + (state->getDisplayMode() == displayContinuous || + state->getDisplayMode() == displaySideBySideContinuous)) { + scrollY = tileMap->getPageTopY(page) + + (state->getScrollY() + - tileMap->getPageTopY(tileMap->getFirstPage())); + } else if (scrollToTop || + state->getDisplayMode() == displayContinuous || + state->getDisplayMode() == displaySideBySideContinuous) { scrollY = tileMap->getPageTopY(page); } else if (scrollToBottom) { scrollY = tileMap->getPageBottomY(page); @@ -679,19 +685,23 @@ void PDFCore::scrollTo(int x, int y, GBool snapToPage) { 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; + // NB: topPage can be out of bounds here, because the scroll + // position isn't adjusted until finishUpdate is called, below + if (topPage > 0 && topPage <= doc->getNumPages()) { topPageY = tileMap->getPageTopY(topPage); + sy = state->getScrollY(); dy = sy - topPageY; - if (-16 < dy && dy < 0) { + // 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); + } } } } @@ -1100,7 +1110,7 @@ void PDFCore::setLinearSelection(int page, TextPosition *pos0, // build the list of rectangles //~ this doesn't handle RtL, vertical, or rotated text - loadText(selectPage); + loadText(page); rects = new GList(); if (begin.colIdx == end.colIdx && begin.parIdx == end.parIdx && @@ -1108,51 +1118,51 @@ void PDFCore::setLinearSelection(int page, TextPosition *pos0, // same line text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointLower(&end, &x1, &y1); - cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } else if (begin.colIdx == end.colIdx) { // same column text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointRightEdge(&begin, &x1, &y1); text->convertPosToPointLeftEdge(&end, &x2, &y2); text->convertPosToPointLower(&end, &x3, &y3); - cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); - cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); - cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } else { // different columns text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointRightEdge(&begin, &x1, &y1); text->getColumnLowerLeft(begin.colIdx, &x2, &y2); - cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); - cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) { text->getColumnLowerLeft(colIdx, &x0, &y0); text->getColumnUpperRight(colIdx, &x1, &y1); - cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } text->getColumnUpperRight(end.colIdx, &x0, &y0); text->convertPosToPointLeftEdge(&end, &x1, &y1); text->convertPosToPointLower(&end, &x2, &y2); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); - cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); - cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); - rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); + cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); + cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); + rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } // get window coord bboxes for old selection and new selection; @@ -1264,8 +1274,31 @@ void PDFCore::moveSelectionDrag(int pg, int x, int y) { } void PDFCore::finishSelectionDrag() { - selectPage = 0; - selectStartX = selectStartY = 0; + // nothing +} + +void PDFCore::selectWord(int pg, int x, int y) { + TextPosition endPos; + + loadText(pg); + if (text->findWordPoints(x, y, &selectStartPos, &endPos)) { + selectPage = pg; + setLinearSelection(pg, &selectStartPos, &endPos); + } else { + selectPage = 0; + } +} + +void PDFCore::selectLine(int pg, int x, int y) { + TextPosition endPos; + + loadText(pg); + if (text->findLinePoints(x, y, &selectStartPos, &endPos)) { + selectPage = pg; + setLinearSelection(pg, &selectStartPos, &endPos); + } else { + selectPage = 0; + } } GBool PDFCore::getSelection(int *pg, double *ulx, double *uly, @@ -1550,6 +1583,45 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive, return gTrue; } +GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive, + GBool wholeWord, int firstPage, int lastPage) { + GList *results = new GList(); + + TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse); + if (!textOut->isOk()) { + delete textOut; + return results; + } + + for (int pg = firstPage; pg <= lastPage; ++pg) { + doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); + GBool first = gTrue; + while (1) { + double xMin, yMin, xMax, yMax; + if (!textOut->findText(u, len, first, gTrue, !first, gFalse, + caseSensitive, gFalse, wholeWord, + &xMin, &yMin, &xMax, &yMax)) { + break; + } + double uxMin, uyMin, uxMax, uyMax, t; + textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin); + textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax); + if (uxMin > uxMax) { + t = uxMin; uxMin = uxMax; uxMax = t; + } + if (uyMin > uyMax) { + t = uyMin; uyMin = uyMax; uyMax = t; + } + results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax)); + first = gFalse; + } + } + + delete textOut; + + return results; +} + GBool PDFCore::cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu) { @@ -1683,7 +1755,7 @@ Annot *PDFCore::getAnnot(int idx) { return annots->getAnnot(idx); } -FormField *PDFCore::findFormField(int pg, double x, double y) { +AcroFormField *PDFCore::findFormField(int pg, double x, double y) { if (!doc->getCatalog()->getForm()) { return NULL; } @@ -1697,7 +1769,7 @@ int PDFCore::findFormFieldIdx(int pg, double x, double y) { return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y); } -FormField *PDFCore::getFormField(int idx) { +AcroFormField *PDFCore::getFormField(int idx) { if (!doc->getCatalog()->getForm()) { return NULL; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h index 510c01a1a76..accf19d82ef 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h @@ -32,7 +32,7 @@ class LinkDest; class LinkAction; class Annot; class Annots; -class FormField; +class AcroFormField; class TextPage; class HighlightFile; class OptionalContentGroup; @@ -66,6 +66,17 @@ enum SelectMode { }; //------------------------------------------------------------------------ +// FindResult +//------------------------------------------------------------------------ + +struct FindResult { + FindResult(int pageA, double xMinA, double yMinA, double xMaxA, double yMaxA) + : page(pageA), xMin(xMinA), yMin(yMinA), xMax(xMaxA), yMax(yMaxA) {} + int page; + double xMin, yMin, xMax, yMax; +}; + +//------------------------------------------------------------------------ // PDFCore //------------------------------------------------------------------------ @@ -176,6 +187,8 @@ public: void startSelectionDrag(int pg, int x, int y); void moveSelectionDrag(int pg, int x, int y); void finishSelectionDrag(); + void selectWord(int pg, int x, int y); + void selectLine(int pg, int x, int y); // Retrieve the current selection. This function uses user // coordinates. Returns false if there is no selection. @@ -198,6 +211,8 @@ public: virtual GBool findU(Unicode *u, int len, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly); + GList *findAll(Unicode *u, int len, GBool caseSensitive, + GBool wholeWord, int firstPage, int lastPage); //----- coordinate conversion @@ -242,9 +257,9 @@ public: Annot *findAnnot(int pg, double x, double y); int findAnnotIdx(int pg, double x, double y); Annot *getAnnot(int idx); - FormField *findFormField(int pg, double x, double y); + AcroFormField *findFormField(int pg, double x, double y); int findFormFieldIdx(int pg, double x, double y); - FormField *getFormField(int idx); + AcroFormField *getFormField(int idx); GBool overText(int pg, double x, double y); void forceRedraw(); void setTileDoneCbk(void (*cbk)(void *data), void *data); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc index a130b164f91..6efd4aac6c4 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc @@ -21,6 +21,7 @@ #endif #include "gmempp.h" #include "GString.h" +#include "gfile.h" #include "config.h" #include "GlobalParams.h" #include "Page.h" @@ -113,6 +114,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword, ok = setup(ownerPassword, userPassword); } +#if 0 #ifdef _WIN32 PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword, GString *userPassword, PDFCore *coreA) { @@ -122,14 +124,21 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword, init(coreA); + // handle a Windows shortcut + wchar_t wPath[MAX_PATH + 1]; + int n = fileNameLen < MAX_PATH ? fileNameLen : MAX_PATH; + memcpy(wPath, fileNameA, n * sizeof(wchar_t)); + wPath[n] = L'\0'; + readWindowsShortcut(wPath, MAX_PATH + 1); + int wPathLen = (int)wcslen(wPath); + // save both Unicode and 8-bit copies of the file name fileName = new GString(); - fileNameU = (wchar_t *)gmallocn(fileNameLen + 1, sizeof(wchar_t)); - for (i = 0; i < fileNameLen; ++i) { + fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t)); + memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t)); + for (i = 0; i < wPathLen; ++i) { fileName->append((char)fileNameA[i]); - fileNameU[i] = fileNameA[i]; } - fileNameU[fileNameLen] = L'\0'; // try to open file // NB: _wfopen is only available in NT @@ -153,6 +162,7 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword, ok = setup(ownerPassword, userPassword); } #endif +#endif /* 0 */ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword, GString *userPassword, PDFCore *coreA) { @@ -165,7 +175,7 @@ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword, /* #ifdef _WIN32 Unicode u; - int n, i, j; + int i, j; #endif */ @@ -175,17 +185,19 @@ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword, #if defined(_WIN32) #if 0 - n = 0; + wchar_t wPath[MAX_PATH + 1]; i = 0; - while (getUTF8(fileName, &i, &u)) { - ++n; - } - fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t)); - i = j = 0; - while (j < n && getUTF8(fileName, &i, &u)) { - fileNameU[j++] = (wchar_t)u; + j = 0; + while (j < MAX_PATH && getUTF8(fileName, &i, &u)) { + wPath[j++] = (wchar_t)u; } - fileNameU[n] = L'\0'; + wPath[j] = L'\0'; + readWindowsShortcut(wPath, MAX_PATH + 1); + int wPathLen = (int)wcslen(wPath); + + fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t)); + memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t)); + // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); @@ -579,6 +591,18 @@ GBool PDFDoc::saveEmbeddedFile(int idx, const char *path) { return ret; } +GBool PDFDoc::saveEmbeddedFileU(int idx, const char *path) { + FILE *f; + GBool ret; + + if (!(f = openFile(path, "wb"))) { + return gFalse; + } + ret = saveEmbeddedFile2(idx, f); + fclose(f); + return ret; +} + #ifdef _WIN32 GBool PDFDoc::saveEmbeddedFile(int idx, const wchar_t *path, int pathLen) { FILE *f; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h index e7005b16eb2..5069cb78689 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h @@ -185,6 +185,7 @@ public: int getEmbeddedFileNameLength(int idx) { return catalog->getEmbeddedFileNameLength(idx); } GBool saveEmbeddedFile(int idx, const char *path); + GBool saveEmbeddedFileU(int idx, const char *path); #ifdef _WIN32 GBool saveEmbeddedFile(int idx, const wchar_t *path, int pathLen); #endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc index f39a72e9015..0c39472ba85 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc @@ -40,7 +40,7 @@ #include "XRef.h" #include "PreScanOutputDev.h" #include "CharCodeToUnicode.h" -#include "Form.h" +#include "AcroForm.h" #include "TextString.h" #if HAVE_SPLASH # include "Splash.h" @@ -1287,7 +1287,8 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, - GBool honorUserUnitA) { + GBool honorUserUnitA, + GBool fileNameIsUTF8) { FILE *f; PSFileType fileTypeA; @@ -1333,7 +1334,12 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, #endif } else { fileTypeA = psFile; - if (!(f = fopen(fileName, "w"))) { + if (fileNameIsUTF8) { + f = openFile(fileName, "w"); + } else { + f = fopen(fileName, "w"); + } + if (!f) { error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); ok = gFalse; return; @@ -1737,7 +1743,7 @@ void PSOutputDev::writeDocSetup(Catalog *catalog) { Page *page; Dict *resDict; Annots *annots; - Form *form; + AcroForm *form; Object obj1, obj2, obj3; GString *s; GBool needDefaultFont; @@ -1994,6 +2000,7 @@ void PSOutputDev::setupResources(Dict *resDict) { } patDict.free(); + //----- recursively scan SMask transparency groups in ExtGState dicts resDict->lookup("ExtGState", &gsDict); if (gsDict.isDict()) { @@ -2211,13 +2218,15 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { if (fontLoc->locType == gfxFontLocResident && fontLoc->fontType >= fontCIDType0) { subst = gTrue; - if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { - fi->ff->encoding = fontLoc->encoding->copy(); - uMap->decRefCnt(); - } else { - error(errSyntaxError, -1, - "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", - fontLoc->encoding); + if (!fi->ff->encoding) { + if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { + fi->ff->encoding = fontLoc->encoding->copy(); + uMap->decRefCnt(); + } else { + error(errSyntaxError, -1, + "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", + fontLoc->encoding); + } } } @@ -2244,7 +2253,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) { - sprintf(buf, "c%02x", i+j); + snprintf(buf, sizeof(buf), "c%02x", i+j); charName = buf; } else { charName = ((Gfx8BitFont *)font)->getCharName(i+j); @@ -2392,6 +2401,7 @@ PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font, // open the font file if (!(fontFile = fopen(fileName->getCString(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); + delete psName; return NULL; } @@ -3623,7 +3633,7 @@ GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) { p[0] == 0x80 && (p[1] == 0x01 || p[1] == 0x02)) { len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); - if (len > remain - 6) { + if (len < 0 || len > remain - 6) { break; } if (p[1] == 0x01) { @@ -4102,6 +4112,7 @@ void PSOutputDev::setupForm(Object *strRef, Object *strObj) { resObj.free(); } + GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, int rotateA, GBool useMediaBox, GBool crop, int sliceX, int sliceY, @@ -4441,17 +4452,27 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) { landscape = gFalse; } else { rotate = (360 - state->getRotate()) % 360; + double scaledWidth = width * userUnit; + double scaledHeight = height * userUnit; + if (xScale0 > 0 && yScale0 > 0) { + scaledWidth *= xScale0; + scaledHeight *= yScale0; + } if (rotate == 0 || rotate == 180) { - if ((width < height && imgWidth > imgHeight && height > imgHeight) || - (width > height && imgWidth < imgHeight && width > imgWidth)) { + if ((scaledWidth < scaledHeight && imgWidth > imgHeight && + scaledHeight > imgHeight) || + (scaledWidth > scaledHeight && imgWidth < imgHeight && + scaledWidth > imgWidth)) { rotate += 90; landscape = gTrue; } else { landscape = gFalse; } } else { // rotate == 90 || rotate == 270 - if ((height < width && imgWidth > imgHeight && width > imgHeight) || - (height > width && imgWidth < imgHeight && height > imgWidth)) { + if ((scaledHeight < scaledWidth && imgWidth > imgHeight && + scaledWidth > imgHeight) || + (scaledHeight > scaledWidth && imgWidth < imgHeight && + scaledHeight > imgWidth)) { rotate = 270 - rotate; landscape = gTrue; } else { @@ -5252,6 +5273,29 @@ void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx, noStateChanges = gFalse; } +GBool PSOutputDev::shadedFill(GfxState *state, GfxShading *shading) { + if (level != psLevel2 && + level != psLevel2Sep && + level != psLevel3 && + level != psLevel3Sep) { + return gFalse; + } + + switch (shading->getType()) { + case 1: + functionShadedFill(state, (GfxFunctionShading *)shading); + return gTrue; + case 2: + axialShadedFill(state, (GfxAxialShading *)shading); + return gTrue; + case 3: + radialShadedFill(state, (GfxRadialShading *)shading); + return gTrue; + default: + return gFalse; + } +} + GBool PSOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { double x0, y0, x1, y1; @@ -5957,7 +6001,7 @@ void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { int len; @@ -8301,7 +8345,7 @@ void PSOutputDev::writePSString(GString *s) { writePSChar((char)*p); line += 2; } else if (*p < 0x20 || *p >= 0x80) { - sprintf(buf, "\\%03o", *p); + snprintf(buf, sizeof(buf), "\\%03o", *p); writePS(buf); line += 4; } else { @@ -8351,7 +8395,7 @@ GString *PSOutputDev::filterPSName(GString *name) { c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { - sprintf(buf, "#%02x", c & 0xff); + snprintf(buf, sizeof(buf), "#%02x", c & 0xff); name2->append(buf); } else { name2->append(c); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h index 66057a7cb74..466f81f1992 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h @@ -38,6 +38,9 @@ class GfxICCBasedColorSpace; class GfxIndexedColorSpace; class GfxSeparationColorSpace; class GfxDeviceNColorSpace; +class GfxFunctionShading; +class GfxAxialShading; +class GfxRadialShading; class PDFRectangle; class PSOutCustomColor; class PSOutputDev; @@ -82,7 +85,8 @@ public: GBool manualCtrlA = gFalse, PSOutCustomCodeCbk customCodeCbkA = NULL, void *customCodeCbkDataA = NULL, - GBool honorUserUnitA = gFalse); + GBool honorUserUnitA = gFalse, + GBool fileNameIsUTF8 = gFalse); // Open a PSOutputDev that will write to a generic stream. PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, @@ -118,13 +122,6 @@ public: // operations. virtual GBool useTilingPatternFill() { return gTrue; } - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - virtual GBool useShadedFills() - { return level == psLevel2 || level == psLevel2Sep || - level == psLevel3 || level == psLevel3Sep; } - // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual GBool useDrawForm() { return preload; } @@ -214,10 +211,7 @@ public: double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); - virtual GBool functionShadedFill(GfxState *state, - GfxFunctionShading *shading); - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); + virtual GBool shadedFill(GfxState *state, GfxShading *shading); //----- path clipping virtual void clip(GfxState *state); @@ -238,7 +232,8 @@ public: virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); #if OPI_SUPPORT @@ -342,6 +337,10 @@ private: double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); + GBool functionShadedFill(GfxState *state, + GfxFunctionShading *shading); + GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); + GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); void doPath(GfxPath *path); void doImageL1(Object *ref, GfxState *state, GfxImageColorMap *colorMap, diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc index b2820d3a2bf..ace0b33d85c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc @@ -14,6 +14,7 @@ #include <stddef.h> #include "gmempp.h" +#include "Trace.h" #include "GlobalParams.h" #include "Object.h" #include "Array.h" @@ -26,7 +27,7 @@ #include "Gfx.h" #include "GfxState.h" #include "Annot.h" -#include "Form.h" +#include "AcroForm.h" #endif #include "Error.h" #include "Catalog.h" @@ -335,7 +336,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, Gfx *gfx; Object obj; Annots *annotList; - Form *form; + AcroForm *form; int i; if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop, @@ -344,6 +345,8 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, return; } + traceBegin(this, "begin page"); + rotate += getRotate(); if (rotate >= 360) { rotate -= 360; @@ -404,7 +407,9 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, } delete gfx; -#endif +#endif // PDF_PARSER_ONLY + + traceEnd(this, "end page"); } void Page::makeBox(double hDPI, double vDPI, int rotate, diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h index ee30a1a8b47..8ec5a10995a 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h @@ -127,6 +127,7 @@ public: // Get page parameters. int getNum() { return num; } + PageAttrs *getAttrs() { return attrs; } PDFRectangle *getMediaBox() { return attrs->getMediaBox(); } PDFRectangle *getCropBox() { return attrs->getCropBox(); } GBool isCropped() { return attrs->isCropped(); } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc index d6f40333215..e5e00b85f51 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc @@ -76,36 +76,7 @@ void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, } } -GBool PreScanOutputDev::functionShadedFill(GfxState *state, - GfxFunctionShading *shading) { - if (shading->getColorSpace()->getMode() != csDeviceGray && - shading->getColorSpace()->getMode() != csCalGray) { - gray = gFalse; - } - mono = gFalse; - if (state->getFillOpacity() != 1 || - state->getBlendMode() != gfxBlendNormal) { - transparency = gTrue; - } - return gTrue; -} - -GBool PreScanOutputDev::axialShadedFill(GfxState *state, - GfxAxialShading *shading) { - if (shading->getColorSpace()->getMode() != csDeviceGray && - shading->getColorSpace()->getMode() != csCalGray) { - gray = gFalse; - } - mono = gFalse; - if (state->getFillOpacity() != 1 || - state->getBlendMode() != gfxBlendNormal) { - transparency = gTrue; - } - return gTrue; -} - -GBool PreScanOutputDev::radialShadedFill(GfxState *state, - GfxRadialShading *shading) { +GBool PreScanOutputDev::shadedFill(GfxState *state, GfxShading *shading) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = gFalse; @@ -229,7 +200,7 @@ void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { GfxColorSpace *colorSpace; @@ -258,7 +229,7 @@ void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h index 882d8ef39f5..253733772bf 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h @@ -46,11 +46,6 @@ public: // operations. virtual GBool useTilingPatternFill() { return gTrue; } - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - virtual GBool useShadedFills() { return gTrue; } - // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gTrue; } @@ -72,10 +67,7 @@ public: double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); - virtual GBool functionShadedFill(GfxState *state, - GfxFunctionShading *shading); - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); + virtual GBool shadedFill(GfxState *state, GfxShading *shading); //----- path clipping virtual void clip(GfxState *state); @@ -99,12 +91,13 @@ public: virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc new file mode 100644 index 00000000000..d8dc24e0fc6 --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc @@ -0,0 +1,1327 @@ +//======================================================================== +// +// ShadingImage.cc +// +// Copyright 2020 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <math.h> +#include "Trace.h" +#include "GfxState.h" +#include "SplashBitmap.h" +#include "SplashPattern.h" +#include "SplashPath.h" +#include "Splash.h" +#include "ShadingImage.h" + +// Max recursive depth for a patch mesh shading fill. +#define patchMaxDepth 10 + +// Max delta allowed in any color component for a patch mesh shading +// fill. +#define patchColorDelta (dblToCol(1 / 256.0)) + +SplashBitmap *ShadingImage::generateBitmap(GfxState *state, + GfxShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + switch (shading->getType()) { + case 1: + return generateFunctionBitmap(state, (GfxFunctionShading *)shading, + mode, reverseVideo, + parentSplash, parentBitmap, xOut, yOut); + break; + case 2: + return generateAxialBitmap(state, (GfxAxialShading *)shading, + mode, reverseVideo, + parentSplash, parentBitmap, xOut, yOut); + break; + case 3: + return generateRadialBitmap(state, (GfxRadialShading *)shading, + mode, reverseVideo, + parentSplash, parentBitmap, xOut, yOut); + break; + case 4: + case 5: + return generateGouraudTriangleBitmap(state, + (GfxGouraudTriangleShading *)shading, + mode, reverseVideo, + parentSplash, parentBitmap, + xOut, yOut); + break; + case 6: + case 7: + return generatePatchMeshBitmap(state, (GfxPatchMeshShading *)shading, + mode, reverseVideo, + parentSplash, parentBitmap, xOut, yOut); + break; + default: + return NULL; + } +} + +SplashBitmap *ShadingImage::generateFunctionBitmap(GfxState *state, + GfxFunctionShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + // get the shading parameters + double x0, y0, x1, y1; + shading->getDomain(&x0, &y0, &x1, &y1); + double *patternMat = shading->getMatrix(); + + // get the clip bbox + double fxMin, fyMin, fxMax, fyMax; + state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // convert to integer coords + int xMin = (int)floor(fxMin); + int yMin = (int)floor(fyMin); + int xMax = (int)floor(fxMax) + 1; + int yMax = (int)floor(fyMax) + 1; + int bitmapWidth = xMax - xMin; + int bitmapHeight = yMax - yMin; + + // allocate the bitmap + traceMessage("function shading fill bitmap"); + SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, + gTrue, gTrue, parentBitmap); + int nComps = splashColorModeNComps[mode]; + + // compute the domain -> device space transform = mat * CTM + double *ctm = state->getCTM(); + double mat[6]; + mat[0] = patternMat[0] * ctm[0] + patternMat[1] * ctm[2]; + mat[1] = patternMat[0] * ctm[1] + patternMat[1] * ctm[3]; + mat[2] = patternMat[2] * ctm[0] + patternMat[3] * ctm[2]; + mat[3] = patternMat[2] * ctm[1] + patternMat[3] * ctm[3]; + mat[4] = patternMat[4] * ctm[0] + patternMat[5] * ctm[2] + ctm[4]; + mat[5] = patternMat[4] * ctm[1] + patternMat[5] * ctm[3] + ctm[5]; + + // compute the device space -> domain transform + double det = mat[0] * mat[3] - mat[1] * mat[2]; + if (fabs(det) < 0.000001) { + return NULL; + } + det = 1 / det; + double iMat[6]; + iMat[0] = mat[3] * det; + iMat[1] = -mat[1] * det; + iMat[2] = -mat[2] * det; + iMat[3] = mat[0] * det; + iMat[4] = (mat[2] * mat[5] - mat[3] * mat[4]) * det; + iMat[5] = (mat[1] * mat[4] - mat[0] * mat[5]) * det; + + // fill the bitmap + SplashColorPtr dataPtr = bitmap->getDataPtr(); + Guchar *alphaPtr = bitmap->getAlphaPtr(); + for (int y = 0; y < bitmapHeight; ++y) { + for (int x = 0; x < bitmapWidth; ++x) { + + // convert coords to the pattern domain + double tx = xMin + x + 0.5; + double ty = yMin + y + 0.5; + double xx = tx * iMat[0] + ty * iMat[2] + iMat[4]; + double yy = tx * iMat[1] + ty * iMat[3] + iMat[5]; + + // get the color + if (xx >= x0 && xx <= x1 && yy >= y0 && yy <= y1) { + GfxColor color; + shading->getColor(xx, yy, &color); + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &color, sColor); + for (int i = 0; i < nComps; ++i) { + *dataPtr++ = sColor[i]; + } + *alphaPtr++ = 0xff; + } else { + dataPtr += nComps; + *alphaPtr++ = 0; + } + } + } + + *xOut = xMin; + *yOut = yMin; + return bitmap; +} + +SplashBitmap *ShadingImage::generateAxialBitmap(GfxState *state, + GfxAxialShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + // get the shading parameters + double x0, y0, x1, y1; + shading->getCoords(&x0, &y0, &x1, &y1); + double t0 = shading->getDomain0(); + double t1 = shading->getDomain1(); + GBool ext0 = shading->getExtend0(); + GBool ext1 = shading->getExtend1(); + double dx = x1 - x0; + double dy = y1 - y0; + double d = dx * dx + dy * dy; + GBool dZero = fabs(d) < 0.0001; + if (!dZero) { + d = 1 / d; + } + if (dZero && !ext0 && !ext1) { + return NULL; + } + + // get the clip bbox + double fxMin, fyMin, fxMax, fyMax; + state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // convert to integer coords + int xMin = (int)floor(fxMin); + int yMin = (int)floor(fyMin); + int xMax = (int)floor(fxMax) + 1; + int yMax = (int)floor(fyMax) + 1; + int bitmapWidth = xMax - xMin; + int bitmapHeight = yMax - yMin; + + // compute the inverse CTM + double *ctm = state->getCTM(); + double det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; + if (fabs(det) < 0.000001) { + return NULL; + } + det = 1 / det; + double ictm[6]; + ictm[0] = ctm[3] * det; + ictm[1] = -ctm[1] * det; + ictm[2] = -ctm[2] * det; + ictm[3] = ctm[0] * det; + ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; + ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; + + // convert axis endpoints to device space + double xx0, yy0, xx1, yy1; + state->transform(x0, y0, &xx0, &yy0); + state->transform(x1, y1, &xx1, &yy1); + + // allocate the bitmap + traceMessage("axial shading fill bitmap"); + SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, + gTrue, gTrue, parentBitmap); + int nComps = splashColorModeNComps[mode]; + + // special case: zero-length axis + if (dZero) { + GfxColor color; + if (ext0) { + shading->getColor(t0, &color); + } else { + shading->getColor(t1, &color); + } + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &color, sColor); + SplashColorPtr dataPtr = bitmap->getDataPtr(); + for (int y = 0; y < bitmapHeight; ++y) { + for (int x = 0; x < bitmapWidth; ++x) { + for (int i = 0; i < nComps; ++i) { + *dataPtr++ = sColor[i]; + } + } + } + memset(bitmap->getAlphaPtr(), 0xff, (size_t)bitmapWidth * bitmapHeight); + + // special case: horizontal axis (in device space) + } else if (fabs(yy0 - yy1) < 0.01) { + for (int x = 0; x < bitmapWidth; ++x) { + SplashColorPtr dataPtr = bitmap->getDataPtr() + x * nComps; + Guchar *alphaPtr = bitmap->getAlphaPtr() + x; + double tx = xMin + x + 0.5; + double ty = yMin + 0.5; + double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; + double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + double s = ((xx - x0) * dx + (yy - y0) * dy) * d; + GBool go = gFalse; + if (s < 0) { + go = ext0; + } else if (s > 1) { + go = ext1; + } else { + go = gTrue; + } + if (go) { + GfxColor color; + if (s <= 0) { + shading->getColor(t0, &color); + } else if (s >= 1) { + shading->getColor(t1, &color); + } else { + double t = t0 + s * (t1 - t0); + shading->getColor(t, &color); + } + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &color, sColor); + for (int y = 0; y < bitmapHeight; ++y) { + for (int i = 0; i < nComps; ++i) { + dataPtr[i] = sColor[i]; + } + dataPtr += bitmap->getRowSize(); + *alphaPtr = 0xff; + alphaPtr += bitmapWidth; + } + } else { + for (int y = 0; y < bitmapHeight; ++y) { + *alphaPtr = 0; + alphaPtr += bitmapWidth; + } + } + } + + // special case: vertical axis (in device space) + } else if (fabs(xx0 - xx1) < 0.01) { + for (int y = 0; y < bitmapHeight; ++y) { + SplashColorPtr dataPtr = bitmap->getDataPtr() + y * bitmap->getRowSize(); + Guchar *alphaPtr = bitmap->getAlphaPtr() + y * bitmapWidth; + double tx = xMin + 0.5; + double ty = yMin + y + 0.5; + double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; + double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + double s = ((xx - x0) * dx + (yy - y0) * dy) * d; + GBool go = gFalse; + if (s < 0) { + go = ext0; + } else if (s > 1) { + go = ext1; + } else { + go = gTrue; + } + if (go) { + GfxColor color; + if (s <= 0) { + shading->getColor(t0, &color); + } else if (s >= 1) { + shading->getColor(t1, &color); + } else { + double t = t0 + s * (t1 - t0); + shading->getColor(t, &color); + } + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &color, sColor); + for (int x = 0; x < bitmapWidth; ++x) { + for (int i = 0; i < nComps; ++i) { + dataPtr[i] = sColor[i]; + } + dataPtr += nComps; + } + memset(alphaPtr, 0xff, bitmapWidth); + } else { + memset(alphaPtr, 0, bitmapWidth); + } + } + + // general case + } else { + // pre-compute colors along the axis + int nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0) + + (yy1 - yy0) * (yy1 - yy0))); + if (nColors < 16) { + nColors = 16; + } else if (nColors > 1024) { + nColors = 1024; + } + SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps); + SplashColorPtr sColor = sColors; + for (int i = 0; i < nColors; ++i) { + double s = (double)i / (double)(nColors - 1); + double t = t0 + s * (t1 - t0); + GfxColor color; + shading->getColor(t, &color); + computeShadingColor(state, mode, reverseVideo, &color, sColor); + sColor += nComps; + } + + SplashColorPtr dataPtr = bitmap->getDataPtr(); + Guchar *alphaPtr = bitmap->getAlphaPtr(); + for (int y = 0; y < bitmapHeight; ++y) { + for (int x = 0; x < bitmapWidth; ++x) { + + // convert coords to user space + double tx = xMin + x + 0.5; + double ty = yMin + y + 0.5; + double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; + double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + + // compute the position along the axis + double s = ((xx - x0) * dx + (yy - y0) * dy) * d; + GBool go = gFalse; + if (s < 0) { + go = ext0; + } else if (s > 1) { + go = ext1; + } else { + go = gTrue; + } + if (go) { + if (s <= 0) { + sColor = sColors; + } else if (s >= 1) { + sColor = sColors + (nColors - 1) * nComps; + } else { + int i = (int)((nColors - 1) * s + 0.5); + sColor = sColors + i * nComps; + } + for (int i = 0; i < nComps; ++i) { + *dataPtr++ = sColor[i]; + } + *alphaPtr++ = 0xff; + } else { + dataPtr += nComps; + *alphaPtr++ = 0; + } + } + } + gfree(sColors); + } + + *xOut = xMin; + *yOut = yMin; + return bitmap; +} + +SplashBitmap *ShadingImage::generateRadialBitmap(GfxState *state, + GfxRadialShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + // get the shading parameters + double x0, y0, r0, x1, y1, r1; + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + double t0 = shading->getDomain0(); + double t1 = shading->getDomain1(); + GBool ext0 = shading->getExtend0(); + GBool ext1 = shading->getExtend1(); + double h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); + GBool enclosed = fabs(r1 - r0) >= h; + + // get the clip bbox + double fxMin, fyMin, fxMax, fyMax; + state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // intersect with shading region (in user space): if the extend + // flags are false (or just the larger extend flag is false, in the + // "enclosed" case), we can use the bbox for the two circles + if ((!ext0 && !ext1) || + (enclosed && !(r0 > r1 ? ext0 : ext1))) { + double uxMin = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1); + double uxMax = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1); + double uyMin = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1); + double uyMax = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1); + double dxMin, dyMin, dxMax, dyMax; + transformBBox(state, uxMin, uyMin, uxMax, uyMax, + &dxMin, &dyMin, &dxMax, &dyMax); + if (dxMin > fxMin) { + fxMin = dxMin; + } + if (dxMax < dxMax) { + fxMax = dxMax; + } + if (dyMin > fyMin) { + fyMin = dyMin; + } + if (dyMax < fyMax) { + fyMax = dyMax; + } + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + } + + // convert to integer coords + int xMin = (int)floor(fxMin); + int yMin = (int)floor(fyMin); + int xMax = (int)floor(fxMax) + 1; + int yMax = (int)floor(fyMax) + 1; + int bitmapWidth = xMax - xMin; + int bitmapHeight = yMax - yMin; + + // compute the inverse CTM + double *ctm = state->getCTM(); + double det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; + if (fabs(det) < 0.000001) { + return NULL; + } + det = 1 / det; + double ictm[6]; + ictm[0] = ctm[3] * det; + ictm[1] = -ctm[1] * det; + ictm[2] = -ctm[2] * det; + ictm[3] = ctm[0] * det; + ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; + ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; + + // allocate the bitmap + traceMessage("radial shading fill bitmap"); + SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, + gTrue, gTrue, parentBitmap); + int nComps = splashColorModeNComps[mode]; + + // pre-compute colors along the axis + int nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth + + bitmapHeight * bitmapHeight)); + if (nColors < 16) { + nColors = 16; + } else if (nColors > 1024) { + nColors = 1024; + } + SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps); + SplashColorPtr sColor = sColors; + for (int i = 0; i < nColors; ++i) { + double s = (double)i / (double)(nColors - 1); + double t = t0 + s * (t1 - t0); + GfxColor color; + shading->getColor(t, &color); + computeShadingColor(state, mode, reverseVideo, &color, sColor); + sColor += nComps; + } + + // special case: in the "enclosed" + extended case, we can fill the + // bitmap with the outer color and just render inside the larger + // circle + int bxMin, byMin, bxMax, byMax; + if (enclosed && + ((r0 > r1 && ext0) || (r1 > r0 && ext1))) { + double uxMin, uyMin, uxMax, uyMax; + if (r0 > r1) { + sColor = sColors; + uxMin = x0 - r0; + uxMax = x0 + r0; + uyMin = y0 - r0; + uyMax = y0 + r0; + } else { + sColor = sColors + (nColors - 1) * nComps; + uxMin = x1 - r1; + uxMax = x1 + r1; + uyMin = y1 - r1; + uyMax = y1 + r1; + } + + // convert bbox of larger circle to device space + double dxMin, dyMin, dxMax, dyMax; + transformBBox(state, uxMin, uyMin, uxMax, uyMax, + &dxMin, &dyMin, &dxMax, &dyMax); + bxMin = (int)floor(dxMin - xMin); + if (bxMin < 0) { + bxMin = 0; + } + byMin = (int)floor(dyMin - yMin); + if (byMin < 0) { + byMin = 0; + } + bxMax = (int)floor(dxMax - xMin) + 1; + if (bxMax > bitmapWidth) { + bxMax = bitmapWidth; + } + byMax = (int)floor(dyMax - yMin) + 1; + if (byMax > bitmapHeight) { + byMax = bitmapHeight; + } + + // fill bitmap (except for the rectangle containing the larger circle) + SplashColorPtr dataPtr = bitmap->getDataPtr(); + Guchar *alphaPtr = bitmap->getAlphaPtr(); + for (int y = 0; y < bitmapHeight; ++y) { + for (int x = 0; x < bitmapWidth; ++x) { + if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) { + dataPtr += nComps; + ++alphaPtr; + } else { + for (int i = 0; i < nComps; ++i) { + *dataPtr++ = sColor[i]; + } + *alphaPtr++ = 0xff; + } + } + } + + } else { + bxMin = 0; + byMin = 0; + bxMax = bitmapWidth; + byMax = bitmapHeight; + } + + // render the shading into the bitmap + double dx = x1 - x0; + double dy = y1 - y0; + double dr = r1 - r0; + double r0dr = r0 * dr; + double r02 = r0 * r0; + double a = dx * dx + dy * dy - dr * dr; + GBool aIsZero; + double a2; + if (fabs(a) < 0.00001) { + aIsZero = gTrue; + a2 = 0; + } else { + aIsZero = gFalse; + a2 = 1 / (2 * a); + } + for (int y = byMin; y < byMax; ++y) { + SplashColorPtr dataPtr = bitmap->getDataPtr() + + y * bitmap->getRowSize() + bxMin * nComps; + Guchar *alphaPtr = bitmap->getAlphaPtr() + + y * bitmap->getAlphaRowSize() + bxMin; + for (int x = bxMin; x < bxMax; ++x) { + + // convert coords to user space + double tx = xMin + x + 0.5; + double ty = yMin + y + 0.5; + double xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; + double yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; + + // compute the radius of the circle at x,y + double b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr); + double c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02; + double s = 0; + GBool go = gFalse; + if (aIsZero) { + if (fabs(b) < 0.000001) { + if (c <= 0) { + if (ext0) { + s = 0; + go = gTrue; + } + } else { + if (ext1) { + s = 1; + go = gTrue; + } + } + } else { + double s0 = c / b; + double rs0 = r0 + s0 * (r1 - r0); + if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { + s = s0; + go = gTrue; + } + } + } else { + double e = b*b - 4*a*c; + if (e >= 0) { + double es = sqrt(e); + double s0 = (b + es) * a2; + double s1 = (b - es) * a2; + double rs0 = r0 + s0 * (r1 - r0); + double rs1 = r0 + s1 * (r1 - r0); + if (s0 > s1) { + if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { + s = s0; + go = gTrue; + } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { + s = s1; + go = gTrue; + } + } else { + if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { + s = s1; + go = gTrue; + } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { + s = s0; + go = gTrue; + } + } + } + } + if (!go) { + dataPtr += nComps; + *alphaPtr++ = 0x00; + continue; + } + if (s <= 0) { + sColor = sColors; + } else if (s >= 1) { + sColor = sColors + (nColors - 1) * nComps; + } else { + int i = (int)((nColors - 1) * s + 0.5); + sColor = sColors + i * nComps; + } + for (int i = 0; i < nComps; ++i) { + *dataPtr++ = sColor[i]; + } + *alphaPtr++ = 0xff; + } + } + + gfree(sColors); + + *xOut = xMin; + *yOut = yMin; + return bitmap; +} + +SplashBitmap *ShadingImage::generateGouraudTriangleBitmap( + GfxState *state, + GfxGouraudTriangleShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + // get the clip bbox + double fxMin, fyMin, fxMax, fyMax; + state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // get the shading bbox + double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax; + shading->getBBox(&tx0, &ty0, &tx1, &ty1); + state->transform(tx0, ty0, &dx, &dy); + txMin = txMax = dx; + tyMin = tyMax = dy; + state->transform(tx0, ty1, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + state->transform(tx1, ty0, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + state->transform(tx1, ty1, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + if (txMin > fxMin) { + fxMin = txMin; + } + if (txMax < fxMax) { + fxMax = txMax; + } + if (tyMin > fyMin) { + fyMin = tyMin; + } + if (tyMax < fyMax) { + fyMax = tyMax; + } + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // convert to integer coords + int xMin = (int)floor(fxMin); + int yMin = (int)floor(fyMin); + int xMax = (int)floor(fxMax) + 1; + int yMax = (int)floor(fyMax) + 1; + int bitmapWidth = xMax - xMin; + int bitmapHeight = yMax - yMin; + + // allocate the bitmap + traceMessage("Gouraud triangle shading fill bitmap"); + SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, + gTrue, gTrue, parentBitmap); + + // clear the bitmap + memset(bitmap->getDataPtr(), 0, bitmap->getHeight() * bitmap->getRowSize()); + memset(bitmap->getAlphaPtr(), 0, bitmap->getHeight() * bitmap->getWidth()); + + // draw the triangles + for (int i = 0; i < shading->getNTriangles(); ++i) { + double x0, y0, x1, y1, x2, y2; + double color0[gfxColorMaxComps]; + double color1[gfxColorMaxComps]; + double color2[gfxColorMaxComps]; + shading->getTriangle(i, &x0, &y0, color0, + &x1, &y1, color1, + &x2, &y2, color2); + gouraudFillTriangle(state, bitmap, mode, reverseVideo, + xMin, yMin, xMax, yMax, + x0, y0, color0, x1, y1, color1, x2, y2, color2, + shading); + } + + *xOut = xMin; + *yOut = yMin; + return bitmap; +} + +void ShadingImage::gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap, + SplashColorMode mode, + GBool reverseVideo, + int xMin, int yMin, int xMax, int yMax, + double x0, double y0, double *color0, + double x1, double y1, double *color1, + double x2, double y2, double *color2, + GfxGouraudTriangleShading *shading) { + int nShadingComps = shading->getNComps(); + int nBitmapComps = splashColorModeNComps[mode]; + + //--- transform the vertices to device space, sort by y + double dx0, dy0, dx1, dy1, dx2, dy2; + state->transform(x0, y0, &dx0, &dy0); + state->transform(x1, y1, &dx1, &dy1); + state->transform(x2, y2, &dx2, &dy2); + if (dy0 > dy1) { + double t = dx0; dx0 = dx1; dx1 = t; + t = dy0; dy0 = dy1; dy1 = t; + double *tc = color0; color0 = color1; color1 = tc; + } + if (dy1 > dy2) { + double t = dx1; dx1 = dx2; dx2 = t; + t = dy1; dy1 = dy2; dy2 = t; + double *tc = color1; color1 = color2; color2 = tc; + } + if (dy0 > dy1) { + double t = dx0; dx0 = dx1; dx1 = t; + t = dy0; dy0 = dy1; dy1 = t; + double *tc = color0; color0 = color1; color1 = tc; + } + + //--- y loop + int syMin = (int)floor(dy0); + if (syMin < yMin) { + syMin = yMin; + } + int syMax = (int)floor(dy2) + 1; + if (syMax > yMax) { + syMax = yMax; + } + for (int sy = syMin; sy < syMax; ++sy) { + + //--- vertical interpolation + double xx0, xx1; + double cc0[gfxColorMaxComps], cc1[gfxColorMaxComps]; + if (sy <= dy0) { + xx0 = xx1 = dx0; + for (int i = 0; i < nShadingComps; ++i) { + cc0[i] = cc1[i] = color0[i]; + } + } else if (sy >= dy2) { + xx0 = xx1 = dx2; + for (int i = 0; i < nShadingComps; ++i) { + cc0[i] = cc1[i] = color2[i]; + } + } else { + if (sy <= dy1) { + double interp = (sy - dy0) / (dy1 - dy0); + xx0 = dx0 + interp * (dx1 - dx0); + for (int i = 0; i < nShadingComps; ++i) { + cc0[i] = color0[i] + interp * (color1[i] - color0[i]); + } + } else { + double interp = (sy - dy1) / (dy2 - dy1); + xx0 = dx1 + interp * (dx2 - dx1); + for (int i = 0; i < nShadingComps; ++i) { + cc0[i] = color1[i] + interp * (color2[i] - color1[i]); + } + } + double interp = (sy - dy0) / (dy2 - dy0); + xx1 = dx0 + interp * (dx2 - dx0); + for (int i = 0; i < nShadingComps; ++i) { + cc1[i] = color0[i] + interp * (color2[i] - color0[i]); + } + } + + //--- x loop + if (xx0 > xx1) { + double t = xx0; xx0 = xx1; xx1 = t; + for (int i = 0; i < nShadingComps; ++i) { + t = cc0[i]; cc0[i] = cc1[i]; cc1[i] = t; + } + } + int sxMin = (int)floor(xx0); + if (sxMin < xMin) { + sxMin = xMin; + } + int sxMax = (int)floor(xx1) + 1; + if (sxMax > xMax) { + sxMax = xMax; + } + SplashColorPtr dataPtr = bitmap->getDataPtr() + + (sy - yMin) * bitmap->getRowSize() + + (sxMin - xMin) * nBitmapComps; + if (sxMin < sxMax) { + Guchar *alphaPtr = bitmap->getAlphaPtr() + + (sy - yMin) * bitmap->getWidth() + + (sxMin - xMin); + memset(alphaPtr, 0xff, sxMax - sxMin); + } + for (int sx = sxMin; sx < sxMax; ++sx) { + + //--- horizontal interpolation + double cc[gfxColorMaxComps]; + if (sx <= xx0) { + for (int i = 0; i < nShadingComps; ++i) { + cc[i] = cc0[i]; + } + } else if (sx >= xx1) { + for (int i = 0; i < nShadingComps; ++i) { + cc[i] = cc1[i]; + } + } else { + for (int i = 0; i < nShadingComps; ++i) { + double interp = (sx - xx0) / (xx1 - xx0); + cc[i] = cc0[i] + interp * (cc1[i] - cc0[i]); + } + } + + //--- compute color and set pixel + GfxColor gColor; + shading->getColor(cc, &gColor); + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &gColor, sColor); + for (int i = 0; i < nBitmapComps; ++i) { + dataPtr[i] = sColor[i]; + } + dataPtr += nBitmapComps; + } + } +} + +SplashBitmap *ShadingImage::generatePatchMeshBitmap( + GfxState *state, + GfxPatchMeshShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut) { + // get the clip bbox + double fxMin, fyMin, fxMax, fyMax; + state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax); + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // get the shading bbox + double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax; + shading->getBBox(&tx0, &ty0, &tx1, &ty1); + state->transform(tx0, ty0, &dx, &dy); + txMin = txMax = dx; + tyMin = tyMax = dy; + state->transform(tx0, ty1, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + state->transform(tx1, ty0, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + state->transform(tx1, ty1, &dx, &dy); + if (dx < txMin) { + txMin = dx; + } else if (dx > txMax) { + txMax = dx; + } + if (dy < tyMin) { + tyMin = dy; + } else if (dy > tyMax) { + tyMax = dy; + } + if (txMin > fxMin) { + fxMin = txMin; + } + if (txMax < fxMax) { + fxMax = txMax; + } + if (tyMin > fyMin) { + fyMin = tyMin; + } + if (tyMax < fyMax) { + fyMax = tyMax; + } + if (fxMin > fxMax || fyMin > fyMax) { + return NULL; + } + + // convert to integer coords + int xMin = (int)floor(fxMin); + int yMin = (int)floor(fyMin); + int xMax = (int)floor(fxMax) + 1; + int yMax = (int)floor(fyMax) + 1; + int bitmapWidth = xMax - xMin; + int bitmapHeight = yMax - yMin; + + // allocate the bitmap + traceMessage("Gouraud triangle shading fill bitmap"); + SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode, + gTrue, gTrue, parentBitmap); + + // allocate a Splash object + // vector antialiasing is disabled to avoid artifacts along triangle edges + Splash *splash = new Splash(bitmap, gFalse, + parentSplash->getImageCache(), + parentSplash->getScreen()); + SplashColor zero; + for (int i = 0; i < splashColorModeNComps[mode]; ++i) { + zero[i] = 0; + } + splash->clear(zero, 0x00); + + // draw the patches + int start; + if (shading->getNPatches() > 128) { + start = 3; + } else if (shading->getNPatches() > 64) { + start = 2; + } else if (shading->getNPatches() > 16) { + start = 1; + } else { + start = 0; + } + for (int i = 0; i < shading->getNPatches(); ++i) { + fillPatch(state, splash, mode, reverseVideo, + xMin, yMin, shading->getPatch(i), shading, start); + } + + delete splash; + + *xOut = xMin; + *yOut = yMin; + return bitmap; +} + +void ShadingImage::fillPatch(GfxState *state, Splash *splash, + SplashColorMode mode, GBool reverseVideo, + int xMin, int yMin, + GfxPatch *patch, + GfxPatchMeshShading *shading, + int depth) { + GfxColor c00; + shading->getColor(patch->color[0][0], &c00); + GBool stop = gFalse; + + // stop subdivision at max depth + if (depth == patchMaxDepth) { + stop = gTrue; + } + + // stop subdivision if colors are close enough + if (!stop) { + int nComps = shading->getColorSpace()->getNComps(); + GfxColor c01, c10, c11; + shading->getColor(patch->color[0][1], &c01); + shading->getColor(patch->color[1][0], &c10); + shading->getColor(patch->color[1][1], &c11); + int i; + for (i = 0; i < nComps; ++i) { + if (abs(c00.c[i] - c01.c[i]) > patchColorDelta || + abs(c01.c[i] - c11.c[i]) > patchColorDelta || + abs(c11.c[i] - c10.c[i]) > patchColorDelta || + abs(c10.c[i] - c00.c[i]) > patchColorDelta) { + break; + } + } + if (i == nComps) { + stop = gTrue; + } + } + + // stop subdivision if patch is small enough + if (!stop) { + double xxMin = 0; + double yyMin = 0; + double xxMax = 0; + double yyMax = 0; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + double xx, yy; + state->transformDelta(patch->x[i][j], patch->y[i][j], &xx, &yy); + if (i == 0 && j == 0) { + xxMin = xxMax = xx; + yyMin = yyMax = yy; + } else { + if (xx < xxMin) { + xxMin = xx; + } else if (xx > xxMax) { + xxMax = xx; + } + if (yy < yyMin) { + yyMin = yy; + } else if (yy > yyMax) { + yyMax = yy; + } + } + } + } + if (xxMax - xxMin < 1 && yyMax - yyMin < 1) { + stop = gTrue; + } + } + + // draw the patch + if (stop) { + SplashColor sColor; + computeShadingColor(state, mode, reverseVideo, &c00, sColor); + splash->setFillPattern(new SplashSolidColor(sColor)); + SplashPath *path = new SplashPath(); + double xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3; + state->transform(patch->x[0][0], patch->y[0][0], &xx0, &yy0); + path->moveTo(xx0 - xMin, yy0 - yMin); + state->transform(patch->x[0][1], patch->y[0][1], &xx1, &yy1); + state->transform(patch->x[0][2], patch->y[0][2], &xx2, &yy2); + state->transform(patch->x[0][3], patch->y[0][3], &xx3, &yy3); + path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, + xx3 - xMin, yy3 - yMin); + state->transform(patch->x[1][3], patch->y[1][3], &xx1, &yy1); + state->transform(patch->x[2][3], patch->y[2][3], &xx2, &yy2); + state->transform(patch->x[3][3], patch->y[3][3], &xx3, &yy3); + path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, + xx3 - xMin, yy3 - yMin); + state->transform(patch->x[3][2], patch->y[3][2], &xx1, &yy1); + state->transform(patch->x[3][1], patch->y[3][1], &xx2, &yy2); + state->transform(patch->x[3][0], patch->y[3][0], &xx3, &yy3); + path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, + xx3 - xMin, yy3 - yMin); + state->transform(patch->x[2][0], patch->y[2][0], &xx1, &yy1); + state->transform(patch->x[1][0], patch->y[1][0], &xx2, &yy2); + path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin, + xx0 - xMin, yy0 - yMin); + path->close(); + splash->fill(path, gFalse); + delete path; + + // subdivide the patch + } else { + double xx[4][8], yy[4][8]; + for (int i = 0; i < 4; ++i) { + xx[i][0] = patch->x[i][0]; + yy[i][0] = patch->y[i][0]; + xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); + yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); + double xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); + double yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); + xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); + yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); + xx[i][2] = 0.5 * (xx[i][1] + xxm); + yy[i][2] = 0.5 * (yy[i][1] + yym); + xx[i][5] = 0.5 * (xxm + xx[i][6]); + yy[i][5] = 0.5 * (yym + yy[i][6]); + xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); + yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); + xx[i][7] = patch->x[i][3]; + yy[i][7] = patch->y[i][3]; + } + GfxPatch patch00, patch01, patch10, patch11; + for (int i = 0; i < 4; ++i) { + patch00.x[0][i] = xx[0][i]; + patch00.y[0][i] = yy[0][i]; + patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); + patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); + double xxm = 0.5 * (xx[1][i] + xx[2][i]); + double yym = 0.5 * (yy[1][i] + yy[2][i]); + patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); + patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); + patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); + patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); + patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); + patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); + patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); + patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); + patch10.x[0][i] = patch00.x[3][i]; + patch10.y[0][i] = patch00.y[3][i]; + patch10.x[3][i] = xx[3][i]; + patch10.y[3][i] = yy[3][i]; + } + for (int i = 4; i < 8; ++i) { + patch01.x[0][i-4] = xx[0][i]; + patch01.y[0][i-4] = yy[0][i]; + patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]); + patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]); + double xxm = 0.5 * (xx[1][i] + xx[2][i]); + double yym = 0.5 * (yy[1][i] + yy[2][i]); + patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]); + patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]); + patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm); + patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym); + patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]); + patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]); + patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]); + patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]); + patch11.x[0][i-4] = patch01.x[3][i-4]; + patch11.y[0][i-4] = patch01.y[3][i-4]; + patch11.x[3][i-4] = xx[3][i]; + patch11.y[3][i-4] = yy[3][i]; + } + for (int i = 0; i < shading->getNComps(); ++i) { + patch00.color[0][0][i] = patch->color[0][0][i]; + patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + + patch->color[0][1][i]); + patch01.color[0][0][i] = patch00.color[0][1][i]; + patch01.color[0][1][i] = patch->color[0][1][i]; + patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] + + patch->color[1][1][i]); + patch11.color[0][1][i] = patch01.color[1][1][i]; + patch11.color[1][1][i] = patch->color[1][1][i]; + patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] + + patch->color[1][0][i]); + patch10.color[1][1][i] = patch11.color[1][0][i]; + patch10.color[1][0][i] = patch->color[1][0][i]; + patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] + + patch->color[0][0][i]); + patch00.color[1][0][i] = patch10.color[0][0][i]; + patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] + + patch01.color[1][1][i]); + patch01.color[1][0][i] = patch00.color[1][1][i]; + patch11.color[0][0][i] = patch00.color[1][1][i]; + patch10.color[0][1][i] = patch00.color[1][1][i]; + } + fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch00, + shading, depth + 1); + fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch10, + shading, depth + 1); + fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch01, + shading, depth + 1); + fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch11, + shading, depth + 1); + } +} + +void ShadingImage::computeShadingColor(GfxState *state, + SplashColorMode mode, + GBool reverseVideo, + GfxColor *color, + SplashColorPtr sColor) { + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + + state->setFillColor(color); + switch (mode) { + case splashModeMono8: + state->getFillGray(&gray); + if (reverseVideo) { + gray = gfxColorComp1 - gray; + } + sColor[0] = colToByte(gray); + break; + case splashModeRGB8: + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = gfxColorComp1 - rgb.r; + rgb.g = gfxColorComp1 - rgb.g; + rgb.b = gfxColorComp1 - rgb.b; + } + sColor[0] = colToByte(rgb.r); + sColor[1] = colToByte(rgb.g); + sColor[2] = colToByte(rgb.b); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + state->getFillCMYK(&cmyk); + sColor[0] = colToByte(cmyk.c); + sColor[1] = colToByte(cmyk.m); + sColor[2] = colToByte(cmyk.y); + sColor[3] = colToByte(cmyk.k); + break; +#endif + case splashModeMono1: + case splashModeBGR8: + // mode cannot be Mono1 or BGR8 + break; + } +} + +// Transform a user space bbox to a device space bbox. +void ShadingImage::transformBBox(GfxState *state, + double uxMin, double uyMin, + double uxMax, double uyMax, + double *dxMin, double *dyMin, + double *dxMax, double *dyMax) { + double tx, ty; + state->transform(uxMin, uyMin, &tx, &ty); + *dxMin = *dxMax = tx; + *dyMin = *dyMax = ty; + state->transform(uxMin, uyMax, &tx, &ty); + if (tx < *dxMin) { + *dxMin = tx; + } else if (tx > *dxMax) { + *dxMax = tx; + } + if (ty < *dyMin) { + *dyMin = ty; + } else if (ty > *dyMax) { + *dyMax = ty; + } + state->transform(uxMax, uyMin, &tx, &ty); + if (tx < *dxMin) { + *dxMin = tx; + } else if (tx > *dxMax) { + *dxMax = tx; + } + if (ty < *dyMin) { + *dyMin = ty; + } else if (ty > *dyMax) { + *dyMax = ty; + } + state->transform(uxMax, uyMax, &tx, &ty); + if (tx < *dxMin) { + *dxMin = tx; + } else if (tx > *dxMax) { + *dxMax = tx; + } + if (ty < *dyMin) { + *dyMin = ty; + } else if (ty > *dyMax) { + *dyMax = ty; + } +} + diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h new file mode 100644 index 00000000000..3bbff6922a4 --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h @@ -0,0 +1,106 @@ +//======================================================================== +// +// ShadingImage.h +// +// Convert shading patterns to bitmaps. +// +// Copyright 2020 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef SHADINGIMAGE_H +#define SHADINGIMAGE_H + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "SplashTypes.h" + +class GfxState; +class GfxShading; +class SplashBitmap; +class Splash; + +class ShadingImage { +public: + + // Generate a [mode] bitmap for [shading], using the clipping and + // CTM info from [state]. Returns the bitmap and sets [xOut],[yOut] + // to the upper-left corner of the region in device space. Returns + // NULL if the clip region is empty. + static SplashBitmap *generateBitmap(GfxState *state, GfxShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + + +private: + + static SplashBitmap *generateFunctionBitmap(GfxState *state, + GfxFunctionShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + static SplashBitmap *generateAxialBitmap(GfxState *state, + GfxAxialShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + static SplashBitmap *generateRadialBitmap(GfxState *state, + GfxRadialShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + static SplashBitmap *generateGouraudTriangleBitmap( + GfxState *state, + GfxGouraudTriangleShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + static void gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap, + SplashColorMode mode, + GBool reverseVideo, + int xMin, int yMin, int xMax, int yMax, + double x0, double y0, double *color0, + double x1, double y1, double *color1, + double x2, double y2, double *color2, + GfxGouraudTriangleShading *shading); + static SplashBitmap *generatePatchMeshBitmap(GfxState *state, + GfxPatchMeshShading *shading, + SplashColorMode mode, + GBool reverseVideo, + Splash *parentSplash, + SplashBitmap *parentBitmap, + int *xOut, int *yOut); + static void fillPatch(GfxState *state, Splash *splash, + SplashColorMode mode, GBool reverseVideo, + int xMin, int yMin, + GfxPatch *patch, + GfxPatchMeshShading *shading, + int depth); + static void computeShadingColor(GfxState *state, + SplashColorMode mode, + GBool reverseVideo, + GfxColor *color, + SplashColorPtr sColor); + static void transformBBox(GfxState *state, + double uxMin, double uyMin, + double uxMax, double uyMax, + double *dxMin, double *dyMin, + double *dxMax, double *dyMax); +}; + +#endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc index 187ef5657b5..89c7c02a7b6 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc @@ -17,11 +17,13 @@ #include <limits.h> #include "gmempp.h" #include "gfile.h" +#include "Trace.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Gfx.h" #include "GfxFont.h" +#include "ShadingImage.h" #include "Link.h" #include "CharCodeToUnicode.h" #include "FontEncodingTables.h" @@ -52,6 +54,18 @@ extern "C" int unlink(char *filename); //------------------------------------------------------------------------ +// max tile size (used in tilingPatternFill()) +// - Adobe uses a resolution-independent threshold here, of 6M sq pts +// - xpdf uses a resolution-dependent max, but with different values +// on 32-bit and 64-bit systems +#if SplashBitmapRowSizeMax == INT_MAX +# define maxTileSize 200000000 +#else +# define maxTileSize 2000000000 +#endif + +//------------------------------------------------------------------------ + // Type 3 font cache size parameters #define type3FontCacheAssoc 8 #define type3FontCacheMaxSets 8 @@ -67,6 +81,11 @@ static SplashStrokeAdjustMode mapStrokeAdjustMode[3] = { //------------------------------------------------------------------------ +// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. +static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +} + // Clip x to lie in [0, 255]. static inline Guchar clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; @@ -499,7 +518,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 + int refCount; // active reference count for this T3 font }; T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, @@ -541,7 +560,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; + refCount = 0; } T3FontCache::~T3FontCache() { @@ -565,6 +584,7 @@ struct T3GlyphStack { SplashBitmap *origBitmap; Splash *origSplash; double origCTM4, origCTM5; + SplashStrokeAdjustMode savedStrokeAdjust; T3GlyphStack *next; // next object on stack }; @@ -711,6 +731,9 @@ SplashOutputDev::~SplashOutputDev() { if (bitmap) { delete bitmap; } + if (textClipPath) { + delete textClipPath; + } } void SplashOutputDev::startDoc(XRef *xrefA) { @@ -763,6 +786,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { delete bitmap; bitmap = NULL; } + traceMessage("page bitmap"); bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown, NULL); @@ -1194,18 +1218,21 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { goto err1; } - // sanity-check the font size - skip anything larger than 20 inches - // (this avoids problems allocating memory for the font cache) + // sanity-check the font size: skip anything larger than 10k x 10k, + // to avoid problems allocating a bitmap (note that code in + // SplashFont disables caching at a smaller size than this) state->textTransformDelta(state->getFontSize(), state->getFontSize(), &fsx, &fsy); state->transformDelta(fsx, fsy, &fsx, &fsy); - if (fabs(fsx) > 20 * state->getHDPI() || - fabs(fsy) > 20 * state->getVDPI()) { + if (fabs(fsx) > 20000 || fabs(fsy) > 20000) { goto err1; } // check the font file cache id = new SplashOutFontFileID(gfxFont->getID()); + if (fontEngine->checkForBadFontFile(id)) { + goto err2; + } if ((fontFile = fontEngine->getFontFile(id))) { delete id; @@ -1308,7 +1335,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontType1C: @@ -1337,7 +1364,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontType1COT: @@ -1370,7 +1397,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontTrueType: @@ -1415,7 +1442,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontCIDType0: @@ -1443,7 +1470,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontCIDType0COT: @@ -1513,7 +1540,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; case fontCIDType2: @@ -1588,7 +1615,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) { gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; - goto err2; + goto err1; } break; default: @@ -1734,6 +1761,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, SplashBitmap *origBitmap, *tileBitmap; Splash *origSplash; SplashColor color; + Guint *overprintMaskBitmap; double *ctm; double ictm[6], tileMat[6], mat1[6], mat2[6]; double tileXMin, tileYMin, tileXMax, tileYMax; @@ -1818,7 +1846,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, if (tileXMax - tileXMin + 0.5 > (double)INT_MAX || tileYMax - tileYMin + 0.5 > (double)INT_MAX || tileW > INT_MAX / tileH || - tileSize > 1000000) { + tileSize > maxTileSize) { mat1[0] = mat[0]; mat1[1] = mat[1]; mat1[2] = mat[2]; @@ -1969,6 +1997,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, // create a temporary bitmap origBitmap = bitmap; origSplash = splash; + traceMessage("tiling pattern bitmap"); bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad, colorMode, gTrue, bitmapTopDown, origBitmap); @@ -1978,6 +2007,20 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, color[i] = 0; } splash->clear(color); +#if SPLASH_CMYK + // if we're doing overprint preview, we need to track the overprint + // mask at each pixel in the tile bitmap + if (globalParams->getOverprintPreview() && + colorMode == splashModeCMYK8) { + overprintMaskBitmap = (Guint *)gmallocn(tileH, tileW * (int)sizeof(Guint)); + memset(overprintMaskBitmap, 0, tileH * tileW * sizeof(Guint)); + splash->setOverprintMaskBitmap(overprintMaskBitmap); + } else { + overprintMaskBitmap = NULL; + } +#else // SPLASH_CMYK + overprintMaskBitmap = NULL; +#endif // SPLASH_CMYK splash->setMinLineWidth(globalParams->getMinLineWidth()); splash->setStrokeAdjust( mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); @@ -2005,434 +2048,48 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, splash->setOverprintMask(0xffffffff); // draw the tiles - for (iy = iyMin; iy < iyMax; ++iy) { - for (ix = ixMin; ix < ixMax; ++ix) { - x = (int)(adjXMin + ix * xStepX + iy * yStepX + 0.5); - y = (int)(adjYMin + ix * xStepY + iy * yStepY + 0.5); - splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH, - gFalse, gFalse); - } - } - - delete tileBitmap; -} - -GBool SplashOutputDev::axialShadedFill(GfxState *state, - GfxAxialShading *shading) { - double x0, y0, x1, y1, t0, t1; - GBool ext0, ext1; - double uxMin, uyMin, uxMax, uyMax, det; - double *ctm; - double ictm[6]; - double xMin, yMin, xMax, yMax, tx, ty, xx, yy; - double xx0, yy0, xx1, yy1, dx, dy, d, s, t; - GBool dZero, go; - int ixMin, iyMin, ixMax, iyMax, bitmapWidth, bitmapHeight, nColors; - SplashClipResult clipRes; - SplashColorMode srcMode; - SplashBitmap *tBitmap; - int nComps; - int x, y, i; - SplashColorPtr dataPtr; - Guchar *alphaPtr; - GfxColor color; - SplashColorPtr sColors, sColor; - SplashColor sColor0; - - - // get the shading parameters - shading->getCoords(&x0, &y0, &x1, &y1); - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - ext0 = shading->getExtend0(); - ext1 = shading->getExtend1(); - dx = x1 - x0; - dy = y1 - y0; - d = dx * dx + dy * dy; - dZero = fabs(d) < 0.0001; - if (!dZero) { - d = 1 / d; - } - if (dZero && !ext0 && !ext1) { - return gTrue; - } - - // get clip region (in user space) - state->getUserClipBBox(&uxMin, &uyMin, &uxMax, &uyMax); - if (uxMin > uxMax || uyMin > uyMax) { - return gTrue; - } - - // convert the region to device space - ctm = state->getCTM(); - tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5]; - xMin = xMax = tx; - yMin = yMax = ty; - tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - ixMin = (int)floor(xMin); - iyMin = (int)floor(yMin); - ixMax = (int)floor(xMax) + 1; - iyMax = (int)floor(yMax) + 1; - clipRes = splash->limitRectToClipRect(&ixMin, &iyMin, &ixMax, &iyMax); - if (clipRes == splashClipAllOutside) { - return gTrue; - } - - // allocate a bitmap - if (colorMode == splashModeMono1) { - srcMode = splashModeMono8; - } else if (colorMode == splashModeBGR8) { - srcMode = splashModeRGB8; - } else { - srcMode = colorMode; - } - bitmapWidth = ixMax - ixMin; - bitmapHeight = iyMax - iyMin; - tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, - srcMode, gTrue, gTrue, bitmap); - memset(tBitmap->getAlphaPtr(), 0, (size_t)bitmapWidth * bitmapHeight); - nComps = splashColorModeNComps[srcMode]; - - // compute the inverse CTM - det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); - ictm[0] = ctm[3] * det; - ictm[1] = -ctm[1] * det; - ictm[2] = -ctm[2] * det; - ictm[3] = ctm[0] * det; - ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; - ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; - - // render the shading into the bitmap - xx0 = x0 * ctm[0] + y0 * ctm[2] + ctm[4]; - yy0 = x0 * ctm[1] + y0 * ctm[3] + ctm[5]; - xx1 = x1 * ctm[0] + y1 * ctm[2] + ctm[4]; - yy1 = x1 * ctm[1] + y1 * ctm[3] + ctm[5]; - sColors = NULL; - - // special case: zero-length axis - if (dZero) { - if (ext0) { - shading->getColor(t0, &color); - } else { - shading->getColor(t1, &color); - } - computeShadingColor(state, srcMode, &color, sColor0); - dataPtr = tBitmap->getDataPtr(); - alphaPtr = tBitmap->getAlphaPtr(); - for (y = 0; y < bitmapHeight; ++y) { - for (x = 0; x < bitmapWidth; ++x) { - for (i = 0; i < nComps; ++i) { - *dataPtr++ = sColor0[i]; - } - *alphaPtr++ = 0xff; - } - } - - // special case: horizontal axis (in device space) - } else if (fabs(yy0 - yy1) < 0.01) { - for (x = 0; x < bitmapWidth; ++x) { - dataPtr = tBitmap->getDataPtr() + x * nComps; - alphaPtr = tBitmap->getAlphaPtr() + x; - tx = ixMin + x + 0.5; - ty = iyMin + 0.5; - xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; - yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; - s = ((xx - x0) * dx + (yy - y0) * dy) * d; - go = gFalse; - if (s < 0) { - go = ext0; - } else if (s > 1) { - go = ext1; - } else { - go = gTrue; - } - if (!go) { - continue; - } - if (s <= 0) { - shading->getColor(t0, &color); - } else if (s >= 1) { - shading->getColor(t1, &color); - } else { - t = t0 + s * (t1 - t0); - shading->getColor(t, &color); - } - computeShadingColor(state, srcMode, &color, sColor0); - for (y = 0; y < bitmapHeight; ++y) { - for (i = 0; i < nComps; ++i) { - dataPtr[i] = sColor0[i]; - } - *alphaPtr = 0xff; - dataPtr += tBitmap->getRowSize(); - alphaPtr += bitmapWidth; + if (tileW == 1 && tileH == 1 && + fabs(xStepX * yStepY - xStepY * yStepX) < 0.9) { + // if the tile is 1x1 pixel, and the stepping completely fills the + // area, just composite the 1x1 image across the clip region + // (this avoids performance problems in cases where the step size + // is very small) (we compare to 0.9 instead of 1.0 to avoid fp + // jitter issues) + ixMin = (int)floor(clipXMin); + ixMax = (int)floor(clipXMax) + 1; + iyMin = (int)floor(clipYMin); + iyMax = (int)floor(clipYMax) + 1; + for (iy = iyMin; iy < iyMax; ++iy) { + for (ix = ixMin; ix < ixMax; ++ix) { + splash->composite(tileBitmap, 0, 0, ix, iy, tileW, tileH, + gFalse, gFalse); } } - - // special case: vertical axis (in device space) - } else if (fabs(xx0 - xx1) < 0.01) { - dataPtr = tBitmap->getDataPtr(); - alphaPtr = tBitmap->getAlphaPtr(); - for (y = 0; y < bitmapHeight; ++y) { - tx = ixMin + 0.5; - ty = iyMin + y + 0.5; - xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; - yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; - s = ((xx - x0) * dx + (yy - y0) * dy) * d; - go = gFalse; - if (s < 0) { - go = ext0; - } else if (s > 1) { - go = ext1; - } else { - go = gTrue; - } - if (!go) { - continue; - } - if (s <= 0) { - shading->getColor(t0, &color); - } else if (s >= 1) { - shading->getColor(t1, &color); - } else { - t = t0 + s * (t1 - t0); - shading->getColor(t, &color); - } - computeShadingColor(state, srcMode, &color, sColor0); - for (x = 0; x < bitmapWidth; ++x) { - for (i = 0; i < nComps; ++i) { - *dataPtr++ = sColor0[i]; - } - *alphaPtr++ = 0xff; - } - } - - // general case } else { - // pre-compute colors along the axis - nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0) - + (yy1 - yy0) * (yy1 - yy0))); - if (nColors < 16) { - nColors = 16; - } else if (nColors > 1024) { - nColors = 1024; - } - sColors = (SplashColorPtr)gmallocn(nColors, nComps); - sColor = sColors; - for (i = 0; i < nColors; ++i) { - s = (double)i / (double)(nColors - 1); - t = t0 + s * (t1 - t0); - shading->getColor(t, &color); - computeShadingColor(state, srcMode, &color, sColor); - sColor += nComps; - } - - dataPtr = tBitmap->getDataPtr(); - alphaPtr = tBitmap->getAlphaPtr(); - for (y = 0; y < bitmapHeight; ++y) { - for (x = 0; x < bitmapWidth; ++x) { - - // convert coords to user space - tx = ixMin + x + 0.5; - ty = iyMin + y + 0.5; - xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; - yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; - - // compute the position along the axis - s = ((xx - x0) * dx + (yy - y0) * dy) * d; - go = gFalse; - if (s < 0) { - go = ext0; - } else if (s > 1) { - go = ext1; + for (iy = iyMin; iy < iyMax; ++iy) { + for (ix = ixMin; ix < ixMax; ++ix) { + x = (int)(adjXMin + ix * xStepX + iy * yStepX + 0.5); + y = (int)(adjYMin + ix * xStepY + iy * yStepY + 0.5); + if (overprintMaskBitmap) { + splash->compositeWithOverprint(tileBitmap, overprintMaskBitmap, + 0, 0, x, y, tileW, tileH, + gFalse, gFalse); } else { - go = gTrue; - } - if (!go) { - dataPtr += nComps; - ++alphaPtr; - continue; + splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH, + gFalse, gFalse); } - if (s <= 0) { - sColor = sColors; - } else if (s >= 1) { - sColor = sColors + (nColors - 1) * nComps; - } else { - i = (int)((nColors - 1) * s + 0.5); - sColor = sColors + i * nComps; - } - for (i = 0; i < nComps; ++i) { - *dataPtr++ = sColor[i]; - } - *alphaPtr++ = 0xff; } } } - // composite the bitmap - setOverprintMask(state, state->getFillColorSpace(), - state->getFillOverprint(), state->getOverprintMode(), - NULL); - splash->composite(tBitmap, 0, 0, ixMin, iyMin, bitmapWidth, bitmapHeight, - clipRes == splashClipAllInside, gFalse); - - gfree(sColors); - delete tBitmap; - - return gTrue; + gfree(overprintMaskBitmap); + delete tileBitmap; } -GBool SplashOutputDev::radialShadedFill(GfxState *state, - GfxRadialShading *shading) { - double x0, y0, r0, x1, y1, r1, t0, t1, h; - GBool ext0, ext1, enclosed; - double uxMin, uyMin, uxMax, uyMax, det; - double *ctm; - double ictm[6]; - double xMin, yMin, xMax, yMax, tx, ty, xx, yy; - double dx, dy, dr, r0dr, r02, a, a2, b, c, e, es, s, s0, s1, rs0, rs1, t; - GBool aIsZero, go; - int ixMin, iyMin, ixMax, iyMax, bitmapWidth, bitmapHeight, nColors; - int bxMin, byMin, bxMax, byMax; - SplashClipResult clipRes; - SplashColorMode srcMode; - SplashBitmap *tBitmap; - int nComps; - int x, y, i; - SplashColorPtr dataPtr; - Guchar *alphaPtr; - GfxColor color; - SplashColorPtr sColors, sColor; - - - // get the shading parameters - shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - ext0 = shading->getExtend0(); - ext1 = shading->getExtend1(); - h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); - enclosed = fabs(r1 - r0) >= h; - - // get clip region (in user space) - state->getUserClipBBox(&uxMin, &uyMin, &uxMax, &uyMax); - if (uxMin > uxMax || uyMin > uyMax) { - return gTrue; - } +GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) { - // intersect with shading region (in user space): if the extend - // flags are false (or just the larger extend flag is false, in the - // "enclosed" case), we can use the bbox for the two circles - if ((!ext0 && !ext1) || - (enclosed && !(r0 > r1 ? ext0 : ext1))) { - tx = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1); - if (tx > uxMin) { - uxMin = tx; - } - tx = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1); - if (tx < uxMax) { - uxMax = tx; - } - ty = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1); - if (ty > uyMin) { - uyMin = ty; - } - ty = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1); - if (ty < uyMax) { - uyMax = ty; - } - } - if (uxMin > uxMax || uyMin > uyMax) { - return gTrue; - } - - // convert the region to device space - ctm = state->getCTM(); - tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5]; - xMin = xMax = tx; - yMin = yMax = ty; - tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - ixMin = (int)floor(xMin); - iyMin = (int)floor(yMin); - ixMax = (int)floor(xMax) + 1; - iyMax = (int)floor(yMax) + 1; - clipRes = splash->limitRectToClipRect(&ixMin, &iyMin, &ixMax, &iyMax); - if (clipRes == splashClipAllOutside) { - return gTrue; - } - - // allocate a bitmap + // generate the bitmap + SplashColorMode srcMode; if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else if (colorMode == splashModeBGR8) { @@ -2440,287 +2097,36 @@ GBool SplashOutputDev::radialShadedFill(GfxState *state, } else { srcMode = colorMode; } - bitmapWidth = ixMax - ixMin; - bitmapHeight = iyMax - iyMin; - tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, - srcMode, gTrue, gTrue, bitmap); - memset(tBitmap->getAlphaPtr(), 0, tBitmap->getAlphaRowSize() * bitmapHeight); - nComps = splashColorModeNComps[srcMode]; - - // compute the inverse CTM - det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); - ictm[0] = ctm[3] * det; - ictm[1] = -ctm[1] * det; - ictm[2] = -ctm[2] * det; - ictm[3] = ctm[0] * det; - ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; - ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; - - // pre-compute colors along the axis - nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth - + bitmapHeight * bitmapHeight)); - if (nColors < 16) { - nColors = 16; - } else if (nColors > 1024) { - nColors = 1024; - } - sColors = (SplashColorPtr)gmallocn(nColors, nComps); - sColor = sColors; - for (i = 0; i < nColors; ++i) { - s = (double)i / (double)(nColors - 1); - t = t0 + s * (t1 - t0); - shading->getColor(t, &color); - computeShadingColor(state, srcMode, &color, sColor); - sColor += nComps; - } - - // special case: in the "enclosed" + extended case, we can fill the - // bitmap with the outer color and just render inside the larger - // circle - if (enclosed && - ((r0 > r1 && ext0) || (r1 > r0 && ext1))) { - if (r0 > r1) { - sColor = sColors; - uxMin = x0 - r0; - uxMax = x0 + r0; - uyMin = y0 - r0; - uyMax = y0 + r0; - } else { - sColor = sColors + (nColors - 1) * nComps; - uxMin = x1 - r1; - uxMax = x1 + r1; - uyMin = y1 - r1; - uyMax = y1 + r1; - } - - // convert bbox of larger circle to device space - tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5]; - xMin = xMax = tx; - yMin = yMax = ty; - tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4]; - ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5]; - if (tx < xMin) { - xMin = tx; - } else if (tx > xMax) { - xMax = tx; - } - if (ty < yMin) { - yMin = ty; - } else if (ty > yMax) { - yMax = ty; - } - bxMin = (int)floor(xMin - ixMin); - if (bxMin < 0) { - bxMin = 0; - } - byMin = (int)floor(yMin - iyMin); - if (byMin < 0) { - byMin = 0; - } - bxMax = (int)floor(xMax - ixMin) + 1; - if (bxMax > bitmapWidth) { - bxMax = bitmapWidth; - } - byMax = (int)floor(yMax - iyMin) + 1; - if (byMax > bitmapHeight) { - byMax = bitmapHeight; - } - - // fill bitmap (except for the rectangle containing the larger circle) - dataPtr = tBitmap->getDataPtr(); - alphaPtr = tBitmap->getAlphaPtr(); - for (y = 0; y < bitmapHeight; ++y) { - for (x = 0; x < bitmapWidth; ++x) { - if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) { - dataPtr += nComps; - ++alphaPtr; - } else { - for (i = 0; i < nComps; ++i) { - *dataPtr++ = sColor[i]; - } - *alphaPtr++ = 0xff; - } - } - } - - } else { - bxMin = 0; - byMin = 0; - bxMax = bitmapWidth; - byMax = bitmapHeight; - } - - // render the shading into the bitmap - dx = x1 - x0; - dy = y1 - y0; - dr = r1 - r0; - r0dr = r0 * dr; - r02 = r0 * r0; - a = dx * dx + dy * dy - dr * dr; - if (fabs(a) < 0.00001) { - aIsZero = gTrue; - a2 = 0; - } else { - aIsZero = gFalse; - a2 = 1 / (2 * a); - } - for (y = byMin; y < byMax; ++y) { - dataPtr = tBitmap->getDataPtr() - + y * tBitmap->getRowSize() + bxMin * nComps; - alphaPtr = tBitmap->getAlphaPtr() + y * tBitmap->getAlphaRowSize() + bxMin; - for (x = bxMin; x < bxMax; ++x) { - - // convert coords to user space - tx = ixMin + x + 0.5; - ty = iyMin + y + 0.5; - xx = tx * ictm[0] + ty * ictm[2] + ictm[4]; - yy = tx * ictm[1] + ty * ictm[3] + ictm[5]; - - // compute the radius of the circle at x,y - b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr); - c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02; - s = 0; - go = gFalse; - if (aIsZero) { - if (fabs(b) < 0.000001) { - if (c <= 0) { - if (ext0) { - s = 0; - go = gTrue; - } - } else { - if (ext1) { - s = 1; - go = gTrue; - } - } - } else { - s0 = c / b; - rs0 = r0 + s0 * (r1 - r0); - if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { - s = s0; - go = gTrue; - } - } - } else { - e = b*b - 4*a*c; - if (e >= 0) { - es = sqrt(e); - s0 = (b + es) * a2; - s1 = (b - es) * a2; - rs0 = r0 + s0 * (r1 - r0); - rs1 = r0 + s1 * (r1 - r0); - if (s0 > s1) { - if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { - s = s0; - go = gTrue; - } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { - s = s1; - go = gTrue; - } - } else { - if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) { - s = s1; - go = gTrue; - } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) { - s = s0; - go = gTrue; - } - } - } - } - if (!go) { - dataPtr += nComps; - ++alphaPtr; - continue; - } - if (s <= 0) { - sColor = sColors; - } else if (s >= 1) { - sColor = sColors + (nColors - 1) * nComps; - } else { - i = (int)((nColors - 1) * s + 0.5); - sColor = sColors + i * nComps; - } - for (i = 0; i < nComps; ++i) { - *dataPtr++ = sColor[i]; - } - *alphaPtr++ = 0xff; - } + int x, y; + SplashBitmap *tBitmap = ShadingImage::generateBitmap(state, shading, srcMode, + reverseVideo, + splash, bitmap, &x, &y); + if (!tBitmap) { + // clip region is empty - nothing to draw + return gTrue; } - // composite the bitmap - setOverprintMask(state, state->getFillColorSpace(), - state->getFillOverprint(), state->getOverprintMode(), - NULL); - splash->composite(tBitmap, 0, 0, ixMin, iyMin, bitmapWidth, bitmapHeight, - clipRes == splashClipAllInside, gFalse); + // check clipping and composite the bitmap + int xMin = x; + int yMin = y; + int xMax = x + tBitmap->getWidth(); + int yMax = y + tBitmap->getHeight(); + SplashClipResult clipRes = splash->limitRectToClipRect(&xMin, &yMin, + &xMax, &yMax); + if (clipRes != splashClipAllOutside) { + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + NULL); + splash->composite(tBitmap, xMin - x, yMin - y, xMin, yMin, + xMax - xMin, yMax - yMin, + clipRes == splashClipAllInside, gFalse); + } - gfree(sColors); delete tBitmap; return gTrue; } -void SplashOutputDev::computeShadingColor(GfxState *state, - SplashColorMode mode, - GfxColor *color, - SplashColorPtr sColor) { - GfxGray gray; - GfxRGB rgb; -#if SPLASH_CMYK - GfxCMYK cmyk; -#endif - - state->setFillColor(color); - switch (mode) { - case splashModeMono8: - state->getFillGray(&gray); - getColor(gray, sColor); - break; - case splashModeRGB8: - state->getFillRGB(&rgb); - getColor(&rgb, sColor); - break; -#if SPLASH_CMYK - case splashModeCMYK8: - state->getFillCMYK(&cmyk); - getColor(&cmyk, sColor); - break; -#endif - case splashModeMono1: - case splashModeBGR8: - // mode cannot be Mono1 or BGR8 - break; - } -} - - void SplashOutputDev::clip(GfxState *state) { SplashPath *path; @@ -2799,6 +2205,7 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y, if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); + // this matches the 'diagonal' test in TextPage::updateFont() horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { @@ -2924,7 +2331,7 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, } if (!(gfxFont = state->getFont())) { - return gFalse; + return gTrue; } fontID = gfxFont->getID(); ctm = state->getCTM(); @@ -2954,7 +2361,7 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, } } else { for (j = nT3Fonts - 1; j >= 0; --j) { - if (!t3FontCache[j]->inUse) { + if (t3FontCache[j]->refCount == 0) { break; } } @@ -3038,7 +2445,11 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, } } - t3Font->inUse = gTrue; + if (t3Font->refCount > 1000) { + error(errSyntaxError, -1, "Type 3 CharProcs nested too deeply"); + return gTrue; + } + ++t3Font->refCount; // push a new Type 3 glyph record t3gs = new T3GlyphStack(); @@ -3050,6 +2461,10 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, t3GlyphStack->cacheData = NULL; t3GlyphStack->haveDx = gFalse; t3GlyphStack->doNotCache = gFalse; +#if 1 //~t3-sa + t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust(); + splash->setStrokeAdjust(splashStrokeAdjustOff); +#endif return gFalse; } @@ -3074,9 +2489,12 @@ void SplashOutputDev::endType3Char(GfxState *state) { drawType3Glyph(state, t3GlyphStack->cache, t3GlyphStack->cacheTag, t3GlyphStack->cacheData); } +#if 1 //~t3-sa + splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust); +#endif t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; - t3gs->cache->inUse = gFalse; + --t3gs->cache->refCount; delete t3gs; } @@ -3186,6 +2604,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, // create the temporary bitmap if (colorMode == splashModeMono1) { colorMode = splashModeMono1; + traceMessage("T3 glyph bitmap"); bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono1, gFalse, gTrue, bitmap); splash = new Splash(bitmap, gFalse, @@ -3196,6 +2615,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, color[0] = 0xff; } else { colorMode = splashModeMono8; + traceMessage("T3 glyph bitmap"); bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono8, gFalse, gTrue, bitmap); splash = new Splash(bitmap, vectorAntialias, @@ -3299,7 +2719,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, imgMaskData.height = height; imgMaskData.y = 0; - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL); splash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL, interpolate); @@ -3344,6 +2764,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, imgMaskData.width = width; imgMaskData.height = height; imgMaskData.y = 0; + traceMessage("image mask soft mask bitmap"); maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse, gTrue, bitmap); maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache()); @@ -3354,7 +2775,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, clearMaskRegion(state, maskSplash, 0, 0, 1, 1); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL); maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData, width, height, mat, gFalse, interpolate); delete imgTag; @@ -3643,7 +3064,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, srcMode = colorMode; } src = maskColors ? &alphaImageSrc : &imageSrc; - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); splash->drawImage(imgTag, src, &imgData, srcMode, maskColors ? gTrue : gFalse, width, height, mat, interpolate); @@ -3775,9 +3197,9 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, - int maskHeight, GBool maskInvert, - GBool interpolate) { + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, + GBool maskInvert, GBool interpolate) { GfxImageColorMap *maskColorMap; Object maskDecode, decodeLow, decodeHigh; double *ctm; @@ -3817,7 +3239,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, new GfxDeviceGrayColorSpace()); maskDecode.free(); drawSoftMaskedImage(state, ref, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskColorMap, + maskRef, maskStr, maskWidth, maskHeight, maskColorMap, NULL, interpolate); delete maskColorMap; @@ -3837,6 +3259,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; + traceMessage("masked image bitmap"); maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse, gTrue, bitmap); maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache()); @@ -3931,7 +3354,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, } else { srcMode = colorMode; } - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); splash->drawImage(imgTag, &maskedImageSrc, &imgData, srcMode, gTrue, width, height, mat, interpolate); @@ -4055,7 +3479,7 @@ GBool SplashOutputDev::softMaskMatteImageSrc(void *data, void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate) { @@ -4147,18 +3571,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, #endif } //~ could add the matteImgData.lookup special case - if (colorMap->getBits() <= 8) { - n = 1 << maskColorMap->getBits(); - } else { - // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit - n = 1 << 8; - } matteImgData.colorMode = colorMode; matteImgData.invert = reverseVideo && reverseVideoInvertImages; matteImgData.width = width; matteImgData.height = height; matteImgData.y = 0; - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData, srcMode, gTrue, width, height, mat, interpolate); delete imgTag; @@ -4186,13 +3605,19 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; - n = 1 << maskColorMap->getBits(); + if (maskColorMap->getBits() <= 8) { + n = 1 << maskColorMap->getBits(); + } else { + // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit + n = 1 << 8; + } imgMaskData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (Guchar)i; maskColorMap->getGray(&pix, &gray, state->getRenderingIntent()); imgMaskData.lookup[i] = colToByte(gray); } + traceMessage("soft masked image bitmap"); maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse, gTrue, bitmap); maskSplash = new Splash(maskBitmap, vectorAntialias, @@ -4273,7 +3698,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, } } - imgTag = makeImageTag(ref); + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); splash->drawImage(imgTag, &imageSrc, &imgData, srcMode, gFalse, width, height, mat, interpolate); @@ -4288,11 +3714,14 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, } } -GString *SplashOutputDev::makeImageTag(Object *ref) { - if (!ref || !ref->isRef()) { +GString *SplashOutputDev::makeImageTag(Object *ref, GfxRenderingIntent ri, + GfxColorSpace *colorSpace) { + if (!ref || !ref->isRef() || + (colorSpace && colorSpace->isDefaultColorSpace())) { return NULL; } - return GString::format("{0:d}_{1:d}", ref->getRefNum(), ref->getRefGen()); + return GString::format("{0:d}_{1:d}_{2:d}", + ref->getRefNum(), ref->getRefGen(), (int)ri); } void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm, @@ -4471,14 +3900,16 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, ty = bh - 1; } w = (int)ceil(xMax) - tx + 1; - if (tx + w > bw) { + // NB bw and tx are both non-negative, so 'bw - tx' can't overflow + if (bw - tx < w) { w = bw - tx; } if (w < 1) { w = 1; } h = (int)ceil(yMax) - ty + 1; - if (ty + h > bh) { + // NB bh and ty are both non-negative, so 'bh - ty' can't overflow + if (bh - ty < h) { h = bh - ty; } if (h < 1) { @@ -4524,6 +3955,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, } // create the temporary bitmap + traceMessage("t-group bitmap"); bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, bitmapTopDown, transpGroup->origBitmap); splash = new Splash(bitmap, vectorAntialias, @@ -4535,32 +3967,70 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, splash->setEnablePathSimplification( globalParams->getEnablePathSimplification()); copyState(transpGroup->origSplash, gTrue); + if (!isolated || knockout) { + // non-isolated and knockout groups nested in another group will + // read the parent group bitmap, so we need to force any deferred + // initialization on the parent + transpGroup->origSplash->forceDeferredInit(ty, h); + } if (isolated) { - for (i = 0; i < splashMaxColorComps; ++i) { - color[i] = 0; + // isolated group + backdropBitmap = transpGroup->origBitmap; + transpGroup->backdropBitmap = NULL; + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + for (i = 0; i < splashMaxColorComps; ++i) { + color[i] = 0; + } + splash->clear(color, 0); + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestPreInit, + gFalse, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestInitZero, + gFalse, knockout); } - splash->clear(color, 0); - } else { - splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); - } - if (!isolated && - transpGroup->origBitmap->getAlphaPtr() && - transpGroup->origSplash->getInNonIsolatedGroup() && - colorMode != splashModeMono1) { - // when drawing a non-isolated group into another non-isolated group, + } else if (transpGroup->origBitmap->getAlphaPtr() && + transpGroup->origSplash->getInNonIsolatedGroup() && + colorMode != splashModeMono1) { + // non-isolated group drawn in another non-isolated group: // compute a backdrop bitmap with corrected alpha values + traceMessage("t-group backdrop bitmap"); backdropBitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, bitmapTopDown, transpGroup->origBitmap); transpGroup->origSplash->blitCorrectedAlpha(backdropBitmap, tx, ty, 0, 0, w, h); transpGroup->backdropBitmap = backdropBitmap; - splash->setInTransparencyGroup(backdropBitmap, 0, 0, - !isolated, knockout); + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); + splash->setInTransparencyGroup(backdropBitmap, 0, 0, + splashGroupDestPreInit, + gTrue, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, 0, 0, + splashGroupDestInitCopy, + gTrue, knockout); + } } else { + // other non-isolated group backdropBitmap = transpGroup->origBitmap; transpGroup->backdropBitmap = NULL; - splash->setInTransparencyGroup(backdropBitmap, tx, ty, - !isolated, knockout); + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestPreInit, + gTrue, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestInitCopy, + gTrue, knockout); + } } splash->clearModRegion(); transpGroup->tBitmap = bitmap; @@ -4632,14 +4102,17 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, Splash *tSplash; SplashTransparencyGroup *transpGroup; SplashColor color; - SplashColorPtr p; + SplashColorPtr p, colorPtr, colorPtr2; + Guchar *alphaPtr; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif double backdrop, backdrop2, lum, lum2; - int tx, ty, x, y; + Guchar lum8; + SplashBitmapRowSize rowSize; + int tw, th, tNComps, tx, ty, x, y; tx = transpGroupStack->tx; ty = transpGroupStack->ty; @@ -4709,51 +4182,72 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, backdrop2 = backdrop; } + traceMessage("soft mask bitmap"); softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 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()) { p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; - for (y = 0; y < tBitmap->getHeight(); ++y) { - for (x = 0; x < tBitmap->getWidth(); ++x) { - if (alpha) { - lum = tBitmap->getAlpha(x, y) / 255.0; - } else { - tBitmap->getPixel(x, y, color); + tw = tBitmap->getWidth(); + th = tBitmap->getHeight(); + rowSize = softMask->getRowSize(); + if (alpha) { + alphaPtr = tBitmap->getAlphaPtr(); + for (y = 0; y < th; ++y) { + for (x = 0; x < tw; ++x) { + lum = *alphaPtr++ / 255.0; + if (transferFunc) { + transferFunc->transform(&lum, &lum2); + } else { + lum2 = lum; + } + p[x] = (Guchar)(lum2 * 255.0 + 0.5); + } + p += rowSize; + } + } else { + colorPtr = tBitmap->getDataPtr(); + tNComps = splashColorModeNComps[tBitmap->getMode()]; + lum8 = 0; // make gcc happy + for (y = 0; y < th; ++y) { + colorPtr2 = colorPtr; + for (x = 0; x < tw; ++x) { // convert to luminosity switch (tBitmap->getMode()) { case splashModeMono1: + lum8 = 0; + break; case splashModeMono8: - lum = color[0] / 255.0; + lum8 = colorPtr2[0]; break; case splashModeRGB8: case splashModeBGR8: - lum = (0.3 / 255.0) * color[0] + - (0.59 / 255.0) * color[1] + - (0.11 / 255.0) * color[2]; + // [0.3, 0.59, 0.11] * 255 = [77, 150, 28] + lum8 = div255(77 * colorPtr2[0] + + 150 * colorPtr2[1] + + 28 * colorPtr2[1]); break; #if SPLASH_CMYK case splashModeCMYK8: - lum = (1 - color[3] / 255.0) - - (0.3 / 255.0) * color[0] - - (0.59 / 255.0) * color[1] - - (0.11 / 255.0) * color[2]; - if (lum < 0) { - lum = 0; - } + lum8 = clip255(255 - colorPtr2[3] + - div255(77 * colorPtr2[0] + + 150 * colorPtr2[1] + + 28 * colorPtr2[2])); break; #endif } + if (transferFunc) { + lum = lum8 / 255.0; + transferFunc->transform(&lum, &lum2); + lum8 = (Guchar)(lum2 * 255.0 + 0.5); + } + p[x] = lum8; + colorPtr2 += tNComps; } - if (transferFunc) { - transferFunc->transform(&lum, &lum2); - } else { - lum2 = lum; - } - p[x] = (Guchar)(lum2 * 255.0 + 0.5); + p += rowSize; + colorPtr += tBitmap->getRowSize(); } - p += softMask->getRowSize(); } } splash->setSoftMask(softMask); @@ -4993,6 +4487,10 @@ void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) { } #if 1 //~tmp: turn off anti-aliasing temporarily +// This was originally used with gradient shadings -- that's no longer +// necessary, now that shadings are all converted to device space +// images. It's still used with knockout groups, however, because the +// rasterizer doesn't properly separate opacity and shape. void SplashOutputDev::setInShading(GBool sh) { splash->setInShading(sh); } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h index 2215f883d32..cbe47b61cb7 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h @@ -69,11 +69,6 @@ public: // operations. virtual GBool useTilingPatternFill() { return gTrue; } - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. - virtual GBool useShadedFills() { return gTrue; } - // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gTrue; } @@ -123,8 +118,7 @@ public: double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); + virtual GBool shadedFill(GfxState *state, GfxShading *shading); //----- path clipping virtual void clip(GfxState *state); @@ -156,12 +150,13 @@ public: virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, int maskWidth, int maskHeight, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - Stream *maskStr, + Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, double *matte, GBool interpolate); @@ -240,10 +235,6 @@ public: // Get the screen parameters. SplashScreenParams *getScreenParams() { return &screenParams; } -#if 1 //~tmp: turn off anti-aliasing temporarily - virtual void setInShading(GBool sh); -#endif - private: void setupScreenParams(double hDPI, double vDPI); @@ -260,10 +251,6 @@ private: void setOverprintMask(GfxState *state, GfxColorSpace *colorSpace, GBool overprintFlag, int overprintMode, GfxColor *singleColor); - void computeShadingColor(GfxState *state, - SplashColorMode mode, - GfxColor *color, - SplashColorPtr sColor); SplashPath *convertPath(GfxState *state, GfxPath *path, GBool dropEmptySubpaths); void doUpdateFont(GfxState *state); @@ -279,7 +266,8 @@ private: static GBool softMaskMatteImageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine); - GString *makeImageTag(Object *ref); + GString *makeImageTag(Object *ref, GfxRenderingIntent ri, + GfxColorSpace *colorSpace); void reduceImageResolution(Stream *str, double *mat, int *width, int *height); void clearMaskRegion(GfxState *state, @@ -287,6 +275,9 @@ private: double xMin, double yMin, double xMax, double yMax); void copyState(Splash *oldSplash, GBool copyColors); +#if 1 //~tmp: turn off anti-aliasing temporarily + void setInShading(GBool sh); +#endif SplashColorMode colorMode; int bitmapRowPad; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc index a7c13c9eab3..601add89be4 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc @@ -1317,6 +1317,7 @@ void LZWStream::reset() { eof = gFalse; inputBits = 0; clearTable(); + totalIn = totalOut = 0; } GBool LZWStream::processNextCode() { @@ -1382,6 +1383,14 @@ GBool LZWStream::processNextCode() { nextBits = 12; } prevCode = code; + totalOut += seqLength; + + // check for a 'decompression bomb' + if (totalOut > 50000000 && totalIn < totalOut / 250) { + error(errSyntaxError, getPos(), "Decompression bomb in flate stream"); + eof = gTrue; + return gFalse; + } // reset buffer seqIndex = 0; @@ -1405,6 +1414,7 @@ int LZWStream::getCode() { return EOF; inputBuf = (inputBuf << 8) | (c & 0xff); inputBits += 8; + ++totalIn; } code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); inputBits -= nextBits; @@ -2268,7 +2278,6 @@ short CCITTFaxStream::lookBits(int n) { GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent, GBool okToReadStream) { GString *s; - char s1[50]; if (psLevel < 2) { return NULL; @@ -2278,8 +2287,7 @@ GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent, } s->append(indent)->append("<< "); if (encoding != 0) { - sprintf(s1, "/K %d ", encoding); - s->append(s1); + s->appendf("/K {0:d} ", encoding); } if (endOfLine) { s->append("/EndOfLine true "); @@ -2287,11 +2295,9 @@ GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent, if (byteAlign) { s->append("/EncodedByteAlign true "); } - sprintf(s1, "/Columns %d ", columns); - s->append(s1); + s->appendf("/Columns {0:d} ", columns); if (rows != 0) { - sprintf(s1, "/Rows %d ", rows); - s->append(s1); + s->appendf("/Rows {0:d} ", rows); } if (!endOfBlock) { s->append("/EndOfBlock false "); @@ -4059,7 +4065,7 @@ GBool DCTStream::readQuantTables() { error(errSyntaxError, getPos(), "Bad DCT quantization table"); return gFalse; } - if (index == numQuantTables) { + if (index >= numQuantTables) { numQuantTables = index + 1; } for (i = 0; i < 64; ++i) { @@ -4951,6 +4957,8 @@ void FlateStream::reset() { endOfBlock = eof = gTrue; cmf = str->getChar(); flg = str->getChar(); + totalIn = 2; + totalOut = 0; if (cmf == EOF || flg == EOF) return; if ((cmf & 0x0f) != 0x08) { @@ -5017,7 +5025,7 @@ int FlateStream::getRawChar() { } int FlateStream::getBlock(char *blk, int size) { - int n; + int n, k; if (pred) { return pred->getBlock(blk, size); @@ -5031,11 +5039,17 @@ int FlateStream::getBlock(char *blk, int size) { } readSome(); } - while (remain && n < size) { - blk[n++] = buf[index]; - index = (index + 1) & flateMask; - --remain; + k = remain; + if (size - n < k) { + k = size - n; + } + if (flateWindow - index < k) { + k = flateWindow - index; } + memcpy(blk + n, buf + index, k); + n += k; + index = (index + k) & flateMask; + remain -= k; } return n; } @@ -5061,7 +5075,7 @@ GBool FlateStream::isBinary(GBool last) { void FlateStream::readSome() { int code1, code2; int len, dist; - int i, j, k; + int src, dest, n1, n2, n3, i, j, k; int c; if (endOfBlock) { @@ -5090,12 +5104,78 @@ void FlateStream::readSome() { if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; dist = distDecode[code1].first + code2; - i = index; - j = (index - dist) & flateMask; - for (k = 0; k < len; ++k) { - buf[i] = buf[j]; - i = (i + 1) & flateMask; - j = (j + 1) & flateMask; + dest = index; + src = (index - dist) & flateMask; + // the following is an optimized version of: + // for (k = 0; k < len; ++k) { + // buf[dest] = buf[src]; + // dest = (dest + 1) & flateMask; + // src = (src + 1) & flateMask; + // } + if (dest + len <= flateWindow) { + if (src + len <= flateWindow) { + for (k = 0; k < len; ++k) { + buf[dest + k] = buf[src + k]; + } + } else { + n1 = flateWindow - src; + n2 = len - n1; + for (k = 0; k < n1; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = dest + n1; + src = 0; + for (k = 0; k < n2; ++k) { + buf[dest + k] = buf[src + k]; + } + } + } else { + if (src + len <= flateWindow) { + n1 = flateWindow - dest; + n2 = len - n1; + for (k = 0; k < n1; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = 0; + src = src + n1; + for (k = 0; k < n2; ++k) { + buf[dest + k] = buf[src + k]; + } + } else if (src < dest) { + n1 = flateWindow - dest; + n2 = dest - src; + n3 = len - n1 - n2; + for (k = 0; k < n1; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = 0; + src = src + n1; + for (k = 0; k < n2; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = n2; + src = 0; + for (k = 0; k < n3; ++k) { + buf[dest + k] = buf[src + k]; + } + } else { + n1 = flateWindow - src; + n2 = src - dest; + n3 = len - n1 - n2; + for (k = 0; k < n1; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = dest + n1; + src = 0; + for (k = 0; k < n2; ++k) { + buf[dest + k] = buf[src + k]; + } + dest = 0; + src = n2; + for (k = 0; k < n3; ++k) { + buf[dest + k] = buf[src + k]; + } + } } remain = len; } @@ -5113,6 +5193,15 @@ void FlateStream::readSome() { blockLen -= len; if (blockLen == 0) endOfBlock = gTrue; + totalIn += remain; + } + totalOut += remain; + + // check for a 'decompression bomb' + if (totalOut > 50000000 && totalIn < totalOut / 250) { + error(errSyntaxError, getPos(), "Decompression bomb in flate stream"); + endOfBlock = eof = gTrue; + remain = 0; } return; @@ -5164,6 +5253,7 @@ GBool FlateStream::startBlock() { "Bad uncompressed block length in flate stream"); codeBuf = 0; codeSize = 0; + totalIn += 4; // compressed block with fixed codes } else if (blockHdr == 1) { @@ -5358,6 +5448,7 @@ int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; + ++totalIn; } code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; if (codeSize == 0 || codeSize < code->len || code->len == 0) { @@ -5376,6 +5467,7 @@ int FlateStream::getCodeWord(int bits) { return EOF; codeBuf |= (c & 0xff) << codeSize; codeSize += 8; + ++totalIn; } c = codeBuf & ((1 << bits) - 1); codeBuf >>= bits; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h index 38a8dd3497c..59cafadc14f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h @@ -501,6 +501,8 @@ private: int seqLength; // length of current sequence int seqIndex; // index into current sequence GBool first; // first code after a table clear + unsigned long long totalIn; // total number of encoded bytes read so far + unsigned long long totalOut; // total number of bytes decoded so far GBool processNextCode(); void clearTable(); @@ -814,6 +816,8 @@ private: int blockLen; // remaining length of uncompressed block GBool endOfBlock; // set when end of block is reached GBool eof; // set when end of stream is reached + unsigned long long totalIn; // total number of encoded bytes read so far + unsigned long long totalOut; // total number of bytes decoded so far static int // code length code reordering codeLenCodeMap[flateMaxCodeLenCodes]; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc index 4f594384915..9ec906c7379 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc @@ -26,6 +26,7 @@ #include "gmempp.h" #include "GString.h" #include "GList.h" +#include "gfile.h" #include "config.h" #include "Error.h" #include "GlobalParams.h" @@ -88,11 +89,13 @@ // Inter-character spacing that varies by less than this multiple of // font size is assumed to be equivalent. #define uniformSpacing 0.07 +#define tableModeUniformSpacing 0.14 // Typical word spacing, as a fraction of font size. This will be // added to the minimum inter-character spacing, to account for wide // character spacing. #define wordSpacing 0.1 +#define tableModeWordSpacing 0.2 // Minimum paragraph indent from left margin, as a fraction of font // size. @@ -114,6 +117,9 @@ // value multiplied by font size. #define simpleLayoutGapThreshold 0.7 +// Minimum overlap in simple2 mode. +#define simple2MinOverlap 0.2 + // Table cells (TextColumns) are allowed to overlap by this much // in table layout mode (as a fraction of cell width or height). #define tableCellOverlapSlack 0.05 @@ -157,9 +163,28 @@ // rectangles, in order to work around flakey ascent values in fonts. #define selectionAscent 0.8 +// Grid size used to bin sort characters for overlap detection. +#define overlapGridWidth 20 +#define overlapGridHeight 20 + +// Minimum character bbox overlap (horizontal and vertical) as a +// fraction of character bbox width/height for a character to be +// treated as overlapping. +#define minCharOverlap 0.3 + #define maxUnicodeLen 16 //------------------------------------------------------------------------ + +static inline double dmin(double x, double y) { + return x < y ? x : y; +} + +static inline double dmax(double x, double y) { + return x > y ? x : y; +} + +//------------------------------------------------------------------------ // TextChar //------------------------------------------------------------------------ @@ -168,31 +193,36 @@ public: TextChar(Unicode cA, int charPosA, int charLenA, double xMinA, double yMinA, double xMaxA, double yMaxA, - int rotA, GBool clippedA, GBool invisibleA, + int rotA, GBool rotatedA, GBool clippedA, GBool invisibleA, TextFontInfo *fontA, double fontSizeA, double colorRA, double colorGA, double colorBA); static int cmpX(const void *p1, const void *p2); static int cmpY(const void *p1, const void *p2); + static int cmpCharPos(const void *p1, const void *p2); Unicode c; int charPos; int charLen; double xMin, yMin, xMax, yMax; - Guchar rot; - char clipped; - char invisible; - char spaceAfter; TextFontInfo *font; double fontSize; double colorR, colorG, colorB; + + // group the byte-size fields to minimize object size + Guchar rot; + char rotated; + char clipped; + char invisible; + char spaceAfter; + char overlap; }; TextChar::TextChar(Unicode cA, int charPosA, int charLenA, double xMinA, double yMinA, double xMaxA, double yMaxA, - int rotA, GBool clippedA, GBool invisibleA, + int rotA, GBool rotatedA, GBool clippedA, GBool invisibleA, TextFontInfo *fontA, double fontSizeA, double colorRA, double colorGA, double colorBA) { double t; @@ -228,6 +258,7 @@ TextChar::TextChar(Unicode cA, int charPosA, int charLenA, yMax = 1e8; } rot = (Guchar)rotA; + rotated = (char)rotatedA; clipped = (char)clippedA; invisible = (char)invisibleA; spaceAfter = (char)gFalse; @@ -236,6 +267,7 @@ TextChar::TextChar(Unicode cA, int charPosA, int charLenA, colorR = colorRA; colorG = colorGA; colorB = colorBA; + overlap = gFalse; } int TextChar::cmpX(const void *p1, const void *p2) { @@ -264,6 +296,12 @@ int TextChar::cmpY(const void *p1, const void *p2) { } } +int TextChar::cmpCharPos(const void *p1, const void *p2) { + const TextChar *ch1 = *(const TextChar **)p1; + const TextChar *ch2 = *(const TextChar **)p2; + return ch1->charPos - ch2->charPos; +} + //------------------------------------------------------------------------ // TextBlock //------------------------------------------------------------------------ @@ -408,6 +446,40 @@ void TextBlock::updateBounds(int childIdx) { } //------------------------------------------------------------------------ +// TextCharLine +//------------------------------------------------------------------------ + +class TextCharLine { +public: + + TextCharLine(int rotA); + ~TextCharLine(); + void add(TextChar *ch); + + GList *chars; + double yMin, yMax; + int rot; + TextCharLine *next, *prev; +}; + +TextCharLine::TextCharLine(int rotA) { + chars = new GList(); + yMin = yMax = 0; + rot = rotA; + next = prev = NULL; +} + +TextCharLine::~TextCharLine() { + delete chars; +} + +void TextCharLine::add(TextChar *ch) { + chars->append(ch); + yMin = ch->yMin; + yMax = ch->yMax; +} + +//------------------------------------------------------------------------ // TextGaps //------------------------------------------------------------------------ @@ -539,8 +611,12 @@ TextOutputControl::TextOutputControl() { html = gFalse; clipText = gFalse; discardDiagonalText = gFalse; + discardRotatedText = gFalse; discardInvisibleText = gFalse; discardClippedText = gFalse; + splitRotatedWords = gFalse; + overlapHandling = textOutIgnoreOverlaps; + separateLargeChars = gTrue; insertBOM = gFalse; marginLeft = 0; marginRight = 0; @@ -602,13 +678,15 @@ TextFontInfo::~TextFontInfo() { } GBool TextFontInfo::matches(GfxState *state) { - Ref *id; + Ref id; - if (!state->getFont()) { - return gFalse; + if (state->getFont()) { + id = *state->getFont()->getID(); + } else { + id.num = -1; + id.gen = -1; } - id = state->getFont()->getID(); - return id->num == fontID.num && id->gen == fontID.gen; + return id.num == fontID.num && id.gen == fontID.gen; } //------------------------------------------------------------------------ @@ -618,11 +696,12 @@ GBool TextFontInfo::matches(GfxState *state) { // Build a TextWord object, using chars[start .. start+len-1]. // (If rot >= 2, the chars list is in reverse order.) TextWord::TextWord(GList *chars, int start, int lenA, - int rotA, int dirA, GBool spaceAfterA) { + int rotA, GBool rotatedA, int dirA, GBool spaceAfterA) { TextChar *ch; int i; - rot = rotA; + rot = (char)rotA; + rotated = (char)rotatedA; len = lenA; text = (Unicode *)gmallocn(len, sizeof(Unicode)); edge = (double *)gmallocn(len + 1, sizeof(double)); @@ -680,8 +759,8 @@ TextWord::TextWord(GList *chars, int start, int lenA, ch = (TextChar *)chars->get(start); font = ch->font; fontSize = ch->fontSize; - dir = dirA; - spaceAfter = spaceAfterA; + dir = (char)dirA; + spaceAfter = (char)spaceAfterA; underlined = gFalse; link = NULL; colorR = ch->colorR; @@ -1057,6 +1136,7 @@ TextPage::TextPage(TextOutputControl *controlA) { curFontSize = 0; curRot = 0; diagonal = gFalse; + rotated = gFalse; nTinyChars = 0; actualText = NULL; actualTextLen = 0; @@ -1108,6 +1188,7 @@ void TextPage::clear() { curFontSize = 0; curRot = 0; diagonal = gFalse; + rotated = gFalse; nTinyChars = 0; gfree(actualText); actualText = NULL; @@ -1219,7 +1300,12 @@ void TextPage::updateFont(GfxState *state) { m[2] = m2[2]; m[3] = m2[3]; } - if (fabs(m[0]) >= fabs(m[1])) { + if (curFontSize == 0) { + // special case - if the font size is zero, just assume plain + // horizontal text + curRot = 0; + diagonal = gFalse; + } else if (fabs(m[0]) >= fabs(m[1])) { if (m[0] > 0) { curRot = 0; } else { @@ -1234,6 +1320,9 @@ void TextPage::updateFont(GfxState *state) { } diagonal = fabs(m[0]) > diagonalThreshold * fabs(m[1]); } + // this matches the 'horiz' test in SplashOutputDev::drawChar() + rotated = !(m[0] > 0 && fabs(m[1]) < 0.001 && + fabs(m[2]) < 0.001 && m[3] < 0); } void TextPage::addChar(GfxState *state, double x, double y, @@ -1260,8 +1349,9 @@ void TextPage::addChar(GfxState *state, double x, double y, return; } - // throw away diagonal chars - if (control.discardDiagonalText && diagonal) { + // throw away diagonal/rotated chars + if ((control.discardDiagonalText && diagonal) || + (control.discardRotatedText && rotated)) { charPos += nBytes; return; } @@ -1299,9 +1389,15 @@ void TextPage::addChar(GfxState *state, double x, double y, } // skip space, tab, and non-breaking space characters - if (uLen == 1 && (u[0] == (Unicode)0x20 || - u[0] == (Unicode)0x09 || - u[0] == (Unicode)0xa0)) { + // (ActualText spans can result in multiple space chars) + for (i = 0; i < uLen; ++i) { + if (u[i] != (Unicode)0x20 && + u[i] != (Unicode)0x09 && + u[i] != (Unicode)0xa0) { + break; + } + } + if (i == uLen && uLen >= 1) { charPos += nBytes; if (chars->getLength() > 0) { ((TextChar *)chars->get(chars->getLength() - 1))->spaceAfter = @@ -1400,7 +1496,7 @@ void TextPage::addChar(GfxState *state, double x, double y, } chars->append(new TextChar(uBuf[j], charPos, nBytes, xMin, yMin, xMax, yMax, - curRot, clipped, + curRot, rotated, clipped, state->getRender() == 3 || alpha < 0.001, curFont, curFontSize, colToDbl(rgb.r), colToDbl(rgb.g), @@ -1503,6 +1599,10 @@ void TextPage::write(void *outputStream, TextOutputFunc outputFunc) { writeSimpleLayout(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); break; + case textOutSimple2Layout: + writeSimple2Layout(outputStream, outputFunc, uMap, space, spaceLen, + eol, eolLen); + break; case textOutLinePrinter: writeLinePrinter(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); @@ -1530,11 +1630,20 @@ void TextPage::writeReadingOrder(void *outputStream, TextColumn *col; TextParagraph *par; TextLine *line; + GList *overlappingChars; GList *columns; GBool primaryLR; GString *s; int colIdx, parIdx, lineIdx, rot, n; +#if 0 //~debug + dumpChars(chars); +#endif + if (control.overlapHandling != textOutIgnoreOverlaps) { + overlappingChars = separateOverlappingText(chars); + } else { + overlappingChars = NULL; + } rot = rotateChars(chars); primaryLR = checkPrimaryLR(chars); tree = splitChars(chars); @@ -1553,6 +1662,12 @@ void TextPage::writeReadingOrder(void *outputStream, rotateUnderlinesAndLinks(rot); generateUnderlinesAndLinks(columns); } + if (overlappingChars) { + if (overlappingChars->getLength() > 0) { + columns->append(buildOverlappingTextColumn(overlappingChars)); + } + deleteGList(overlappingChars, TextChar); + } #if 0 //~debug dumpColumns(columns); #endif @@ -1585,23 +1700,43 @@ void TextPage::writeReadingOrder(void *outputStream, GList *TextPage::makeColumns() { TextBlock *tree; + GList *overlappingChars; GList *columns; GBool primaryLR; int rot; - rot = rotateChars(chars); - primaryLR = checkPrimaryLR(chars); - if ((tree = splitChars(chars))) { - columns = buildColumns(tree, primaryLR); - delete tree; + if (control.mode == textOutSimple2Layout) { + primaryLR = checkPrimaryLR(chars); + rotateCharsToZero(chars); + columns = buildSimple2Columns(chars); + unrotateCharsFromZero(chars); + unrotateColumnsFromZero(columns); } else { - // no text - columns = new GList(); - } - unrotateChars(chars, rot); - unrotateColumns(columns, rot); - if (control.html) { - generateUnderlinesAndLinks(columns); + if (control.overlapHandling != textOutIgnoreOverlaps) { + overlappingChars = separateOverlappingText(chars); + } else { + overlappingChars = NULL; + } + rot = rotateChars(chars); + primaryLR = checkPrimaryLR(chars); + if ((tree = splitChars(chars))) { + columns = buildColumns(tree, primaryLR); + delete tree; + } else { + // no text + columns = new GList(); + } + unrotateChars(chars, rot); + unrotateColumns(columns, rot); + if (control.html) { + generateUnderlinesAndLinks(columns); + } + if (overlappingChars) { + if (overlappingChars->getLength() > 0) { + columns->append(buildOverlappingTextColumn(overlappingChars)); + } + deleteGList(overlappingChars, TextChar); + } } return columns; } @@ -1618,6 +1753,7 @@ void TextPage::writePhysLayout(void *outputStream, TextColumn *col; TextParagraph *par; TextLine *line; + GList *overlappingChars; GList *columns; GBool primaryLR; int ph, colIdx, parIdx, lineIdx, rot, y, i; @@ -1628,6 +1764,11 @@ void TextPage::writePhysLayout(void *outputStream, #if 0 //~debug dumpUnderlines(); #endif + if (control.overlapHandling != textOutIgnoreOverlaps) { + overlappingChars = separateOverlappingText(chars); + } else { + overlappingChars = NULL; + } rot = rotateChars(chars); primaryLR = checkPrimaryLR(chars); tree = splitChars(chars); @@ -1700,6 +1841,29 @@ void TextPage::writePhysLayout(void *outputStream, gfree(outLen); deleteGList(columns, TextColumn); + + if (overlappingChars) { + if (overlappingChars->getLength() > 0) { + TextColumn *col = buildOverlappingTextColumn(overlappingChars); + (*outputFunc)(outputStream, eol, eolLen); + for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { + par = (TextParagraph *)col->paragraphs->get(parIdx); + for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { + line = (TextLine *)par->lines->get(lineIdx); + GString *s = new GString(); + encodeFragment(line->text, line->len, uMap, primaryLR, s); + s->append(eol, eolLen); + (*outputFunc)(outputStream, s->getCString(), s->getLength()); + delete s; + } + if (parIdx < col->paragraphs->getLength() - 1) { + (*outputFunc)(outputStream, eol, eolLen); + } + } + delete col; + } + deleteGList(overlappingChars, TextChar); + } } void TextPage::writeSimpleLayout(void *outputStream, @@ -1762,6 +1926,46 @@ void TextPage::writeSimpleLayout(void *outputStream, deleteGList(superLines, TextSuperLine); } +void TextPage::writeSimple2Layout(void *outputStream, + TextOutputFunc outputFunc, + UnicodeMap *uMap, + char *space, int spaceLen, + char *eol, int eolLen) { + GList *columns; + TextColumn *col; + TextParagraph *par; + TextLine *line; + GString *out; + GBool primaryLR; + int colIdx, parIdx, lineIdx; + + primaryLR = checkPrimaryLR(chars); + rotateCharsToZero(chars); +#if 0 //~debug + dumpChars(chars); +#endif + columns = buildSimple2Columns(chars); + unrotateCharsFromZero(chars); + unrotateColumnsFromZero(columns); + + for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { + col = (TextColumn *)columns->get(colIdx); + for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { + par = (TextParagraph *)col->paragraphs->get(parIdx); + for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { + line = (TextLine *)par->lines->get(lineIdx); + out = new GString(); + encodeFragment(line->text, line->len, uMap, primaryLR, out); + (*outputFunc)(outputStream, out->getCString(), out->getLength()); + delete out; + (*outputFunc)(outputStream, eol, eolLen); + } + } + } + + deleteGList(columns, TextColumn); +} + void TextPage::writeLinePrinter(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, @@ -1778,7 +1982,8 @@ void TextPage::writeLinePrinter(void *outputStream, rot = rotateChars(chars); chars->sort(&TextChar::cmpX); - removeDuplicates(chars, 0); + // don't call removeDuplicates here, because it expects to be + // working on a secondary list that doesn't own the TextChar objects chars->sort(&TextChar::cmpY); // get character pitch @@ -1796,7 +2001,7 @@ void TextPage::writeLinePrinter(void *outputStream, ch->yMin + ascentAdjustFactor * (ch->yMax - ch->yMin) < ch2->yMax - descentAdjustFactor * (ch2->yMax - ch2->yMin)) { delta = fabs(ch2->xMin - ch->xMin); - if (delta > 0 && delta < pitch) { + if (delta > 0.01 && delta < pitch) { pitch = delta; } } @@ -2135,6 +2340,53 @@ int TextPage::rotateChars(GList *charsA) { return rot; } +// Rotate all chars to zero rotation. This leaves the TextChar.rot +// fields unchanged. +void TextPage::rotateCharsToZero(GList *charsA) { + TextChar *ch; + double xMin, yMin, xMax, yMax; + int i; + + for (i = 0; i < charsA->getLength(); ++i) { + ch = (TextChar *)charsA->get(i); + switch (ch->rot) { + case 0: + default: + break; + case 1: + xMin = ch->yMin; + xMax = ch->yMax; + yMin = pageWidth - ch->xMax; + yMax = pageWidth - ch->xMin; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + case 2: + xMin = pageWidth - ch->xMax; + xMax = pageWidth - ch->xMin; + yMin = pageHeight - ch->yMax; + yMax = pageHeight - ch->yMin; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + case 3: + xMin = pageHeight - ch->yMax; + xMax = pageHeight - ch->yMin; + yMin = ch->xMin; + yMax = ch->xMax; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + } + } +} + // Rotate the TextUnderlines and TextLinks to match the transform // performed by rotateChars(). void TextPage::rotateUnderlinesAndLinks(int rot) { @@ -2286,6 +2538,224 @@ void TextPage::unrotateChars(GList *charsA, int rot) { } } +// Undo the coordinate transform performed by rotateCharsToZero(). +void TextPage::unrotateCharsFromZero(GList *charsA) { + TextChar *ch; + double xMin, yMin, xMax, yMax; + int i; + + for (i = 0; i < charsA->getLength(); ++i) { + ch = (TextChar *)charsA->get(i); + switch (ch->rot) { + case 0: + default: + break; + case 1: + xMin = pageWidth - ch->yMax; + xMax = pageWidth - ch->yMin; + yMin = ch->xMin; + yMax = ch->xMax; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + case 2: + xMin = pageWidth - ch->xMax; + xMax = pageWidth - ch->xMin; + yMin = pageHeight - ch->yMax; + yMax = pageHeight - ch->yMin; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + case 3: + xMin = ch->yMin; + xMax = ch->yMax; + yMin = pageHeight - ch->xMax; + yMax = pageHeight - ch->xMin; + ch->xMin = xMin; + ch->xMax = xMax; + ch->yMin = yMin; + ch->yMax = yMax; + break; + } + } +} + +// Undo the coordinate transform performed by rotateCharsToZero(). +void TextPage::unrotateColumnsFromZero(GList *columns) { + TextColumn *col; + TextParagraph *par; + TextLine *line; + TextWord *word; + double xMin, yMin, xMax, yMax; + int colIdx, parIdx, lineIdx, wordIdx, i; + + for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { + col = (TextColumn *)columns->get(colIdx); + switch (col->getRotation()) { + case 0: + default: + break; + case 1: + xMin = pageWidth - col->yMax; + xMax = pageWidth - col->yMin; + yMin = col->xMin; + yMax = col->xMax; + col->xMin = xMin; + col->xMax = xMax; + col->yMin = yMin; + col->yMax = yMax; + for (parIdx = 0; + parIdx < col->paragraphs->getLength(); + ++parIdx) { + par = (TextParagraph *)col->paragraphs->get(parIdx); + xMin = pageWidth - par->yMax; + xMax = pageWidth - par->yMin; + yMin = par->xMin; + yMax = par->xMax; + par->xMin = xMin; + par->xMax = xMax; + par->yMin = yMin; + par->yMax = yMax; + for (lineIdx = 0; + lineIdx < par->lines->getLength(); + ++lineIdx) { + line = (TextLine *)par->lines->get(lineIdx); + xMin = pageWidth - line->yMax; + xMax = pageWidth - line->yMin; + yMin = line->xMin; + yMax = line->xMax; + line->xMin = xMin; + line->xMax = xMax; + line->yMin = yMin; + line->yMax = yMax; + for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { + word = (TextWord *)line->words->get(wordIdx); + xMin = pageWidth - word->yMax; + xMax = pageWidth - word->yMin; + yMin = word->xMin; + yMax = word->xMax; + word->xMin = xMin; + word->xMax = xMax; + word->yMin = yMin; + word->yMax = yMax; + } + } + } + break; + case 2: + xMin = pageWidth - col->xMax; + xMax = pageWidth - col->xMin; + yMin = pageHeight - col->yMax; + yMax = pageHeight - col->yMin; + col->xMin = xMin; + col->xMax = xMax; + col->yMin = yMin; + col->yMax = yMax; + for (parIdx = 0; + parIdx < col->paragraphs->getLength(); + ++parIdx) { + par = (TextParagraph *)col->paragraphs->get(parIdx); + xMin = pageWidth - par->xMax; + xMax = pageWidth - par->xMin; + yMin = pageHeight - par->yMax; + yMax = pageHeight - par->yMin; + par->xMin = xMin; + par->xMax = xMax; + par->yMin = yMin; + par->yMax = yMax; + for (lineIdx = 0; + lineIdx < par->lines->getLength(); + ++lineIdx) { + line = (TextLine *)par->lines->get(lineIdx); + xMin = pageWidth - line->xMax; + xMax = pageWidth - line->xMin; + yMin = pageHeight - line->yMax; + yMax = pageHeight - line->yMin; + line->xMin = xMin; + line->xMax = xMax; + line->yMin = yMin; + line->yMax = yMax; + for (i = 0; i <= line->len; ++i) { + line->edge[i] = pageWidth - line->edge[i]; + } + for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { + word = (TextWord *)line->words->get(wordIdx); + xMin = pageWidth - word->xMax; + xMax = pageWidth - word->xMin; + yMin = pageHeight - word->yMax; + yMax = pageHeight - word->yMin; + word->xMin = xMin; + word->xMax = xMax; + word->yMin = yMin; + word->yMax = yMax; + for (i = 0; i <= word->len; ++i) { + word->edge[i] = pageWidth - word->edge[i]; + } + } + } + } + break; + case 3: + xMin = col->yMin; + xMax = col->yMax; + yMin = pageHeight - col->xMax; + yMax = pageHeight - col->xMin; + col->xMin = xMin; + col->xMax = xMax; + col->yMin = yMin; + col->yMax = yMax; + for (parIdx = 0; + parIdx < col->paragraphs->getLength(); + ++parIdx) { + par = (TextParagraph *)col->paragraphs->get(parIdx); + xMin = par->yMin; + xMax = par->yMax; + yMin = pageHeight - par->xMax; + yMax = pageHeight - par->xMin; + par->xMin = xMin; + par->xMax = xMax; + par->yMin = yMin; + par->yMax = yMax; + for (lineIdx = 0; + lineIdx < par->lines->getLength(); + ++lineIdx) { + line = (TextLine *)par->lines->get(lineIdx); + xMin = line->yMin; + xMax = line->yMax; + yMin = pageHeight - line->xMax; + yMax = pageHeight - line->xMin; + line->xMin = xMin; + line->xMax = xMax; + line->yMin = yMin; + line->yMax = yMax; + for (i = 0; i <= line->len; ++i) { + line->edge[i] = pageHeight - line->edge[i]; + } + for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { + word = (TextWord *)line->words->get(wordIdx); + xMin = word->yMin; + xMax = word->yMax; + yMin = pageHeight - word->xMax; + yMax = pageHeight - word->xMin; + word->xMin = xMin; + word->xMax = xMax; + word->yMin = yMin; + word->yMax = yMax; + for (i = 0; i <= word->len; ++i) { + word->edge[i] = pageHeight - word->edge[i]; + } + } + } + } + break; + } + } +} + // Undo the coordinate transform performed by rotateChars(). void TextPage::unrotateColumns(GList *columns, int rot) { TextColumn *col; @@ -2601,7 +3071,8 @@ void TextPage::removeDuplicates(GList *charsA, int rot) { int i, j; if (rot & 1) { - for (i = 0; i < charsA->getLength(); ++i) { + i = 0; + while (i < charsA->getLength()) { ch = (TextChar *)charsA->get(i); xDelta = dupMaxSecDelta * ch->fontSize; yDelta = dupMaxPriDelta * ch->fontSize; @@ -2615,6 +3086,11 @@ void TextPage::removeDuplicates(GList *charsA, int rot) { fabs(ch2->xMin - ch->xMin) < xDelta && fabs(ch2->xMax - ch->xMax) < xDelta && fabs(ch2->yMax - ch->yMax) < yDelta) { + if (ch->invisible && !ch2->invisible) { + charsA->del(i); + --i; + break; + } if (ch2->spaceAfter) { ch->spaceAfter = (char)gTrue; } @@ -2623,9 +3099,11 @@ void TextPage::removeDuplicates(GList *charsA, int rot) { ++j; } } + ++i; } } else { - for (i = 0; i < charsA->getLength(); ++i) { + i = 0; + while (i < charsA->getLength()) { ch = (TextChar *)charsA->get(i); xDelta = dupMaxPriDelta * ch->fontSize; yDelta = dupMaxSecDelta * ch->fontSize; @@ -2639,15 +3117,257 @@ void TextPage::removeDuplicates(GList *charsA, int rot) { fabs(ch2->xMax - ch->xMax) < xDelta && fabs(ch2->yMin - ch->yMin) < yDelta && fabs(ch2->yMax - ch->yMax) < yDelta) { + if (ch->invisible && !ch2->invisible) { + charsA->del(i); + --i; + break; + } + if (ch2->spaceAfter) { + ch->spaceAfter = (char)gTrue; + } charsA->del(j); } else { ++j; } } + ++i; } } } +struct TextCharNode { + TextCharNode(TextChar *chA, TextCharNode *nextA): ch(chA), next(nextA) {} + TextChar *ch; + TextCharNode *next; +}; + +// Separate out any overlapping text. If handling is +// textOutAppendOverlaps, return a list of the overlapping chars; else +// delete them and return NULL. +GList *TextPage::separateOverlappingText(GList *charsA) { + // bin-sort the TextChars + TextCharNode *grid[overlapGridHeight][overlapGridWidth]; + for (int y = 0; y < overlapGridHeight; ++y) { + for (int x = 0; x < overlapGridWidth; ++x) { + grid[y][x] = NULL; + } + } + for (int i = 0; i < charsA->getLength(); ++i) { + TextChar *ch = (TextChar *)charsA->get(i); + int y0 = (int)floor(overlapGridHeight * ch->yMin / pageHeight); + int y1 = (int)ceil(overlapGridHeight * ch->yMax / pageHeight); + int x0 = (int)floor(overlapGridWidth * ch->xMin / pageWidth); + int x1 = (int)ceil(overlapGridWidth * ch->yMin / pageWidth); + if (y0 < 0) { + y0 = 0; + } + if (y1 >= overlapGridHeight) { + y1 = overlapGridHeight - 1; + } + if (x0 < 0) { + x0 = 0; + } + if (x1 >= overlapGridWidth) { + x1 = overlapGridWidth - 1; + } + for (int y = y0; y <= y1; ++y) { + for (int x = x0; x <= x1; ++x) { + grid[y][x] = new TextCharNode(ch, grid[y][x]); + } + } + } + + // look for overlaps in each cell + GBool foundOverlaps = gFalse; + for (int y = 0; y < overlapGridHeight; ++y) { + for (int x = 0; x < overlapGridWidth; ++x) { + for (TextCharNode *p0 = grid[y][x]; p0; p0 = p0->next) { + for (TextCharNode *p1 = p0->next; p1; p1 = p1->next) { + if (p0->ch->colorR != p1->ch->colorR || + p0->ch->colorG != p1->ch->colorG || + p0->ch->colorB != p1->ch->colorB) { + double ovx = (dmin(p0->ch->xMax, p1->ch->xMax) + - dmax(p0->ch->xMin, p1->ch->xMin)) + / dmin(p0->ch->xMax - p0->ch->xMin, + p1->ch->xMax - p1->ch->xMin); + double ovy = (dmin(p0->ch->yMax, p1->ch->yMax) + - dmax(p0->ch->yMin, p1->ch->yMin)) + / dmin(p0->ch->yMax - p0->ch->yMin, + p1->ch->yMax - p1->ch->yMin); + if (ovx > minCharOverlap && ovy > minCharOverlap) { + // assume the lighter colored text is extraneous + if (p0->ch->colorR + p0->ch->colorG + p0->ch->colorB + < p1->ch->colorR + p1->ch->colorG + p1->ch->colorB) { + p1->ch->overlap = gTrue; + } else { + p0->ch->overlap = gTrue; + } + foundOverlaps = gTrue; + } + } + } + } + } + } + + // find overlapped strings + GList *overlapChars = NULL; + if (control.overlapHandling == textOutAppendOverlaps) { + overlapChars = new GList(); + } + if (foundOverlaps) { + charsA->sort(&TextChar::cmpCharPos); + int i = 0; + while (i < charsA->getLength()) { + TextChar *ch0 = (TextChar *)charsA->get(i); + if (ch0->overlap) { + int j0, j1; + for (j0 = i - 1; j0 >= 0; --j0) { + TextChar *ch1 = (TextChar *)charsA->get(j0); + if (ch1->colorR != ch0->colorR || + ch1->colorG != ch0->colorG || + ch1->colorB != ch0->colorB || + ch1->rot != ch0->rot) { + break; + } + } + ++j0; + for (j1 = i + 1; j1 < charsA->getLength(); ++j1) { + TextChar *ch1 = (TextChar *)charsA->get(j1); + if (ch1->colorR != ch0->colorR || + ch1->colorG != ch0->colorG || + ch1->colorB != ch0->colorB || + ch1->rot != ch0->rot) { + break; + } + } + --j1; + for (int j = j0; j <= j1; ++j) { + if (overlapChars) { + overlapChars->append(charsA->get(j0)); + } else { + delete (TextChar *)charsA->get(j0); + } + charsA->del(j0); + } + i = j0; + } else { + ++i; + } + } + } + + // free memory + for (int y = 0; y < overlapGridHeight; ++y) { + for (int x = 0; x < overlapGridWidth; ++x) { + TextCharNode *p0 = grid[y][x]; + while (p0) { + TextCharNode *p1 = p0->next; + delete p0; + p0 = p1; + } + } + } + + return overlapChars; +} + +// Construct a TextColumn from the list of separated overlapping +// chars. +TextColumn *TextPage::buildOverlappingTextColumn(GList *overlappingChars) { + GList *pars = new GList(); + GList *lines = new GList(); + GList *words = new GList(); + int wordStart = 0; + double lineXMin = 0, lineYMin = 0, lineXMax = 0, lineYMax = 0; + double colXMin = 0, colYMin = 0, colXMax = 0, colYMax = 0; + for (int i = 0; i < overlappingChars->getLength(); ++i) { + TextChar *ch = (TextChar *)overlappingChars->get(i); + TextChar *chNext = NULL; + if (i + 1 < overlappingChars->getLength()) { + chNext = (TextChar *)overlappingChars->get(i + 1); + } + double sp = 0; + double dy = 0; + if (chNext) { + switch (ch->rot) { + case 0: + default: + sp = chNext->xMin - ch->xMax; + dy = chNext->yMin - ch->yMin; + break; + case 1: + sp = chNext->yMin - ch->yMax; + dy = chNext->xMax - ch->xMax; + break; + case 2: + sp = ch->xMin - chNext->xMax; + dy = ch->yMax - chNext->yMax; + break; + case 3: + sp = ch->yMin - chNext->yMax; + dy = ch->xMin - chNext->xMin; + break; + } + } + // the +1 here allows for a space character after ch + GBool parBreak = !chNext || + chNext->rot != ch->rot || + chNext->charPos > ch->charPos + ch->charLen + 1; + GBool lineBreak = parBreak || + sp < -rawModeCharOverlap * ch->fontSize || + fabs(dy) > rawModeLineDelta * ch->fontSize; + GBool wordBreak = lineBreak || + ch->spaceAfter || + sp > rawModeWordSpacing * ch->fontSize; + if (!wordBreak) { + continue; + } + TextWord *word = new TextWord(overlappingChars, wordStart, + i - wordStart + 1, ch->rot, ch->rotated, + getCharDirection(ch), !lineBreak); + words->append(word); + if (words->getLength() == 0) { + lineXMin = word->xMin; + lineYMin = word->yMin; + lineXMax = word->xMax; + lineYMax = word->yMax; + } else { + lineXMin = dmin(lineXMin, word->xMin); + lineYMin = dmin(lineYMin, word->yMin); + lineXMax = dmax(lineXMax, word->xMax); + lineYMax = dmax(lineYMax, word->yMax); + } + wordStart = i + 1; + if (!lineBreak) { + continue; + } + lines->append(new TextLine(words, lineXMin, lineYMin, lineXMax, lineYMax, + ((TextWord *)words->get(0))->fontSize)); + words = new GList(); + if (!parBreak) { + continue; + } + TextParagraph *par = new TextParagraph(lines, gFalse); + pars->append(par); + if (pars->getLength() == 0) { + colXMin = par->xMin; + colYMin = par->yMin; + colXMax = par->xMax; + colYMax = par->yMax; + } else { + colXMin = dmin(colXMin, par->xMin); + colYMin = dmin(colYMin, par->yMin); + colXMax = dmax(colXMax, par->xMax); + colYMax = dmax(colYMax, par->yMax); + } + lines = new GList(); + } + delete words; + delete lines; + return new TextColumn(pars, colXMin, colYMin, colXMax, colYMax); +} + // Split the characters into trees of TextBlocks, one tree for each // rotation. Merge into a single tree (with the primary rotation). TextBlock *TextPage::splitChars(GList *charsA) { @@ -2860,12 +3580,15 @@ TextBlock *TextPage::split(GList *charsA, int rot) { // largeCharSize could otherwise have slightly different values // here and where it's used below to do the large char partition // (because it gets truncated from 80 to 64 bits when spilled) - largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0; nLargeChars = 0; - for (i = 0; i < charsA->getLength(); ++i) { - ch = (TextChar *)charsA->get(i); - if (ch->fontSize > largeCharSize) { - ++nLargeChars; + largeCharSize = 0; + if (control.separateLargeChars) { + largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0; + for (i = 0; i < charsA->getLength(); ++i) { + ch = (TextChar *)charsA->get(i); + if (ch->fontSize > largeCharSize) { + ++nLargeChars; + } } } @@ -3092,7 +3815,7 @@ void TextPage::findGaps(GList *charsA, int rot, 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) { + yMax * invSplitPrecision > 0.5 * INT_MAX) { return; } // add some slack to the array bounds to avoid floating point @@ -3543,7 +4266,7 @@ TextColumn *TextPage::buildColumn(TextBlock *blk) { int i; lines = new GList(); - buildLines(blk, lines); + buildLines(blk, lines, gFalse); spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines); @@ -3712,43 +4435,231 @@ double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) { return sp; } -void TextPage::buildLines(TextBlock *blk, GList *lines) { +void TextPage::buildLines(TextBlock *blk, GList *lines, + GBool splitSuperLines) { TextLine *line; int i; - switch (blk->tag) { - case blkTagLine: + if (blk->tag == blkTagLine || + (blk->tag == blkTagSuperLine && !splitSuperLines)) { line = buildLine(blk); if (blk->rot == 1 || blk->rot == 2) { lines->insert(0, line); } else { lines->append(line); } - break; - case blkTagSuperLine: - case blkTagColumn: - case blkTagMulticolumn: // multicolumn should never happen here + } else { for (i = 0; i < blk->children->getLength(); ++i) { - buildLines((TextBlock *)blk->children->get(i), lines); + buildLines((TextBlock *)blk->children->get(i), lines, splitSuperLines); } - break; } } +GList *TextPage::buildSimple2Columns(GList *charsA) { + GList *columns, *paragraphs, *lines; + TextParagraph *paragraph; + int rot; + + charsA->sort(&TextChar::cmpX); + columns = new GList(); + for (rot = 0; rot < 4; ++rot) { + lines = buildSimple2Lines(charsA, rot); + if (lines->getLength() == 0) { + delete lines; + continue; + } + paragraph = new TextParagraph(lines, gFalse); + paragraphs = new GList(); + paragraphs->append(paragraph); + columns->append(new TextColumn(paragraphs, + paragraph->xMin, paragraph->yMin, + paragraph->xMax, paragraph->yMax)); + } + return columns; +} + +GList *TextPage::buildSimple2Lines(GList *charsA, int rot) { + GList *openCharLines, *lines; + TextCharLine *firstCharLine, *lastCharLine, *charLine, *p; + TextChar *ch; + TextLine *line; + double bestOverlap, overlap, xMin, yMin, xMax, yMax; + int bestLine, i, j, k, m; + + firstCharLine = lastCharLine = NULL; + openCharLines = new GList(); + for (i = 0; i < charsA->getLength(); ++i) { + ch = (TextChar *)charsA->get(i); + if (ch->rot != rot) { + continue; + } + + // find the first open line with line.yMax > ch.yMin + j = -1; + k = openCharLines->getLength(); + while (j < k - 1) { + // invariants: openLines[j].yMax <= ch.yMin (or j = -1) + // openLines[k].yMax > ch.yMin (or k = nOpenLines) + // j < k - 1 + m = j + (k - j) / 2; + charLine = (TextCharLine *)openCharLines->get(m); + if (charLine->yMax <= ch->yMin) { + j = m; + } else { + k = m; + } + } + + // check overlap for all overlapping lines + // i.e., all lines with line.yMin < ch.yMax and line.yMax > ch.yMin + bestLine = -1; + bestOverlap = 0; + for (; k < openCharLines->getLength(); ++k) { + charLine = (TextCharLine *)openCharLines->get(k); + if (charLine->yMin >= ch->yMax) { + break; + } + overlap = ((ch->yMax < charLine->yMax ? ch->yMax : charLine->yMax) + - (ch->yMin > charLine->yMin ? ch->yMin : charLine->yMin)) + / (ch->yMax - ch->yMin); + if (overlap > bestOverlap) { + bestLine = k; + bestOverlap = overlap; + } + } + + // found an overlapping line + if (bestLine >= 0 && bestOverlap > simple2MinOverlap) { + k = bestLine; + charLine = (TextCharLine *)openCharLines->get(k); + + // else insert a new line immediately before line k + } else { + charLine = new TextCharLine(ch->rot); + if (k < openCharLines->getLength()) { + p = (TextCharLine *)openCharLines->get(k); + if (p->prev) { + p->prev->next = charLine; + charLine->prev = p->prev; + } else { + firstCharLine = charLine; + } + p->prev = charLine; + charLine->next = p; + } else { + if (lastCharLine) { + lastCharLine->next = charLine; + charLine->prev = lastCharLine; + } else { + firstCharLine = charLine; + } + lastCharLine = charLine; + } + openCharLines->insert(k, charLine); + } + + // add the char to the line + charLine->add(ch); + charLine->yMin = ch->yMin; + charLine->yMax = ch->yMax; + + // update open lines before k + j = k - 1; + while (j >= 0) { + charLine = (TextCharLine *)openCharLines->get(j); + if (charLine->yMax <= ch->yMin) { + break; + } + charLine->yMax = ch->yMin; + if (charLine->yMin < charLine->yMax) { + break; + } + openCharLines->del(j); + --j; + } + + // update open lines after k + j = k + 1; + while (j < openCharLines->getLength()) { + charLine = (TextCharLine *)openCharLines->get(j); + if (charLine->yMin >= ch->yMax) { + break; + } + charLine->yMin = ch->yMax; + if (charLine->yMin < charLine->yMax) { + break; + } + openCharLines->del(j); + } + } + + // build TextLine objects + lines = new GList(); + for (charLine = firstCharLine; charLine; charLine = p) { + xMin = yMin = xMax = yMax = 0; + for (j = 0; j < charLine->chars->getLength(); ++j) { + ch = (TextChar *)charLine->chars->get(j); + if (j == 0) { + xMin = ch->xMin; + yMin = ch->yMin; + xMax = ch->xMax; + yMax = ch->yMax; + } else { + if (ch->xMin < xMin) { + xMin = ch->xMin; + } + if (ch->yMin < yMin) { + yMin = ch->yMin; + } + if (ch->xMax < xMax) { + xMax = ch->xMax; + } + if (ch->yMax < yMax) { + yMax = ch->yMax; + } + } + } + // the chars have been rotated to 0, without changing the + // TextChar.rot values, so we need to tell buildLine to use rot=0, + // and then set the word and line rotation correctly afterward + line = buildLine(charLine->chars, 0, xMin, yMin, xMax, yMax); + line->rot = charLine->rot; + for (i = 0; i < line->words->getLength(); ++i) { + ((TextWord *)line->words->get(i))->rot = (char)charLine->rot; + } + lines->append(line); + p = charLine->next; + delete charLine; + } + + delete openCharLines; + + return lines; +} + TextLine *TextPage::buildLine(TextBlock *blk) { GList *charsA; + + charsA = new GList(); + getLineChars(blk, charsA); + TextLine *line = buildLine(charsA, blk->rot, + blk->xMin, blk->yMin, blk->xMax, blk->yMax); + delete charsA; + return line; +} + +TextLine *TextPage::buildLine(GList *charsA, int rot, + double xMin, double yMin, + double xMax, double yMax) { GList *words; TextChar *ch, *ch2; TextWord *word; double wordSp, lineFontSize, sp; int dir, dir2; - GBool spaceAfter, spaceBefore; + GBool rotated, spaceAfter, spaceBefore; int i, j; - charsA = new GList(); - getLineChars(blk, charsA); - - wordSp = computeWordSpacingThreshold(charsA, blk->rot); + wordSp = computeWordSpacingThreshold(charsA, rot); words = new GList(); lineFontSize = 0; @@ -3758,10 +4669,11 @@ TextLine *TextPage::buildLine(TextBlock *blk) { sp = wordSp - 1; spaceAfter = gFalse; dir = getCharDirection((TextChar *)charsA->get(i)); + rotated = ((TextChar *)charsA->get(i))->rotated; for (j = i+1; j < charsA->getLength(); ++j) { ch = (TextChar *)charsA->get(j-1); ch2 = (TextChar *)charsA->get(j); - sp = (blk->rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax); + sp = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax); if (sp > wordSp) { spaceAfter = gTrue; break; @@ -3775,6 +4687,7 @@ TextLine *TextPage::buildLine(TextBlock *blk) { dir2 = getCharDirection(ch2); if (ch->font != ch2->font || fabs(ch->fontSize - ch2->fontSize) > 0.01 || + (control.splitRotatedWords && ch2->rotated != rotated) || (dir && dir2 && dir2 != dir) || (control.mode == textOutRawOrder && ch2->charPos != ch->charPos + ch->charLen)) { @@ -3785,11 +4698,11 @@ TextLine *TextPage::buildLine(TextBlock *blk) { } sp = wordSp - 1; } - word = new TextWord(charsA, i, j - i, blk->rot, dir, - (blk->rot >= 2) ? spaceBefore : spaceAfter); + word = new TextWord(charsA, i, j - i, rot, rotated, dir, + (rot >= 2) ? spaceBefore : spaceAfter); spaceBefore = spaceAfter; i = j; - if (blk->rot >= 2) { + if (rot >= 2) { words->insert(0, word); } else { words->append(word); @@ -3799,10 +4712,7 @@ TextLine *TextPage::buildLine(TextBlock *blk) { } } - delete charsA; - - return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax, - lineFontSize); + return new TextLine(words, xMin, yMin, xMax, yMax, lineFontSize); } void TextPage::getLineChars(TextBlock *blk, GList *charsA) { @@ -3822,10 +4732,19 @@ void TextPage::getLineChars(TextBlock *blk, GList *charsA) { // spaces. double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { TextChar *ch, *ch2; + double uniformSp, wordSp; double avgFontSize; double minAdjGap, maxAdjGap, minSpGap, maxSpGap, minGap, maxGap, gap, gap2; int i; + if (control.mode == textOutTableLayout) { + uniformSp = tableModeUniformSpacing; + wordSp = tableModeWordSpacing; + } else { + uniformSp = uniformSpacing; + wordSp = wordSpacing; + } + avgFontSize = 0; minGap = maxGap = 0; minAdjGap = minSpGap = 1; @@ -3876,13 +4795,13 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { // (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 (maxGap - minGap < uniformSp * avgFontSize) { if (minSpGap <= maxSpGap) { if (minAdjGap <= maxAdjGap && minSpGap - maxAdjGap > 0.01) { return 0.5 * (maxAdjGap + minSpGap); } else if (minAdjGap > maxAdjGap && - maxSpGap - minSpGap < uniformSpacing * avgFontSize) { + maxSpGap - minSpGap < uniformSp * avgFontSize) { return minSpGap - 1; } } @@ -3890,7 +4809,7 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { // if there is some variation in spacing, but it's small, assume // there are some inter-word spaces - } else if (maxGap - minGap < wordSpacing * avgFontSize) { + } else if (maxGap - minGap < wordSp * avgFontSize) { return 0.5 * (minGap + maxGap); // if there is a large variation in spacing, use the SpGap/AdjGap @@ -3901,12 +4820,12 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { } else { if (minAdjGap <= maxAdjGap && minSpGap <= maxSpGap && - minSpGap - maxAdjGap > uniformSpacing * avgFontSize) { - gap = wordSpacing * avgFontSize; + minSpGap - maxAdjGap > uniformSp * avgFontSize) { + gap = wordSp * avgFontSize; gap2 = 0.5 * (minSpGap - minGap); return minGap + (gap < gap2 ? gap : gap2); } else { - return minGap + wordSpacing * avgFontSize; + return minGap + wordSp * avgFontSize; } } } @@ -4069,15 +4988,9 @@ void TextPage::buildSuperLines(TextBlock *blk, GList *superLines) { GList *lines; int i; - if (blk->tag == blkTagLine) { + if (blk->tag == blkTagLine || blk->tag == blkTagSuperLine) { lines = new GList(); - buildLines(blk, lines); - superLines->append(new TextSuperLine(lines)); - } else if (blk->tag == blkTagSuperLine) { - lines = new GList(); - for (i = 0; i < blk->children->getLength(); ++i) { - buildLines((TextBlock *)blk->children->get(i), lines); - } + buildLines(blk, lines, gTrue); superLines->append(new TextSuperLine(lines)); } else { for (i = 0; i < blk->children->getLength(); ++i) { @@ -4681,6 +5594,59 @@ GBool TextPage::findPointNear(double x, double y, TextPosition *pos) { return gTrue; } +GBool TextPage::findWordPoints(double x, double y, + TextPosition *startPos, TextPosition *endPos) { + TextPosition pos; + TextColumn *col; + TextParagraph *par; + TextLine *line; + int startCharIdx, endCharIdx; + + if (!findPointInside(x, y, &pos)) { + return gFalse; + } + col = (TextColumn *)findCols->get(pos.colIdx); + par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx); + line = (TextLine *)par->getLines()->get(pos.lineIdx); + + for (startCharIdx = pos.charIdx; + startCharIdx > 0 && line->text[startCharIdx - 1] != 0x20; + --startCharIdx) ; + *startPos = pos; + startPos->charIdx = startCharIdx; + + for (endCharIdx = pos.charIdx; + endCharIdx < line->len && line->text[endCharIdx] != 0x20; + ++endCharIdx) ; + *endPos = pos; + endPos->charIdx = endCharIdx; + + return gTrue; +} + +GBool TextPage::findLinePoints(double x, double y, + TextPosition *startPos, TextPosition *endPos) { + TextPosition pos; + TextColumn *col; + TextParagraph *par; + TextLine *line; + + if (!findPointInside(x, y, &pos)) { + return gFalse; + } + col = (TextColumn *)findCols->get(pos.colIdx); + par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx); + line = (TextLine *)par->getLines()->get(pos.lineIdx); + + *startPos = pos; + startPos->charIdx = 0; + + *endPos = pos; + endPos->charIdx = line->len; + + return gTrue; +} + // Find the position in [col] corresponding to [x],[y]. The column, // [col], was found by findPointInside() or findPointNear(). void TextPage::findPointInColumn(TextColumn *col, double x, double y, @@ -4860,33 +5826,56 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) { TextParagraph *par; TextLine *line; TextWord *word; + GList *overlappingChars; GList *words; GBool primaryLR; int rot, colIdx, parIdx, lineIdx, wordIdx; #if 0 //~debug - dumpCharList(charList); + dumpChars(charList); #endif - rot = rotateChars(charList); - primaryLR = checkPrimaryLR(charList); - tree = splitChars(charList); + + if (control.mode == textOutSimple2Layout) { + rot = 0; + primaryLR = checkPrimaryLR(chars); + rotateCharsToZero(chars); + columns = buildSimple2Columns(chars); + unrotateCharsFromZero(chars); + unrotateColumnsFromZero(columns); + + } else { + if (control.overlapHandling != textOutIgnoreOverlaps) { + overlappingChars = separateOverlappingText(chars); + } else { + overlappingChars = NULL; + } + rot = rotateChars(charList); + primaryLR = checkPrimaryLR(charList); + tree = splitChars(charList); #if 0 //~debug - dumpTree(tree); + dumpTree(tree); #endif - if (!tree) { - // no text - unrotateChars(charList, rot); - return new TextWordList(new GList(), gTrue); - } - columns = buildColumns(tree, primaryLR); + if (!tree) { + // no text + unrotateChars(charList, rot); + return new TextWordList(new GList(), gTrue); + } + columns = buildColumns(tree, primaryLR); #if 0 //~debug - dumpColumns(columns, gTrue); + dumpColumns(columns, gTrue); #endif - delete tree; - unrotateChars(charList, rot); - if (control.html) { - rotateUnderlinesAndLinks(rot); - generateUnderlinesAndLinks(columns); + delete tree; + unrotateChars(charList, rot); + if (control.html) { + rotateUnderlinesAndLinks(rot); + generateUnderlinesAndLinks(columns); + } + if (overlappingChars) { + if (overlappingChars->getLength() > 0) { + columns->append(buildOverlappingTextColumn(overlappingChars)); + } + deleteGList(overlappingChars, TextChar); + } } words = new GList(); @@ -4897,8 +5886,12 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) { for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { line = (TextLine *)par->lines->get(lineIdx); for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { - word = (TextWord *)line->words->get(wordIdx); - words->append(word->copy()); + word = ((TextWord *)line->words->get(wordIdx))->copy(); + if (wordIdx == line->words->getLength() - 1 && + !line->getHyphenated()) { + word->spaceAfter = gTrue; + } + words->append(word); } } } @@ -4906,6 +5899,7 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) { switch (control.mode) { case textOutReadingOrder: + case textOutSimple2Layout: // already in reading order break; case textOutPhysLayout: @@ -4958,7 +5952,8 @@ void TextPage::dumpTree(TextBlock *tree, int indent) { tree->type == blkLeaf ? "leaf" : tree->type == blkHorizSplit ? "horiz" : "vert", tree->tag == blkTagMulticolumn ? "multicolumn" : - tree->tag == blkTagColumn ? "column" : "line", + tree->tag == blkTagColumn ? "column" : + tree->tag == blkTagSuperLine ? "superline" : "line", tree->smallSplit, tree->rot, tree->xMin, tree->yMin, tree->xMax, tree->yMax); if (tree->type == blkLeaf) { @@ -5001,18 +5996,18 @@ void TextPage::dumpColumns(GList *columns, GBool dumpWords) { word = (TextWord *)line->words->get(wordIdx); printf(" word: xMin=%g yMin=%g xMax=%g yMax=%g\n", word->xMin, word->yMin, word->xMax, word->yMax); - printf(" "); + printf(" '"); for (i = 0; i < word->len; ++i) { printf("%c", word->text[i] & 0xff); } - printf("\n"); + printf("'\n"); } } else { - printf(" "); + printf(" '"); for (i = 0; i < line->len; ++i) { printf("%c", line->text[i] & 0xff); } - printf("\n"); + printf("'\n"); } } } @@ -5041,7 +6036,7 @@ static void outputToFile(void *stream, const char *text, int len) { } TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA, - GBool append) { + GBool append, GBool fileNameIsUTF8) { text = NULL; control = *controlA; ok = gTrue; @@ -5055,15 +6050,22 @@ TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA, // keep DOS from munging the end-of-line characters setmode(fileno(stdout), O_BINARY); #endif - } else if ((outputStream = fopen(fileName, append ? "ab" : "wb"))) { - needClose = gTrue; } else { - error(errIO, -1, "Couldn't open text file '{0:s}'", fileName); - ok = gFalse; - return; + if (fileNameIsUTF8) { + outputStream = openFile(fileName, append ? "ab" : "wb"); + } else { + outputStream = fopen(fileName, append ? "ab" : "wb"); + } + if (!outputStream) { + error(errIO, -1, "Couldn't open text file '{0:s}'", fileName); + ok = gFalse; + return; + } + needClose = gTrue; } outputFunc = &outputToFile; } else { + outputFunc = NULL; outputStream = NULL; } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h index 0f00cf5ae7b..a1d19c22d7d 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h @@ -42,12 +42,19 @@ enum TextOutputMode { textOutReadingOrder, // format into reading order textOutPhysLayout, // maintain original physical layout textOutSimpleLayout, // simple one-column physical layout + textOutSimple2Layout, // simple one-column physical layout textOutTableLayout, // similar to PhysLayout, but optimized // for tables textOutLinePrinter, // strict fixed-pitch/height layout textOutRawOrder // keep text in content stream order }; +enum TextOutputOverlapHandling { + textOutIgnoreOverlaps, // no special handling for overlaps + textOutAppendOverlaps, // append overlapping text to main text + textOutDiscardOverlaps // discard overlapping text +}; + class TextOutputControl { public: @@ -66,8 +73,17 @@ public: // in after forming columns GBool discardDiagonalText; // discard all text that's not close to // 0/90/180/270 degrees + GBool discardRotatedText; // discard all text that's not horizontal + // (0 degrees) GBool discardInvisibleText; // discard all invisible characters GBool discardClippedText; // discard all clipped characters + GBool splitRotatedWords; // do not combine horizontal and + // non-horizontal chars in a single + // word + TextOutputOverlapHandling // how to handle overlapping text + overlapHandling; + GBool separateLargeChars; // separate "large" characters from + // "regular" characters GBool insertBOM; // insert a Unicode BOM at the start of // the text output double marginLeft, // characters outside the margins are @@ -124,7 +140,7 @@ class TextWord { public: TextWord(GList *chars, int start, int lenA, - int rotA, int dirA, GBool spaceAfterA); + int rotA, GBool rotatedA, int dirA, GBool spaceAfterA); ~TextWord(); TextWord *copy() { return new TextWord(this); } @@ -144,6 +160,7 @@ public: double *xMaxA, double *yMaxA); double getFontSize() { return fontSize; } int getRotation() { return rot; } + GBool isRotated() { return (GBool)rotated; } int getCharPos() { return charPos[0]; } int getCharLen() { return charPos[len] - charPos[0]; } int getDirection() { return dir; } @@ -158,8 +175,6 @@ private: static int cmpYX(const void *p1, const void *p2); static int cmpCharPos(const void *p1, const void *p2); - int rot; // rotation, multiple of 90 degrees - // (0, 1, 2, or 3) double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates Unicode *text; // the text @@ -171,19 +186,22 @@ private: int len; // number of characters TextFontInfo *font; // font information double fontSize; // font size - int dir; // character direction (+1 = left-to-right; - // -1 = right-to-left; 0 = neither) - GBool spaceAfter; // set if there is a space between this - // word and the next word on the line - - GBool underlined; TextLink *link; - double colorR, // word color colorG, colorB; GBool invisible; // set for invisible text (render mode 3) + // group the byte-size fields to minimize object size + Guchar rot; // rotation, multiple of 90 degrees + // (0, 1, 2, or 3) + char rotated; // set if this word is non-horizontal + char dir; // character direction (+1 = left-to-right; + // -1 = right-to-left; 0 = neither) + char spaceAfter; // set if there is a space between this + // word and the next word on the line + char underlined; + friend class TextBlock; friend class TextLine; friend class TextPage; @@ -209,6 +227,7 @@ public: GList *getWords() { return words; } int getLength() { return len; } double getEdge(int idx) { return edge[idx]; } + GBool getHyphenated() { return hyphenated; } private: @@ -407,6 +426,16 @@ public: // are no columns. GBool findPointNear(double x, double y, TextPosition *pos); + // Find the start and end of a word inside a column. Returns false + // if x,y fall outside all columns. + GBool findWordPoints(double x, double y, + TextPosition *startPos, TextPosition *endPos); + + // Find the start and end of a line inside a column. Returns false + // if x,y fall outside all columns. + GBool findLinePoints(double x, double y, + TextPosition *startPos, TextPosition *endPos); + // Get the upper point of a TextPosition. void convertPosToPointUpper(TextPosition *pos, double *x, double *y); @@ -478,6 +507,11 @@ private: UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen); + void writeSimple2Layout(void *outputStream, + TextOutputFunc outputFunc, + UnicodeMap *uMap, + char *space, int spaceLen, + char *eol, int eolLen); void writeLinePrinter(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, @@ -493,12 +527,17 @@ private: // analysis int rotateChars(GList *charsA); + void rotateCharsToZero(GList *charsA); void rotateUnderlinesAndLinks(int rot); void unrotateChars(GList *charsA, int rot); + void unrotateCharsFromZero(GList *charsA); + void unrotateColumnsFromZero(GList *columns); void unrotateColumns(GList *columns, int rot); void unrotateWords(GList *words, int rot); GBool checkPrimaryLR(GList *charsA); void removeDuplicates(GList *charsA, int rot); + GList *separateOverlappingText(GList *charsA); + TextColumn *buildOverlappingTextColumn(GList *overlappingChars); TextBlock *splitChars(GList *charsA); TextBlock *split(GList *charsA, int rot); GList *getChars(GList *charsA, double xMin, double yMin, @@ -522,8 +561,12 @@ private: double getLineIndent(TextLine *line, TextBlock *blk); double getAverageLineSpacing(GList *lines); double getLineSpacing(TextLine *line0, TextLine *line1); - void buildLines(TextBlock *blk, GList *lines); + void buildLines(TextBlock *blk, GList *lines, GBool splitSuperLines); + GList *buildSimple2Columns(GList *charsA); + GList *buildSimple2Lines(GList *charsA, int rot); TextLine *buildLine(TextBlock *blk); + TextLine *buildLine(GList *charsA, int rot, + double xMin, double yMin, double xMax, double yMax); void getLineChars(TextBlock *blk, GList *charsA); double computeWordSpacingThreshold(GList *charsA, int rot); int getCharDirection(TextChar *ch); @@ -563,6 +606,7 @@ private: int curRot; // current rotation GBool diagonal; // set if rotation is not close to // 0/90/180/270 degrees + GBool rotated; // set if text is not horizontal (0 degrees) int nTinyChars; // number of "tiny" chars seen so far Unicode *actualText; // current "ActualText" span int actualTextLen; @@ -605,7 +649,7 @@ public: // is maintained. If <rawOrder> is true, the text is kept in // content stream order. TextOutputDev(char *fileName, TextOutputControl *controlA, - GBool append); + GBool append, GBool fileNameIsUTF8 = gFalse); // Create a TextOutputDev which will write to a generic stream. If // <physLayoutA> is true, the original physical layout of the text diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc index 4701fed98eb..8e54a1d5471 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc @@ -201,3 +201,13 @@ GString *TextString::toPDFTextString() { } return s; } + +GString *TextString::toUTF8() { + GString *s = new GString(); + for (int i = 0; i < len; ++i) { + char buf[8]; + int n = mapUTF8(u[i], buf, sizeof(buf)); + s->append(buf, n); + } + return s; +} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h index c5e7e613af8..c296d7d6d7a 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h @@ -56,6 +56,9 @@ public: // Create a PDF text string from a TextString. GString *toPDFTextString(); + // Convert a TextString to UTF-8. + GString *toUTF8(); + private: void expand(int delta); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc index 95f0e983a31..9ae636226ae 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc @@ -22,6 +22,7 @@ WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) { GfxFontType type; + Ref id; gfxFont = gfxFontA; fontBuf = NULL; @@ -29,23 +30,25 @@ WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) { 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; + if (gfxFont->getEmbeddedFontID(&id)) { + 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; + } } } } diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc deleted file mode 100644 index 3b88270c798..00000000000 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc +++ /dev/null @@ -1,3245 +0,0 @@ -//======================================================================== -// -// XFAForm.cc -// -// Copyright 2012 Glyph & Cog, LLC -// -//======================================================================== - -#include <aconf.h> - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include <stdlib.h> -#include "gmem.h" -#include "gmempp.h" -#include "GString.h" -#include "GList.h" -#include "GHash.h" -#include "Error.h" -#include "Object.h" -#include "PDFDoc.h" -#include "Gfx.h" -#include "GfxFont.h" -#include "Zoox.h" -#include "PDF417Barcode.h" -#include "UTF8.h" -#include "XFAForm.h" - -#ifdef _WIN32 -# undef strcasecmp -# undef strncasecmp -# define strcasecmp _stricmp -# define strncasecmp _strnicmp -#endif - -//------------------------------------------------------------------------ - -// 5 bars + 5 spaces -- each can be wide (1) or narrow (0) -// (there are always exactly 3 wide elements; -// the last space is always narrow) -static Guchar code3Of9Data[128][10] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x10 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24 - { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a - { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d - { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e - { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f - { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30 - { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1' - { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2' - { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3' - { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4' - { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5' - { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6' - { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7' - { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8' - { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9' - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x40 - { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41 - { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B' - { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C' - { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D' - { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E' - { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F' - { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G' - { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H' - { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I' - { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J' - { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K' - { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L' - { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M' - { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N' - { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O' - { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50 - { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q' - { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R' - { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S' - { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T' - { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U' - { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V' - { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W' - { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X' - { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y' - { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z' - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x60 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x70 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -}; - -// 3 bars + 3 spaces -- each can be 1, 2, 3, or 4 units wide -static Guchar code128Data[107][6] = { - { 2, 1, 2, 2, 2, 2 }, - { 2, 2, 2, 1, 2, 2 }, - { 2, 2, 2, 2, 2, 1 }, - { 1, 2, 1, 2, 2, 3 }, - { 1, 2, 1, 3, 2, 2 }, - { 1, 3, 1, 2, 2, 2 }, - { 1, 2, 2, 2, 1, 3 }, - { 1, 2, 2, 3, 1, 2 }, - { 1, 3, 2, 2, 1, 2 }, - { 2, 2, 1, 2, 1, 3 }, - { 2, 2, 1, 3, 1, 2 }, - { 2, 3, 1, 2, 1, 2 }, - { 1, 1, 2, 2, 3, 2 }, - { 1, 2, 2, 1, 3, 2 }, - { 1, 2, 2, 2, 3, 1 }, - { 1, 1, 3, 2, 2, 2 }, - { 1, 2, 3, 1, 2, 2 }, - { 1, 2, 3, 2, 2, 1 }, - { 2, 2, 3, 2, 1, 1 }, - { 2, 2, 1, 1, 3, 2 }, - { 2, 2, 1, 2, 3, 1 }, - { 2, 1, 3, 2, 1, 2 }, - { 2, 2, 3, 1, 1, 2 }, - { 3, 1, 2, 1, 3, 1 }, - { 3, 1, 1, 2, 2, 2 }, - { 3, 2, 1, 1, 2, 2 }, - { 3, 2, 1, 2, 2, 1 }, - { 3, 1, 2, 2, 1, 2 }, - { 3, 2, 2, 1, 1, 2 }, - { 3, 2, 2, 2, 1, 1 }, - { 2, 1, 2, 1, 2, 3 }, - { 2, 1, 2, 3, 2, 1 }, - { 2, 3, 2, 1, 2, 1 }, - { 1, 1, 1, 3, 2, 3 }, - { 1, 3, 1, 1, 2, 3 }, - { 1, 3, 1, 3, 2, 1 }, - { 1, 1, 2, 3, 1, 3 }, - { 1, 3, 2, 1, 1, 3 }, - { 1, 3, 2, 3, 1, 1 }, - { 2, 1, 1, 3, 1, 3 }, - { 2, 3, 1, 1, 1, 3 }, - { 2, 3, 1, 3, 1, 1 }, - { 1, 1, 2, 1, 3, 3 }, - { 1, 1, 2, 3, 3, 1 }, - { 1, 3, 2, 1, 3, 1 }, - { 1, 1, 3, 1, 2, 3 }, - { 1, 1, 3, 3, 2, 1 }, - { 1, 3, 3, 1, 2, 1 }, - { 3, 1, 3, 1, 2, 1 }, - { 2, 1, 1, 3, 3, 1 }, - { 2, 3, 1, 1, 3, 1 }, - { 2, 1, 3, 1, 1, 3 }, - { 2, 1, 3, 3, 1, 1 }, - { 2, 1, 3, 1, 3, 1 }, - { 3, 1, 1, 1, 2, 3 }, - { 3, 1, 1, 3, 2, 1 }, - { 3, 3, 1, 1, 2, 1 }, - { 3, 1, 2, 1, 1, 3 }, - { 3, 1, 2, 3, 1, 1 }, - { 3, 3, 2, 1, 1, 1 }, - { 3, 1, 4, 1, 1, 1 }, - { 2, 2, 1, 4, 1, 1 }, - { 4, 3, 1, 1, 1, 1 }, - { 1, 1, 1, 2, 2, 4 }, - { 1, 1, 1, 4, 2, 2 }, - { 1, 2, 1, 1, 2, 4 }, - { 1, 2, 1, 4, 2, 1 }, - { 1, 4, 1, 1, 2, 2 }, - { 1, 4, 1, 2, 2, 1 }, - { 1, 1, 2, 2, 1, 4 }, - { 1, 1, 2, 4, 1, 2 }, - { 1, 2, 2, 1, 1, 4 }, - { 1, 2, 2, 4, 1, 1 }, - { 1, 4, 2, 1, 1, 2 }, - { 1, 4, 2, 2, 1, 1 }, - { 2, 4, 1, 2, 1, 1 }, - { 2, 2, 1, 1, 1, 4 }, - { 4, 1, 3, 1, 1, 1 }, - { 2, 4, 1, 1, 1, 2 }, - { 1, 3, 4, 1, 1, 1 }, - { 1, 1, 1, 2, 4, 2 }, - { 1, 2, 1, 1, 4, 2 }, - { 1, 2, 1, 2, 4, 1 }, - { 1, 1, 4, 2, 1, 2 }, - { 1, 2, 4, 1, 1, 2 }, - { 1, 2, 4, 2, 1, 1 }, - { 4, 1, 1, 2, 1, 2 }, - { 4, 2, 1, 1, 1, 2 }, - { 4, 2, 1, 2, 1, 1 }, - { 2, 1, 2, 1, 4, 1 }, - { 2, 1, 4, 1, 2, 1 }, - { 4, 1, 2, 1, 2, 1 }, - { 1, 1, 1, 1, 4, 3 }, - { 1, 1, 1, 3, 4, 1 }, - { 1, 3, 1, 1, 4, 1 }, - { 1, 1, 4, 1, 1, 3 }, - { 1, 1, 4, 3, 1, 1 }, - { 4, 1, 1, 1, 1, 3 }, - { 4, 1, 1, 3, 1, 1 }, - { 1, 1, 3, 1, 4, 1 }, - { 1, 1, 4, 1, 3, 1 }, - { 3, 1, 1, 1, 4, 1 }, - { 4, 1, 1, 1, 3, 1 }, - { 2, 1, 1, 4, 1, 2 }, // start code A - { 2, 1, 1, 2, 1, 4 }, // start code B - { 2, 1, 1, 2, 3, 2 }, // start code C - { 2, 3, 3, 1, 1, 1 } // stop code (without final bar) -}; - -//------------------------------------------------------------------------ - -class XFATableInfo { -public: - - XFATableInfo(ZxAttr *columnWidthsAttr); - ~XFATableInfo(); - void computeRowHeight(ZxElement *rowSubform); - - int nColumns; // number of columns - double *columnRight; // right edge (x coord) of each column - int rowIdx; // current row index - int columnIdx; // current column index - double rowTop; // top edge (y coord) of current row - double rowHeight; // height of current row (max cell height - // so far) -}; - -XFATableInfo::XFATableInfo(ZxAttr *columnWidthsAttr) { - GString *s; - double w; - int i, columnRightSize; - - nColumns = 0; - columnRight = NULL; - columnRightSize = 0; - if (columnWidthsAttr) { - s = columnWidthsAttr->getValue(); - i = 0; - while (1) { - for (; - i < s->getLength() && - (s->getChar(i) == ' ' || s->getChar(i) == '\t' || - s->getChar(i) == '\r' || s->getChar(i) == '\n'); - ++i) ; - if (i == s->getLength()) { - break; - } - w = XFAFormField::getMeasurement(s, i); - if (nColumns == columnRightSize) { - columnRightSize = columnRightSize ? 2 * columnRightSize : 8; - columnRight = (double *)greallocn(columnRight, columnRightSize, - sizeof(double)); - } - columnRight[nColumns] = (nColumns > 0 ? columnRight[nColumns - 1] : 0) + w; - ++nColumns; - for (++i; - i < s->getLength() && - !(s->getChar(i) == ' ' || s->getChar(i) == '\t' || - s->getChar(i) == '\r' || s->getChar(i) == '\n'); - ++i) ; - } - } - rowIdx = -1; - columnIdx = 0; - rowTop = rowHeight = 0; -} - -XFATableInfo::~XFATableInfo() { - gfree(columnRight); -} - -void XFATableInfo::computeRowHeight(ZxElement *rowSubform) { - ZxNode *child; - ZxAttr *attr; - double h; - - rowHeight = 0; - for (child = rowSubform->getFirstChild(); - child; - child = child->getNextChild()) { - if (child->isElement("field") || child->isElement("draw")) { - if (!(attr = ((ZxElement *)child)->findAttr("h"))) { - attr = ((ZxElement *)child)->findAttr("minH"); - } - h = XFAFormField::getMeasurement(attr, 0); - if (h > rowHeight) { - rowHeight = h; - } - } - } -} - -//------------------------------------------------------------------------ -// XFAForm -//------------------------------------------------------------------------ - -XFAForm *XFAForm::load(PDFDoc *docA, Catalog *catalog, - Object *acroFormObj, Object *xfaObj) { - XFAForm *xfaForm; - XFAFormField *field; - ZxDoc *xmlA; - ZxElement *tmpl; - Object catDict, resourceDictA, obj1; - GString *data; - GBool fullXFAA; - GString *name, *fullName; - GHash *nameCount, *nameIdx, *fullNameCount, *fullNameIdx; - char buf[4096]; - int n, i; - - if (catalog->getNumPages() == 0) { - return NULL; - } - - docA->getXRef()->getCatalog(&catDict); - catDict.dictLookup("NeedsRendering", &obj1); - fullXFAA = obj1.isBool() && obj1.getBool(); - obj1.free(); - catDict.free(); - - if (xfaObj->isStream()) { - data = new GString(); - xfaObj->streamReset(); - while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) { - data->append(buf, n); - } - } else if (xfaObj->isArray()) { - data = new GString(); - for (i = 1; i < xfaObj->arrayGetLength(); i += 2) { - if (!xfaObj->arrayGet(i, &obj1)->isStream()) { - error(errSyntaxError, -1, "XFA array element is wrong type"); - obj1.free(); - delete data; - return NULL; - } - obj1.streamReset(); - while ((n = obj1.getStream()->getBlock(buf, sizeof(buf))) > 0) { - data->append(buf, n); - } - obj1.free(); - } - } else { - error(errSyntaxError, -1, "XFA object is wrong type"); - return NULL; - } - - xmlA = ZxDoc::loadMem(data->getCString(), data->getLength()); - delete data; - if (!xmlA) { - error(errSyntaxError, -1, "Invalid XML in XFA form"); - return NULL; - } - - if (acroFormObj->isDict()) { - acroFormObj->dictLookup("DR", &resourceDictA); - } - - xfaForm = new XFAForm(docA, catalog->getNumPages(), - xmlA, &resourceDictA, fullXFAA); - - resourceDictA.free(); - - if (xfaForm->xml->getRoot()) { - if ((tmpl = xfaForm->xml->getRoot()->findFirstChildElement("template"))) { - xfaForm->curPageNum = 0; - xfaForm->curXOffset = xfaForm->curYOffset = 0; - name = new GString(); - fullName = new GString(); - nameCount = new GHash(); - nameIdx = new GHash(); - fullNameCount = new GHash(); - fullNameIdx = new GHash(); - xfaForm->scanNode(tmpl, name, fullName, gFalse, NULL, - nameCount, nameIdx, fullNameCount, fullNameIdx, - NULL, catalog); - delete nameCount; - delete nameIdx; - delete fullNameCount; - delete fullNameIdx; - delete name; - delete fullName; - - // apply pageOffsetX/Y (the pageSet/pageArea/contentArea offset) - // to all fields - if (xfaForm->pageSetNPages >= 1 && - xfaForm->pageSetNPages < xfaForm->nPages) { - for (i = xfaForm->pageSetNPages + 1; i <= xfaForm->nPages; ++i) { - xfaForm->pageOffsetX[i - 1] = - xfaForm->pageOffsetX[xfaForm->pageSetNPages - 1]; - xfaForm->pageOffsetY[i - 1] = - xfaForm->pageOffsetY[xfaForm->pageSetNPages - 1]; - } - } - for (i = 0; i < xfaForm->fields->getLength(); ++i) { - field = (XFAFormField *)xfaForm->fields->get(i); - if (field->pageNum >= 1 && field->pageNum <= xfaForm->nPages) { - field->xOffset += xfaForm->pageOffsetX[field->pageNum - 1]; - field->yOffset += xfaForm->pageOffsetY[field->pageNum - 1]; - } - } - } - } - - return xfaForm; -} - -XFAForm::XFAForm(PDFDoc *docA, int nPagesA, ZxDoc *xmlA, - Object *resourceDictA, GBool fullXFAA): - Form(docA) { - int pg; - - xml = xmlA; - fields = new GList(); - resourceDictA->copy(&resourceDict); - fullXFA = fullXFAA; - nPages = nPagesA; - pageSetNPages = 0; - pageOffsetX = (double *)gmallocn(nPages, sizeof(double)); - pageOffsetY = (double *)gmallocn(nPages, sizeof(double)); - for (pg = 0; pg < nPages; ++pg) { - pageOffsetX[pg] = pageOffsetY[pg] = 0; - } -} - -XFAForm::~XFAForm() { - delete xml; - deleteGList(fields, XFAFormField); - resourceDict.free(); - gfree(pageOffsetX); - gfree(pageOffsetY); -} - -// 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, - GString *parentName, GString *parentFullName, - GBool inPageSet, XFATableInfo *tableInfo, - GHash *nameCount, GHash *nameIdx, - GHash *fullNameCount, GHash *fullNameIdx, - GString *exclGroupName, Catalog *catalog) { - ZxAttr *attr; - GString *name, *fullName, *namePart, *fullNamePart; - GHash *childNameCount, *childNameIdx, *childFullNameCount, *childFullNameIdx; - int colSpan, i; - - if (elem->isElement("template")) { - name = new GString("form"); - fullName = new GString("form"); - childNameCount = new GHash(); - scanNames(elem, childNameCount); - childNameIdx = new GHash(); - childFullNameCount = new GHash(); - scanFullNames(elem, childFullNameCount); - childFullNameIdx = new GHash(); - } else { - if ((namePart = getNodeName(elem))) { - name = GString::format("{0:t}.{1:t}", parentName, namePart); - if (nameCount->lookupInt(namePart) > 1) { - i = nameIdx->lookupInt(namePart); - name->appendf("[{0:d}]", i); - nameIdx->replace(namePart, i + 1); - } - childNameCount = new GHash(); - scanNames(elem, childNameCount); - childNameIdx = new GHash(); - } else { - name = parentName->copy(); - childNameCount = nameCount; - childNameIdx = nameIdx; - } - if ((fullNamePart = getNodeFullName(elem))) { - fullName = GString::format("{0:t}.{1:t}", parentFullName, fullNamePart); - if (fullNameCount->lookupInt(fullNamePart) > 1) { - i = fullNameIdx->lookupInt(fullNamePart); - fullName->appendf("[{0:d}]", i); - fullNameIdx->replace(fullNamePart, i + 1); - } - childFullNameCount = new GHash(); - scanFullNames(elem, childFullNameCount); - childFullNameIdx = new GHash(); - } else { - fullName = parentFullName->copy(); - childFullNameCount = fullNameCount; - childFullNameIdx = fullNameIdx; - } - } - - if (tableInfo && (elem->isElement("field") || elem->isElement("draw"))) { - if ((attr = elem->findAttr("colSpan"))) { - colSpan = atoi(attr->getValue()->getCString()); - } else { - colSpan = 1; - } - } else { - colSpan = 0; - } - - if (elem->isElement("field")) { - scanField(elem, name, fullName, exclGroupName, - inPageSet, tableInfo, colSpan, catalog); - } else { - scanNonField(elem, name, fullName, inPageSet, tableInfo, colSpan, - childNameCount, childNameIdx, - childFullNameCount, childFullNameIdx, - catalog); - } - - if (tableInfo) { - tableInfo->columnIdx += colSpan; - } - - delete name; - delete fullName; - if (childNameCount != nameCount) { - delete childNameCount; - } - if (childNameIdx != nameIdx) { - delete childNameIdx; - } - if (childFullNameCount != fullNameCount) { - delete childFullNameCount; - } - if (childFullNameIdx != fullNameIdx) { - delete childFullNameIdx; - } -} - -// Traverse all children of <elem>, incrementing nameCount[name] for -// each named child. Traversal stops at each named child. -void XFAForm::scanNames(ZxElement *elem, GHash *nameCount) { - ZxNode *node; - ZxElement *child; - GString *namePart; - - for (node = elem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement()) { - child = (ZxElement *)node; - if ((namePart = getNodeName(child))) { - if (nodeIsBindGlobal(child)) { - nameCount->replace(namePart, 1); - } else { - nameCount->replace(namePart, nameCount->lookupInt(namePart) + 1); - } - } else { - scanNames(child, nameCount); - } - } - } -} - -// Traverse all children of <elem>, incrementing fullNameCount[name] -// for each full-named child. Traversal stops at each full-named -// child. -void XFAForm::scanFullNames(ZxElement *elem, GHash *fullNameCount) { - ZxNode *node; - ZxElement *child; - GString *fullNamePart; - - for (node = elem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement()) { - child = (ZxElement *)node; - if ((fullNamePart = getNodeFullName(child))) { - if (nodeIsBindGlobal(child)) { - fullNameCount->replace(fullNamePart, 1); - } else { - fullNameCount->replace(fullNamePart, - fullNameCount->lookupInt(fullNamePart) + 1); - } - } else { - scanFullNames(child, fullNameCount); - } - } - } -} - -void XFAForm::scanField(ZxElement *elem, GString *name, GString *fullName, - GString *exclGroupName, GBool inPageSet, - XFATableInfo *tableInfo, int colSpan, - Catalog *catalog) { - double xSubOffset, ySubOffset, columnWidth, rowHeight; - - if (curPageNum == 0) { - curPageNum = 1; - } - - xSubOffset = ySubOffset = 0; - columnWidth = rowHeight = 0; - if (tableInfo) { - if (tableInfo->columnIdx > 0 && - tableInfo->columnIdx <= tableInfo->nColumns) { - xSubOffset = tableInfo->columnRight[tableInfo->columnIdx - 1]; - } - if (tableInfo->columnIdx + colSpan <= tableInfo->nColumns) { - columnWidth = tableInfo->columnRight[tableInfo->columnIdx + colSpan - 1] - - xSubOffset; - } - ySubOffset = tableInfo->rowTop; - rowHeight = tableInfo->rowHeight; - curXOffset += xSubOffset; - curYOffset += ySubOffset; - } - - fields->append(new XFAFormField(this, elem, name->copy(), fullName->copy(), - exclGroupName ? exclGroupName->copy() - : (GString *)NULL, - curPageNum, curXOffset, curYOffset, - columnWidth, rowHeight)); - - if (tableInfo) { - curXOffset -= xSubOffset; - curYOffset -= ySubOffset; - } -} - -void XFAForm::scanNonField(ZxElement *elem, GString *name, GString *fullName, - GBool inPageSet, - XFATableInfo *tableInfo, int colSpan, - GHash *nameCount, GHash *nameIdx, - GHash *fullNameCount, GHash *fullNameIdx, - Catalog *catalog) { - XFATableInfo *newTableInfo; - ZxElement *brk, *contentArea; - ZxNode *child; - ZxAttr *attr; - PDFRectangle *box; - GString *exclGroupName; - double xSubOffset, ySubOffset; - int savedPageNum; - - newTableInfo = tableInfo; - - if (elem->isElement("subform")) { - - // update page number - if (((brk = elem->findFirstChildElement("breakBefore")) && - (attr = brk->findAttr("targetType")) && - !attr->getValue()->cmp("pageArea")) || - ((brk = elem->findFirstChildElement("break")) && - (attr = brk->findAttr("before")) && - !attr->getValue()->cmp("pageArea")) || - (curPageNum < nPages && - (attr = elem->findAttr("w")) && - (box = catalog->getPage(curPageNum + 1)->getMediaBox()) && - XFAFormField::getMeasurement(attr, 0) == box->x2 - box->x1 && - (attr = elem->findAttr("h")) && - XFAFormField::getMeasurement(attr, 0) == box->y2 - box->y1)) { - if (curPageNum < nPages) { - ++curPageNum; - } - } - - // update tableInfo - if ((attr = elem->findAttr("layout"))) { - if (!attr->getValue()->cmp("table")) { - newTableInfo = new XFATableInfo(elem->findAttr("columnWidths")); - newTableInfo->rowIdx = -1; - newTableInfo->columnIdx = 0; - } else if (tableInfo && !attr->getValue()->cmp("row")) { - ++tableInfo->rowIdx; - tableInfo->columnIdx = 0; - tableInfo->rowTop += tableInfo->rowHeight; - tableInfo->computeRowHeight(elem); - } - } - - // update position - xSubOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0); - ySubOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0); - curXOffset += xSubOffset; - curYOffset += ySubOffset; - - } else if (elem->isElement("area") || - elem->isElement("exclGroup")) { - xSubOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0); - ySubOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0); - curXOffset += xSubOffset; - curYOffset += ySubOffset; - - } else { - xSubOffset = ySubOffset = 0; - } - - savedPageNum = curPageNum; - if (elem->isElement("pageSet")) { - inPageSet = gTrue; - curPageNum = 0; - - } else if (elem->isElement("pageArea")) { - if (inPageSet) { - if (curPageNum < nPages) { - ++curPageNum; - } - if ((contentArea = elem->findFirstChildElement("contentArea"))) { - pageOffsetX[curPageNum - 1] = - XFAFormField::getMeasurement(contentArea->findAttr("x"), 0); - pageOffsetY[curPageNum - 1] = - XFAFormField::getMeasurement(contentArea->findAttr("y"), 0); - // looks like the contentArea offset (pageOffsetX/Y) should - // not be added to fields defined inside the pageArea - // element (?) - xSubOffset -= pageOffsetX[curPageNum - 1]; - ySubOffset -= pageOffsetY[curPageNum - 1]; - curXOffset -= pageOffsetX[curPageNum - 1]; - curYOffset -= pageOffsetY[curPageNum - 1]; - } - } - } - - 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, - exclGroupName, catalog); - } - } - - curXOffset -= xSubOffset; - curYOffset -= ySubOffset; - - if (newTableInfo != tableInfo) { - delete newTableInfo; - } - - if (elem->isElement("pageSet")) { - pageSetNPages = curPageNum; - curPageNum = savedPageNum; - inPageSet = gFalse; - } -} - -GString *XFAForm::getNodeName(ZxElement *elem) { - ZxElement *bindElem; - ZxAttr *attr; - - if (!(elem->getType()->cmp("field") && - (bindElem = elem->findFirstChildElement("bind")) && - (attr = bindElem->findAttr("match")) && - !attr->getValue()->cmp("none")) && - !elem->isElement("area") && - (attr = elem->findAttr("name"))) { - return attr->getValue(); - } - return NULL; -} - -GString *XFAForm::getNodeFullName(ZxElement *elem) { - ZxAttr *attr; - - if (!elem->isElement("area") && - (attr = elem->findAttr("name"))) { - return attr->getValue(); - } - return NULL; -} - -GBool XFAForm::nodeIsBindGlobal(ZxElement *elem) { - ZxElement *bindElem; - ZxAttr *attr; - - return (bindElem = elem->findFirstChildElement("bind")) && - (attr = bindElem->findAttr("match")) && - !attr->getValue()->cmp("global"); -} - -void XFAForm::draw(int pageNum, Gfx *gfx, GBool printing) { - GfxFontDict *fontDict; - Object obj1; - int i; - - // build the font dictionary - fontDict = NULL; - if (resourceDict.isDict()) { - if (resourceDict.dictLookup("Font", &obj1)->isDict()) { - fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict()); - } - obj1.free(); - } - - for (i = 0; i < fields->getLength(); ++i) { - ((XFAFormField *)fields->get(i))->draw(pageNum, gfx, printing, fontDict); - } - - delete fontDict; -} - -int XFAForm::getNumFields() { - return fields->getLength(); -} - -FormField *XFAForm::getField(int idx) { - return (XFAFormField *)fields->get(idx); -} - -//------------------------------------------------------------------------ -// XFAFormField -//------------------------------------------------------------------------ - -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; - columnWidth = columnWidthA; - rowHeight = rowHeightA; -} - -XFAFormField::~XFAFormField() { - delete name; - delete fullName; - if (exclGroupName) { - delete exclGroupName; - } -} - -int XFAFormField::getPageNum() { - return pageNum; -} - -const char *XFAFormField::getType() { - ZxElement *uiElem; - ZxNode *node; - - if ((uiElem = xml->findFirstChildElement("ui"))) { - for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement("textEdit")) { - return "Text"; - } else if (node->isElement("numericEdit")) { - return "Numeric"; - } else if (node->isElement("dateTimeEdit")) { - return "DateTime"; - } else if (node->isElement("choiceList")) { - return "ChoiceList"; - } else if (node->isElement("checkButton")) { - return "CheckButton"; - } else if (node->isElement("barcode")) { - return "BarCode"; - } - //~ other field types go here - } - } - return NULL; -} - -Unicode *XFAFormField::getName(int *length) { - //~ assumes name is UTF-8 - return utf8ToUnicode(name, length); -} - -Unicode *XFAFormField::getValue(int *length) { - ZxElement *uiElem; - ZxNode *node; - GString *s; - - //~ assumes value is UTF-8 - s = NULL; - if ((uiElem = xml->findFirstChildElement("ui"))) { - for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement("textEdit")) { - s = getFieldValue("text"); - break; - } else if (node->isElement("numericEdit")) { - //~ not sure if this is correct - s = getFieldValue("text"); - break; - } else if (node->isElement("dateTimeEdit")) { - s = getFieldValue("text"); - break; - } else if (node->isElement("checkButton")) { - if (!(s = getFieldValue("integer"))) { - s = getFieldValue("text"); - } - break; - } else if (node->isElement("barcode")) { - s = getFieldValue("text"); - break; - } - //~ other field types go here - } - } - if (!s) { - return NULL; - } - return utf8ToUnicode(s, length); -} - -Unicode *XFAFormField::utf8ToUnicode(GString *s, int *length) { - Unicode *u; - int n, size, c0, c1, c2, c3, c4, c5, i; - - n = size = 0; - u = NULL; - i = 0; - while (i < s->getLength()) { - if (n == size) { - size = size ? size * 2 : 16; - u = (Unicode *)greallocn(u, size, sizeof(Unicode)); - } - c0 = s->getChar(i++) & 0xff; - if (c0 <= 0x7f) { - u[n++] = c0; - } else if (c0 <= 0xdf && i < n) { - c1 = s->getChar(i++) & 0xff; - u[n++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f); - } else if (c0 <= 0xef && i+1 < n) { - c1 = s->getChar(i++) & 0xff; - c2 = s->getChar(i++) & 0xff; - u[n++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f); - } else if (c0 <= 0xf7 && i+2 < n) { - c1 = s->getChar(i++) & 0xff; - c2 = s->getChar(i++) & 0xff; - c3 = s->getChar(i++) & 0xff; - u[n++] = ((c0 & 0x07) << 18) | ((c1 & 0x3f) << 12) | ((c2 & 0x3f) << 6) - | (c3 & 0x3f); - } else if (c0 <= 0xfb && i+3 < n) { - c1 = s->getChar(i++) & 0xff; - c2 = s->getChar(i++) & 0xff; - c3 = s->getChar(i++) & 0xff; - c4 = s->getChar(i++) & 0xff; - u[n++] = ((c0 & 0x03) << 24) | ((c1 & 0x3f) << 18) | ((c2 & 0x3f) << 12) - | ((c3 & 0x3f) << 6) | (c4 & 0x3f); - } else if (c0 <= 0xfd && i+4 < n) { - c1 = s->getChar(i++) & 0xff; - c2 = s->getChar(i++) & 0xff; - c3 = s->getChar(i++) & 0xff; - c4 = s->getChar(i++) & 0xff; - c5 = s->getChar(i++) & 0xff; - u[n++] = ((c0 & 0x01) << 30) | ((c1 & 0x3f) << 24) | ((c2 & 0x3f) << 18) - | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f); - } else { - u[n++] = '?'; - } - } - *length = n; - return u; -} - -void XFAFormField::getBBox(double *llx, double *lly, - double *urx, double *ury) { - double xfaX, xfaY, xfaW, xfaH, pdfX, pdfY, pdfW, pdfH; - int pdfRot; - - getRectangle(&xfaX, &xfaY, &xfaW, &xfaH, - &pdfX, &pdfY, &pdfW, &pdfH, &pdfRot); - *llx = pdfX; - *lly = pdfY; - *urx = pdfX + pdfW; - *ury = pdfY + pdfH; -} - -void XFAFormField::getFont(Ref *fontID, double *fontSize) { - ZxElement *fontElem; - ZxAttr *attr; - GBool bold, italic; - - fontID->num = fontID->gen = -1; - *fontSize = 0; - if ((fontElem = xml->findFirstChildElement("font"))) { - bold = italic = gFalse; - if ((attr = fontElem->findAttr("weight"))) { - if (!attr->getValue()->cmp("bold")) { - bold = gTrue; - } - } - if ((attr = fontElem->findAttr("posture"))) { - if (!attr->getValue()->cmp("italic")) { - italic = gTrue; - } - } - if ((attr = fontElem->findAttr("typeface"))) { - *fontID = findFontName(attr->getValue(), bold, italic); - } - if ((attr = fontElem->findAttr("size"))) { - *fontSize = getMeasurement(attr, 0); - } - } -} - -void XFAFormField::getColor(double *red, double *green, double *blue) { - ZxElement *fontElem, *fillElem, *colorElem; - ZxAttr *attr; - int r, g, b; - - *red = *green = *blue = 0; - if ((fontElem = xml->findFirstChildElement("font"))) { - if ((fillElem = fontElem->findFirstChildElement("fill"))) { - if ((colorElem = fillElem->findFirstChildElement("color"))) { - if ((attr = colorElem->findAttr("value"))) { - if (sscanf(attr->getValue()->getCString(), "%d,%d,%d", - &r, &g, &b) == 3) { - *red = r / 255.0; - *green = g / 255.0; - *blue = b / 255.0; - } - } - } - } - } -} - -void XFAFormField::draw(int pageNumA, Gfx *gfx, GBool printing, - GfxFontDict *fontDict) { - ZxElement *uiElem; - ZxNode *node; - ZxAttr *attr; - GString *appearBuf; - MemStream *appearStream; - Object appearDict, appearance, resourceDict, resourceSubdict; - Object fontResources, fontResource, defaultFont; - Object obj1, obj2; - char *resType, *fontName; - double mat[6]; - double xfaX, xfaY, xfaW, xfaH, pdfX, pdfY, pdfW, pdfH; - int rot3, i; - - if (pageNumA != pageNum) { - return; - } - - // check the 'presence' attribute - if ((attr = xml->findAttr("presence"))) { - if (!attr->getValue()->cmp("hidden") || - !attr->getValue()->cmp("invisible")) { - return; - } - } - - getRectangle(&xfaX, &xfaY, &xfaW, &xfaH, &pdfX, &pdfY, &pdfW, &pdfH, &rot3); - - // generate transform matrix - switch (rot3) { - case 0: - default: - mat[0] = 1; mat[1] = 0; - mat[2] = 0; mat[3] = 1; - mat[4] = 0; mat[5] = 0; - break; - case 90: - mat[0] = 0; mat[1] = 1; - mat[2] = -1; mat[3] = 0; - mat[4] = xfaH; mat[5] = 0; - break; - case 180: - mat[0] = -1; mat[1] = 0; - mat[2] = 0; mat[3] = -1; - mat[4] = xfaW; mat[5] = xfaH; - break; - case 270: - mat[0] = 0; mat[1] = -1; - mat[2] = 1; mat[3] = 0; - mat[4] = 0; mat[5] = xfaW; - break; - } - - // get the appearance stream data - appearBuf = new GString(); -#if 0 //~ for debugging - double debugPad = 0; - appearBuf->appendf("q 1 1 0 rg {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n", - debugPad, debugPad, xfaW - 2*debugPad, xfaH - 2*debugPad); -#endif - if ((uiElem = xml->findFirstChildElement("ui"))) { - for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement("textEdit") || - node->isElement("numericEdit")) { - drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf); - break; - } else if (node->isElement("dateTimeEdit")) { - drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf); - break; - } else if (node->isElement("choiceList")) { - drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf); - break; - } else if (node->isElement("checkButton")) { - drawCheckButton(fontDict, xfaW, xfaH, rot3, appearBuf); - break; - } else if (node->isElement("barcode")) { - drawBarCode(fontDict, xfaW, xfaH, rot3, appearBuf); - break; - } - //~ other field types go here - } - } else { - drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf); - } - - // create the appearance stream - appearDict.initDict(xfaForm->doc->getXRef()); - appearDict.dictAdd(copyString("Length"), - obj1.initInt(appearBuf->getLength())); - appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form")); - obj1.initArray(xfaForm->doc->getXRef()); - obj1.arrayAdd(obj2.initReal(0)); - obj1.arrayAdd(obj2.initReal(0)); - obj1.arrayAdd(obj2.initReal(xfaW)); - obj1.arrayAdd(obj2.initReal(xfaH)); - appearDict.dictAdd(copyString("BBox"), &obj1); - obj1.initArray(xfaForm->doc->getXRef()); - obj1.arrayAdd(obj2.initReal(mat[0])); - obj1.arrayAdd(obj2.initReal(mat[1])); - obj1.arrayAdd(obj2.initReal(mat[2])); - obj1.arrayAdd(obj2.initReal(mat[3])); - obj1.arrayAdd(obj2.initReal(mat[4])); - obj1.arrayAdd(obj2.initReal(mat[5])); - appearDict.dictAdd(copyString("Matrix"), &obj1); - // NB: we need to deep-copy the resource dict and font resource - // subdict, because the font subdict is modified, and multiple - // threads can be sharing these objects. - resourceDict.initDict(xfaForm->doc->getXRef()); - if (xfaForm->resourceDict.isDict()) { - for (i = 0; i < xfaForm->resourceDict.dictGetLength(); ++i) { - resType = xfaForm->resourceDict.dictGetKey(i); - if (strcmp(resType, "Font")) { - xfaForm->resourceDict.dictGetVal(i, &resourceSubdict); - resourceDict.dictAdd(copyString(resType), &resourceSubdict); - } - } - } - fontResources.initDict(xfaForm->doc->getXRef()); - if (xfaForm->resourceDict.isDict() && - xfaForm->resourceDict.dictLookup("Font", &resourceSubdict)->isDict()) { - for (i = 0; i < resourceSubdict.dictGetLength(); ++i) { - fontName = resourceSubdict.dictGetKey(i); - resourceSubdict.dictGetVal(i, &fontResource); - fontResources.dictAdd(copyString(fontName), &fontResource); - } - resourceSubdict.free(); - } - defaultFont.initDict(xfaForm->doc->getXRef()); - defaultFont.dictAdd(copyString("Type"), obj1.initName("Font")); - defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1")); - defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica")); - defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding")); - fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont); - resourceDict.dictAdd(copyString("Font"), &fontResources); - appearDict.dictAdd(copyString("Resources"), &resourceDict); - appearStream = new MemStream(appearBuf->getCString(), 0, - appearBuf->getLength(), &appearDict); - appearance.initStream(appearStream); - gfx->drawAnnot(&appearance, NULL, pdfX, pdfY, pdfX + pdfW, pdfY + pdfH); - appearance.free(); - delete appearBuf; -} - -void XFAFormField::getRectangle(double *xfaX, double *xfaY, - double *xfaW, double *xfaH, - double *pdfX, double *pdfY, - double *pdfW, double *pdfH, - int *pdfRot) { - Page *page; - PDFRectangle *pageRect; - ZxElement *captionElem, *paraElem, *marginElem; - ZxAttr *attr; - double x2, y2, w2, h2, t; - double anchorX, anchorY; - int pageRot, rot; - - page = xfaForm->doc->getCatalog()->getPage(pageNum); - pageRect = page->getMediaBox(); - pageRot = page->getRotate(); - - anchorX = 0; - anchorY = 0; - if ((attr = xml->findAttr("anchorType"))) { - if (!attr->getValue()->cmp("topLeft")) { - anchorX = 0; - anchorY = 0; - } else if (!attr->getValue()->cmp("topCenter")) { - anchorX = 0.5; - anchorY = 0; - } else if (!attr->getValue()->cmp("topRight")) { - anchorX = 1; - anchorY = 0; - } else if (!attr->getValue()->cmp("middleLeft")) { - anchorX = 0; - anchorY = 0.5; - } else if (!attr->getValue()->cmp("middleCenter")) { - anchorX = 0.5; - anchorY = 0.5; - } else if (!attr->getValue()->cmp("middleRight")) { - anchorX = 1; - anchorY = 0.5; - } else if (!attr->getValue()->cmp("bottomLeft")) { - anchorX = 0; - anchorY = 1; - } else if (!attr->getValue()->cmp("bottomCenter")) { - anchorX = 0.5; - anchorY = 1; - } else if (!attr->getValue()->cmp("bottomRight")) { - anchorX = 1; - anchorY = 1; - } - } - *xfaX = getMeasurement(xml->findAttr("x"), 0) + xOffset; - *xfaY = getMeasurement(xml->findAttr("y"), 0) + yOffset; - if (!(attr = xml->findAttr("w"))) { - attr = xml->findAttr("minW"); - } - *xfaW = getMeasurement(attr, 0); - if (*xfaW < columnWidth) { - *xfaW = columnWidth; - } - if (!(attr = xml->findAttr("h"))) { - attr = xml->findAttr("minH"); - } - *xfaH = getMeasurement(attr, 0); - if (*xfaH < rowHeight) { - *xfaH = rowHeight; - } - if ((attr = xml->findAttr("rotate"))) { - rot = atoi(attr->getValue()->getCString()); - if ((rot %= 360) < 0) { - rot += 360; - } - } else { - rot = 0; - } - - // look for <caption reserve="...."> -- add the caption width/height - // (plus a margin allowance) - if ((captionElem = xml->findFirstChildElement("caption"))) { - if ((attr = captionElem->findAttr("reserve"))) { - t = getMeasurement(attr, 0); - if ((attr = captionElem->findAttr("placement"))) { - if (!attr->getValue()->cmp("left")) { - *xfaX += t + 1.5; - *xfaW -= t + 1.5; - } else if (!attr->getValue()->cmp("right")) { - *xfaW -= t + 1.5; - } else if (!attr->getValue()->cmp("top")) { - *xfaY += t; - *xfaH -= t; - } else if (!attr->getValue()->cmp("bottom")) { - *xfaH -= t; - } - } else { - // default is placement="left" - *xfaX += t + 1.5; - *xfaW -= t + 1.5; - } - } - } - - // look for <margin> - if ((marginElem = xml->findFirstChildElement("margin"))) { - if ((attr = marginElem->findAttr("leftInset"))) { - t = getMeasurement(attr, 0); - *xfaX += t; - *xfaW -= t; - } - if ((attr = marginElem->findAttr("rightInset"))) { - t = getMeasurement(attr, 0); - *xfaW -= t; - } - if ((attr = marginElem->findAttr("topInset"))) { - t = getMeasurement(attr, 0); - *xfaY += t; - *xfaH -= t; - } - if ((attr = marginElem->findAttr("bottomInset"))) { - t = getMeasurement(attr, 0); - *xfaH -= t; - } - } - - // look for <para> -- add the margins - if ((paraElem = xml->findFirstChildElement("para"))) { - if ((attr = paraElem->findAttr("marginLeft"))) { - t = getMeasurement(attr, 0); - *xfaX += t; - *xfaW -= t; - } - if ((attr = paraElem->findAttr("marginRight"))) { - t = getMeasurement(attr, 0); - *xfaW -= t; - } - if ((attr = paraElem->findAttr("spaceAbove"))) { - t = getMeasurement(attr, 0); - *xfaY += t; - *xfaH -= t; - } - if ((attr = paraElem->findAttr("spaceBelow"))) { - t = getMeasurement(attr, 0); - *xfaH -= t; - } - } - - // get annot rect (UL corner, width, height) in XFA coords - // notes: - // - XFA coordinates are top-left origin, after page rotation - // - XFA coordinates are dependent on choice of anchor point - // and field rotation - switch (rot) { - case 0: - default: - x2 = *xfaX - anchorX * *xfaW; - y2 = *xfaY - anchorY * *xfaH; - w2 = *xfaW; - h2 = *xfaH; - break; - case 90: - x2 = *xfaX - anchorY * *xfaH; - y2 = *xfaY - (1 - anchorX) * *xfaW; - w2 = *xfaH; - h2 = *xfaW; - break; - case 180: - x2 = *xfaX - (1 - anchorX) * *xfaW; - y2 = *xfaY - (1 - anchorY) * *xfaH; - w2 = *xfaW; - h2 = *xfaH; - break; - case 270: - x2 = *xfaX - (1 - anchorY) * *xfaH; - y2 = *xfaY - anchorX * *xfaW; - w2 = *xfaH; - h2 = *xfaW; - break; - } - - // convert annot rect to PDF coords (LL corner, width, height), - // taking page rotation into account - switch (pageRot) { - case 0: - default: - *pdfX = pageRect->x1 + x2; - *pdfY = pageRect->y2 - (y2 + h2); - *pdfW = w2; - *pdfH = h2; - break; - case 90: - *pdfX = pageRect->x1 + y2; - *pdfY = pageRect->y1 + x2; - *pdfW = h2; - *pdfH = w2; - break; - case 180: - *pdfX = pageRect->x2 - (x2 + w2); - *pdfY = pageRect->y1 + y2; - *pdfW = w2; - *pdfH = h2; - break; - case 270: - *pdfX = pageRect->x2 - (y2 + h2); - *pdfY = pageRect->y1 + (x2 + w2); - *pdfW = h2; - *pdfH = w2; - break; - } - *pdfRot = (rot + pageRot) % 360; -} - -void XFAFormField::drawTextEdit(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf) { - ZxElement *formatElem, *pictureElem; - ZxNode *pictureChildElem; - ZxElement *valueElem, *textElem, *uiElem, *textEditElem, *combElem; - ZxElement *fontElem, *paraElem; - ZxAttr *attr; - GString *picture, *value, *formattedValue, *fontName; - double fontSize; - int maxChars, combCells; - GBool multiLine, bold, italic; - XFAHorizAlign hAlign; - XFAVertAlign vAlign; - - if (!(value = getFieldValue("text"))) { - return; - } - - uiElem = xml->findFirstChildElement("ui"); - - //--- picture formatting - if (uiElem && - (formatElem = xml->findFirstChildElement("format")) && - (pictureElem = formatElem->findFirstChildElement("picture")) && - (pictureChildElem = pictureElem->getFirstChild()) && - pictureChildElem->isCharData()) { - picture = ((ZxCharData *)pictureChildElem)->getData(); - if (uiElem->findFirstChildElement("dateTimeEdit")) { - formattedValue = pictureFormatDateTime(value, picture); - } else if (uiElem->findFirstChildElement("numericEdit")) { - formattedValue = pictureFormatNumber(value, picture); - } else if (uiElem->findFirstChildElement("textEdit")) { - formattedValue = pictureFormatText(value, picture); - } else { - formattedValue = value->copy(); - } - } else { - formattedValue = value->copy(); - } - - maxChars = 0; - if ((valueElem = xml->findFirstChildElement("value")) && - (textElem = valueElem->findFirstChildElement("text")) && - (attr = textElem->findAttr("maxChars"))) { - maxChars = atoi(attr->getValue()->getCString()); - } - - multiLine = gFalse; - combCells = 0; - if (uiElem && - (textEditElem = uiElem->findFirstChildElement("textEdit"))) { - if ((attr = textEditElem->findAttr("multiLine")) && - !attr->getValue()->cmp("1")) { - multiLine = gTrue; - } - if ((combElem = textEditElem->findFirstChildElement("comb"))) { - if ((attr = combElem->findAttr("numberOfCells"))) { - combCells = atoi(attr->getValue()->getCString()); - } else { - combCells = maxChars; - } - } - } - - fontName = NULL; - fontSize = 10; - bold = gFalse; - italic = gFalse; - if ((fontElem = xml->findFirstChildElement("font"))) { - if ((attr = fontElem->findAttr("typeface"))) { - fontName = attr->getValue()->copy(); - } - if ((attr = fontElem->findAttr("weight"))) { - if (!attr->getValue()->cmp("bold")) { - bold = gTrue; - } - } - if ((attr = fontElem->findAttr("posture"))) { - if (!attr->getValue()->cmp("italic")) { - italic = gTrue; - } - } - if ((attr = fontElem->findAttr("size"))) { - fontSize = getMeasurement(attr, fontSize); - } - } - if (!fontName) { - fontName = new GString("Courier"); - } - - hAlign = xfaHAlignLeft; - vAlign = xfaVAlignTop; - if ((paraElem = xml->findFirstChildElement("para"))) { - if ((attr = paraElem->findAttr("hAlign"))) { - if (!attr->getValue()->cmp("left")) { - hAlign = xfaHAlignLeft; - } else if (!attr->getValue()->cmp("center")) { - hAlign = xfaHAlignCenter; - } else if (!attr->getValue()->cmp("right")) { - hAlign = xfaHAlignRight; - } - //~ other hAlign values (justify, justifyAll, radix) are - //~ currently unsupported - } - if ((attr = paraElem->findAttr("vAlign"))) { - if (!attr->getValue()->cmp("top")) { - vAlign = xfaVAlignTop; - } else if (!attr->getValue()->cmp("bottom")) { - vAlign = xfaVAlignBottom; - } else if (!attr->getValue()->cmp("middle")) { - vAlign = xfaVAlignMiddle; - } - } - } - - drawText(formattedValue, multiLine, combCells, - fontName, bold, italic, fontSize, - hAlign, vAlign, 0, 0, w, h, gFalse, fontDict, appearBuf); - delete fontName; - delete formattedValue; -} - -void XFAFormField::drawCheckButton(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf) { - ZxElement *items; - ZxNode *firstItem, *firstItemVal; - const char *itemType; - const char *onValue; - GString *value; - double x, y; - - // get the "on" value for this check button - itemType = "integer"; - onValue = "1"; - if ((items = xml->findFirstChildElement("items"))) { - if ((firstItem = items->getFirstChild())) { - if (firstItem->isElement("text") && firstItem->getFirstChild()) { - itemType = "text"; - if ((firstItemVal = firstItem->getFirstChild())->isCharData()) { - onValue = ((ZxCharData *)firstItemVal)->getData()->getCString(); - } - } else if (firstItem->isElement("integer") && - firstItem->getFirstChild()) { - itemType = "integer"; - if ((firstItemVal = firstItem->getFirstChild())->isCharData()) { - onValue = ((ZxCharData *)firstItemVal)->getData()->getCString(); - } - } - } - } - - // compare its value to the "on" value - if (!(value = getFieldValue(itemType))) { - return; - } - if (value->cmp(onValue)) { - return; - } - - // adjust shape to be square - x = y = 0; - if (w > h) { - x += 0.5 * (w - h); - w = h; - } else { - y += 0.5 * (h - w); - h = w; - } - - // margins - x += 1.5; - w -= 3; - y += 1.5; - h -= 3; - - //~ this should look at the "shape" and "mark" attributes - - appearBuf->appendf("0.5 w {0:.4f} {1:.4f} m {2:.4f} {3:.4f} l {0:.4f} {3:.4f} m {2:.4f} {1:.4f} l S\n", - x, y, x + w, y + h); -} - -void XFAFormField::drawBarCode(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf) { - ZxElement *uiElem, *barcodeElem, *fontElem; - ZxAttr *attr; - GString *value, *value2, *barcodeType, *textLocation, *fontName, *s1, *s2; - XFAVertAlign textAlign; - double wideNarrowRatio, moduleWidth, moduleHeight, fontSize; - double yText, wText, yBarcode, hBarcode, wNarrow, xx; - GBool doText; - int dataLength, errorCorrectionLevel, checksum; - GBool bold, italic; - char *p; - int i, j, c; - - //--- get field value - if (!(value = getFieldValue("text"))) { - return; - } - - //--- get font - fontName = NULL; - fontSize = 0.2 * h; - bold = gFalse; - italic = gFalse; - if ((fontElem = xml->findFirstChildElement("font"))) { - if ((attr = fontElem->findAttr("typeface"))) { - fontName = attr->getValue()->copy(); - } - if ((attr = fontElem->findAttr("weight"))) { - if (!attr->getValue()->cmp("bold")) { - bold = gTrue; - } - } - if ((attr = fontElem->findAttr("posture"))) { - if (!attr->getValue()->cmp("italic")) { - italic = gTrue; - } - } - if ((attr = fontElem->findAttr("size"))) { - fontSize = getMeasurement(attr, fontSize); - } - } - if (!fontName) { - fontName = new GString("Courier"); - } - - //--- get field attributes - barcodeType = NULL; - wideNarrowRatio = 3; - moduleWidth = 0; - moduleHeight = 0; - dataLength = 0; - errorCorrectionLevel = 0; - textLocation = NULL; - if ((uiElem = xml->findFirstChildElement("ui")) && - (barcodeElem = uiElem->findFirstChildElement("barcode"))) { - if ((attr = barcodeElem->findAttr("type"))) { - barcodeType = attr->getValue(); - } - if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) { - s1 = attr->getValue(); - if ((p = strchr(s1->getCString(), ':'))) { - s2 = new GString(s1, 0, (int)(p - s1->getCString())); - wideNarrowRatio = atof(p + 1); - if (wideNarrowRatio == 0) { - wideNarrowRatio = 1; - } - wideNarrowRatio = atof(s2->getCString()) / wideNarrowRatio; - delete s2; - } else { - wideNarrowRatio = atof(s1->getCString()); - } - } - if ((attr = barcodeElem->findAttr("moduleWidth"))) { - moduleWidth = getMeasurement(attr, (0.25 / 25.4) * 72.0); // 0.25mm - } - if ((attr = barcodeElem->findAttr("moduleHeight"))) { - moduleHeight = getMeasurement(attr, (0.5 / 25.4) * 72.0); // 0.5mm - } - if ((attr = barcodeElem->findAttr("dataLength"))) { - dataLength = atoi(attr->getValue()->getCString()); - } - if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) { - errorCorrectionLevel = atoi(attr->getValue()->getCString()); - } - if ((attr = barcodeElem->findAttr("textLocation"))) { - textLocation = attr->getValue(); - } - } - if (!barcodeType) { - error(errSyntaxError, -1, "Missing 'type' attribute in XFA barcode field"); - delete fontName; - return; - } - - //--- compute the embedded text type position - doText = gTrue; - yText = yBarcode = hBarcode = 0; - if (textLocation && !textLocation->cmp("above")) { - textAlign = xfaVAlignTop; - yText = h; - yBarcode = 0; - hBarcode = h - fontSize; - } else if (textLocation && !textLocation->cmp("belowEmbedded")) { - textAlign = xfaVAlignBottom; - yText = 0; - yBarcode = 0; - hBarcode = h; - } else if (textLocation && !textLocation->cmp("aboveEmbedded")) { - textAlign = xfaVAlignTop; - yText = h; - yBarcode = 0; - hBarcode = h; - } else if (textLocation && !textLocation->cmp("none")) { - textAlign = xfaVAlignBottom; // make gcc happy - doText = gFalse; - } else { // default is "below" - textAlign = xfaVAlignBottom; - yText = 0; - yBarcode = fontSize; - hBarcode = h - fontSize; - } - wText = w; - - //--- remove extraneous start/stop chars - value2 = value->copy(); - if (!barcodeType->cmp("code3Of9")) { - if (value2->getLength() >= 1 && value2->getChar(0) == '*') { - value2->del(0); - } - if (value2->getLength() >= 1 && - value2->getChar(value2->getLength() - 1) == '*') { - value2->del(value2->getLength() - 1); - } - } - - //--- draw the bar code - if (!barcodeType->cmp("code3Of9")) { - if (!dataLength) { - error(errSyntaxError, -1, - "Missing 'dataLength' attribute in XFA barcode field"); - goto err; - } - appearBuf->append("0 g\n"); - wNarrow = w / ((7 + 3 * wideNarrowRatio) * (dataLength + 2)); - xx = 0; - for (i = -1; i <= value2->getLength(); ++i) { - if (i < 0 || i >= value2->getLength()) { - c = '*'; - } else { - c = value2->getChar(i) & 0x7f; - } - for (j = 0; j < 10; j += 2) { - appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", - xx, yBarcode, - (code3Of9Data[c][j] ? wideNarrowRatio : 1) * wNarrow, - hBarcode); - xx += ((code3Of9Data[c][j] ? wideNarrowRatio : 1) + - (code3Of9Data[c][j+1] ? wideNarrowRatio : 1)) * wNarrow; - } - } - // center the text on the drawn barcode (not the max length barcode) - wText = (value2->getLength() + 2) * (7 + 3 * wideNarrowRatio) * wNarrow; - } else if (!barcodeType->cmp("code128B")) { - if (!dataLength) { - error(errSyntaxError, -1, - "Missing 'dataLength' attribute in XFA barcode field"); - goto err; - } - appearBuf->append("0 g\n"); - wNarrow = w / (11 * (dataLength + 3) + 2); - xx = 0; - checksum = 0; - for (i = -1; i <= value2->getLength() + 1; ++i) { - if (i == -1) { - // start code B - c = 104; - checksum += c; - } else if (i == value2->getLength()) { - // checksum - c = checksum % 103; - } else if (i == value2->getLength() + 1) { - // stop code - c = 106; - } else { - c = value2->getChar(i) & 0xff; - if (c >= 32 && c <= 127) { - c -= 32; - } else { - c = 0; - } - checksum += (i + 1) * c; - } - for (j = 0; j < 6; j += 2) { - appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", - xx, yBarcode, - code128Data[c][j] * wNarrow, hBarcode); - xx += (code128Data[c][j] + code128Data[c][j+1]) * wNarrow; - } - } - // final bar of the stop code - appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", - xx, yBarcode, 2 * wNarrow, hBarcode); - // center the text on the drawn barcode (not the max length barcode) - wText = (11 * (value2->getLength() + 3) + 2) * wNarrow; - } else if (!barcodeType->cmp("pdf417")) { - drawPDF417Barcode(w, h, moduleWidth, moduleHeight, errorCorrectionLevel, - value2, appearBuf); - doText = gFalse; - } else { - error(errSyntaxError, -1, - "Unimplemented barcode type '{0:t}' in XFA barcode field", - barcodeType); - } - //~ add other barcode types here - - //--- draw the embedded text - if (doText) { - appearBuf->append("0 g\n"); - drawText(value2, gFalse, 0, - fontName, bold, italic, fontSize, - xfaHAlignCenter, textAlign, 0, yText, wText, h, gTrue, - fontDict, appearBuf); - } - - err: - delete fontName; - delete value2; -} - -Object *XFAFormField::getResources(Object *res) { - return xfaForm->resourceDict.copy(res); -} - -double XFAFormField::getMeasurement(ZxAttr *attr, double defaultVal) { - GString *s; - - if (!attr) { - return defaultVal; - } - s = attr->getValue(); - return getMeasurement(s, 0); -} - -double XFAFormField::getMeasurement(GString *s, int begin) { - double val, mul; - GBool neg; - int i; - - i = begin; - neg = gFalse; - if (i < s->getLength() && s->getChar(i) == '+') { - ++i; - } else if (i < s->getLength() && s->getChar(i) == '-') { - neg = gTrue; - ++i; - } - val = 0; - while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') { - val = val * 10 + s->getChar(i) - '0'; - ++i; - } - if (i < s->getLength() && s->getChar(i) == '.') { - ++i; - mul = 0.1; - while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') { - val += mul * (s->getChar(i) - '0'); - mul *= 0.1; - ++i; - } - } - if (neg) { - val = -val; - } - if (i+1 < s->getLength()) { - if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') { - val *= 72; - } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') { - // no change - } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') { - val *= 72 / 2.54; - } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') { - val *= 72 / 25.4; - } else { - // default to inches - val *= 72; - } - } else { - // default to inches - val *= 72; - } - return val; -} - -GString *XFAFormField::getFieldValue(const char *valueChildType) { - ZxElement *valueElem, *datasets, *data, *formElem, *elem; - char *p; - - // check the <datasets> packet - p = name->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(); - } - } - } - 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(); - if (xfaForm->xml->getRoot() && !strncmp(p, "form.", 5)) { - if ((formElem = xfaForm->xml->getRoot()->findFirstChildElement("form"))) { - elem = findFieldInFormElem(formElem, p + 5); - if (elem && - (valueElem = elem->findFirstChildElement("value")) && - (elem = valueElem->findFirstChildElement(valueChildType))) { - if (elem->getFirstChild() && - elem->getFirstChild()->isCharData() && - ((ZxCharData *)elem->getFirstChild()) - ->getData()->getLength() > 0) { - return ((ZxCharData *)elem->getFirstChild())->getData(); - } - } - } - } - - // check the <value> element within the field - if ((valueElem = xml->findFirstChildElement("value")) && - (elem = valueElem->findFirstChildElement(valueChildType))) { - if (elem->getFirstChild() && - elem->getFirstChild()->isCharData() && - ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) { - return ((ZxCharData *)elem->getFirstChild())->getData(); - } - } - - return NULL; -} - -ZxElement *XFAFormField::findFieldInDatasets(ZxElement *elem, char *partName) { - ZxNode *node; - ZxElement *result; - GString *nodeName; - char *next; - int curIdx, idx, n; - - curIdx = 0; - for (node = elem->getFirstChild(); node; node = node->getNextChild()) { - if (node->isElement()) { - nodeName = ((ZxElement *)node)->getType(); - n = nodeName->getLength(); - if (!strncmp(partName, nodeName->getCString(), n)) { - if (partName[n] == '[') { - idx = atoi(partName + n + 1); - if (idx == curIdx) { - for (++n; partName[n] && partName[n-1] != ']'; ++n) ; - } else { - ++curIdx; - continue; - } - } - if (!partName[n]) { - return (ZxElement *)node; - } else if (partName[n] == '.') { - 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; -} - -ZxElement *XFAFormField::findFieldInFormElem(ZxElement *elem, char *partName) { - ZxElement *elem2; - ZxNode *node; - ZxAttr *attr; - GString *nodeName; - int curIdx, idx, n; - - curIdx = 0; - for (node = elem->getFirstChild(); node; node = node->getNextChild()) { - if ((node->isElement("subform") || node->isElement("field")) && - ((attr = ((ZxElement *)node)->findAttr("name")))) { - nodeName = attr->getValue(); - n = nodeName->getLength(); - if (!strncmp(partName, nodeName->getCString(), n)) { - if (partName[n] == '[') { - idx = atoi(partName + n + 1); - if (idx == curIdx) { - for (++n; partName[n] && partName[n-1] != ']'; ++n) ; - } else { - ++curIdx; - continue; - } - } - if (!partName[n]) { - return (ZxElement *)node; - } else if (partName[n] == '.') { - return findFieldInFormElem((ZxElement *)node, partName + n + 1); - } - } - } else if (node->isElement("subform")) { - if ((elem2 = findFieldInFormElem((ZxElement *)node, partName))) { - return elem2; - } - } - } - return NULL; -} - -void XFAFormField::transform(int rot, double w, double h, - double *wNew, double *hNew, GString *appearBuf) { - switch (rot) { - case 0: - default: - appearBuf->appendf("1 0 0 1 0 {0:.4f} cm\n", -h); - break; - case 90: - appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", w); - *wNew = h; - *hNew = w; - break; - case 180: - appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", w, h); - *wNew = w; - *hNew = h; - break; - case 270: - appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", h); - *wNew = h; - *hNew = w; - break; - } -} - -void XFAFormField::drawText(GString *text, GBool multiLine, int combCells, - GString *fontName, GBool bold, - GBool italic, double fontSize, - XFAHorizAlign hAlign, XFAVertAlign vAlign, - 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; - - // 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))) { - fontTag = font->getTag()->getCString(); - ascent = font->getAscent() * fontSize; - descent = font->getDescent() * fontSize; - } else { - error(errSyntaxError, -1, "Couldn't find a font for '{0:t}', {1:s}, {2:s} used in XFA field", - fontName, bold ? "bold" : "non-bold", - italic ? "italic" : "non-italic"); - fontTag = "xpdf_default_font"; - ascent = 0.718 * fontSize; - descent = -0.207 * fontSize; - } - - // setup - rectW = rectH = 0; - rectI = appearBuf->getLength(); - appearBuf->append("BT\n"); - appearBuf->appendf("/{0:s} {1:.2f} Tf\n", fontTag, fontSize); - - // multi-line text - if (multiLine) { - - // compute line height - lineHeight = 1.2 * fontSize; - - // handle bottom/middle alignment - if (vAlign == xfaVAlignBottom || vAlign == xfaVAlignMiddle) { - nLines = 0; - i = 0; - while (i < text2->getLength()) { - getNextLine(text2, i, font, fontSize, w, &j, &tw, &k); - ++nLines; - i = k; - } - blkH = nLines * lineHeight - (lineHeight - fontSize); - if (vAlign == xfaVAlignBottom) { - yTop = y + blkH; - } else { - yTop = y + 0.5 * (h + blkH); - } - if (yTop > y + h) { - yTop = y + h; - } - } else { - yTop = y + h; - } - - // write a series of lines of text - line = 0; - i = 0; - while (i < text2->getLength()) { - - getNextLine(text2, i, font, fontSize, w, &j, &tw, &k); - if (tw > rectW) { - rectW = tw; - } - - // compute text start position - switch (hAlign) { - case xfaHAlignLeft: - default: - xx = x; - break; - case xfaHAlignCenter: - xx = x + 0.5 * (w - tw); - break; - case xfaHAlignRight: - xx = x + w - tw; - break; - } - yy = yTop - line * lineHeight - ascent; - - // draw the line - appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy); - appearBuf->append('('); - for (; i < j; ++i) { - c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append((char)c); - } else if (c < 0x20 || c >= 0x80) { - appearBuf->appendf("\\{0:03o}", c); - } else { - appearBuf->append((char)c); - } - } - appearBuf->append(") Tj\n"); - - // next line - i = k; - ++line; - } - rectH = line * lineHeight; - rectY = y + h - rectH; - - // comb formatting - } else if (combCells > 0) { - - // compute comb spacing - tw = w / combCells; - - // compute text start position - switch (hAlign) { - case xfaHAlignLeft: - default: - xx = x; - break; - case xfaHAlignCenter: - xx = x + (int)(0.5 * (combCells - text2->getLength())) * tw; - break; - case xfaHAlignRight: - xx = x + w - text2->getLength() * tw; - break; - } - rectW = text2->getLength() * tw; - switch (vAlign) { - case xfaVAlignTop: - default: - yy = y + h - ascent; - break; - case xfaVAlignMiddle: - yy = y + 0.5 * (h - (ascent + descent)); - break; - case xfaVAlignBottom: - yy = y - descent; - break; - } - rectY = yy + descent; - rectH = ascent - descent; - - // write the text string - 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", - xx + i * tw + 0.5 * (tw - charWidth), yy); - } else { - appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", - xx + i * tw, yy); - } - appearBuf->append('('); - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append((char)c); - } else if (c < 0x20 || c >= 0x80) { - appearBuf->appendf("{0:.4f} 0 Td\n", w); - } else { - appearBuf->append((char)c); - } - appearBuf->append(") Tj\n"); - } - - // regular (non-comb) formatting - } else { - - // compute string width - if (font && !font->isCIDFont()) { - tw = 0; - for (i = 0; i < text2->getLength(); ++i) { - tw += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i)); - } - } else { - // otherwise, make a crude estimate - tw = text2->getLength() * 0.5; - } - tw *= fontSize; - rectW = tw; - - // compute text start position - switch (hAlign) { - case xfaHAlignLeft: - default: - xx = x; - break; - case xfaHAlignCenter: - xx = x + 0.5 * (w - tw); - break; - case xfaHAlignRight: - xx = x + w - tw; - break; - } - switch (vAlign) { - case xfaVAlignTop: - default: - yy = y + h - ascent; - break; - case xfaVAlignMiddle: - yy = y + 0.5 * (h - (ascent + descent)); - break; - case xfaVAlignBottom: - yy = y - descent; - break; - } - rectY = yy + descent; - rectH = ascent - descent; - appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx, yy); - - // write the text string - appearBuf->append('('); - for (i = 0; i < text2->getLength(); ++i) { - c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append((char)c); - } else if (c < 0x20 || c >= 0x80) { - appearBuf->appendf("\\{0:03o}", c); - } else { - appearBuf->append((char)c); - } - } - appearBuf->append(") Tj\n"); - } - - // cleanup - appearBuf->append("ET\n"); - - // draw a white rectangle behind the text - if (whiteBackground) { - switch (hAlign) { - case xfaHAlignLeft: - default: - rectX = x; - break; - case xfaHAlignCenter: - rectX = x + 0.5 * (w - rectW); - break; - case xfaHAlignRight: - rectX = x + w - rectW; - break; - } - rectX -= 0.25 * fontSize; - rectW += 0.5 * fontSize; - rectY -= 0.1 * fontSize; - rectH += 0.2 * fontSize; - s = GString::format("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n", - rectX, rectY, rectW, rectH); - appearBuf->insert(rectI, s); - delete s; - } - - delete text2; -} - -// Searches <fontDict> for a font matching(<fontName>, <bold>, -// <italic>). -GfxFont *XFAFormField::findFont(GfxFontDict *fontDict, GString *fontName, - GBool bold, GBool italic) { - GString *reqName, *testName; - GfxFont *font; - GBool foundName, foundBold, foundItalic; - char *p; - char c; - int i, j; - - if (!fontDict) { - return NULL; - } - - reqName = new GString(); - for (i = 0; i < fontName->getLength(); ++i) { - c = fontName->getChar(i); - if (c != ' ') { - reqName->append(c); - } - } - - for (i = 0; i < fontDict->getNumFonts(); ++i) { - font = fontDict->getFont(i); - if (!font || !font->getName()) { - continue; - } - testName = new GString(); - for (j = 0; j < font->getName()->getLength(); ++j) { - c = font->getName()->getChar(j); - if (c != ' ') { - testName->append(c); - } - } - foundName = foundBold = foundItalic = gFalse; - for (p = testName->getCString(); *p; ++p) { - if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) { - foundName = gTrue; - } - if (!strncasecmp(p, "bold", 4)) { - foundBold = gTrue; - } - if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) { - foundItalic = gTrue; - } - } - delete testName; - if (foundName && foundBold == bold && foundItalic == italic) { - delete reqName; - return font; - } - } - - delete reqName; - return NULL; -} - -// Searches the font resource dict for a font matching(<name>, <bold>, -// <italic>), and returns the font object ID. This does not require a -// GfxFontDict, and it does not do any parsing of font objects beyond -// looking at BaseFont. -Ref XFAFormField::findFontName(GString *name, GBool bold, GBool italic) { - Object fontDictObj, fontObj, baseFontObj, fontRef; - Ref fontID; - GString *reqName, *testName; - GBool foundName, foundBold, foundItalic; - char *p; - char c; - int i; - - fontID.num = fontID.gen = -1; - - // remove space chars from the requested name - reqName = new GString(); - for (i = 0; i < name->getLength(); ++i) { - c = name->getChar(i); - if (c != ' ') { - reqName->append(c); - } - } - - if (xfaForm->resourceDict.isDict()) { - if (xfaForm->resourceDict.dictLookup("Font", &fontDictObj)->isDict()) { - for (i = 0; i < fontDictObj.dictGetLength() && fontID.num < 0; ++i) { - fontDictObj.dictGetVal(i, &fontObj); - if (fontObj.dictLookup("BaseFont", &baseFontObj)->isName()) { - - // remove space chars from the font name - testName = new GString(); - for (p = baseFontObj.getName(); *p; ++p) { - if (*p != ' ') { - testName->append(p); - } - } - - // compare the names - foundName = foundBold = foundItalic = gFalse; - for (p = testName->getCString(); *p; ++p) { - if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) { - foundName = gTrue; - } - if (!strncasecmp(p, "bold", 4)) { - foundBold = gTrue; - } - if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) { - foundItalic = gTrue; - } - } - delete testName; - if (foundName && foundBold == bold && foundItalic == italic) { - if (fontObj.dictGetValNF(i, &fontRef)) { - fontID = fontRef.getRef(); - } - fontRef.free(); - } - } - - baseFontObj.free(); - fontObj.free(); - } - } - fontDictObj.free(); - } - delete reqName; - - return fontID; -} - -// Figure out how much text will fit on the next line. Returns: -// *end = one past the last character to be included -// *width = width of the characters start .. end-1 -// *next = index of first character on the following line -void XFAFormField::getNextLine(GString *text, int start, - GfxFont *font, double fontSize, double wMax, - int *end, double *width, int *next) { - double w, dw; - int j, k, c; - - // figure out how much text will fit on the line - //~ what does Adobe do with tabs? - w = 0; - for (j = start; j < text->getLength() && w <= wMax; ++j) { - c = text->getChar(j) & 0xff; - if (c == 0x0a || c == 0x0d) { - break; - } - if (font && !font->isCIDFont()) { - dw = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize; - } else { - // otherwise, make a crude estimate - dw = 0.5 * fontSize; - } - w += dw; - } - if (w > wMax) { - for (k = j; k > start && text->getChar(k-1) != ' '; --k) ; - for (; k > start && text->getChar(k-1) == ' '; --k) ; - if (k > start) { - j = k; - } - if (j == start) { - // handle the pathological case where the first character is - // too wide to fit on the line all by itself - j = start + 1; - } - } - *end = j; - - // compute the width - w = 0; - for (k = start; k < j; ++k) { - if (font && !font->isCIDFont()) { - dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize; - } else { - // otherwise, make a crude estimate - dw = 0.5 * fontSize; - } - w += dw; - } - *width = w; - - // next line - while (j < text->getLength() && text->getChar(j) == ' ') { - ++j; - } - if (j < text->getLength() && text->getChar(j) == 0x0d) { - ++j; - } - if (j < text->getLength() && text->getChar(j) == 0x0a) { - ++j; - } - *next = j; -} - -//------------------------------------------------------------------------ -// 'picture' formatting -//------------------------------------------------------------------------ - -class XFAPictureNode { -public: - virtual ~XFAPictureNode() {} - virtual GBool isLiteral() { return gFalse; } - virtual GBool isSign() { return gFalse; } - virtual GBool isDigit() { return gFalse; } - virtual GBool isDecPt() { return gFalse; } - virtual GBool isSeparator() { return gFalse; } - virtual GBool isYear() { return gFalse; } - virtual GBool isMonth() { return gFalse; } - virtual GBool isDay() { return gFalse; } - virtual GBool isHour() { return gFalse; } - virtual GBool isMinute() { return gFalse; } - virtual GBool isSecond() { return gFalse; } - virtual GBool isChar() { return gFalse; } -}; - -class XFAPictureLiteral: public XFAPictureNode { -public: - XFAPictureLiteral(GString *sA) { s = sA; } - virtual ~XFAPictureLiteral() { delete s; } - virtual GBool isLiteral() { return gTrue; } - GString *s; -}; - -class XFAPictureSign: public XFAPictureNode { -public: - XFAPictureSign(char cA) { c = cA; } - virtual GBool isSign() { return gTrue; } - char c; -}; - -class XFAPictureDigit: public XFAPictureNode { -public: - XFAPictureDigit(char cA) { c = cA; pos = 0; } - virtual GBool isDigit() { return gTrue; } - char c; - int pos; -}; - -class XFAPictureDecPt: public XFAPictureNode { -public: - XFAPictureDecPt() { } - virtual GBool isDecPt() { return gTrue; } -}; - -class XFAPictureSeparator: public XFAPictureNode { -public: - XFAPictureSeparator() { } - virtual GBool isSeparator() { return gTrue; } -}; - -class XFAPictureYear: public XFAPictureNode { -public: - XFAPictureYear(int nDigitsA) { nDigits = nDigitsA; } - virtual GBool isYear() { return gTrue; } - int nDigits; -}; - -class XFAPictureMonth: public XFAPictureNode { -public: - XFAPictureMonth(int nDigitsA) { nDigits = nDigitsA; } - virtual GBool isMonth() { return gTrue; } - int nDigits; -}; - -class XFAPictureDay: public XFAPictureNode { -public: - XFAPictureDay(int nDigitsA) { nDigits = nDigitsA; } - virtual GBool isDay() { return gTrue; } - int nDigits; -}; - -class XFAPictureHour: public XFAPictureNode { -public: - XFAPictureHour(GBool is24HourA, int nDigitsA) - { is24Hour = is24HourA; nDigits = nDigitsA; } - virtual GBool isHour() { return gTrue; } - GBool is24Hour; - int nDigits; -}; - -class XFAPictureMinute: public XFAPictureNode { -public: - XFAPictureMinute(int nDigitsA) { nDigits = nDigitsA; } - virtual GBool isMinute() { return gTrue; } - int nDigits; -}; - -class XFAPictureSecond: public XFAPictureNode { -public: - XFAPictureSecond(int nDigitsA) { nDigits = nDigitsA; } - virtual GBool isSecond() { return gTrue; } - int nDigits; -}; - -class XFAPictureChar: public XFAPictureNode { -public: - XFAPictureChar() {} - virtual GBool isChar() { return gTrue; } -}; - -GString *XFAFormField::pictureFormatDateTime(GString *value, GString *picture) { - GList *pic; - XFAPictureNode *node; - GString *ret, *s; - char c; - int year, month, day, hour, min, sec; - int len, picStart, picEnd, u, n, i, j; - - len = value->getLength(); - if (len == 0) { - return value->copy(); - } - - //--- parse the value - - // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)? - // where: - // - the '-'s and ':'s are optional - // - the 'T' is literal - // - we're ignoring optional time zone info at the end - // (if the value is not in this canonical format, we just punt and - // return the value string) - //~ another option would be to parse the value following the - //~ <ui><picture> element - year = month = day = hour = min = sec = 0; - i = 0; - if (!(i + 4 <= len && isValidInt(value, i, 4))) { - return value->copy(); - } - year = convertInt(value, i, 4); - i += 4; - if (i < len && value->getChar(i) == '-') { - ++i; - } - if (i + 2 <= len && isValidInt(value, i, 2)) { - month = convertInt(value, i, 2); - i += 2; - if (i < len && value->getChar(i) == '-') { - ++i; - } - if (i + 2 <= len && isValidInt(value, i, 2)) { - day = convertInt(value, i, 2); - i += 2; - } - } - if (i < len) { - if (value->getChar(i) != 'T') { - return value->copy(); - } - ++i; - if (!(i + 2 <= len && isValidInt(value, i, 2))) { - return value->copy(); - } - hour = convertInt(value, i, 2); - i += 2; - if (i < len && value->getChar(i) == ':') { - ++i; - } - if (i + 2 <= len && isValidInt(value, i, 2)) { - min = convertInt(value, i, 2); - i += 2; - if (i < len && value->getChar(i) == ':') { - ++i; - } - if (i + 2 <= len && isValidInt(value, i, 2)) { - sec = convertInt(value, i, 2); - i += 2; - } - } - } - if (i < len) { - return value->copy(); - } - - //--- skip the category and locale in the picture - - picStart = 0; - picEnd = picture->getLength(); - for (i = 0; i < picture->getLength(); ++i) { - c = picture->getChar(i); - if (c == '{') { - picStart = i + 1; - for (picEnd = picStart; - picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; - ++picEnd) ; - break; - } else if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '(' || - c == ')')) { - break; - } - } - - //--- parse the picture - - pic = new GList(); - i = picStart; - while (i < picEnd) { - c = picture->getChar(i); - ++i; - if (c == '\'') { - s = new GString(); - while (i < picEnd) { - c = picture->getChar(i); - if (c == '\'') { - ++i; - if (i < picEnd && picture->getChar(i) == '\'') { - s->append('\''); - ++i; - } else { - break; - } - } else if (c == '\\') { - ++i; - if (i == picEnd) { - break; - } - c = picture->getChar(i); - ++i; - if (c == 'u' && i+4 <= picEnd) { - u = 0; - for (j = 0; j < 4; ++j, ++i) { - c = picture->getChar(i); - u <<= 4; - if (c >= '0' && c <= '9') { - u += c - '0'; - } else if (c >= 'a' && c <= 'f') { - u += c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - u += c - 'A' + 10; - } - } - //~ this should convert to UTF-8 (?) - if (u <= 0xff) { - s->append((char)u); - } - } else { - s->append(c); - } - } else { - s->append(c); - } - } - pic->append(new XFAPictureLiteral(s)); - } else if (c == ',' || c == '-' || c == ':' || - c == '/' || c == '.' || c == ' ') { - s = new GString(); - s->append(c); - pic->append(new XFAPictureLiteral(s)); - } else if (c == 'Y') { - for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ; - pic->append(new XFAPictureYear(n)); - } else if (c == 'M') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ; - pic->append(new XFAPictureMonth(n)); - } else if (c == 'D') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ; - pic->append(new XFAPictureDay(n)); - } else if (c == 'h') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ; - pic->append(new XFAPictureHour(gFalse, n)); - } else if (c == 'H') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ; - pic->append(new XFAPictureHour(gTrue, n)); - } else if (c == 'M') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ; - pic->append(new XFAPictureMinute(n)); - } else if (c == 'S') { - for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ; - pic->append(new XFAPictureSecond(n)); - } - } - - //--- generate formatted text - - ret = new GString(); - for (i = 0; i < pic->getLength(); ++i) { - node = (XFAPictureNode *)pic->get(i); - if (node->isLiteral()) { - ret->append(((XFAPictureLiteral *)node)->s); - } else if (node->isYear()) { - if (((XFAPictureYear *)node)->nDigits == 2) { - if (year >= 1930 && year < 2030) { - ret->appendf("{0:02d}", year % 100); - } else { - ret->append("??"); - } - } else { - ret->appendf("{0:04d}", year); - } - } else if (node->isMonth()) { - if (((XFAPictureMonth *)node)->nDigits == 1) { - ret->appendf("{0:d}", month); - } else { - ret->appendf("{0:02d}", month); - } - } else if (node->isDay()) { - if (((XFAPictureDay *)node)->nDigits == 1) { - ret->appendf("{0:d}", day); - } else { - ret->appendf("{0:02d}", day); - } - } else if (node->isHour()) { - if (((XFAPictureHour *)node)->is24Hour) { - n = hour; - } else { - n = hour % 12; - if (n == 0) { - n = 12; - } - } - if (((XFAPictureHour *)node)->nDigits == 1) { - ret->appendf("{0:d}", n); - } else { - ret->appendf("{0:02d}", n); - } - } else if (node->isMinute()) { - if (((XFAPictureMinute *)node)->nDigits == 1) { - ret->appendf("{0:d}", min); - } else { - ret->appendf("{0:02d}", min); - } - } else if (node->isSecond()) { - if (((XFAPictureSecond *)node)->nDigits == 1) { - ret->appendf("{0:d}", sec); - } else { - ret->appendf("{0:02d}", sec); - } - } - } - deleteGList(pic, XFAPictureNode); - - return ret; -} - -GString *XFAFormField::pictureFormatNumber(GString *value, GString *picture) { - GList *pic; - XFAPictureNode *node; - GString *ret, *s; - GBool neg, haveDigits; - char c; - int start, decPt, trailingZero, len; - int picStart, picEnd, u, pos, i, j; - - len = value->getLength(); - if (len == 0) { - return value->copy(); - } - - //--- parse the value - - // -nnnn.nnnn0000 - // ^ ^ ^ ^ - // | | | +-- len - // | | +------ trailingZero - // | +----------- decPt - // +--------------- start - start = 0; - neg = gFalse; - if (value->getChar(start) == '-') { - neg = gTrue; - ++start; - } else if (value->getChar(start) == '+') { - ++start; - } - for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ; - for (trailingZero = len; - trailingZero > decPt && value->getChar(trailingZero - 1) == '0'; - --trailingZero) ; - - //--- skip the category and locale in the picture - - picStart = 0; - picEnd = picture->getLength(); - for (i = 0; i < picture->getLength(); ++i) { - c = picture->getChar(i); - if (c == '{') { - picStart = i + 1; - for (picEnd = picStart; - picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; - ++picEnd) ; - break; - } else if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '(' || - c == ')')) { - break; - } - } - - //--- parse the picture - - pic = new GList(); - i = picStart; - while (i < picEnd) { - c = picture->getChar(i); - ++i; - if (c == '\'') { - s = new GString(); - while (i < picEnd) { - c = picture->getChar(i); - if (c == '\'') { - ++i; - if (i < picEnd && picture->getChar(i) == '\'') { - s->append('\''); - ++i; - } else { - break; - } - } else if (c == '\\') { - ++i; - if (i == picEnd) { - break; - } - c = picture->getChar(i); - ++i; - if (c == 'u' && i+4 <= picEnd) { - u = 0; - for (j = 0; j < 4; ++j, ++i) { - c = picture->getChar(i); - u <<= 4; - if (c >= '0' && c <= '9') { - u += c - '0'; - } else if (c >= 'a' && c <= 'F') { - u += c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - u += c - 'A' + 10; - } - } - //~ this should convert to UTF-8 (?) - if (u <= 0xff) { - s->append((char)u); - } - } else { - s->append(c); - } - } else { - s->append(c); - ++i; - } - } - pic->append(new XFAPictureLiteral(s)); - } else if (c == '-' || c == ':' || c == '/' || c == ' ') { - s = new GString(); - s->append(c); - pic->append(new XFAPictureLiteral(s)); - } else if (c == 's' || c == 'S') { - pic->append(new XFAPictureSign(c)); - } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') { - pic->append(new XFAPictureDigit(c)); - } else if (c == '.') { - pic->append(new XFAPictureDecPt()); - } else if (c == ',') { - pic->append(new XFAPictureSeparator()); - } - } - for (i = 0; i < pic->getLength(); ++i) { - node = (XFAPictureNode *)pic->get(i); - if (node->isDecPt()) { - break; - } - } - pos = 0; - for (j = i - 1; j >= 0; --j) { - node = (XFAPictureNode *)pic->get(j); - if (node->isDigit()) { - ((XFAPictureDigit *)node)->pos = pos; - ++pos; - } - } - pos = -1; - for (j = i + 1; j < pic->getLength(); ++j) { - node = (XFAPictureNode *)pic->get(j); - if (node->isDigit()) { - ((XFAPictureDigit *)node)->pos = pos; - --pos; - } - } - - //--- generate formatted text - - ret = new GString(); - haveDigits = gFalse; - for (i = 0; i < pic->getLength(); ++i) { - node = (XFAPictureNode *)pic->get(i); - if (node->isLiteral()) { - ret->append(((XFAPictureLiteral *)node)->s); - } else if (node->isSign()) { - if (((XFAPictureSign *)node)->c == 'S') { - ret->append(neg ? '-' : ' '); - } else { - if (neg) { - ret->append('-'); - } - } - } else if (node->isDigit()) { - pos = ((XFAPictureDigit *)node)->pos; - c = ((XFAPictureDigit *)node)->c; - if (pos >= 0 && pos < decPt - start) { - ret->append(value->getChar(decPt - 1 - pos)); - haveDigits = gTrue; - } else if (pos < 0 && -pos <= trailingZero - decPt - 1) { - ret->append(value->getChar(decPt - pos)); - haveDigits = gTrue; - } else if (c == '8' && - pos < 0 && - -pos <= len - decPt - 1) { - ret->append('0'); - haveDigits = gTrue; - } else if (c == '9') { - ret->append('0'); - haveDigits = gTrue; - } else if (c == 'Z' && pos >= 0) { - ret->append(' '); - } - } else if (node->isDecPt()) { - if (!(i+1 < pic->getLength() && - ((XFAPictureNode *)pic->get(i+1))->isDigit() && - ((XFAPictureDigit *)pic->get(i+1))->c == 'z') || - trailingZero > decPt + 1) { - ret->append('.'); - } - } else if (node->isSeparator()) { - if (haveDigits) { - ret->append(','); - } - } - } - deleteGList(pic, XFAPictureNode); - - return ret; -} - -GString *XFAFormField::pictureFormatText(GString *value, GString *picture) { - GList *pic; - XFAPictureNode *node; - GString *ret, *s; - char c; - int len, picStart, picEnd, u, i, j; - - len = value->getLength(); - if (len == 0) { - return value->copy(); - } - - //--- skip the category and locale in the picture - - picStart = 0; - picEnd = picture->getLength(); - for (i = 0; i < picture->getLength(); ++i) { - c = picture->getChar(i); - if (c == '{') { - picStart = i + 1; - for (picEnd = picStart; - picEnd < picture->getLength() && picture->getChar(picEnd) != '}'; - ++picEnd) ; - break; - } else if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '(' || - c == ')')) { - break; - } - } - - //--- parse the picture - - pic = new GList(); - i = picStart; - while (i < picEnd) { - c = picture->getChar(i); - ++i; - if (c == '\'') { - s = new GString(); - while (i < picEnd) { - c = picture->getChar(i); - if (c == '\'') { - ++i; - if (i < picEnd && picture->getChar(i) == '\'') { - s->append('\''); - ++i; - } else { - break; - } - } else if (c == '\\') { - ++i; - if (i == picEnd) { - break; - } - c = picture->getChar(i); - ++i; - if (c == 'u' && i+4 <= picEnd) { - u = 0; - for (j = 0; j < 4; ++j, ++i) { - c = picture->getChar(i); - u <<= 4; - if (c >= '0' && c <= '9') { - u += c - '0'; - } else if (c >= 'a' && c <= 'F') { - u += c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - u += c - 'A' + 10; - } - } - //~ this should convert to UTF-8 (?) - if (u <= 0xff) { - s->append((char)u); - } - } else { - s->append(c); - } - } else { - s->append(c); - ++i; - } - } - pic->append(new XFAPictureLiteral(s)); - } else if (c == ',' || c == '-' || c == ':' || - c == '/' || c == '.' || c == ' ') { - s = new GString(); - s->append(c); - pic->append(new XFAPictureLiteral(s)); - } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') { - pic->append(new XFAPictureChar()); - } - } - - //--- generate formatted text - - ret = new GString(); - j = 0; - for (i = 0; i < pic->getLength(); ++i) { - node = (XFAPictureNode *)pic->get(i); - if (node->isLiteral()) { - ret->append(((XFAPictureLiteral *)node)->s); - } else if (node->isChar()) { - // if there are more chars in the picture than in the value, - // Adobe renders the value as-is, without picture formatting - if (j >= value->getLength()) { - delete ret; - ret = value->copy(); - break; - } - ret->append(value->getChar(j)); - ++j; - } - } - deleteGList(pic, XFAPictureNode); - - return ret; -} - -GBool XFAFormField::isValidInt(GString *s, int start, int len) { - int i; - - for (i = 0; i < len; ++i) { - if (!(start + i < s->getLength() && - s->getChar(start + i) >= '0' && - s->getChar(start + i) <= '9')) { - return gFalse; - } - } - return gTrue; -} - -int XFAFormField::convertInt(GString *s, int start, int len) { - char c; - int x, i; - - x = 0; - for (i = 0; i < len && start + i < s->getLength(); ++i) { - c = s->getChar(start + i); - if (c < '0' || c > '9') { - break; - } - x = x * 10 + (c - '0'); - } - return x; -} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h deleted file mode 100644 index 2f56e728a98..00000000000 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h +++ /dev/null @@ -1,181 +0,0 @@ -//======================================================================== -// -// XFAForm.h -// -// Copyright 2012 Glyph & Cog, LLC -// -//======================================================================== - -#ifndef XFAFORM_H -#define XFAFORM_H - -#include <aconf.h> - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "Form.h" - -class GHash; -class XFATableInfo; -class ZxDoc; -class ZxElement; -class ZxAttr; - -//------------------------------------------------------------------------ - -enum XFAHorizAlign { - xfaHAlignLeft, - xfaHAlignCenter, - xfaHAlignRight -}; - -enum XFAVertAlign { - xfaVAlignTop, - xfaVAlignBottom, - xfaVAlignMiddle -}; - -//------------------------------------------------------------------------ - -class XFAForm: public Form { -public: - - static XFAForm *load(PDFDoc *docA, Catalog *catalog, - Object *acroFormObj, Object *xfaObj); - - virtual ~XFAForm(); - - virtual const char *getType() { return "XFA"; } - - virtual void draw(int pageNum, Gfx *gfx, GBool printing); - - virtual int getNumFields(); - virtual FormField *getField(int idx); - -private: - - XFAForm(PDFDoc *docA, int nPagesA, ZxDoc *xmlA, - Object *resourceDictA, GBool fullXFAA); - void scanNode(ZxElement *elem, - GString *parentName, GString *parentFullName, - GBool inPageSet, XFATableInfo *tableInfo, - GHash *nameCount, GHash *nameIdx, - GHash *fullNameCount, GHash *fullNameIdx, - GString *exclGroupName, Catalog *catalog); - void scanNames(ZxElement *elem, GHash *nameCount); - void scanFullNames(ZxElement *elem, GHash *fullNameCount); - void scanField(ZxElement *elem, GString *name, GString *fullName, - GString *exclGroupName, GBool inPageSet, - XFATableInfo *tableInfo, int colSpan, - Catalog *catalog); - void scanNonField(ZxElement *elem, GString *name, GString *fullName, - GBool inPageSet, - XFATableInfo *tableInfo, int colSpan, - GHash *nameCount, GHash *nameIdx, - GHash *fullNameCount, GHash *fullNameIdx, - Catalog *catalog); - GString *getNodeName(ZxElement *elem); - GString *getNodeFullName(ZxElement *elem); - GBool nodeIsBindGlobal(ZxElement *elem); - - ZxDoc *xml; - GList *fields; // [XFAFormField] - Object resourceDict; - GBool fullXFA; // true for "Full XFA", false for - // "XFA Foreground" - int nPages; // number of pages in PDF file - double *pageOffsetX, // x,y offset for each page - *pageOffsetY; - int pageSetNPages; // number of pages in pageSet element - int curPageNum; // current page number - used by scanFields() - double curXOffset, // current x,y offset - used by scanFields() - curYOffset; - - friend class XFAFormField; -}; - -//------------------------------------------------------------------------ - -class XFAFormField: public FormField { -public: - - XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, - GString *nameA, GString *fullNameA, GString *exclGroupNameA, - int pageNumA, double xOffsetA, double yOffsetA, - double columnWidthA, double rowHeightA); - - virtual ~XFAFormField(); - - virtual int getPageNum(); - virtual const char *getType(); - virtual Unicode *getName(int *length); - virtual Unicode *getValue(int *length); - virtual void getBBox(double *llx, double *lly, double *urx, double *ury); - virtual void getFont(Ref *fontID, double *fontSize); - virtual void getColor(double *red, double *green, double *blue); - - virtual Object *getResources(Object *res); - -private: - - Unicode *utf8ToUnicode(GString *s, int *length); - void draw(int pageNumA, Gfx *gfx, GBool printing, GfxFontDict *fontDict); - void getRectangle(double *xfaX, double *xfaY, - double *xfaW, double *xfaH, - double *pdfX, double *pdfY, - double *pdfW, double *pdfH, - int *pdfRot); - void drawTextEdit(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf); - void drawCheckButton(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf); - void drawBarCode(GfxFontDict *fontDict, - double w, double h, int rot, - GString *appearBuf); - static double getMeasurement(ZxAttr *attr, double defaultVal); - static double getMeasurement(GString *s, int begin); - GString *getFieldValue(const char *valueChildType); - ZxElement *findFieldInDatasets(ZxElement *elem, char *partName); - ZxElement *findFieldInFormElem(ZxElement *elem, char *partName); - void transform(int rot, double w, double h, - double *wNew, double *hNew, GString *appearBuf); - void drawText(GString *text, GBool multiLine, int combCells, - GString *fontName, GBool bold, - GBool italic, double fontSize, - XFAHorizAlign hAlign, XFAVertAlign vAlign, - double x, double y, double w, double h, - GBool whiteBackground, - GfxFontDict *fontDict, GString *appearBuf); - GfxFont *findFont(GfxFontDict *fontDict, GString *fontName, - GBool bold, GBool italic); - Ref findFontName(GString *name, GBool bold, GBool italic); - void getNextLine(GString *text, int start, - GfxFont *font, double fontSize, double wMax, - int *end, double *width, int *next); - GString *pictureFormatDateTime(GString *value, GString *picture); - GString *pictureFormatNumber(GString *value, GString *picture); - GString *pictureFormatText(GString *value, GString *picture); - GBool isValidInt(GString *s, int start, int len); - int convertInt(GString *s, int start, int len); - - XFAForm *xfaForm; - ZxElement *xml; - GString *name; - GString *fullName; - GString *exclGroupName; - int pageNum; - double xOffset, yOffset; - double columnWidth; // column width, if this field is in a - // table; otherwise zero - double rowHeight; // row height, if this field is in a - // table; otherwise zero - - friend class XFAForm; - friend class XFATableInfo; -}; - -#endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc new file mode 100644 index 00000000000..6928fa9c9a8 --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc @@ -0,0 +1,675 @@ +//======================================================================== +// +// XFAScanner.cc +// +// Copyright 2020 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "GString.h" +#include "GHash.h" +#include "Object.h" +#include "Error.h" +#include "Zoox.h" +#include "XFAScanner.h" + +//------------------------------------------------------------------------ + +// fields have two names: +// +// name: +// - nodes with bind=global set the index to 0 ("foo[0]") regardless +// of the number of nodes with the same name +// - nodes with bind=none are dropped from the name +// - <area> nodes are dropped from the name +// - used for field value lookup in <xfa:datasets> +// +// fullName: +// - all named nodes are treated the same, regardless of bind=global +// or bind=none +// - <area> nodes are included in the name, but don't reset the +// numbering (i.e., <area> nodes are "transparent" with respect to +// node numbering) +// - used for field value lookup in <form> +// - used for matching with AcroForm names +// +// Both names use indexes on all nodes, even if there's only one node +// with the name -- this isn't correct for XFA naming, but it matches +// the AcroForm behavior. + +//------------------------------------------------------------------------ + +XFAFieldLayoutInfo::XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA, + XFAFieldLayoutVAlign vAlignA) { + hAlign = hAlignA; + vAlign = vAlignA; +} + +//------------------------------------------------------------------------ + +XFAFieldPictureInfo::XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA, + GString *formatA) { + subtype = subtypeA; + format = formatA; +} + +XFAFieldPictureInfo::~XFAFieldPictureInfo() { + delete format; +} + +//------------------------------------------------------------------------ + +XFAFieldBarcodeInfo::XFAFieldBarcodeInfo(GString *barcodeTypeA, + double wideNarrowRatioA, + double moduleWidthA, + double moduleHeightA, + int dataLengthA, + int errorCorrectionLevelA, + GString *textLocationA) { + barcodeType = barcodeTypeA; + wideNarrowRatio = wideNarrowRatioA; + moduleWidth = moduleWidthA; + moduleHeight = moduleHeightA; + dataLength = dataLengthA; + errorCorrectionLevel = errorCorrectionLevelA; + textLocation = textLocationA; +} + +XFAFieldBarcodeInfo::~XFAFieldBarcodeInfo() { + delete barcodeType; + delete textLocation; +} + +//------------------------------------------------------------------------ + +XFAField::XFAField(GString *nameA, GString *fullNameA, GString *valueA, + XFAFieldLayoutInfo *layoutInfoA, + XFAFieldPictureInfo *pictureInfoA, + XFAFieldBarcodeInfo *barcodeInfoA) + : name(nameA) + , fullName(fullNameA) + , value(valueA) + , layoutInfo(layoutInfoA) + , pictureInfo(pictureInfoA) + , barcodeInfo(barcodeInfoA) +{ +} + +XFAField::~XFAField() { + delete name; + delete fullName; + delete value; + delete layoutInfo; + delete pictureInfo; + delete barcodeInfo; +} + +//------------------------------------------------------------------------ + +XFAScanner *XFAScanner::load(Object *xfaObj) { + GString *xfaData = readXFAStreams(xfaObj); + if (!xfaData) { + return NULL; + } + ZxDoc *xml = ZxDoc::loadMem(xfaData->getCString(), xfaData->getLength()); + delete xfaData; + if (!xml) { + error(errSyntaxError, -1, "Invalid XML in XFA form"); + return NULL; + } + + XFAScanner *scanner = new XFAScanner(); + + if (xml->getRoot()) { + GHash *formValues = scanner->scanFormValues(xml->getRoot()); + ZxElement *dataElem = NULL; + ZxElement *datasets = + xml->getRoot()->findFirstChildElement("xfa:datasets"); + if (datasets) { + dataElem = datasets->findFirstChildElement("xfa:data"); + } + ZxElement *tmpl = xml->getRoot()->findFirstChildElement("template"); + if (tmpl) { + scanner->scanNode(tmpl, NULL, NULL, NULL, NULL, NULL, + dataElem, formValues); + } + deleteGHash(formValues, GString); + } + + delete xml; + + return scanner; +} + +XFAScanner::XFAScanner() { + fields = new GHash(); +} + +XFAScanner::~XFAScanner() { + deleteGHash(fields, XFAField); +} + +XFAField *XFAScanner::findField(GString *acroFormFieldName) { + return (XFAField *)fields->lookup(acroFormFieldName); +} + +GString *XFAScanner::readXFAStreams(Object *xfaObj) { + GString *data = new GString(); + char buf[4096]; + int n; + if (xfaObj->isStream()) { + xfaObj->streamReset(); + while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) { + data->append(buf, n); + } + } else if (xfaObj->isArray()) { + for (int i = 1; i < xfaObj->arrayGetLength(); i += 2) { + Object obj; + if (!xfaObj->arrayGet(i, &obj)->isStream()) { + error(errSyntaxError, -1, "XFA array element is wrong type"); + obj.free(); + delete data; + return NULL; + } + obj.streamReset(); + while ((n = obj.getStream()->getBlock(buf, sizeof(buf))) > 0) { + data->append(buf, n); + } + obj.free(); + } + } else { + error(errSyntaxError, -1, "XFA object is wrong type"); + return NULL; + } + return data; +} + +GHash *XFAScanner::scanFormValues(ZxElement *xmlRoot) { + GHash *formValues = new GHash(gTrue); + ZxElement *formElem = xmlRoot->findFirstChildElement("form"); + if (formElem) { + scanFormNode(formElem, NULL, formValues); + } + return formValues; +} + +void XFAScanner::scanFormNode(ZxElement *elem, GString *fullName, + GHash *formValues) { + GHash *fullNameIdx = new GHash(); + for (ZxNode *node = elem->getFirstChild(); + node; + node = node->getNextChild()) { + if (node->isElement("value")) { + if (fullName) { + ZxNode *child1Node = ((ZxElement *)node)->getFirstChild(); + if (child1Node && child1Node->isElement()) { + ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild(); + if (child2Node && child2Node->isCharData()) { + formValues->add(fullName->copy(), + ((ZxCharData *)child2Node)->getData()->copy()); + } + } + } + } else if (node->isElement()) { + ZxAttr *nameAttr = ((ZxElement *)node)->findAttr("name"); + if (nameAttr && (node->isElement("subform") || + node->isElement("field"))) { + GString *nodeName = nameAttr->getValue(); + GString *childFullName; + if (fullName) { + childFullName = GString::format("{0:t}.{1:t}", fullName, nodeName); + } else { + childFullName = nodeName->copy(); + } + int idx = fullNameIdx->lookupInt(nodeName); + childFullName->appendf("[{0:d}]", idx); + fullNameIdx->replace(nodeName, idx + 1); + scanFormNode((ZxElement *)node, childFullName, formValues); + delete childFullName; + } else if (node->isElement("subform")) { + scanFormNode((ZxElement *)node, fullName, formValues); + } + } + } + delete fullNameIdx; +} + +void XFAScanner::scanNode(ZxElement *elem, + GString *parentName, GString *parentFullName, + GHash *nameIdx, GHash *fullNameIdx, + GString *exclGroupName, ZxElement *dataElem, + GHash *formValues) { + GString *nodeName = getNodeName(elem); + GHash *childNameIdx; + if (!nameIdx || nodeName) { + childNameIdx = new GHash(); + } else { + childNameIdx = nameIdx; + } + GString *nodeFullName = getNodeFullName(elem); + GHash *childFullNameIdx; + if (!fullNameIdx || (nodeFullName && !elem->isElement("area"))) { + childFullNameIdx = new GHash(); + } else { + childFullNameIdx = fullNameIdx; + } + + GString *childName; + if (nodeName) { + if (parentName) { + childName = GString::format("{0:t}.{1:t}", parentName, nodeName); + } else { + childName = nodeName->copy(); + } + int idx = nameIdx->lookupInt(nodeName); + nameIdx->replace(nodeName, idx + 1); + if (nodeIsBindGlobal(elem)) { + childName->appendf("[0]"); + } else { + childName->appendf("[{0:d}]", idx); + } + } else { + childName = parentName; + } + GString *childFullName; + if (nodeFullName) { + if (parentFullName) { + childFullName = GString::format("{0:t}.{1:t}", + parentFullName, nodeFullName); + } else { + childFullName = nodeFullName->copy(); + } + int idx = fullNameIdx->lookupInt(nodeFullName); + fullNameIdx->replace(nodeFullName, idx + 1); + childFullName->appendf("[{0:d}]", idx); + } else { + childFullName = parentFullName; + } + + if (elem->isElement("field")) { + scanField(elem, childName, childFullName, exclGroupName, + dataElem, formValues); + } else { + GString *childExclGroupName; + if (elem->isElement("exclGroup")) { + childExclGroupName = childName; + } else { + childExclGroupName = NULL; + } + for (ZxNode *child = elem->getFirstChild(); + child; + child = child->getNextChild()) { + if (child->isElement()) { + scanNode((ZxElement *)child, childName, childFullName, + childNameIdx, childFullNameIdx, childExclGroupName, + dataElem, formValues); + } + } + } + + if (childName != parentName) { + delete childName; + } + if (childFullName != parentFullName) { + delete childFullName; + } + if (childNameIdx != nameIdx) { + delete childNameIdx; + } + if (childFullNameIdx != fullNameIdx) { + delete childFullNameIdx; + } +} + +void XFAScanner::scanField(ZxElement *elem, GString *name, GString *fullName, + GString *exclGroupName, ZxElement *dataElem, + GHash *formValues) { + GString *value = getFieldValue(elem, name, fullName, exclGroupName, + dataElem, formValues); + XFAFieldLayoutInfo *layoutInfo = getFieldLayoutInfo(elem); + XFAFieldPictureInfo *pictureInfo = getFieldPictureInfo(elem); + XFAFieldBarcodeInfo *barcodeInfo = getFieldBarcodeInfo(elem); + XFAField *field = new XFAField(name->copy(), fullName->copy(), value, + layoutInfo, pictureInfo, barcodeInfo); + fields->add(field->fullName, field); +} + +GString *XFAScanner::getFieldValue(ZxElement *elem, GString *name, + GString *fullName, GString *exclGroupName, + ZxElement *dataElem, GHash *formValues) { + GString *val = NULL; + + //--- check the <xfa:datasets> packet + val = getDatasetsValue(name->getCString(), dataElem); + if (!val && exclGroupName) { + val = (GString *)getDatasetsValue(exclGroupName->getCString(), dataElem); + } + + //--- check the <form> element + if (!val) { + val = (GString *)formValues->lookup(fullName); + } + + //--- check the <value> element within the field + if (!val) { + ZxElement *valueElem = elem->findFirstChildElement("value"); + if (valueElem) { + ZxNode *child1Node = valueElem->getFirstChild(); + if (child1Node && child1Node->isElement()) { + ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild(); + if (child2Node && child2Node->isCharData()) { + val = ((ZxCharData *)child2Node)->getData(); + } + } + } + } + + //--- get the checkbutton item value + GString *checkbuttonItem = NULL; + ZxElement *uiElem = elem->findFirstChildElement("ui"); + if (uiElem) { + ZxNode *uiChild = uiElem->getFirstChild(); + if (uiChild && uiChild->isElement("checkButton")) { + ZxElement *itemsElem = elem->findFirstChildElement("items"); + if (itemsElem) { + ZxNode *node1 = itemsElem->getFirstChild(); + if (node1 && node1->isElement()) { + ZxNode *node2 = ((ZxElement *)node1)->getFirstChild(); + if (node2 && node2->isCharData()) { + checkbuttonItem = ((ZxCharData *)node2)->getData(); + } + } + } + } + } + // convert XFA checkbutton value to AcroForm-style On/Off value + if (checkbuttonItem && val) { + if (val->cmp(checkbuttonItem)) { + val = new GString("Off"); + } else { + val = new GString("On"); + } + } else if (val) { + val = val->copy(); + } + + return val; +} + +GString *XFAScanner::getDatasetsValue(char *partName, ZxElement *elem) { + if (!elem) { + return NULL; + } + + // partName = xxxx[nn].yyyy---- + char *p = strchr(partName, '['); + if (!p) { + return NULL; + } + int partLen = (int)(p - partName); + int idx = atoi(p + 1); + p = strchr(p + 1, '.'); + if (p) { + ++p; + } + + int curIdx = 0; + for (ZxNode *node = elem->getFirstChild(); + node; + node = node->getNextChild()) { + if (!node->isElement()) { + continue; + } + GString *nodeName = ((ZxElement *)node)->getType(); + if (nodeName->getLength() != partLen || + strncmp(nodeName->getCString(), partName, partLen)) { + continue; + } + if (curIdx != idx) { + ++curIdx; + continue; + } + if (p) { + GString *val = getDatasetsValue(p, (ZxElement *)node); + if (val) { + return val; + } + break; + } else { + ZxNode *child = ((ZxElement *)node)->getFirstChild(); + if (!child || !child->isCharData()) { + return NULL; + } + return ((ZxCharData *)child)->getData(); + } + } + + // search for an 'ancestor match' + if (p) { + return getDatasetsValue(p, elem); + } + + return NULL; +} + +XFAFieldLayoutInfo *XFAScanner::getFieldLayoutInfo(ZxElement *elem) { + ZxElement *paraElem = elem->findFirstChildElement("para"); + if (!paraElem) { + return NULL; + } + XFAFieldLayoutHAlign hAlign = xfaFieldLayoutHAlignLeft; + ZxAttr *hAlignAttr = paraElem->findAttr("hAlign"); + if (hAlignAttr) { + if (!hAlignAttr->getValue()->cmp("left")) { + hAlign = xfaFieldLayoutHAlignLeft; + } else if (!hAlignAttr->getValue()->cmp("center")) { + hAlign = xfaFieldLayoutHAlignCenter; + } else if (!hAlignAttr->getValue()->cmp("right")) { + hAlign = xfaFieldLayoutHAlignRight; + } + } + XFAFieldLayoutVAlign vAlign = xfaFieldLayoutVAlignTop; + ZxAttr *vAlignAttr = paraElem->findAttr("vAlign"); + if (vAlignAttr) { + if (!vAlignAttr->getValue()->cmp("top")) { + vAlign = xfaFieldLayoutVAlignTop; + } else if (!vAlignAttr->getValue()->cmp("middle")) { + vAlign = xfaFieldLayoutVAlignMiddle; + } else if (!vAlignAttr->getValue()->cmp("bottom")) { + vAlign = xfaFieldLayoutVAlignBottom; + } + } + return new XFAFieldLayoutInfo(hAlign, vAlign); +} + +XFAFieldPictureInfo *XFAScanner::getFieldPictureInfo(ZxElement *elem) { + ZxElement *uiElem = elem->findFirstChildElement("ui"); + if (!uiElem) { + return NULL; + } + XFAFieldPictureSubtype subtype; + if (uiElem->findFirstChildElement("dateTimeEdit")) { + subtype = xfaFieldPictureDateTime; + } else if (uiElem->findFirstChildElement("numericEdit")) { + subtype = xfaFieldPictureNumeric; + } else if (uiElem->findFirstChildElement("textEdit")) { + subtype = xfaFieldPictureText; + } else { + return NULL; + } + + ZxElement *formatElem, *pictureElem; + ZxNode *pictureChildNode; + if (!(formatElem = elem->findFirstChildElement("format")) || + !(pictureElem = formatElem->findFirstChildElement("picture")) || + !(pictureChildNode = pictureElem->getFirstChild()) || + !pictureChildNode->isCharData()) { + return NULL; + } + GString *format = ((ZxCharData *)pictureChildNode)->getData()->copy(); + + return new XFAFieldPictureInfo(subtype, format); +} + +XFAFieldBarcodeInfo *XFAScanner::getFieldBarcodeInfo(ZxElement *elem) { + ZxElement *uiElem, *barcodeElem; + if (!(uiElem = elem->findFirstChildElement("ui")) || + !(barcodeElem = uiElem->findFirstChildElement("barcode"))) { + return NULL; + } + + ZxAttr *attr; + if (!(attr = barcodeElem->findAttr("type"))) { + return NULL; + } + GString *barcodeType = attr->getValue()->copy(); + + double wideNarrowRatio = 3; + if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) { + char *s = attr->getValue()->getCString(); + char *colon = strchr(s, ':'); + if (colon) { + GString *numStr = new GString(s, (int)(colon - s)); + double num = atof(numStr->getCString()); + delete numStr; + double den = atof(colon + 1); + if (den == 0) { + wideNarrowRatio = num; + } else { + wideNarrowRatio = num / den; + } + } else { + wideNarrowRatio = atof(s); + } + } + + double moduleWidth = (0.25 / 25.4) * 72.0; // 0.25mm + if ((attr = barcodeElem->findAttr("moduleWidth"))) { + moduleWidth = getMeasurement(attr->getValue()); + } + + double moduleHeight = (5.0 / 25.4) * 72.0; // 5mm + if ((attr = barcodeElem->findAttr("moduleHeight"))) { + moduleHeight = getMeasurement(attr->getValue()); + } + + int dataLength = 0; + if ((attr = barcodeElem->findAttr("dataLength"))) { + dataLength = atoi(attr->getValue()->getCString()); + } + + int errorCorrectionLevel = 0; + if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) { + errorCorrectionLevel = atoi(attr->getValue()->getCString()); + } + + GString *textLocation; + if ((attr = barcodeElem->findAttr("textLocation"))) { + textLocation = attr->getValue()->copy(); + } else { + textLocation = new GString("below"); + } + + return new XFAFieldBarcodeInfo(barcodeType, wideNarrowRatio, + moduleWidth, moduleHeight, dataLength, + errorCorrectionLevel, textLocation); +} + +double XFAScanner::getMeasurement(GString *s) { + int i = 0; + GBool neg = gFalse; + if (i < s->getLength() && s->getChar(i) == '+') { + ++i; + } else if (i < s->getLength() && s->getChar(i) == '-') { + neg = gTrue; + ++i; + } + double val = 0; + while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') { + val = val * 10 + s->getChar(i) - '0'; + ++i; + } + if (i < s->getLength() && s->getChar(i) == '.') { + ++i; + double mul = 0.1; + while (i < s->getLength() && + s->getChar(i) >= '0' && s->getChar(i) <= '9') { + val += mul * (s->getChar(i) - '0'); + mul *= 0.1; + ++i; + } + } + if (neg) { + val = -val; + } + if (i+1 < s->getLength()) { + if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') { + val *= 72; + } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') { + // no change + } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') { + val *= 72 / 2.54; + } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') { + val *= 72 / 25.4; + } else { + // default to inches + val *= 72; + } + } else { + // default to inches + val *= 72; + } + return val; +} + +GString *XFAScanner::getNodeName(ZxElement *elem) { + if (elem->isElement("template") || + elem->isElement("area") || + elem->isElement("draw")) { + return NULL; + } + if (!elem->isElement("field") && nodeIsBindNone(elem)) { + return NULL; + } + ZxAttr *nameAttr = elem->findAttr("name"); + if (!nameAttr) { + return NULL; + } + return nameAttr->getValue(); +} + +GString *XFAScanner::getNodeFullName(ZxElement *elem) { + if (elem->isElement("template") || + elem->isElement("draw")) { + return NULL; + } + ZxAttr *nameAttr = elem->findAttr("name"); + if (!nameAttr) { + return NULL; + } + return nameAttr->getValue(); +} + +GBool XFAScanner::nodeIsBindGlobal(ZxElement *elem) { + ZxElement *bindElem = elem->findFirstChildElement("bind"); + if (!bindElem) { + return gFalse; + } + ZxAttr *attr = bindElem->findAttr("match"); + return attr && !attr->getValue()->cmp("global"); +} + +GBool XFAScanner::nodeIsBindNone(ZxElement *elem) { + ZxElement *bindElem = elem->findFirstChildElement("bind"); + if (!bindElem) { + return gFalse; + } + ZxAttr *attr = bindElem->findAttr("match"); + return attr && !attr->getValue()->cmp("none"); +} diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h new file mode 100644 index 00000000000..00ee6f7fcee --- /dev/null +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h @@ -0,0 +1,166 @@ +//======================================================================== +// +// XFAScanner.h +// +// Copyright 2020 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XFASCANNER_H +#define XFASCANNER_H + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +class GHash; +class ZxElement; + +//------------------------------------------------------------------------ + +enum XFAFieldLayoutHAlign { + xfaFieldLayoutHAlignLeft, + xfaFieldLayoutHAlignCenter, + xfaFieldLayoutHAlignRight +}; + +enum XFAFieldLayoutVAlign { + xfaFieldLayoutVAlignTop, + xfaFieldLayoutVAlignMiddle, + xfaFieldLayoutVAlignBottom +}; + +class XFAFieldLayoutInfo { +public: + + XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA, + XFAFieldLayoutVAlign vAlignA); + + XFAFieldLayoutHAlign hAlign; + XFAFieldLayoutVAlign vAlign; +}; + +//------------------------------------------------------------------------ + +enum XFAFieldPictureSubtype { + xfaFieldPictureDateTime, + xfaFieldPictureNumeric, + xfaFieldPictureText +}; + +class XFAFieldPictureInfo { +public: + + XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA, GString *formatA); + ~XFAFieldPictureInfo(); + + XFAFieldPictureSubtype subtype; + GString *format; // picture format string +}; + +//------------------------------------------------------------------------ + +class XFAFieldBarcodeInfo { +public: + + XFAFieldBarcodeInfo(GString *barcodeTypeA, double wideNarrowRatioA, + double moduleWidthA, double moduleHeightA, + int dataLengthA, int errorCorrectionLevelA, + GString *textLocationA); + ~XFAFieldBarcodeInfo(); + + GString *barcodeType; + double wideNarrowRatio; + double moduleWidth; + double moduleHeight; + int dataLength; + int errorCorrectionLevel; + GString *textLocation; +}; + +//------------------------------------------------------------------------ + +class XFAField { +public: + + XFAField(GString *nameA, GString *fullNameA, GString *valueA, + XFAFieldLayoutInfo *layoutInfoA, + XFAFieldPictureInfo *pictureInfoA, + XFAFieldBarcodeInfo *barcodeInfoA); + ~XFAField(); + + // Get the field's value, or NULL if it doesn't have a value. Sets + // *[length] to the length of the Unicode string. + GString *getValue() { return value; } + + // Return a pointer to the field's picture formatting info object, + // or NULL if the field doesn't have picture formatting. + XFAFieldPictureInfo *getPictureInfo() { return pictureInfo; } + + // Return a pointer to the field's layout info object, or NULL if + // the field doesn't have layout info. + XFAFieldLayoutInfo *getLayoutInfo() { return layoutInfo; } + + // Return a pointer to the field's barcode info object, or NULL if + // the field isn't a barcode. + XFAFieldBarcodeInfo *getBarcodeInfo() { return barcodeInfo; } + +private: + + friend class XFAScanner; + + GString *name; // UTF-8 + GString *fullName; // UTF-8 + GString *value; // UTF-8 + XFAFieldLayoutInfo *layoutInfo; + XFAFieldPictureInfo *pictureInfo; + XFAFieldBarcodeInfo *barcodeInfo; +}; + +//------------------------------------------------------------------------ + +class XFAScanner { +public: + + static XFAScanner *load(Object *xfaObj); + + virtual ~XFAScanner(); + + // Find an XFA field matchined the specified AcroForm field name. + // Returns NULL if there is no matching field. + XFAField *findField(GString *acroFormFieldName); + +private: + + XFAScanner(); + static GString *readXFAStreams(Object *xfaObj); + GHash *scanFormValues(ZxElement *xmlRoot); + void scanFormNode(ZxElement *elem, GString *fullName, + GHash *formValues); + void scanNode(ZxElement *elem, + GString *parentName, GString *parentFullName, + GHash *nameIdx, GHash *fullNameIdx, + GString *exclGroupName, ZxElement *xmlRoot, + GHash *formValues); + void scanField(ZxElement *elem, GString *name, GString *fullName, + GString *exclGroupName, ZxElement *xmlRoot, + GHash *formValues); + GString *getFieldValue(ZxElement *elem, GString *name, + GString *fullName, GString *exclGroupName, + ZxElement *xmlRoot, GHash *formValues); + GString *getDatasetsValue(char *partName, ZxElement *elem); + XFAFieldLayoutInfo *getFieldLayoutInfo(ZxElement *elem); + XFAFieldPictureInfo *getFieldPictureInfo(ZxElement *elem); + XFAFieldBarcodeInfo *getFieldBarcodeInfo(ZxElement *elem); + double getMeasurement(GString *s); + GString *getNodeName(ZxElement *elem); + GString *getNodeFullName(ZxElement *elem); + GBool nodeIsBindGlobal(ZxElement *elem); + GBool nodeIsBindNone(ZxElement *elem); + + GHash *fields; // [XFAField] +}; + +#endif diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc index d6b43cd3bea..0c364638b39 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc @@ -350,7 +350,7 @@ XRef::XRef(BaseStream *strA, GBool repair) { // read the xref table posSet = new XRefPosSet(); - while (readXRef(&pos, posSet)) ; + while (readXRef(&pos, posSet, gFalse)) ; xrefTablePosLen = posSet->getLength(); xrefTablePos = (GFileOffset *)gmallocn(xrefTablePosLen, sizeof(GFileOffset)); @@ -438,8 +438,10 @@ GFileOffset XRef::getStartXref() { } // Read one xref table section. Also reads the associated trailer -// dictionary, and returns the prev pointer (if any). -GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) { +// dictionary, and returns the prev pointer (if any). The [hybrid] +// flag is true when following the XRefStm link in a hybrid-reference +// file. +GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid) { Parser *parser; Object obj; GBool more; @@ -461,47 +463,44 @@ GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) { for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ; // parse an old-style xref table - if (i + 4 < n && + if (!hybrid && + i + 4 < n && buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' && Lexer::isSpace(buf[i+4])) { more = readXRefTable(pos, i + 5, posSet); // parse an xref stream - } else if (i < n && buf[i] >= '0' && buf[i] <= '9') { + } else { obj.initNull(); parser = new Parser(NULL, new Lexer(NULL, str->makeSubStream(start + *pos, gFalse, 0, &obj)), gTrue); if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj, gTrue)->isCmd("obj")) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj)->isStream()) { - goto err2; + goto err; } - more = readXRefStream(obj.getStream(), pos); + more = readXRefStream(obj.getStream(), pos, hybrid); obj.free(); delete parser; - - } else { - goto err1; } return more; - err2: + err: obj.free(); delete parser; - err1: ok = gFalse; return gFalse; } @@ -662,7 +661,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) { //~ this can be a 64-bit int (?) if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) { pos2 = (GFileOffset)(Guint)obj2.getInt(); - readXRef(&pos2, posSet); + readXRef(&pos2, posSet, gTrue); if (!ok) { obj2.free(); obj.free(); @@ -679,7 +678,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) { return gFalse; } -GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) { +GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid) { Dict *dict; int w[3]; GBool more; @@ -857,125 +856,250 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) { // Attempt to construct an xref table for a damaged file. GBool XRef::constructXRef() { - Parser *parser; - Object newTrailerDict, obj; - char buf[256]; - GFileOffset pos; - int num, gen; - int newSize; - int streamEndsSize; - char *p; - int i; - GBool gotRoot; - - gfree(entries); - size = 0; - entries = NULL; - - gotRoot = gFalse; - streamEndsLen = streamEndsSize = 0; - + int *streamObjNums = NULL; + int streamObjNumsLen = 0; + int streamObjNumsSize = 0; + int lastObjNum = -1; + rootNum = -1; + int streamEndsSize = 0; + streamEndsLen = 0; + char buf[4096 + 1]; str->reset(); + GFileOffset bufPos = start; + char *p = buf; + char *end = buf; + GBool startOfLine = gTrue; + GBool eof = gFalse; while (1) { - pos = str->getPos(); - if (!str->getLine(buf, 256)) { + if (end - p < 256 && !eof) { + memcpy(buf, p, end - p); + bufPos += p - buf; + p = buf + (end - p); + int n = (int)(buf + 4096 - p); + int m = str->getBlock(p, n); + end = p + m; + *end = '\0'; + p = buf; + eof = m < n; + } + if (p == end && eof) { break; } - p = buf; + if (startOfLine && !strncmp(p, "trailer", 7)) { + constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf))); + p += 7; + startOfLine = gFalse; + } else if (startOfLine && !strncmp(p, "endstream", 9)) { + if (streamEndsLen == streamEndsSize) { + streamEndsSize += 64; + streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize, + sizeof(GFileOffset)); + } + streamEnds[streamEndsLen++] = (GFileOffset)(bufPos + (p - buf)); + p += 9; + startOfLine = gFalse; + } else if (startOfLine && *p >= '0' && *p <= '9') { + p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)), + &lastObjNum); + startOfLine = gFalse; + } else if (p[0] == '>' && p[1] == '>') { + p += 2; + startOfLine = gFalse; + // skip any PDF whitespace except for '\0' + while (*p == '\t' || *p == '\n' || *p == '\x0c' || + *p == '\r' || *p == ' ') { + startOfLine = *p == '\n' || *p == '\r'; + ++p; + } + if (!strncmp(p, "stream", 6)) { + if (lastObjNum >= 0) { + if (streamObjNumsLen == streamObjNumsSize) { + streamObjNumsSize += 64; + streamObjNums = (int *)greallocn(streamObjNums, streamObjNumsSize, + sizeof(int)); + } + streamObjNums[streamObjNumsLen++] = lastObjNum; + } + p += 6; + startOfLine = gFalse; + } + } else { + startOfLine = *p == '\n' || *p == '\r'; + ++p; + } + } - // skip whitespace - while (*p && Lexer::isSpace(*p & 0xff)) ++p; + // read each stream object, check for xref or object stream + for (int i = 0; i < streamObjNumsLen; ++i) { + Object obj; + fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj); + if (obj.isStream()) { + Dict *dict = obj.streamGetDict(); + Object type; + dict->lookup("Type", &type); + if (type.isName("XRef")) { + saveTrailerDict(dict, gTrue); + } else if (type.isName("ObjStm")) { + constructObjectStreamEntries(&obj, streamObjNums[i]); + } + type.free(); + } + obj.free(); + } + + gfree(streamObjNums); - // got trailer dictionary - if (!strncmp(p, "trailer", 7)) { - obj.initNull(); - parser = new Parser(NULL, + if (rootNum < 0) { + error(errSyntaxError, -1, "Couldn't find trailer dictionary"); + return gFalse; + } + return gTrue; +} + +// Attempt to construct a trailer dict at [pos] in the stream. +void XRef::constructTrailerDict(GFileOffset pos) { + Object newTrailerDict, obj; + obj.initNull(); + Parser *parser = + new Parser(NULL, new Lexer(NULL, - str->makeSubStream(pos + 7, gFalse, 0, &obj)), + str->makeSubStream(pos, gFalse, 0, &obj)), gFalse); - parser->getObj(&newTrailerDict); - if (newTrailerDict.isDict()) { - newTrailerDict.dictLookupNF("Root", &obj); - if (obj.isRef()) { - rootNum = obj.getRefNum(); - rootGen = obj.getRefGen(); - if (!trailerDict.isNone()) { - trailerDict.free(); - } - newTrailerDict.copy(&trailerDict); - gotRoot = gTrue; - } - obj.free(); - } - newTrailerDict.free(); - delete parser; + parser->getObj(&newTrailerDict); + if (newTrailerDict.isDict()) { + saveTrailerDict(newTrailerDict.getDict(), gFalse); + } + newTrailerDict.free(); + delete parser; +} - // look for object - } else if (isdigit(*p & 0xff)) { - num = atoi(p); - if (num > 0) { - do { - ++p; - } while (*p && isdigit(*p & 0xff)); - if (isspace(*p & 0xff)) { - do { - ++p; - } while (*p && isspace(*p & 0xff)); - if (isdigit(*p & 0xff)) { - gen = atoi(p); - do { - ++p; - } while (*p && isdigit(*p & 0xff)); - if (isspace(*p & 0xff)) { - do { - ++p; - } while (*p && isspace(*p & 0xff)); - if (!strncmp(p, "obj", 3)) { - if (num >= size) { - newSize = (num + 1 + 255) & ~255; - if (newSize < 0) { - error(errSyntaxError, -1, "Bad object number"); - return gFalse; - } - entries = (XRefEntry *) - greallocn(entries, newSize, sizeof(XRefEntry)); - for (i = size; i < newSize; ++i) { - entries[i].offset = (GFileOffset)-1; - entries[i].type = xrefEntryFree; - } - size = newSize; - } - if (entries[num].type == xrefEntryFree || - gen >= entries[num].gen) { - entries[num].offset = pos - start; - entries[num].gen = gen; - entries[num].type = xrefEntryUncompressed; - if (num > last) { - last = num; - } - } - } - } - } - } +// If [dict] "looks like" a trailer dict (i.e., has a Root entry), +// save it as the trailer dict. +void XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) { + Object obj; + dict->lookupNF("Root", &obj); + if (obj.isRef()) { + int newRootNum = obj.getRefNum(); + // the xref stream scanning code runs after all objects are found, + // so we can check for a valid root object number at that point + if (!isXRefStream || newRootNum <= last) { + rootNum = newRootNum; + rootGen = obj.getRefGen(); + if (!trailerDict.isNone()) { + trailerDict.free(); } + trailerDict.initDict(dict); + } + } + obj.free(); +} - } else if (!strncmp(p, "endstream", 9)) { - if (streamEndsLen == streamEndsSize) { - streamEndsSize += 64; - streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize, - sizeof(GFileOffset)); +// Look for an object header ("nnn ggg obj") at [p]. The first +// character at *[p] is a digit. [pos] is the position of *[p]. +char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) { + // we look for non-end-of-line space characters here, to deal with + // situations like: + // nnn <-- garbage digits on a line + // nnn nnn obj <-- actual object + // and we also ignore '\0' (because it's used to terminate the + // buffer in this damage-scanning code) + int num = 0; + do { + num = (num * 10) + (*p - '0'); + ++p; + } while (*p >= '0' && *p <= '9' && num < 100000000); + if (*p != '\t' && *p != '\x0c' && *p != ' ') { + return p; + } + do { + ++p; + } while (*p == '\t' || *p == '\x0c' || *p == ' '); + if (!(*p >= '0' && *p <= '9')) { + return p; + } + int gen = 0; + do { + gen = (gen * 10) + (*p - '0'); + ++p; + } while (*p >= '0' && *p <= '9' && gen < 100000000); + if (*p != '\t' && *p != '\x0c' && *p != ' ') { + return p; + } + do { + ++p; + } while (*p == '\t' || *p == '\x0c' || *p == ' '); + if (strncmp(p, "obj", 3)) { + return p; + } + + if (constructXRefEntry(num, gen, pos - start, xrefEntryUncompressed)) { + *objNum = num; + } + + return p; +} + +// Read the header from an object stream, and add xref entries for all +// of its objects. +void XRef::constructObjectStreamEntries(Object *objStr, int objStrObjNum) { + Object obj1, obj2; + + // get the object count + if (!objStr->streamGetDict()->lookup("N", &obj1)->isInt()) { + obj1.free(); + return; + } + int nObjects = obj1.getInt(); + obj1.free(); + if (nObjects <= 0 || nObjects > 1000000) { + return; + } + + // parse the header: object numbers and offsets + Parser *parser = new Parser(NULL, + new Lexer(NULL, objStr->getStream()->copy()), + gFalse); + for (int i = 0; i < nObjects; ++i) { + parser->getObj(&obj1, gTrue); + parser->getObj(&obj2, gTrue); + if (obj1.isInt() && obj2.isInt()) { + int num = obj1.getInt(); + if (num >= 0 && num < 1000000) { + constructXRefEntry(num, i, objStrObjNum, xrefEntryCompressed); } - streamEnds[streamEndsLen++] = pos; } + obj2.free(); + obj1.free(); } + delete parser; +} - if (gotRoot) { - return gTrue; +GBool XRef::constructXRefEntry(int num, int gen, GFileOffset pos, + XRefEntryType type) { + if (num >= size) { + int newSize = (num + 1 + 255) & ~255; + if (newSize < 0) { + return gFalse; + } + entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); + for (int i = size; i < newSize; ++i) { + entries[i].offset = (GFileOffset)-1; + entries[i].type = xrefEntryFree; + } + size = newSize; } - error(errSyntaxError, -1, "Couldn't find trailer dictionary"); - return gFalse; + if (entries[num].type == xrefEntryFree || + gen >= entries[num].gen) { + entries[num].offset = pos; + entries[num].gen = gen; + entries[num].type = type; + if (num > last) { + last = num; + } + } + + return gTrue; } void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA, @@ -1153,6 +1277,9 @@ GBool XRef::getObjectStreamObject(int objStrNum, int objIdx, gLockMutex(&objStrsMutex); #endif if (!(objStr = getObjectStream(objStrNum))) { +#if MULTITHREADED + gUnlockMutex(&objStrsMutex); +#endif return gFalse; } cleanObjectStreamCache(); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h index 9e63dcf04da..00a8644f3a9 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h @@ -166,11 +166,17 @@ private: #endif GFileOffset getStartXref(); - GBool readXRef(GFileOffset *pos, XRefPosSet *posSet); + GBool readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid); GBool readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet); + GBool readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid); GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n); - GBool readXRefStream(Stream *xrefStr, GFileOffset *pos); GBool constructXRef(); + void constructTrailerDict(GFileOffset pos); + void saveTrailerDict(Dict *dict, GBool isXRefStream); + char *constructObjectEntry(char *p, GFileOffset pos, int *objNum); + void constructObjectStreamEntries(Object *objStr, int objStrObjNum); + GBool constructXRefEntry(int num, int gen, GFileOffset pos, + XRefEntryType type); GBool getObjectStreamObject(int objStrNum, int objIdx, int objNum, Object *obj); ObjectStream *getObjectStream(int objStrNum); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc index ba6e11496ae..9e4fb47900c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc @@ -430,7 +430,8 @@ ZxAttr *ZxDoc::parseAttr() { GString *name, *value; const char *start; char quote, c; - int x, n; + unsigned int x; + int n; name = parseName(); parseSpace(); @@ -559,7 +560,8 @@ void ZxDoc::parseCharData(ZxElement *par) { GString *data; const char *start; char c; - int x, n; + unsigned int x; + int n; data = new GString(); while (parsePtr < parseEnd && *parsePtr != '<') { @@ -638,7 +640,7 @@ void ZxDoc::parseCharData(ZxElement *par) { par->addChild(new ZxCharData(data, true)); } -void ZxDoc::appendUTF8(GString *s, int c) { +void ZxDoc::appendUTF8(GString *s, unsigned int c) { if (c <= 0x7f) { s->append((char)c); } else if (c <= 0x7ff) { diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h index 49f9f6b1da8..c04ca95064b 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h @@ -103,7 +103,7 @@ private: ZxAttr *parseAttr(); void parseContent(ZxElement *par); void parseCharData(ZxElement *par); - void appendUTF8(GString *s, int c); + void appendUTF8(GString *s, unsigned int c); void parseCDSect(ZxNode *par); void parseMisc(ZxNode *par); void parseComment(ZxNode *par); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h index 3dbed2ae93c..39cade1682c 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h @@ -2,7 +2,7 @@ // // config.h // -// Copyright 1996-2019 Glyph & Cog, LLC +// Copyright 1996-2021 Glyph & Cog, LLC // //======================================================================== @@ -14,13 +14,13 @@ //------------------------------------------------------------------------ // xpdf version -#define xpdfVersion "4.02" -#define xpdfVersionNum 4.02 +#define xpdfVersion "4.03" +#define xpdfVersionNum 4.03 #define xpdfMajorVersion 4 -#define xpdfMinorVersion 2 +#define xpdfMinorVersion 3 #define xpdfUpdateVersion 0 #define xpdfMajorVersionStr "4" -#define xpdfMinorVersionStr "2" +#define xpdfMinorVersionStr "3" #define xpdfUpdateVersionStr "0" // supported PDF version @@ -28,11 +28,11 @@ #define supportedPDFVersionNum 2.0 // copyright notice -#define xpdfCopyright "Copyright 1996-2019 Glyph & Cog, LLC" +#define xpdfCopyright "Copyright 1996-2021 Glyph & Cog, LLC" // Windows resource file stuff -#define winxpdfVersion "WinXpdf 4.02" -#define xpdfCopyrightAmp "Copyright 1996-2019 Glyph && Cog, LLC" +#define winxpdfVersion "WinXpdf 4.03" +#define xpdfCopyrightAmp "Copyright 1996-2021 Glyph && Cog, LLC" //------------------------------------------------------------------------ // paper size @@ -59,12 +59,14 @@ #endif // system config file name (set via the configure script) -#ifdef SYSTEM_XPDFRC -#define xpdfSysConfigFile SYSTEM_XPDFRC -#else +#if defined(_WIN32) // under Windows, we get the directory with the executable and then // append this file name #define xpdfSysConfigFile "xpdfrc" +#elif defined(SYSTEM_XPDFRC) +#define xpdfSysConfigFile SYSTEM_XPDFRC +#else +#define xpdfSysConfigFile "/etc/xpdfrc" #endif //------------------------------------------------------------------------ diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc index 34fd439bd76..e8b74381017 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc @@ -16,6 +16,7 @@ #include "PDFDoc.h" #include "CharTypes.h" #include "UnicodeMap.h" +#include "UTF8.h" #include "Error.h" #include "config.h" @@ -67,8 +68,7 @@ int main(int argc, char *argv[]) { PDFDoc *doc; Unicode *name; char uBuf[8]; - char path[1024]; - char *p; + GString *path; GBool ok; int exitCode; int nFiles, nameLen, n, i, j; @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) { ok = gFalse; } if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdfdetach version %s\n", xpdfVersion); + fprintf(stderr, "pdfdetach version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfdetach", "<PDF-file>", argDesc); @@ -148,32 +148,24 @@ int main(int argc, char *argv[]) { } else if (saveAll) { for (i = 0; i < nFiles; ++i) { if (savePath[0]) { - n = (int)strlen(savePath); - if (n > (int)sizeof(path) - 2) { - n = sizeof(path) - 2; - } - memcpy(path, savePath, n); - path[n] = '/'; - p = path + n + 1; + path = new GString(savePath); + path->append('/'); } else { - p = path; + path = new GString(); } name = doc->getEmbeddedFileName(i); nameLen = doc->getEmbeddedFileNameLength(i); for (j = 0; j < nameLen; ++j) { - n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) { - break; - } - memcpy(p, uBuf, n); - p += n; + n = mapUTF8(name[j], uBuf, sizeof(uBuf)); + path->append(uBuf, n); } - *p = '\0'; - if (!doc->saveEmbeddedFile(i, path)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + if (!doc->saveEmbeddedFileU(i, path->getCString())) { + error(errIO, -1, "Error saving embedded file as '{0:t}'", path); + delete path; exitCode = 2; goto err2; } + delete path; } // save an embedded file @@ -183,27 +175,23 @@ int main(int argc, char *argv[]) { goto err2; } if (savePath[0]) { - p = savePath; + path = new GString(savePath); } else { name = doc->getEmbeddedFileName(saveNum - 1); nameLen = doc->getEmbeddedFileNameLength(saveNum - 1); - p = path; + path = new GString(); for (j = 0; j < nameLen; ++j) { - n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) { - break; - } - memcpy(p, uBuf, n); - p += n; + n = mapUTF8(name[j], uBuf, sizeof(uBuf)); + path->append(uBuf, n); } - *p = '\0'; - p = path; } - if (!doc->saveEmbeddedFile(saveNum - 1, p)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + if (!doc->saveEmbeddedFileU(saveNum - 1, path->getCString())) { + error(errIO, -1, "Error saving embedded file as '{0:t}'", path); + delete path; exitCode = 2; goto err2; } + delete path; } exitCode = 0; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc index 22298b138c5..42c46223d8b 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc @@ -23,7 +23,7 @@ #include "Dict.h" #include "GfxFont.h" #include "Annot.h" -#include "Form.h" +#include "AcroForm.h" #include "PDFDoc.h" #include "config.h" @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) { Page *page; Dict *resDict; Annots *annots; - Form *form; + AcroForm *form; Object obj1, obj2; int pg, i, j; int exitCode; @@ -113,7 +113,7 @@ int main(int argc, char *argv[]) { fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdffonts version %s\n", xpdfVersion); + fprintf(stderr, "pdffonts version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdffonts", "<PDF-file>", argDesc); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc index 24b7c0d448b..cd5d34014f4 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 3 || printVersion || printHelp) { - fprintf(stderr, "pdfimages version %s\n", xpdfVersion); + fprintf(stderr, "pdfimages version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfimages", "<PDF-file> <image-root>", argDesc); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc index f5f593b9f13..621fb69ea6f 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc @@ -102,6 +102,9 @@ int main(int argc, char *argv[]) { FILE *f; GString *metadata; ZxDoc *xmp; + int permFlags, keyLength, encVersion; + GBool ownerPasswordOk; + CryptAlgorithm encAlgorithm; GBool ok; int exitCode; int pg, i; @@ -113,7 +116,7 @@ int main(int argc, char *argv[]) { fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdfinfo version %s\n", xpdfVersion); + fprintf(stderr, "pdfinfo version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfinfo", "<PDF-file>", argDesc); @@ -216,15 +219,19 @@ int main(int argc, char *argv[]) { printf("Pages: %d\n", doc->getNumPages()); // print encryption info - printf("Encrypted: "); if (doc->isEncrypted()) { - printf("yes (print:%s copy:%s change:%s addNotes:%s)\n", + doc->getXRef()->getEncryption(&permFlags, &ownerPasswordOk, &keyLength, + &encVersion, &encAlgorithm); + printf("Encrypted: %s %d-bit\n", + encAlgorithm == cryptRC4 ? "RC4" : "AES", + keyLength * 8); + printf("Permissions: print:%s copy:%s change:%s addNotes:%s\n", doc->okToPrint(gTrue) ? "yes" : "no", doc->okToCopy(gTrue) ? "yes" : "no", doc->okToChange(gTrue) ? "yes" : "no", doc->okToAddNotes(gTrue) ? "yes" : "no"); } else { - printf("no\n"); + printf("Encrypted: no\n"); } // print page size @@ -261,18 +268,18 @@ int main(int argc, char *argv[]) { if (multiPage) { for (pg = firstPage; pg <= lastPage; ++pg) { page = doc->getCatalog()->getPage(pg); - sprintf(buf, "Page %4d MediaBox: ", pg); + snprintf(buf, sizeof(buf), "Page %4d MediaBox: ", pg); printBox(buf, page->getMediaBox()); - sprintf(buf, "Page %4d CropBox: ", pg); + snprintf(buf, sizeof(buf), "Page %4d CropBox: ", pg); printBox(buf, page->getCropBox()); - sprintf(buf, "Page %4d BleedBox: ", pg); + snprintf(buf, sizeof(buf), "Page %4d BleedBox: ", pg); printBox(buf, page->getBleedBox()); - sprintf(buf, "Page %4d TrimBox: ", pg); + snprintf(buf, sizeof(buf), "Page %4d TrimBox: ", pg); printBox(buf, page->getTrimBox()); - sprintf(buf, "Page %4d ArtBox: ", pg); + snprintf(buf, sizeof(buf), "Page %4d ArtBox: ", pg); printBox(buf, page->getArtBox()); } - } else { + } else if (doc->getNumPages() > 0) { page = doc->getCatalog()->getPage(firstPage); printBox("MediaBox: ", page->getMediaBox()); printBox("CropBox: ", page->getCropBox()); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc index 29095509bed..af8fe93fbd5 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc @@ -31,6 +31,7 @@ static int firstPage = 1; static int lastPage = 0; static double zoom = 1; static int resolution = 150; +static GBool noFonts = gFalse; static GBool skipInvisible = gFalse; static GBool allInvisible = gFalse; static char ownerPassword[33] = "\001"; @@ -45,10 +46,12 @@ static ArgDesc argDesc[] = { "first page to convert"}, {"-l", argInt, &lastPage, 0, "last page to convert"}, - {"-z", argFP, &zoom, 0, + {"-z", argFP, &zoom, 0, "initial zoom level (1.0 means 72dpi)"}, - {"-r", argInt, &resolution, 0, + {"-r", argInt, &resolution, 0, "resolution, in DPI (default is 150)"}, + {"-nofonts", argFlag, &noFonts, 0, + "do not extract embedded fonts"}, {"-skipinvisible", argFlag, &skipInvisible, 0, "do not draw invisible text"}, {"-allinvisible", argFlag, &allInvisible, 0, @@ -97,7 +100,7 @@ int main(int argc, char *argv[]) { fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 3 || printVersion || printHelp) { - fprintf(stderr, "pdftohtml version %s\n", xpdfVersion); + fprintf(stderr, "pdftohtml version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftohtml", "<PDF-file> <html-dir>", argDesc); @@ -155,7 +158,7 @@ int main(int argc, char *argv[]) { } // create HTML directory - if (!createDir(htmlDir, 0755)) { + if (makeDir(htmlDir, 0755)) { error(errIO, -1, "Couldn't create HTML output directory '{0:s}'", htmlDir); exitCode = 2; @@ -171,20 +174,20 @@ int main(int argc, char *argv[]) { htmlGen->setZoom(zoom); htmlGen->setDrawInvisibleText(!skipInvisible); htmlGen->setAllTextInvisible(allInvisible); - htmlGen->setExtractFontFiles(gTrue); + htmlGen->setExtractFontFiles(!noFonts); htmlGen->startDoc(doc); // convert the pages for (pg = firstPage; pg <= lastPage; ++pg) { htmlFileName = GString::format("{0:s}/page{1:d}.html", htmlDir, pg); pngFileName = GString::format("{0:s}/page{1:d}.png", htmlDir, pg); - if (!(htmlFile = fopen(htmlFileName->getCString(), "wb"))) { + if (!(htmlFile = openFile(htmlFileName->getCString(), "wb"))) { error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName); delete htmlFileName; delete pngFileName; goto err2; } - if (!(pngFile = fopen(pngFileName->getCString(), "wb"))) { + if (!(pngFile = openFile(pngFileName->getCString(), "wb"))) { error(errIO, -1, "Couldn't open PNG file '{0:t}'", pngFileName); fclose(htmlFile); delete htmlFileName; @@ -236,7 +239,7 @@ static GBool createIndex(char *htmlDir) { int pg; htmlFileName = GString::format("{0:s}/index.html", htmlDir); - html = fopen(htmlFileName->getCString(), "w"); + html = openFile(htmlFileName->getCString(), "w"); delete htmlFileName; if (!html) { error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName); diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc index 64a0087c7d3..f19baccf581 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc @@ -18,6 +18,7 @@ #include "gmempp.h" #include "parseargs.h" #include "GString.h" +#include "gfile.h" #include "GlobalParams.h" #include "Object.h" #include "PDFDoc.h" @@ -32,6 +33,7 @@ static double resolution = 150; static GBool mono = gFalse; static GBool gray = gFalse; static GBool pngAlpha = gFalse; +static int rotate = 0; static char enableFreeTypeStr[16] = ""; static char antialiasStr[16] = ""; static char vectorAntialiasStr[16] = ""; @@ -55,6 +57,8 @@ static ArgDesc argDesc[] = { "generate a grayscale PNG file"}, {"-alpha", argFlag, &pngAlpha, 0, "include an alpha channel in the PNG file"}, + {"-rot", argInt, &rotate, 0, + "set page rotation: 0, 90, 180, or 270"}, #if HAVE_FREETYPE_H {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no"}, @@ -119,7 +123,7 @@ int main(int argc, char *argv[]) { goto err0; } if (!ok || argc != 3 || printVersion || printHelp) { - fprintf(stderr, "pdftopng version %s\n", xpdfVersion); + fprintf(stderr, "pdftopng version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftopng", "<PDF-file> <PNG-root>", argDesc); @@ -197,7 +201,7 @@ int main(int argc, char *argv[]) { } splashOut->startDoc(doc->getXRef()); for (pg = firstPage; pg <= lastPage; ++pg) { - doc->displayPage(splashOut, pg, resolution, resolution, 0, + doc->displayPage(splashOut, pg, resolution, resolution, rotate, gFalse, gTrue, gFalse); if (mono) { if (!strcmp(pngRoot, "-")) { @@ -207,7 +211,7 @@ int main(int argc, char *argv[]) { #endif } else { pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg); - if (!(f = fopen(pngFile->getCString(), "wb"))) { + if (!(f = openFile(pngFile->getCString(), "wb"))) { exit(2); } delete pngFile; @@ -225,7 +229,7 @@ int main(int argc, char *argv[]) { #endif } else { pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg); - if (!(f = fopen(pngFile->getCString(), "wb"))) { + if (!(f = openFile(pngFile->getCString(), "wb"))) { exit(2); } delete pngFile; @@ -244,7 +248,7 @@ int main(int argc, char *argv[]) { #endif } else { pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg); - if (!(f = fopen(pngFile->getCString(), "wb"))) { + if (!(f = openFile(pngFile->getCString(), "wb"))) { exit(2); } delete pngFile; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc index e01ac5c1304..0b084b014e5 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc @@ -36,6 +36,7 @@ static GBool gray = gFalse; #if SPLASH_CMYK static GBool cmyk = gFalse; #endif +static int rotate = 0; static char enableFreeTypeStr[16] = ""; static char antialiasStr[16] = ""; static char vectorAntialiasStr[16] = ""; @@ -61,6 +62,8 @@ static ArgDesc argDesc[] = { {"-cmyk", argFlag, &cmyk, 0, "generate a CMYK PAM file"}, #endif + {"-rot", argInt, &rotate, 0, + "set page rotation: 0, 90, 180, or 270"}, #if HAVE_FREETYPE_H {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no"}, @@ -133,7 +136,7 @@ int main(int argc, char *argv[]) { ok = gFalse; } if (!ok || argc != 3 || printVersion || printHelp) { - fprintf(stderr, "pdftoppm version %s\n", xpdfVersion); + fprintf(stderr, "pdftoppm version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftoppm", "<PDF-file> <PPM-root>", argDesc); @@ -226,7 +229,7 @@ int main(int argc, char *argv[]) { } splashOut->startDoc(doc->getXRef()); for (pg = firstPage; pg <= lastPage; ++pg) { - doc->displayPage(splashOut, pg, resolution, resolution, 0, + doc->displayPage(splashOut, pg, resolution, resolution, rotate, gFalse, gTrue, gFalse); if (!strcmp(ppmRoot, "-")) { #ifdef _WIN32 diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc index 8e75dd83f55..36294551a3b 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc @@ -182,7 +182,7 @@ int main(int argc, char *argv[]) { fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftops version %s\n", xpdfVersion); + fprintf(stderr, "pdftops version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc); @@ -355,7 +355,7 @@ int main(int argc, char *argv[]) { // write PostScript file psOut = new PSOutputDev(psFileName->getCString(), doc, firstPage, lastPage, mode, - 0, 0, 0, 0, gFalse, NULL, NULL, userUnit); + 0, 0, 0, 0, gFalse, NULL, NULL, userUnit, gTrue); if (!psOut->isOk()) { delete psOut; exitCode = 2; diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc index aa74fb68612..975cd6a1959 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc @@ -19,6 +19,7 @@ #include "gmempp.h" #include "parseargs.h" #include "GString.h" +#include "GList.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" @@ -39,6 +40,7 @@ static int firstPage = 1; static int lastPage = 0; static GBool physLayout = gFalse; static GBool simpleLayout = gFalse; +static GBool simple2Layout = gFalse; static GBool tableLayout = gFalse; static GBool linePrinter = gFalse; static GBool rawOrder = gFalse; @@ -58,6 +60,7 @@ static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool quiet = gFalse; static char cfgFileName[256] = ""; +static GBool listEncodings = gFalse; static GBool printVersion = gFalse; static GBool printHelp = gFalse; @@ -70,6 +73,8 @@ static ArgDesc argDesc[] = { "maintain original physical layout"}, {"-simple", argFlag, &simpleLayout, 0, "simple one-column page layout"}, + {"-simple2", argFlag, &simple2Layout, 0, + "simple one-column page layout, version 2"}, {"-table", argFlag, &tableLayout, 0, "similar to -layout, but optimized for tables"}, {"-lineprinter", argFlag, &linePrinter, 0, @@ -108,6 +113,8 @@ static ArgDesc argDesc[] = { "don't print any messages or errors"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, + {"-listencodings", argFlag, &listEncodings, 0, + "list all available output text encodings"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, @@ -153,8 +160,19 @@ int main(int argc, char *argv[]) { // parse args fixCommandLine(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); + if (ok && listEncodings) { + // list available encodings + globalParams = new GlobalParams(cfgFileName); + GList *encs = globalParams->getAvailableTextEncodings(); + for (int i = 0; i < encs->getLength(); ++i) { + printf("%s\n", ((GString *)encs->get(i))->getCString()); + } + deleteGList(encs, GString); + delete globalParams; + goto err0; + } if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftotext version %s\n", xpdfVersion); + fprintf(stderr, "pdftotext version %s [www.xpdfreader.com]\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc); @@ -247,6 +265,8 @@ int main(int argc, char *argv[]) { textOutControl.fixedPitch = fixedPitch; } else if (simpleLayout) { textOutControl.mode = textOutSimpleLayout; + } else if (simple2Layout) { + textOutControl.mode = textOutSimple2Layout; } else if (linePrinter) { textOutControl.mode = textOutLinePrinter; textOutControl.fixedPitch = fixedPitch; @@ -264,7 +284,7 @@ int main(int argc, char *argv[]) { textOutControl.marginTop = marginTop; textOutControl.marginBottom = marginBottom; textOut = new TextOutputDev(textFileName->getCString(), &textOutControl, - gFalse); + gFalse, gTrue); if (textOut->isOk()) { doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0, gFalse, gTrue, gFalse); |