summaryrefslogtreecommitdiff
path: root/xpdf
diff options
context:
space:
mode:
Diffstat (limited to 'xpdf')
-rw-r--r--xpdf/AcroForm.cc1897
-rw-r--r--xpdf/AcroForm.h128
-rw-r--r--xpdf/Annot.cc1766
-rw-r--r--xpdf/Annot.h59
-rw-r--r--xpdf/CMap.cc52
-rw-r--r--xpdf/Catalog.cc85
-rw-r--r--xpdf/Catalog.h5
-rw-r--r--xpdf/CharCodeToUnicode.cc16
-rw-r--r--xpdf/CharCodeToUnicode.h2
-rw-r--r--xpdf/Decrypt.cc853
-rw-r--r--xpdf/Decrypt.h18
-rw-r--r--xpdf/Dict.cc78
-rw-r--r--xpdf/Dict.h10
-rw-r--r--xpdf/Error.cc30
-rw-r--r--xpdf/Error.h6
-rw-r--r--xpdf/Form.cc67
-rw-r--r--xpdf/Form.h64
-rw-r--r--xpdf/Function.cc1176
-rw-r--r--xpdf/Function.h15
-rw-r--r--xpdf/Gfx.cc720
-rw-r--r--xpdf/Gfx.h59
-rw-r--r--xpdf/GfxFont.cc129
-rw-r--r--xpdf/GfxFont.h10
-rw-r--r--xpdf/GfxState.cc449
-rw-r--r--xpdf/GfxState.h79
-rw-r--r--xpdf/GlobalParams.cc506
-rw-r--r--xpdf/GlobalParams.h28
-rw-r--r--xpdf/HTMLGen.cc583
-rw-r--r--xpdf/HTMLGen.h63
-rw-r--r--xpdf/ImageOutputDev.cc84
-rw-r--r--xpdf/ImageOutputDev.h18
-rw-r--r--xpdf/JArithmeticDecoder.cc18
-rw-r--r--xpdf/JArithmeticDecoder.h1
-rw-r--r--xpdf/JBIG2Stream.cc73
-rw-r--r--xpdf/JPXStream.cc505
-rw-r--r--xpdf/JPXStream.h24
-rw-r--r--xpdf/Lexer.h9
-rw-r--r--xpdf/Link.cc132
-rw-r--r--xpdf/Link.h82
-rw-r--r--xpdf/Makefile.in580
-rw-r--r--xpdf/Object.h13
-rw-r--r--xpdf/OptionalContent.cc207
-rw-r--r--xpdf/OptionalContent.h21
-rw-r--r--xpdf/Outline.cc92
-rw-r--r--xpdf/Outline.h17
-rw-r--r--xpdf/OutputDev.cc38
-rw-r--r--xpdf/OutputDev.h14
-rw-r--r--xpdf/PDFCore.cc79
-rw-r--r--xpdf/PDFCore.h6
-rw-r--r--xpdf/PDFDoc.cc65
-rw-r--r--xpdf/PDFDoc.h9
-rw-r--r--xpdf/PSOutputDev.cc1639
-rw-r--r--xpdf/PSOutputDev.h65
-rw-r--r--xpdf/Page.cc39
-rw-r--r--xpdf/Page.h3
-rw-r--r--xpdf/Parser.cc27
-rw-r--r--xpdf/Parser.h2
-rw-r--r--xpdf/PreScanOutputDev.cc27
-rw-r--r--xpdf/PreScanOutputDev.h11
-rw-r--r--xpdf/SecurityHandler.cc14
-rw-r--r--xpdf/SplashOutputDev.cc549
-rw-r--r--xpdf/SplashOutputDev.h25
-rw-r--r--xpdf/Stream.cc889
-rw-r--r--xpdf/Stream.h121
-rw-r--r--xpdf/TextOutputDev.cc6527
-rw-r--r--xpdf/TextOutputDev.h513
-rw-r--r--xpdf/TextString.cc164
-rw-r--r--xpdf/TextString.h66
-rw-r--r--xpdf/UnicodeTypeTable.cc40
-rw-r--r--xpdf/UnicodeTypeTable.h4
-rw-r--r--xpdf/XFAForm.cc1458
-rw-r--r--xpdf/XFAForm.h126
-rw-r--r--xpdf/XPDFCore.cc19
-rw-r--r--xpdf/XPDFViewer.cc35
-rw-r--r--xpdf/XPDFViewer.h1
-rw-r--r--xpdf/XRef.cc433
-rw-r--r--xpdf/XRef.h40
-rw-r--r--xpdf/XpdfPluginAPI.cc2
-rw-r--r--xpdf/Zoox.cc839
-rw-r--r--xpdf/Zoox.h241
-rw-r--r--xpdf/config.h20
-rw-r--r--xpdf/pdfdetach.cc2
-rw-r--r--xpdf/pdffonts.cc158
-rw-r--r--xpdf/pdfinfo.cc47
-rw-r--r--xpdf/pdftohtml.cc246
-rw-r--r--xpdf/pdftopng.cc289
-rw-r--r--xpdf/pdftoppm.cc36
-rw-r--r--xpdf/pdftops.cc31
-rw-r--r--xpdf/pdftotext.cc189
-rw-r--r--xpdf/vms_make.com129
-rw-r--r--xpdf/xpdf.cc10
91 files changed, 17485 insertions, 8631 deletions
diff --git a/xpdf/AcroForm.cc b/xpdf/AcroForm.cc
new file mode 100644
index 0000000..86e7037
--- /dev/null
+++ b/xpdf/AcroForm.cc
@@ -0,0 +1,1897 @@
+//========================================================================
+//
+// AcroForm.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "TextString.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "OptionalContent.h"
+#include "Annot.h"
+#include "Lexer.h"
+#include "AcroForm.h"
+
+//------------------------------------------------------------------------
+
+#define acroFormFlagReadOnly (1 << 0) // all
+#define acroFormFlagRequired (1 << 1) // all
+#define acroFormFlagNoExport (1 << 2) // all
+#define acroFormFlagMultiline (1 << 12) // text
+#define acroFormFlagPassword (1 << 13) // text
+#define acroFormFlagNoToggleToOff (1 << 14) // button
+#define acroFormFlagRadio (1 << 15) // button
+#define acroFormFlagPushbutton (1 << 16) // button
+#define acroFormFlagCombo (1 << 17) // choice
+#define acroFormFlagEdit (1 << 18) // choice
+#define acroFormFlagSort (1 << 19) // choice
+#define acroFormFlagFileSelect (1 << 20) // text
+#define acroFormFlagMultiSelect (1 << 21) // choice
+#define acroFormFlagDoNotSpellCheck (1 << 22) // text, choice
+#define acroFormFlagDoNotScroll (1 << 23) // text
+#define acroFormFlagComb (1 << 24) // text
+#define acroFormFlagRadiosInUnison (1 << 25) // button
+#define acroFormFlagRichText (1 << 25) // text
+#define acroFormFlagCommitOnSelChange (1 << 26) // choice
+
+#define acroFormQuadLeft 0
+#define acroFormQuadCenter 1
+#define acroFormQuadRight 2
+
+#define annotFlagHidden 0x0002
+#define annotFlagPrint 0x0004
+#define annotFlagNoView 0x0020
+
+// distance of Bezier control point from center for circle approximation
+// = (4 * (sqrt(2) - 1) / 3) * r
+#define bezierCircle 0.55228475
+
+//------------------------------------------------------------------------
+
+// map an annotation ref to a page number
+class AcroFormAnnotPage {
+public:
+ AcroFormAnnotPage(int annotNumA, int annotGenA, int pageNumA)
+ { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
+ int annotNum;
+ int annotGen;
+ int pageNum;
+};
+
+//------------------------------------------------------------------------
+// AcroForm
+//------------------------------------------------------------------------
+
+AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
+ AcroForm *acroForm;
+ Object fieldsObj, obj1, obj2;
+ int i;
+
+ acroForm = new AcroForm(docA, acroFormObjA);
+
+ if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
+ acroForm->needAppearances = obj1.getBool();
+ }
+ obj1.free();
+
+ acroForm->buildAnnotPageList(catalog);
+
+ if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
+ if (!obj1.isNull()) {
+ error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
+ }
+ obj1.free();
+ delete acroForm;
+ return NULL;
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ acroForm->scanField(&obj2);
+ obj2.free();
+ }
+ obj1.free();
+
+ return acroForm;
+}
+
+AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA): Form(docA) {
+ acroFormObjA->copy(&acroFormObj);
+ needAppearances = gFalse;
+ annotPages = new GList();
+ fields = new GList();
+}
+
+AcroForm::~AcroForm() {
+ acroFormObj.free();
+ deleteGList(annotPages, AcroFormAnnotPage);
+ deleteGList(fields, AcroFormField);
+}
+
+void AcroForm::buildAnnotPageList(Catalog *catalog) {
+ Object annotsObj, annotObj;
+ int pageNum, i;
+
+ for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
+ if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
+ for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
+ if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
+ annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
+ annotObj.getRefGen(),
+ pageNum));
+ }
+ annotObj.free();
+ }
+ }
+ annotsObj.free();
+ }
+ //~ sort the list
+}
+
+int AcroForm::lookupAnnotPage(Object *annotRef) {
+ AcroFormAnnotPage *annotPage;
+ int num, gen, i;
+
+ if (!annotRef->isRef()) {
+ return 0;
+ }
+ num = annotRef->getRefNum();
+ gen = annotRef->getRefGen();
+ //~ use bin search
+ for (i = 0; i < annotPages->getLength(); ++i) {
+ annotPage = (AcroFormAnnotPage *)annotPages->get(i);
+ if (annotPage->annotNum == num && annotPage->annotGen == gen) {
+ return annotPage->pageNum;
+ }
+ }
+ return 0;
+}
+
+void AcroForm::scanField(Object *fieldRef) {
+ AcroFormField *field;
+ Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj;
+ GBool isTerminal;
+ int i;
+
+ fieldRef->fetch(doc->getXRef(), &fieldObj);
+ if (!fieldObj.isDict()) {
+ error(errSyntaxError, -1, "AcroForm field object is wrong type");
+ fieldObj.free();
+ return;
+ }
+
+ // if this field has a Kids array, and all of the kids have a Parent
+ // reference (i.e., they're all form fields, not widget
+ // annotations), then this is a non-terminal field, and we need to
+ // scan the kids
+ isTerminal = gTrue;
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ isTerminal = gFalse;
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &kidObj);
+ if (kidObj.isDict()) {
+ if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
+ isTerminal = gTrue;
+ }
+ subtypeObj.free();
+ }
+ kidObj.free();
+ }
+ if (!isTerminal) {
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &kidRef);
+ scanField(&kidRef);
+ kidRef.free();
+ }
+ }
+ }
+ kidsObj.free();
+
+ if (isTerminal) {
+ if ((field = AcroFormField::load(this, fieldRef))) {
+ fields->append(field);
+ }
+ }
+
+ fieldObj.free();
+}
+
+void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
+ int i;
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
+ }
+}
+
+int AcroForm::getNumFields() {
+ return fields->getLength();
+}
+
+FormField *AcroForm::getField(int idx) {
+ return (AcroFormField *)fields->get(idx);
+}
+
+//------------------------------------------------------------------------
+// AcroFormField
+//------------------------------------------------------------------------
+
+AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
+ GString *typeStr;
+ TextString *nameA;
+ Guint flagsA;
+ GBool haveFlags;
+ Object fieldObjA, parentObj, parentObj2, obj1, obj2;
+ AcroFormFieldType typeA;
+ AcroFormField *field;
+
+ fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
+
+ //----- get field info
+
+ if (fieldObjA.dictLookup("T", &obj1)->isString()) {
+ nameA = new TextString(obj1.getString());
+ } else {
+ nameA = new TextString();
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ } else {
+ typeStr = NULL;
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ } else {
+ flagsA = 0;
+ haveFlags = gFalse;
+ }
+ obj1.free();
+
+ //----- get info from parent non-terminal fields
+
+ fieldObjA.dictLookup("Parent", &parentObj);
+ while (parentObj.isDict()) {
+
+ if (parentObj.dictLookup("T", &obj1)->isString()) {
+ if (nameA->getLength()) {
+ nameA->insert(0, (Unicode)'.');
+ }
+ nameA->insert(0, obj1.getString());
+ }
+ obj1.free();
+
+ if (!typeStr) {
+ if (parentObj.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ }
+ obj1.free();
+ }
+
+ if (!haveFlags) {
+ if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ }
+ obj1.free();
+ }
+
+ parentObj.dictLookup("Parent", &parentObj2);
+ parentObj.free();
+ parentObj = parentObj2;
+ }
+ parentObj.free();
+
+ if (!typeStr) {
+ error(errSyntaxError, -1, "Missing type in AcroForm field");
+ goto err1;
+ } else if (!typeStr->cmp("Btn")) {
+ if (flagsA & acroFormFlagPushbutton) {
+ typeA = acroFormFieldPushbutton;
+ } else if (flagsA & acroFormFlagRadio) {
+ typeA = acroFormFieldRadioButton;
+ } else {
+ typeA = acroFormFieldCheckbox;
+ }
+ } else if (!typeStr->cmp("Tx")) {
+ if (flagsA & acroFormFlagFileSelect) {
+ typeA = acroFormFieldFileSelect;
+ } else if (flagsA & acroFormFlagMultiline) {
+ typeA = acroFormFieldMultilineText;
+ } else {
+ typeA = acroFormFieldText;
+ }
+ } else if (!typeStr->cmp("Ch")) {
+ if (flagsA & acroFormFlagCombo) {
+ typeA = acroFormFieldComboBox;
+ } else {
+ typeA = acroFormFieldListBox;
+ }
+ } else if (!typeStr->cmp("Sig")) {
+ typeA = acroFormFieldSignature;
+ } else {
+ error(errSyntaxError, -1, "Invalid type in AcroForm field");
+ goto err1;
+ }
+ delete typeStr;
+
+ field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
+ typeA, nameA, flagsA);
+ fieldObjA.free();
+ return field;
+
+ err1:
+ delete typeStr;
+ delete nameA;
+ fieldObjA.free();
+ return NULL;
+}
+
+AcroFormField::AcroFormField(AcroForm *acroFormA,
+ Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ Guint flagsA) {
+ acroForm = acroFormA;
+ fieldRefA->copy(&fieldRef);
+ fieldObjA->copy(&fieldObj);
+ type = typeA;
+ name = nameA;
+ flags = flagsA;
+}
+
+AcroFormField::~AcroFormField() {
+ fieldRef.free();
+ fieldObj.free();
+ delete name;
+}
+
+const char *AcroFormField::getType() {
+ switch (type) {
+ case acroFormFieldPushbutton: return "PushButton";
+ case acroFormFieldRadioButton: return "RadioButton";
+ case acroFormFieldCheckbox: return "Checkbox";
+ case acroFormFieldFileSelect: return "FileSelect";
+ case acroFormFieldMultilineText: return "MultilineText";
+ case acroFormFieldText: return "Text";
+ case acroFormFieldComboBox: return "ComboBox";
+ case acroFormFieldListBox: return "ListBox";
+ case acroFormFieldSignature: return "Signature";
+ default: return NULL;
+ }
+}
+
+Unicode *AcroFormField::getName(int *length) {
+ Unicode *u, *ret;
+ int n;
+
+ u = name->getUnicode();
+ n = name->getLength();
+ ret = (Unicode *)gmallocn(n, sizeof(Unicode));
+ memcpy(ret, u, n * sizeof(Unicode));
+ *length = n;
+ return ret;
+}
+
+Unicode *AcroFormField::getValue(int *length) {
+ Object obj1;
+ Unicode *u;
+ char *s;
+ TextString *ts;
+ int n, i;
+
+ 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;
+ return u;
+ } 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;
+ return u;
+ } else {
+ return NULL;
+ }
+}
+
+void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
+ Object kidsObj, annotRef, annotObj;
+ int i;
+
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &annotRef);
+ annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
+ drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
+ annotObj.free();
+ annotRef.free();
+ }
+ } else {
+ drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
+ }
+ kidsObj.free();
+}
+
+void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
+ Object *annotRef, Object *annotObj) {
+ Object obj1, obj2;
+ double xMin, yMin, xMax, yMax, t;
+ int annotFlags;
+ GBool oc;
+
+ if (!annotObj->isDict()) {
+ return;
+ }
+
+ //----- get the page number
+
+ // the "P" (page) field in annotations is optional, so we can't
+ // depend on it here
+ if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
+ return;
+ }
+
+ //----- check annotation flags
+
+ if (annotObj->dictLookup("F", &obj1)->isInt()) {
+ annotFlags = obj1.getInt();
+ } else {
+ annotFlags = 0;
+ }
+ obj1.free();
+ if ((annotFlags & annotFlagHidden) ||
+ (printing && !(annotFlags & annotFlagPrint)) ||
+ (!printing && (annotFlags & annotFlagNoView))) {
+ return;
+ }
+
+ //----- check the optional content entry
+
+ annotObj->dictLookupNF("OC", &obj1);
+ if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- get the bounding box
+
+ if (annotObj->dictLookup("Rect", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ xMin = yMin = xMax = yMax = 0;
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ xMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ yMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ xMax = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ yMax = obj2.getNum();
+ }
+ obj2.free();
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ } else {
+ error(errSyntaxError, -1, "Bad bounding box for annotation");
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- draw it
+
+ if (acroForm->needAppearances) {
+ drawNewAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawExistingAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ }
+}
+
+// Draw the existing appearance stream for a single annotation
+// attached to this field.
+void AcroFormField::drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ Object apObj, asObj, appearance, obj1;
+
+ //----- get the appearance stream
+
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookupNF(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetValNF(0, &appearance);
+ } else {
+ obj1.dictLookupNF("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ apObj.dictLookupNF("N", &appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ //----- draw it
+
+ if (!appearance.isNone()) {
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+ appearance.free();
+ }
+}
+
+// Regenerate the appearnce for this field, and draw it.
+void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
+ Object obj1, obj2, obj3;
+ Dict *mkDict;
+ MemStream *appearStream;
+ GfxFontDict *fontDict;
+ GBool hasCaption;
+ double dx, dy, r;
+ GString *caption, *da;
+ GString **text;
+ GBool *selection;
+ AnnotBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ GString *appearanceState;
+ int borderDashLength, rot, quadding, comb, nOptions, topIdx, i, j;
+
+ appearBuf = new GString();
+
+ // get the appearance characteristics (MK) dictionary
+ if (annot->lookup("MK", &mkObj)->isDict()) {
+ mkDict = mkObj.getDict();
+ } else {
+ mkDict = NULL;
+ }
+
+ // draw the background
+ if (mkDict) {
+ if (mkDict->lookup("BG", &obj1)->isArray() &&
+ obj1.arrayGetLength() > 0) {
+ setColor(obj1.getArray(), gTrue, 0);
+ appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n",
+ xMax - xMin, yMax - yMin);
+ }
+ obj1.free();
+ }
+
+ // get the field type
+ fieldLookup("FT", &ftObj);
+
+ // draw the border
+ borderType = annotBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ if (annot->lookup("BS", &obj1)->isDict()) {
+ if (obj1.dictLookup("S", &obj2)->isName()) {
+ if (obj2.isName("S")) {
+ borderType = annotBorderSolid;
+ } else if (obj2.isName("D")) {
+ borderType = annotBorderDashed;
+ } else if (obj2.isName("B")) {
+ borderType = annotBorderBeveled;
+ } else if (obj2.isName("I")) {
+ borderType = annotBorderInset;
+ } else if (obj2.isName("U")) {
+ borderType = annotBorderUnderlined;
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("W", &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.dictLookup("D", &obj2)->isArray()) {
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ if (annot->lookup("Border", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() >= 3) {
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGetLength() >= 4) {
+ if (obj1.arrayGet(3, &obj2)->isArray()) {
+ borderType = annotBorderDashed;
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ } else {
+ // Adobe draws no border at all if the last element is of
+ // the wrong type.
+ borderWidth = 0;
+ }
+ obj2.free();
+ }
+ }
+ }
+ }
+ obj1.free();
+ if (mkDict) {
+ if (borderWidth > 0) {
+ mkDict->lookup("BC", &obj1);
+ if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
+ mkDict->lookup("BG", &obj1);
+ }
+ if (obj1.isArray() && obj1.arrayGetLength() > 0) {
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+
+ // radio buttons with no caption have a round border
+ hasCaption = mkDict->lookup("CA", &obj2)->isString();
+ obj2.free();
+ if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) {
+ r = 0.5 * (dx < dy ? dx : dy);
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s");
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s");
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? 1 : -1);
+ drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth);
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? -1 : 1);
+ drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth);
+ break;
+ }
+
+ } else {
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
+ 0.5 * borderWidth,
+ dx - borderWidth, dy - borderWidth);
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? 1 : -1);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("0 {0:.4f} l\n", dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? -1 : 1);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("{0:.4f} 0 l\n", dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ break;
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
+ break;
+ }
+
+ // clip to the inside of the border
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
+ borderWidth,
+ dx - 2 * borderWidth, dy - 2 * borderWidth);
+ }
+ }
+ obj1.free();
+ }
+ }
+ gfree(borderDash);
+
+ // get the resource dictionary
+ fieldLookup("DR", &drObj);
+
+ // build the font dictionary
+ if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
+ fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
+ } else {
+ fontDict = NULL;
+ }
+ obj1.free();
+
+ // get the default appearance string
+ if (fieldLookup("DA", &obj1)->isString()) {
+ da = obj1.getString()->copy();
+ } else {
+ da = NULL;
+ }
+ obj1.free();
+
+ // get the rotation value
+ rot = 0;
+ if (mkDict) {
+ if (mkDict->lookup("R", &obj1)->isInt()) {
+ rot = obj1.getInt();
+ }
+ obj1.free();
+ }
+
+ // get the appearance state
+ annot->lookup("AP", &apObj);
+ annot->lookup("AS", &asObj);
+ appearanceState = NULL;
+ if (asObj.isName()) {
+ appearanceState = new GString(asObj.getName());
+ } else if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict() && obj1.dictGetLength() == 1) {
+ appearanceState = new GString(obj1.dictGetKey(0));
+ }
+ obj1.free();
+ }
+ if (!appearanceState) {
+ appearanceState = new GString("Off");
+ }
+ asObj.free();
+ apObj.free();
+
+ // draw the field contents
+ if (ftObj.isName("Btn")) {
+ caption = NULL;
+ if (mkDict) {
+ if (mkDict->lookup("CA", &obj1)->isString()) {
+ caption = obj1.getString()->copy();
+ }
+ obj1.free();
+ }
+ // 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 (caption) {
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth);
+ } else {
+ if (mkDict) {
+ if (mkDict->lookup("BC", &obj2)->isArray() &&
+ obj2.arrayGetLength() > 0) {
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ setColor(obj2.getArray(), gTrue, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f");
+ }
+ obj2.free();
+ }
+ }
+ }
+ obj1.free();
+ // pushbutton
+ } else if (flags & acroFormFlagPushbutton) {
+ if (caption) {
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ // checkbox
+ } else {
+ fieldLookup("V", &obj1);
+ if (obj1.isName() && !obj1.isName("Off")) {
+ if (!caption) {
+ caption = new GString("3"); // ZapfDingbats checkmark
+ }
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ obj1.free();
+ }
+ if (caption) {
+ delete caption;
+ }
+ } else if (ftObj.isName("Tx")) {
+ //~ value strings can be Unicode
+ if (!fieldLookup("V", &obj1)->isString()) {
+ obj1.free();
+ fieldLookup("DV", &obj1);
+ }
+ if (obj1.isString()) {
+ if (fieldLookup("Q", &obj2)->isInt()) {
+ quadding = obj2.getInt();
+ } else {
+ quadding = acroFormQuadLeft;
+ }
+ obj2.free();
+ comb = 0;
+ if (flags & acroFormFlagComb) {
+ if (fieldLookup("MaxLen", &obj2)->isInt()) {
+ comb = obj2.getInt();
+ }
+ obj2.free();
+ }
+ drawText(obj1.getString(), da, fontDict,
+ flags & acroFormFlagMultiline, comb, quadding,
+ gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ obj1.free();
+ } else if (ftObj.isName("Ch")) {
+ //~ value/option strings can be Unicode
+ if (fieldLookup("Q", &obj1)->isInt()) {
+ quadding = obj1.getInt();
+ } else {
+ quadding = acroFormQuadLeft;
+ }
+ obj1.free();
+ // combo box
+ if (flags & acroFormFlagCombo) {
+ if (fieldLookup("V", &obj1)->isString()) {
+ drawText(obj1.getString(), da, fontDict,
+ gFalse, 0, quadding, gTrue, gFalse, rot,
+ xMin, yMin, xMax, yMax, borderWidth);
+ //~ Acrobat draws a popup icon on the right side
+ }
+ obj1.free();
+ // list box
+ } else {
+ if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
+ nOptions = obj1.arrayGetLength();
+ // get the option text
+ text = (GString **)gmallocn(nOptions, sizeof(GString *));
+ for (i = 0; i < nOptions; ++i) {
+ text[i] = NULL;
+ obj1.arrayGet(i, &obj2);
+ if (obj2.isString()) {
+ text[i] = obj2.getString()->copy();
+ } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
+ if (obj2.arrayGet(1, &obj3)->isString()) {
+ text[i] = obj3.getString()->copy();
+ }
+ obj3.free();
+ }
+ obj2.free();
+ if (!text[i]) {
+ text[i] = new GString();
+ }
+ }
+ // 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();
+ }
+ }
+ }
+ obj2.free();
+ // get the top index
+ if (fieldObj.dictLookup("TI", &obj2)->isInt()) {
+ topIdx = obj2.getInt();
+ } else {
+ topIdx = 0;
+ }
+ obj2.free();
+ // draw the text
+ drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
+ xMin, yMin, xMax, yMax, borderWidth);
+ for (i = 0; i < nOptions; ++i) {
+ delete text[i];
+ }
+ gfree(text);
+ gfree(selection);
+ }
+ obj1.free();
+ }
+ } else if (ftObj.isName("Sig")) {
+ //~unimp
+ } else {
+ error(errSyntaxError, -1, "Unknown field type");
+ }
+
+ delete appearanceState;
+ if (da) {
+ delete da;
+ }
+
+ // build the appearance stream dictionary
+ appearDict.initDict(acroForm->doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(acroForm->doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+
+ // set the resource dictionary
+ if (drObj.isDict()) {
+ appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+ }
+ drObj.free();
+
+ // build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.initStream(appearStream);
+
+ // draw it
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+
+ appearance.free();
+ delete appearBuf;
+ appearBuf = NULL;
+ if (fontDict) {
+ delete fontDict;
+ }
+ ftObj.free();
+ mkObj.free();
+}
+
+// Set the current fill or stroke color, based on <a> (which should
+// have 1, 3, or 4 elements). If <adjust> is +1, color is brightened;
+// if <adjust> is -1, color is darkened; otherwise color is not
+// modified.
+void AcroFormField::setColor(Array *a, GBool fill, int adjust) {
+ Object obj1;
+ double color[4];
+ int nComps, i;
+
+ nComps = a->getLength();
+ if (nComps > 4) {
+ nComps = 4;
+ }
+ for (i = 0; i < nComps && i < 4; ++i) {
+ if (a->get(i, &obj1)->isNum()) {
+ color[i] = obj1.getNum();
+ } else {
+ color[i] = 0;
+ }
+ obj1.free();
+ }
+ if (nComps == 4) {
+ adjust = -adjust;
+ }
+ if (adjust > 0) {
+ for (i = 0; i < nComps; ++i) {
+ color[i] = 0.5 * color[i] + 0.5;
+ }
+ } else if (adjust < 0) {
+ for (i = 0; i < nComps; ++i) {
+ color[i] = 0.5 * color[i];
+ }
+ }
+ if (nComps == 4) {
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
+ color[0], color[1], color[2], color[3],
+ fill ? 'k' : 'K');
+ } else if (nComps == 3) {
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
+ color[0], color[1], color[2],
+ fill ? "rg" : "RG");
+ } else {
+ appearBuf->appendf("{0:.2f} {1:c}\n",
+ color[0],
+ fill ? 'g' : 'G');
+ }
+}
+
+// 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 txField, GBool forceZapfDingbats, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ double border) {
+ 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;
+
+ //~ if there is no MK entry, this should use the existing content stream,
+ //~ and only replace the marked content portion of it
+ //~ (this is only relevant for Tx fields)
+
+ // check for a Unicode string
+ //~ this currently drops all non-Latin1 characters
+ if (text->getLength() >= 2 &&
+ text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
+ text2 = new GString();
+ for (i = 2; i+1 < text->getLength(); i += 2) {
+ c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
+ if (c <= 0xff) {
+ text2->append((char)c);
+ } else {
+ text2->append('?');
+ }
+ }
+ } else {
+ text2 = text;
+ }
+
+ // parse the default appearance string
+ tfPos = tmPos = -1;
+ if (da) {
+ daToks = new GList();
+ i = 0;
+ while (i < da->getLength()) {
+ while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
+ ++i;
+ }
+ if (i < da->getLength()) {
+ for (j = i + 1;
+ j < da->getLength() && !Lexer::isSpace(da->getChar(j));
+ ++j) ;
+ daToks->append(new GString(da, i, j - i));
+ i = j;
+ }
+ }
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+ } else {
+ daToks = NULL;
+ }
+
+ // force ZapfDingbats
+ //~ this should create the font if needed (?)
+ if (forceZapfDingbats) {
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->cmp("/ZaDb")) {
+ tok->clear();
+ tok->append("/ZaDb");
+ }
+ }
+ }
+
+ // get the font and font size
+ font = NULL;
+ fontSize = 0;
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+ if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+ error(errSyntaxError, -1, "Unknown font in field's DA string");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ }
+
+ // setup
+ if (txField) {
+ appearBuf->append("/Tx BMC\n");
+ }
+ 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;
+ } 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;
+ } else if (rot == 270) {
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
+ dx = yMax - yMin;
+ dy = xMax - xMin;
+ } else { // assume rot == 0
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ }
+ appearBuf->append("BT\n");
+
+ // multi-line text
+ if (multiline) {
+ // note: the comb flag is ignored in multiline mode
+
+ wMax = dx - 2 * border - 4;
+
+ // compute font autosize
+ if (fontSize == 0) {
+ for (fontSize = 20; fontSize > 1; --fontSize) {
+ y = dy - 3;
+ w2 = 0;
+ i = 0;
+ while (i < text2->getLength()) {
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+ if (w > w2) {
+ w2 = w;
+ }
+ i = k;
+ y -= fontSize;
+ }
+ // approximate the descender for the last line
+ if (y >= 0.33 * fontSize) {
+ break;
+ }
+ }
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.2f}", fontSize);
+ }
+ }
+
+ // starting y coordinate
+ // (note: each line of text starts with a Td operator that moves
+ // down a line)
+ y = dy - 3;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->append('0');
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // write a series of lines of text
+ i = 0;
+ xPrev = 0;
+ while (i < text2->getLength()) {
+
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = dx - border - 2 - w;
+ break;
+ }
+
+ // draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", x - xPrev, -fontSize);
+ appearBuf->append('(');
+ for (; i < j; ++i) {
+ c = text2->getChar(i) & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append(c);
+ }
+ }
+ appearBuf->append(") Tj\n");
+
+ // next line
+ i = k;
+ xPrev = x;
+ }
+
+ // single-line text
+ } else {
+ //~ replace newlines with spaces? - what does Acrobat do?
+
+ // comb formatting
+ if (comb > 0) {
+
+ // compute comb spacing
+ w = (dx - 2 * border) / comb;
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ if (w < fontSize) {
+ fontSize = w;
+ }
+ fontSize = floor(fontSize);
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = border + 2 + 0.5 * (comb - text2->getLength()) * w;
+ break;
+ case acroFormQuadRight:
+ x = border + 2 + (comb - text2->getLength()) * w;
+ break;
+ }
+ y = 0.5 * dy - 0.4 * fontSize;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // write the text string
+ //~ this should center (instead of left-justify) each character within
+ //~ its comb cell
+ 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(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("{0:.4f} 0 Td\n", w);
+ } else {
+ appearBuf->append(c);
+ }
+ appearBuf->append(") Tj\n");
+ }
+
+ // regular (non-comb) formatting
+ } else {
+
+ // compute string width
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (i = 0; i < text2->getLength(); ++i) {
+ w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text2->getLength() * 0.5;
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ fontSize2 = (dx - 4 - 2 * border) / w;
+ if (fontSize2 < fontSize) {
+ fontSize = fontSize2;
+ }
+ fontSize = floor(fontSize);
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = dx - border - 2 - w;
+ break;
+ }
+ y = 0.5 * dy - 0.4 * fontSize;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // 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(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append(c);
+ }
+ }
+ appearBuf->append(") Tj\n");
+ }
+ }
+
+ // cleanup
+ appearBuf->append("ET\n");
+ appearBuf->append("Q\n");
+ if (txField) {
+ appearBuf->append("EMC\n");
+ }
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+ if (text2 != text) {
+ delete text2;
+ }
+}
+
+// Draw the variable text or caption for a field.
+void AcroFormField::drawListBox(GString **text, GBool *selection,
+ int nOptions, int topIdx,
+ GString *da, GfxFontDict *fontDict,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border) {
+ GList *daToks;
+ GString *tok;
+ GfxFont *font;
+ double fontSize, fontSize2, x, y, w, wMax;
+ int tfPos, tmPos, i, j, c;
+
+ //~ if there is no MK entry, this should use the existing content stream,
+ //~ and only replace the marked content portion of it
+ //~ (this is only relevant for Tx fields)
+
+ // parse the default appearance string
+ tfPos = tmPos = -1;
+ if (da) {
+ daToks = new GList();
+ i = 0;
+ while (i < da->getLength()) {
+ while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
+ ++i;
+ }
+ if (i < da->getLength()) {
+ for (j = i + 1;
+ j < da->getLength() && !Lexer::isSpace(da->getChar(j));
+ ++j) ;
+ daToks->append(new GString(da, i, j - i));
+ i = j;
+ }
+ }
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+ } else {
+ daToks = NULL;
+ }
+
+ // get the font and font size
+ font = NULL;
+ fontSize = 0;
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+ if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+ error(errSyntaxError, -1, "Unknown font in field's DA string");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ wMax = 0;
+ for (i = 0; i < nOptions; ++i) {
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text[i]->getLength() * 0.5;
+ }
+ if (w > wMax) {
+ wMax = w;
+ }
+ }
+ fontSize = yMax - yMin - 2 * border;
+ fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
+ if (fontSize2 < fontSize) {
+ fontSize = fontSize2;
+ }
+ fontSize = floor(fontSize);
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // draw the text
+ y = yMax - yMin - 1.1 * fontSize;
+ for (i = topIdx; i < nOptions; ++i) {
+
+ // setup
+ appearBuf->append("q\n");
+
+ // draw the background if selected
+ if (selection[i]) {
+ appearBuf->append("0 g f\n");
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ border,
+ y - 0.2 * fontSize,
+ xMax - xMin - 2 * border,
+ 1.1 * fontSize);
+ }
+
+ // setup
+ appearBuf->append("BT\n");
+
+ // compute string width
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text[i]->getLength() * 0.5;
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (xMax - xMin - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = xMax - xMin - border - 2 - w;
+ break;
+ }
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (j = 0; j < daToks->getLength(); ++j) {
+ appearBuf->append((GString *)daToks->get(j))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // change the text color if selected
+ if (selection[i]) {
+ appearBuf->append("1 g\n");
+ }
+
+ // write the text string
+ appearBuf->append('(');
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ c = text[i]->getChar(j) & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append(c);
+ }
+ }
+ appearBuf->append(") Tj\n");
+
+ // cleanup
+ appearBuf->append("ET\n");
+ appearBuf->append("Q\n");
+
+ // next line
+ y -= 1.1 * fontSize;
+ }
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+}
+
+// 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 AcroFormField::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(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;
+}
+
+// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void AcroFormField::drawCircle(double cx, double cy, double r,
+ const char *cmd) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + r, cy + bezierCircle * r,
+ cx + bezierCircle * r, cy + r,
+ cx, cy + r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - bezierCircle * r, cy + r,
+ cx - r, cy + bezierCircle * r,
+ cx - r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - r, cy - bezierCircle * r,
+ cx - bezierCircle * r, cy - r,
+ cx, cy - r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + bezierCircle * r, cy - r,
+ cx + r, cy - bezierCircle * r,
+ cx + r, cy);
+ appearBuf->appendf("{0:s}\n", cmd);
+}
+
+// Draw the top-left half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void AcroFormField::drawCircleTopLeft(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r2, cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - r2,
+ cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx - (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx - r2,
+ cy - r2);
+ appearBuf->append("S\n");
+}
+
+// Draw the bottom-right half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void AcroFormField::drawCircleBottomRight(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx - r2, cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + r2,
+ cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx + (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx + r2,
+ cy + r2);
+ appearBuf->append("S\n");
+}
+
+Object *AcroFormField::getResources(Object *res) {
+ Object kidsObj, annotObj, obj1;
+ int i;
+
+ if (acroForm->needAppearances) {
+ fieldLookup("DR", res);
+ } else {
+ res->initArray(acroForm->doc->getXRef());
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &annotObj);
+ if (annotObj.isDict()) {
+ if (getAnnotResources(annotObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ annotObj.free();
+ }
+ } else {
+ if (getAnnotResources(fieldObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ kidsObj.free();
+ }
+
+ return res;
+}
+
+Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) {
+ Object apObj, asObj, appearance, obj1;
+
+ // get the appearance stream
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookup(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetVal(0, &appearance);
+ } else {
+ obj1.dictLookup("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ obj1.copy(&appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ if (appearance.isStream()) {
+ appearance.streamGetDict()->lookup("Resources", res);
+ } else {
+ res->initNull();
+ }
+ appearance.free();
+
+ return res;
+}
+
+// Look up an inheritable field dictionary entry.
+Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
+ return fieldLookup(fieldObj.getDict(), key, obj);
+}
+
+Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
+ Object parent;
+
+ 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);
+ }
+ parent.free();
+ return obj;
+}
diff --git a/xpdf/AcroForm.h b/xpdf/AcroForm.h
new file mode 100644
index 0000000..8e0075a
--- /dev/null
+++ b/xpdf/AcroForm.h
@@ -0,0 +1,128 @@
+//========================================================================
+//
+// AcroForm.h
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ACROFORM_H
+#define ACROFORM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Form.h"
+
+class TextString;
+class GfxFont;
+class GfxFontDict;
+
+//------------------------------------------------------------------------
+
+class AcroForm: public Form {
+public:
+
+ static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA);
+
+ virtual ~AcroForm();
+
+ virtual const char *getType() { return "AcroForm"; }
+
+ virtual void draw(int pageNum, Gfx *gfx, GBool printing);
+
+ virtual int getNumFields();
+ virtual FormField *getField(int idx);
+
+private:
+
+ AcroForm(PDFDoc *docA, Object *acroFormObjA);
+ void buildAnnotPageList(Catalog *catalog);
+ int lookupAnnotPage(Object *annotRef);
+ void scanField(Object *fieldRef);
+
+ Object acroFormObj;
+ GBool needAppearances;
+ GList *annotPages; // [AcroFormAnnotPage]
+ GList *fields; // [AcroFormField]
+
+ friend class AcroFormField;
+};
+
+//------------------------------------------------------------------------
+
+enum AcroFormFieldType {
+ acroFormFieldPushbutton,
+ acroFormFieldRadioButton,
+ acroFormFieldCheckbox,
+ acroFormFieldFileSelect,
+ acroFormFieldMultilineText,
+ acroFormFieldText,
+ acroFormFieldComboBox,
+ acroFormFieldListBox,
+ acroFormFieldSignature
+};
+
+class AcroFormField: public FormField {
+public:
+
+ static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA);
+
+ virtual ~AcroFormField();
+
+ virtual const char *getType();
+ virtual Unicode *getName(int *length);
+ virtual Unicode *getValue(int *length);
+
+ virtual Object *getResources(Object *res);
+
+private:
+
+ AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ Guint flagsA);
+ void draw(int pageNum, Gfx *gfx, GBool printing);
+ void drawAnnot(int pageNum, Gfx *gfx, GBool printing,
+ Object *annotRef, Object *annotObj);
+ void drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax);
+ void drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax);
+ void setColor(Array *a, GBool fill, int adjust);
+ void drawText(GString *text, GString *da, GfxFontDict *fontDict,
+ GBool multiline, int comb, int quadding,
+ GBool txField, GBool forceZapfDingbats, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ double border);
+ void drawListBox(GString **text, GBool *selection,
+ int nOptions, int topIdx,
+ GString *da, GfxFontDict *fontDict,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border);
+ void getNextLine(GString *text, int start,
+ GfxFont *font, double fontSize, double wMax,
+ int *end, double *width, int *next);
+ void drawCircle(double cx, double cy, double r, const char *cmd);
+ void drawCircleTopLeft(double cx, double cy, double r);
+ void drawCircleBottomRight(double cx, double cy, double r);
+ Object *getAnnotResources(Dict *annot, Object *res);
+ Object *fieldLookup(const char *key, Object *obj);
+ Object *fieldLookup(Dict *dict, const char *key, Object *obj);
+
+ AcroForm *acroForm;
+ Object fieldRef;
+ Object fieldObj;
+ AcroFormFieldType type;
+ TextString *name;
+ Guint flags;
+ GString *appearBuf;
+
+ friend class AcroForm;
+};
+
+#endif
diff --git a/xpdf/Annot.cc b/xpdf/Annot.cc
index 887157f..ff1e376 100644
--- a/xpdf/Annot.cc
+++ b/xpdf/Annot.cc
@@ -24,56 +24,44 @@
#include "Lexer.h"
#include "PDFDoc.h"
#include "OptionalContent.h"
+#include "Form.h"
#include "Annot.h"
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
//------------------------------------------------------------------------
#define annotFlagHidden 0x0002
#define annotFlagPrint 0x0004
#define annotFlagNoView 0x0020
-#define fieldFlagReadOnly 0x00000001
-#define fieldFlagRequired 0x00000002
-#define fieldFlagNoExport 0x00000004
-#define fieldFlagMultiline 0x00001000
-#define fieldFlagPassword 0x00002000
-#define fieldFlagNoToggleToOff 0x00004000
-#define fieldFlagRadio 0x00008000
-#define fieldFlagPushbutton 0x00010000
-#define fieldFlagCombo 0x00020000
-#define fieldFlagEdit 0x00040000
-#define fieldFlagSort 0x00080000
-#define fieldFlagFileSelect 0x00100000
-#define fieldFlagMultiSelect 0x00200000
-#define fieldFlagDoNotSpellCheck 0x00400000
-#define fieldFlagDoNotScroll 0x00800000
-#define fieldFlagComb 0x01000000
-#define fieldFlagRichText 0x02000000
-#define fieldFlagRadiosInUnison 0x02000000
-#define fieldFlagCommitOnSelChange 0x04000000
-
-#define fieldQuadLeft 0
-#define fieldQuadCenter 1
-#define fieldQuadRight 2
-
// distance of Bezier control point from center for circle approximation
// = (4 * (sqrt(2) - 1) / 3) * r
#define bezierCircle 0.55228475
+#define lineEndSize1 6
+#define lineEndSize2 10
+#define lineArrowAngle (M_PI / 6)
+
//------------------------------------------------------------------------
// AnnotBorderStyle
//------------------------------------------------------------------------
AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
double *dashA, int dashLengthA,
- double rA, double gA, double bA) {
+ double *colorA, int nColorCompsA) {
type = typeA;
width = widthA;
dash = dashA;
dashLength = dashLengthA;
- r = rA;
- g = gA;
- b = bA;
+ color[0] = colorA[0];
+ color[1] = colorA[1];
+ color[2] = colorA[2];
+ color[3] = colorA[3];
+ nColorComps = nColorCompsA;
}
AnnotBorderStyle::~AnnotBorderStyle() {
@@ -92,7 +80,8 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
double borderWidth;
double *borderDash;
int borderDashLength;
- double borderR, borderG, borderB;
+ double borderColor[4];
+ int nBorderColorComps;
double t;
int i;
@@ -160,9 +149,11 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
borderWidth = 1;
borderDash = NULL;
borderDashLength = 0;
- borderR = 0;
- borderG = 0;
- borderB = 1;
+ nBorderColorComps = 3;
+ borderColor[0] = 0;
+ borderColor[1] = 0;
+ borderColor[2] = 1;
+ borderColor[3] = 0;
if (dict->lookup("BS", &obj1)->isDict()) {
if (obj1.dictLookup("S", &obj2)->isName()) {
if (obj2.isName("S")) {
@@ -223,28 +214,31 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
}
obj2.free();
}
+ } else {
+ // an empty Border array also means "no border"
+ borderWidth = 0;
}
}
}
obj1.free();
- if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
- if (obj1.arrayGet(0, &obj2)->isNum()) {
- borderR = obj2.getNum();
- }
- obj1.free();
- if (obj1.arrayGet(1, &obj2)->isNum()) {
- borderG = obj2.getNum();
- }
- obj1.free();
- if (obj1.arrayGet(2, &obj2)->isNum()) {
- borderB = obj2.getNum();
+ if (dict->lookup("C", &obj1)->isArray() &&
+ (obj1.arrayGetLength() == 1 ||
+ obj1.arrayGetLength() == 3 ||
+ obj1.arrayGetLength() == 4)) {
+ nBorderColorComps = obj1.arrayGetLength();
+ for (i = 0; i < nBorderColorComps; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ borderColor[i] = obj2.getNum();
+ } else {
+ borderColor[i] = 0;
+ }
+ obj2.free();
}
- obj1.free();
}
obj1.free();
borderStyle = new AnnotBorderStyle(borderType, borderWidth,
borderDash, borderDashLength,
- borderR, borderG, borderB);
+ borderColor, nBorderColorComps);
//----- get the appearance state
@@ -304,350 +298,188 @@ Annot::~Annot() {
ocObj.free();
}
-void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
- Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
- Dict *mkDict;
- MemStream *appearStream;
- GfxFontDict *fontDict;
- GBool hasCaption;
- double w, dx, dy, r;
- double *dash;
- GString *caption, *da;
- GString **text;
- GBool *selection;
- int rot, dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
+void Annot::generateAnnotAppearance() {
+ Object obj;
+
+ appearance.fetch(doc->getXRef(), &obj);
+ if (!obj.isStream()) {
+ if (type) {
+ if (!type->cmp("Line")) {
+ generateLineAppearance();
+ } else if (!type->cmp("PolyLine")) {
+ generatePolyLineAppearance();
+ } else if (!type->cmp("Polygon")) {
+ generatePolygonAppearance();
+ }
+ }
+ }
+ obj.free();
+}
- // must be a Widget annotation
- if (type && type->cmp("Widget")) {
+//~ this doesn't draw the caption
+void Annot::generateLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, x2, y2, dx, dy, len, w;
+ double lx1, ly1, lx2, ly2;
+ double tx1, ty1, tx2, ty2;
+ double ax1, ay1, ax2, ay2;
+ double bx1, by1, bx2, by2;
+ double leaderLen, leaderExtLen, leaderOffLen;
+ AnnotLineEndType lineEnd1, lineEnd2;
+ GBool fill;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
return;
}
appearBuf = new GString();
- // get the appearance characteristics (MK) dictionary
- if (annot->lookup("MK", &mkObj)->isDict()) {
- mkDict = mkObj.getDict();
- } else {
- mkDict = NULL;
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // draw the background
- if (mkDict) {
- if (mkDict->lookup("BG", &obj1)->isArray() &&
- obj1.arrayGetLength() > 0) {
- setColor(obj1.getArray(), gTrue, 0);
- appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
- xMax - xMin, yMax - yMin);
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ fill = gFalse;
+ if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ if (setFillColor(&obj1)) {
+ fill = gTrue;
}
- obj1.free();
- }
-
- // get the field type
- fieldLookup(field, acroForm, "FT", &ftObj);
-
- // get the field flags (Ff) value
- if (fieldLookup(field, acroForm, "Ff", &obj1)->isInt()) {
- ff = obj1.getInt();
- } else {
- ff = 0;
}
obj1.free();
- // draw the border
- if (mkDict) {
- w = borderStyle->getWidth();
- if (w > 0) {
- mkDict->lookup("BC", &obj1);
- if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
- mkDict->lookup("BG", &obj1);
- }
- if (obj1.isArray() && obj1.arrayGetLength() > 0) {
- dx = xMax - xMin;
- dy = yMax - yMin;
-
- // radio buttons with no caption have a round border
- hasCaption = mkDict->lookup("CA", &obj2)->isString();
- obj2.free();
- if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
- r = 0.5 * (dx < dy ? dx : dy);
- switch (borderStyle->getType()) {
- case annotBorderDashed:
- appearBuf->append("[");
- borderStyle->getDash(&dash, &dashLength);
- for (i = 0; i < dashLength; ++i) {
- appearBuf->appendf(" {0:.2f}", dash[i]);
- }
- appearBuf->append("] 0 d\n");
- // fall through to the solid case
- case annotBorderSolid:
- case annotBorderUnderlined:
- appearBuf->appendf("{0:.2f} w\n", w);
- setColor(obj1.getArray(), gFalse, 0);
- drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
- break;
- case annotBorderBeveled:
- case annotBorderInset:
- appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
- setColor(obj1.getArray(), gFalse, 0);
- drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
- setColor(obj1.getArray(), gFalse,
- borderStyle->getType() == annotBorderBeveled ? 1 : -1);
- drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
- setColor(obj1.getArray(), gFalse,
- borderStyle->getType() == annotBorderBeveled ? -1 : 1);
- drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
- break;
- }
-
- } else {
- switch (borderStyle->getType()) {
- case annotBorderDashed:
- appearBuf->append("[");
- borderStyle->getDash(&dash, &dashLength);
- for (i = 0; i < dashLength; ++i) {
- appearBuf->appendf(" {0:.2f}", dash[i]);
- }
- appearBuf->append("] 0 d\n");
- // fall through to the solid case
- case annotBorderSolid:
- appearBuf->appendf("{0:.2f} w\n", w);
- setColor(obj1.getArray(), gFalse, 0);
- appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
- 0.5 * w, dx - w, dy - w);
- break;
- case annotBorderBeveled:
- case annotBorderInset:
- setColor(obj1.getArray(), gTrue,
- borderStyle->getType() == annotBorderBeveled ? 1 : -1);
- appearBuf->append("0 0 m\n");
- appearBuf->appendf("0 {0:.2f} l\n", dy);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
- appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
- appearBuf->append("f\n");
- setColor(obj1.getArray(), gTrue,
- borderStyle->getType() == annotBorderBeveled ? -1 : 1);
- appearBuf->append("0 0 m\n");
- appearBuf->appendf("{0:.2f} 0 l\n", dx);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
- appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
- appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
- appearBuf->append("f\n");
- break;
- case annotBorderUnderlined:
- appearBuf->appendf("{0:.2f} w\n", w);
- setColor(obj1.getArray(), gFalse, 0);
- appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
- break;
- }
-
- // clip to the inside of the border
- appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
- w, dx - 2 * w, dy - 2 * w);
- }
- }
+ //----- get line properties
+ if (annotObj.dictLookup("L", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ x1 = obj2.getNum();
+ } else {
+ obj2.free();
obj1.free();
+ goto err1;
}
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ y1 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ x2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ y2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ goto err1;
}
-
- // get the resource dictionary
- fieldLookup(field, acroForm, "DR", &drObj);
-
- // build the font dictionary
- if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
- fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+ obj1.free();
+ lineEnd1 = lineEnd2 = annotLineEndNone;
+ if (annotObj.dictLookup("LE", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ lineEnd1 = parseLineEndType(obj1.arrayGet(0, &obj2));
+ obj2.free();
+ lineEnd2 = parseLineEndType(obj1.arrayGet(1, &obj2));
+ obj2.free();
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LL", &obj1)->isNum()) {
+ leaderLen = obj1.getNum();
} else {
- fontDict = NULL;
+ leaderLen = 0;
}
obj1.free();
-
- // get the default appearance string
- if (fieldLookup(field, acroForm, "DA", &obj1)->isNull()) {
- obj1.free();
- acroForm->lookup("DA", &obj1);
+ if (annotObj.dictLookup("LLE", &obj1)->isNum()) {
+ leaderExtLen = obj1.getNum();
+ } else {
+ leaderExtLen = 0;
}
- if (obj1.isString()) {
- da = obj1.getString()->copy();
+ obj1.free();
+ if (annotObj.dictLookup("LLO", &obj1)->isNum()) {
+ leaderOffLen = obj1.getNum();
} else {
- da = NULL;
+ leaderOffLen = 0;
}
obj1.free();
- // get the rotation value
- rot = 0;
- if (mkDict) {
- if (mkDict->lookup("R", &obj1)->isInt()) {
- rot = obj1.getInt();
- }
- obj1.free();
+ //----- compute positions
+ x1 -= xMin;
+ y1 -= yMin;
+ x2 -= xMin;
+ y2 -= yMin;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ len = sqrt(dx*dx + dy*dy);
+ if (len > 0) {
+ dx /= len;
+ dy /= len;
}
-
- // draw the field contents
- if (ftObj.isName("Btn")) {
- caption = NULL;
- if (mkDict) {
- if (mkDict->lookup("CA", &obj1)->isString()) {
- caption = obj1.getString()->copy();
- }
- obj1.free();
- }
- // radio button
- if (ff & fieldFlagRadio) {
- //~ Acrobat doesn't draw a caption if there is no AP dict (?)
- if (fieldLookup(field, acroForm, "V", &obj1)
- ->isName(appearanceState->getCString())) {
- if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gTrue, rot);
- } else {
- if (mkDict) {
- if (mkDict->lookup("BC", &obj2)->isArray() &&
- obj2.arrayGetLength() > 0) {
- dx = xMax - xMin;
- dy = yMax - yMin;
- setColor(obj2.getArray(), gTrue, 0);
- drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
- gTrue);
- }
- obj2.free();
- }
- }
- }
- obj1.free();
- // pushbutton
- } else if (ff & fieldFlagPushbutton) {
- if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gFalse, rot);
- }
- // checkbox
- } else {
- fieldLookup(field, acroForm, "V", &obj1);
- if (obj1.isName() && !obj1.isName("Off")) {
- if (!caption) {
- caption = new GString("3"); // ZapfDingbats checkmark
- }
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gTrue, rot);
- }
- obj1.free();
- }
- if (caption) {
- delete caption;
- }
- } else if (ftObj.isName("Tx")) {
- //~ value strings can be Unicode
- if (!fieldLookup(field, acroForm, "V", &obj1)->isString()) {
- obj1.free();
- fieldLookup(field, acroForm, "DV", &obj1);
- }
- if (obj1.isString()) {
- if (fieldLookup(field, acroForm, "Q", &obj2)->isInt()) {
- quadding = obj2.getInt();
- } else {
- quadding = fieldQuadLeft;
- }
- obj2.free();
- comb = 0;
- if (ff & fieldFlagComb) {
- if (fieldLookup(field, acroForm, "MaxLen", &obj2)->isInt()) {
- comb = obj2.getInt();
- }
- obj2.free();
- }
- drawText(obj1.getString(), da, fontDict,
- ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, rot);
- }
- obj1.free();
- } else if (ftObj.isName("Ch")) {
- //~ value/option strings can be Unicode
- if (fieldLookup(field, acroForm, "Q", &obj1)->isInt()) {
- quadding = obj1.getInt();
- } else {
- quadding = fieldQuadLeft;
- }
- obj1.free();
- // combo box
- if (ff & fieldFlagCombo) {
- if (fieldLookup(field, acroForm, "V", &obj1)->isString()) {
- drawText(obj1.getString(), da, fontDict,
- gFalse, 0, quadding, gTrue, gFalse, rot);
- //~ Acrobat draws a popup icon on the right side
- }
- obj1.free();
- // list box
- } else {
- if (field->lookup("Opt", &obj1)->isArray()) {
- nOptions = obj1.arrayGetLength();
- // get the option text
- text = (GString **)gmallocn(nOptions, sizeof(GString *));
- for (i = 0; i < nOptions; ++i) {
- text[i] = NULL;
- obj1.arrayGet(i, &obj2);
- if (obj2.isString()) {
- text[i] = obj2.getString()->copy();
- } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
- if (obj2.arrayGet(1, &obj3)->isString()) {
- text[i] = obj3.getString()->copy();
- }
- obj3.free();
- }
- obj2.free();
- if (!text[i]) {
- text[i] = new GString();
- }
- }
- // get the selected option(s)
- selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
- //~ need to use the I field in addition to the V field
- fieldLookup(field, acroForm, "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();
- }
- }
- }
- obj2.free();
- // get the top index
- if (field->lookup("TI", &obj2)->isInt()) {
- topIdx = obj2.getInt();
- } else {
- topIdx = 0;
- }
- obj2.free();
- // draw the text
- drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
- for (i = 0; i < nOptions; ++i) {
- delete text[i];
- }
- gfree(text);
- gfree(selection);
- }
- obj1.free();
- }
- } else if (ftObj.isName("Sig")) {
- //~unimp
+ if (leaderLen != 0) {
+ ax1 = x1 + leaderOffLen * dy;
+ ay1 = y1 - leaderOffLen * dx;
+ lx1 = ax1 + leaderLen * dy;
+ ly1 = ay1 - leaderLen * dx;
+ bx1 = lx1 + leaderExtLen * dy;
+ by1 = ly1 - leaderExtLen * dx;
+ ax2 = x2 + leaderOffLen * dy;
+ ay2 = y2 - leaderOffLen * dx;
+ lx2 = ax2 + leaderLen * dy;
+ ly2 = ay2 - leaderLen * dx;
+ bx2 = lx2 + leaderExtLen * dy;
+ by2 = ly2 - leaderExtLen * dx;
} else {
- error(errSyntaxError, -1, "Unknown field type");
+ lx1 = x1;
+ ly1 = y1;
+ lx2 = x2;
+ ly2 = y2;
+ ax1 = ay1 = ax2 = ay2 = 0; // make gcc happy
+ bx1 = by1 = bx2 = by2 = 0;
+ }
+ adjustLineEndpoint(lineEnd1, lx1, ly1, dx, dy, w, &tx1, &ty1);
+ adjustLineEndpoint(lineEnd2, lx2, ly2, -dx, -dy, w, &tx2, &ty2);
+
+ //----- draw leaders
+ if (leaderLen != 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax1, ay1, bx1, by1);
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax2, ay2 , bx2, by2);
}
- if (da) {
- delete da;
+ //----- draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ tx1, ty1, tx2, ty2);
+ appearBuf->append("S\n");
+
+ //----- draw the arrows
+ if (borderStyle->getType() == annotBorderDashed) {
+ appearBuf->append("[] 0 d\n");
}
+ drawLineArrow(lineEnd1, lx1, ly1, dx, dy, w, fill);
+ drawLineArrow(lineEnd2, lx2, ly2, -dx, -dy, w, fill);
- // build the appearance stream dictionary
+ //----- build the appearance stream dictionary
appearDict.initDict(doc->getXRef());
appearDict.dictAdd(copyString("Length"),
obj1.initInt(appearBuf->getLength()));
@@ -658,757 +490,482 @@ void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
obj1.arrayAdd(obj2.initReal(xMax - xMin));
obj1.arrayAdd(obj2.initReal(yMax - yMin));
appearDict.dictAdd(copyString("BBox"), &obj1);
-
- // set the resource dictionary
- if (drObj.isDict()) {
- appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- drObj.free();
- // build the appearance stream
+ //----- build the appearance stream
appearStream = new MemStream(appearBuf->getCString(), 0,
appearBuf->getLength(), &appearDict);
appearance.free();
appearance.initStream(appearStream);
- if (fontDict) {
- delete fontDict;
- }
- ftObj.free();
- mkObj.free();
+ err1:
+ annotObj.free();
}
-// Set the current fill or stroke color, based on <a> (which should
-// have 1, 3, or 4 elements). If <adjust> is +1, color is brightened;
-// if <adjust> is -1, color is darkened; otherwise color is not
-// modified.
-void Annot::setColor(Array *a, GBool fill, int adjust) {
- Object obj1;
- double color[4];
- int nComps, i;
+//~ this doesn't handle line ends (arrows)
+void Annot::generatePolyLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, w;
+ int i;
- nComps = a->getLength();
- if (nComps > 4) {
- nComps = 4;
- }
- for (i = 0; i < nComps && i < 4; ++i) {
- if (a->get(i, &obj1)->isNum()) {
- color[i] = obj1.getNum();
- } else {
- color[i] = 0;
- }
- obj1.free();
- }
- if (nComps == 4) {
- adjust = -adjust;
- }
- if (adjust > 0) {
- for (i = 0; i < nComps; ++i) {
- color[i] = 0.5 * color[i] + 0.5;
- }
- } else if (adjust < 0) {
- for (i = 0; i < nComps; ++i) {
- color[i] = 0.5 * color[i];
- }
- }
- if (nComps == 4) {
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
- color[0], color[1], color[2], color[3],
- fill ? 'k' : 'K');
- } else if (nComps == 3) {
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
- color[0], color[1], color[2],
- fill ? "rg" : "RG");
- } else {
- appearBuf->appendf("{0:.2f} {1:c}\n",
- color[0],
- fill ? 'g' : 'G');
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
}
-}
-// Draw the variable text or caption for a field.
-void Annot::drawText(GString *text, GString *da, GfxFontDict *fontDict,
- GBool multiline, int comb, int quadding,
- GBool txField, GBool forceZapfDingbats, int rot) {
- GString *text2;
- GList *daToks;
- GString *tok;
- GfxFont *font;
- double dx, dy;
- double fontSize, fontSize2, border, x, xPrev, y, w, w2, wMax;
- int tfPos, tmPos, 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
- //~ (this is only relevant for Tx fields)
-
- // check for a Unicode string
- //~ this currently drops all non-Latin1 characters
- if (text->getLength() >= 2 &&
- text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
- text2 = new GString();
- for (i = 2; i+1 < text->getLength(); i += 2) {
- c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
- if (c <= 0xff) {
- text2->append((char)c);
- } else {
- text2->append('?');
- }
- }
- } else {
- text2 = text;
- }
+ appearBuf = new GString();
- // parse the default appearance string
- tfPos = tmPos = -1;
- if (da) {
- daToks = new GList();
- i = 0;
- while (i < da->getLength()) {
- while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
- ++i;
- }
- if (i < da->getLength()) {
- for (j = i + 1;
- j < da->getLength() && !Lexer::isSpace(da->getChar(j));
- ++j) ;
- daToks->append(new GString(da, i, j - i));
- i = j;
- }
- }
- for (i = 2; i < daToks->getLength(); ++i) {
- if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
- tfPos = i - 2;
- } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
- tmPos = i - 6;
- }
- }
- } else {
- daToks = NULL;
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // force ZapfDingbats
- //~ this should create the font if needed (?)
- if (forceZapfDingbats) {
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos);
- if (tok->cmp("/ZaDb")) {
- tok->clear();
- tok->append("/ZaDb");
- }
- }
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ // fill = gFalse;
+ // if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ // if (setFillColor(&obj1)) {
+ // fill = gTrue;
+ // }
+ // }
+ // obj1.free();
+
+ //----- draw line
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
}
-
- // get the font and font size
- font = NULL;
- fontSize = 0;
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos);
- if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
- if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
- error(errSyntaxError, -1, "Unknown font in field's DA string");
- }
+ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
} else {
- error(errSyntaxError, -1,
- "Invalid font name in 'Tf' operator in field's DA string");
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
}
- tok = (GString *)daToks->get(tfPos + 1);
- fontSize = atof(tok->getCString());
- } else {
- error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
}
+ appearBuf->append("S\n");
+ obj1.free();
- // get the border width
- border = borderStyle->getWidth();
-
- // setup
- if (txField) {
- appearBuf->append("/Tx BMC\n");
- }
- appearBuf->append("q\n");
- if (rot == 90) {
- appearBuf->appendf("0 1 -1 0 {0:.2f} 0 cm\n", xMax - xMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
- } else if (rot == 180) {
- appearBuf->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n",
- xMax - xMin, yMax - yMin);
- dx = xMax - yMax;
- dy = yMax - yMin;
- } else if (rot == 270) {
- appearBuf->appendf("0 -1 1 0 0 {0:.2f} cm\n", yMax - yMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
- } else { // assume rot == 0
- dx = xMax - xMin;
- dy = yMax - yMin;
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- appearBuf->append("BT\n");
-
- // multi-line text
- if (multiline) {
- // note: the comb flag is ignored in multiline mode
-
- wMax = dx - 2 * border - 4;
-
- // compute font autosize
- if (fontSize == 0) {
- for (fontSize = 20; fontSize > 1; --fontSize) {
- y = dy - 3;
- w2 = 0;
- i = 0;
- while (i < text2->getLength()) {
- getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
- if (w > w2) {
- w2 = w;
- }
- i = k;
- y -= fontSize;
- }
- // approximate the descender for the last line
- if (y >= 0.33 * fontSize) {
- break;
- }
- }
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos + 1);
- tok->clear();
- tok->appendf("{0:.2f}", fontSize);
- }
- }
-
- // starting y coordinate
- // (note: each line of text starts with a Td operator that moves
- // down a line)
- y = dy - 3;
-
- // set the font matrix
- if (tmPos >= 0) {
- tok = (GString *)daToks->get(tmPos + 4);
- tok->clear();
- tok->append('0');
- tok = (GString *)daToks->get(tmPos + 5);
- tok->clear();
- tok->appendf("{0:.2f}", y);
- }
-
- // write the DA string
- if (daToks) {
- for (i = 0; i < daToks->getLength(); ++i) {
- appearBuf->append((GString *)daToks->get(i))->append(' ');
- }
- }
-
- // write the font matrix (if not part of the DA string)
- if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
- }
-
- // write a series of lines of text
- i = 0;
- xPrev = 0;
- while (i < text2->getLength()) {
-
- getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
-
- // compute text start position
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = (dx - w) / 2;
- break;
- case fieldQuadRight:
- x = dx - border - 2 - w;
- break;
- }
-
- // draw the line
- appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
- appearBuf->append('(');
- for (; i < j; ++i) {
- c = text2->getChar(i) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append(c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("\\{0:03o}", c);
- } else {
- appearBuf->append(c);
- }
- }
- appearBuf->append(") Tj\n");
-
- // next line
- i = k;
- xPrev = x;
- }
-
- // single-line text
- } else {
- //~ replace newlines with spaces? - what does Acrobat do?
-
- // comb formatting
- if (comb > 0) {
-
- // compute comb spacing
- w = (dx - 2 * border) / comb;
-
- // compute font autosize
- if (fontSize == 0) {
- fontSize = dy - 2 * border;
- if (w < fontSize) {
- fontSize = w;
- }
- fontSize = floor(fontSize);
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos + 1);
- tok->clear();
- tok->appendf("{0:.2f}", fontSize);
- }
- }
- // compute text start position
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = border + 2 + 0.5 * (comb - text2->getLength()) * w;
- break;
- case fieldQuadRight:
- x = border + 2 + (comb - text2->getLength()) * w;
- break;
- }
- y = 0.5 * dy - 0.4 * fontSize;
-
- // set the font matrix
- if (tmPos >= 0) {
- tok = (GString *)daToks->get(tmPos + 4);
- tok->clear();
- tok->appendf("{0:.2f}", x);
- tok = (GString *)daToks->get(tmPos + 5);
- tok->clear();
- tok->appendf("{0:.2f}", y);
- }
-
- // write the DA string
- if (daToks) {
- for (i = 0; i < daToks->getLength(); ++i) {
- appearBuf->append((GString *)daToks->get(i))->append(' ');
- }
- }
-
- // write the font matrix (if not part of the DA string)
- if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
- }
-
- // write the text string
- //~ this should center (instead of left-justify) each character within
- //~ its comb cell
- for (i = 0; i < text2->getLength(); ++i) {
- if (i > 0) {
- appearBuf->appendf("{0:.2f} 0 Td\n", w);
- }
- appearBuf->append('(');
- c = text2->getChar(i) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append(c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("{0:.2f} 0 Td\n", w);
- } else {
- appearBuf->append(c);
- }
- appearBuf->append(") Tj\n");
- }
-
- // regular (non-comb) formatting
- } else {
-
- // compute string width
- if (font && !font->isCIDFont()) {
- w = 0;
- for (i = 0; i < text2->getLength(); ++i) {
- w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
- }
- } else {
- // otherwise, make a crude estimate
- w = text2->getLength() * 0.5;
- }
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
- // compute font autosize
- if (fontSize == 0) {
- fontSize = dy - 2 * border;
- fontSize2 = (dx - 4 - 2 * border) / w;
- if (fontSize2 < fontSize) {
- fontSize = fontSize2;
- }
- fontSize = floor(fontSize);
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos + 1);
- tok->clear();
- tok->appendf("{0:.2f}", fontSize);
- }
- }
+ err1:
+ annotObj.free();
+}
- // compute text start position
- w *= fontSize;
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = (dx - w) / 2;
- break;
- case fieldQuadRight:
- x = dx - border - 2 - w;
- break;
- }
- y = 0.5 * dy - 0.4 * fontSize;
-
- // set the font matrix
- if (tmPos >= 0) {
- tok = (GString *)daToks->get(tmPos + 4);
- tok->clear();
- tok->appendf("{0:.2f}", x);
- tok = (GString *)daToks->get(tmPos + 5);
- tok->clear();
- tok->appendf("{0:.2f}", y);
- }
+void Annot::generatePolygonAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1;
+ int i;
- // write the DA string
- if (daToks) {
- for (i = 0; i < daToks->getLength(); ++i) {
- appearBuf->append((GString *)daToks->get(i))->append(' ');
- }
- }
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
- // write the font matrix (if not part of the DA string)
- if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
- }
+ appearBuf = new GString();
- // 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(c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("\\{0:03o}", c);
- } else {
- appearBuf->append(c);
- }
- }
- appearBuf->append(") Tj\n");
- }
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // cleanup
- appearBuf->append("ET\n");
- appearBuf->append("Q\n");
- if (txField) {
- appearBuf->append("EMC\n");
+ //----- set fill color
+ if (!annotObj.dictLookup("IC", &obj1)->isArray() ||
+ !setFillColor(&obj1)) {
+ obj1.free();
+ goto err1;
}
+ obj1.free();
- if (daToks) {
- deleteGList(daToks, GString);
- }
- if (text2 != text) {
- delete text2;
+ //----- fill polygon
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
}
-}
-
-// Draw the variable text or caption for a field.
-void Annot::drawListBox(GString **text, GBool *selection,
- int nOptions, int topIdx,
- GString *da, GfxFontDict *fontDict, GBool quadding) {
- GList *daToks;
- GString *tok;
- GfxFont *font;
- double fontSize, fontSize2, border, x, y, w, wMax;
- int tfPos, tmPos, i, j, c;
-
- //~ if there is no MK entry, this should use the existing content stream,
- //~ and only replace the marked content portion of it
- //~ (this is only relevant for Tx fields)
-
- // parse the default appearance string
- tfPos = tmPos = -1;
- if (da) {
- daToks = new GList();
- i = 0;
- while (i < da->getLength()) {
- while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
- ++i;
- }
- if (i < da->getLength()) {
- for (j = i + 1;
- j < da->getLength() && !Lexer::isSpace(da->getChar(j));
- ++j) ;
- daToks->append(new GString(da, i, j - i));
- i = j;
- }
+ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
}
- for (i = 2; i < daToks->getLength(); ++i) {
- if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
- tfPos = i - 2;
- } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
- tmPos = i - 6;
- }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
}
- } else {
- daToks = NULL;
- }
-
- // get the font and font size
- font = NULL;
- fontSize = 0;
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos);
- if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
- if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
- error(errSyntaxError, -1, "Unknown font in field's DA string");
- }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
} else {
- error(errSyntaxError, -1,
- "Invalid font name in 'Tf' operator in field's DA string");
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
}
- tok = (GString *)daToks->get(tfPos + 1);
- fontSize = atof(tok->getCString());
- } else {
- error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
}
+ appearBuf->append("f\n");
+ obj1.free();
- // get the border width
- border = borderStyle->getWidth();
-
- // compute font autosize
- if (fontSize == 0) {
- wMax = 0;
- for (i = 0; i < nOptions; ++i) {
- if (font && !font->isCIDFont()) {
- w = 0;
- for (j = 0; j < text[i]->getLength(); ++j) {
- w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
- }
- } else {
- // otherwise, make a crude estimate
- w = text[i]->getLength() * 0.5;
- }
- if (w > wMax) {
- wMax = w;
- }
- }
- fontSize = yMax - yMin - 2 * border;
- fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
- if (fontSize2 < fontSize) {
- fontSize = fontSize2;
- }
- fontSize = floor(fontSize);
- if (tfPos >= 0) {
- tok = (GString *)daToks->get(tfPos + 1);
- tok->clear();
- tok->appendf("{0:.2f}", fontSize);
- }
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- // draw the text
- y = yMax - yMin - 1.1 * fontSize;
- for (i = topIdx; i < nOptions; ++i) {
-
- // setup
- appearBuf->append("q\n");
-
- // draw the background if selected
- if (selection[i]) {
- appearBuf->append("0 g f\n");
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
- border,
- y - 0.2 * fontSize,
- xMax - xMin - 2 * border,
- 1.1 * fontSize);
- }
-
- // setup
- appearBuf->append("BT\n");
-
- // compute string width
- if (font && !font->isCIDFont()) {
- w = 0;
- for (j = 0; j < text[i]->getLength(); ++j) {
- w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
- }
- } else {
- // otherwise, make a crude estimate
- w = text[i]->getLength() * 0.5;
- }
-
- // compute text start position
- w *= fontSize;
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = (xMax - xMin - w) / 2;
- break;
- case fieldQuadRight:
- x = xMax - xMin - border - 2 - w;
- break;
- }
-
- // set the font matrix
- if (tmPos >= 0) {
- tok = (GString *)daToks->get(tmPos + 4);
- tok->clear();
- tok->appendf("{0:.2f}", x);
- tok = (GString *)daToks->get(tmPos + 5);
- tok->clear();
- tok->appendf("{0:.2f}", y);
- }
-
- // write the DA string
- if (daToks) {
- for (j = 0; j < daToks->getLength(); ++j) {
- appearBuf->append((GString *)daToks->get(j))->append(' ');
- }
- }
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
- // write the font matrix (if not part of the DA string)
- if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
- }
+ err1:
+ annotObj.free();
+}
- // change the text color if selected
- if (selection[i]) {
- appearBuf->append("1 g\n");
- }
+void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
+ double *dash;
+ double w;
+ int dashLength, i;
- // write the text string
- appearBuf->append('(');
- for (j = 0; j < text[i]->getLength(); ++j) {
- c = text[i]->getChar(j) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append(c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("\\{0:03o}", c);
- } else {
- appearBuf->append(c);
- }
+ if ((w = borderStyle->getWidth()) <= 0) {
+ w = 0.1;
+ }
+ *lineWidth = w;
+ appearBuf->appendf("{0:.4f} w\n", w);
+ // this treats beveled/inset/underline as solid
+ if (borderStyle->getType() == annotBorderDashed) {
+ borderStyle->getDash(&dash, &dashLength);
+ appearBuf->append("[");
+ for (i = 0; i < dashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", dash[i]);
}
- appearBuf->append(") Tj\n");
-
- // cleanup
- appearBuf->append("ET\n");
- appearBuf->append("Q\n");
-
- // next line
- y -= 1.1 * fontSize;
+ appearBuf->append("] 0 d\n");
}
+ appearBuf->append("0 j\n0 J\n");
+}
- if (daToks) {
- deleteGList(daToks, GString);
+void Annot::setStrokeColor(double *color, int nComps) {
+ switch (nComps) {
+ case 0:
+ appearBuf->append("0 G\n");
+ break;
+ case 1:
+ appearBuf->appendf("{0:.2f} G\n", color[0]);
+ break;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} RG\n",
+ color[0], color[1], color[2]);
+ break;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} K\n",
+ color[0], color[1], color[2], color[3]);
+ break;
}
}
-// 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 Annot::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(c) * fontSize;
+GBool Annot::setFillColor(Object *colorObj) {
+ Object obj;
+ double color[4];
+ int i;
+
+ if (!colorObj->isArray()) {
+ return gFalse;
+ }
+ for (i = 0; i < colorObj->arrayGetLength(); ++i) {
+ if (colorObj->arrayGet(i, &obj)->isNum()) {
+ color[i] = obj.getNum();
} else {
- // otherwise, make a crude estimate
- dw = 0.5 * fontSize;
+ color[i] = 0;
}
- w += dw;
+ obj.free();
}
- 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;
- }
+ switch (colorObj->arrayGetLength()) {
+ case 1:
+ appearBuf->appendf("{0:.2f} g\n", color[0]);
+ return gTrue;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} rg\n",
+ color[0], color[1], color[2]);
+ return gTrue;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.3f} k\n",
+ color[0], color[1],
+ color[2], color[3]);
+ return gTrue;
}
- *end = j;
+ return gFalse;
+}
- // 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;
+AnnotLineEndType Annot::parseLineEndType(Object *obj) {
+ if (obj->isName("None")) {
+ return annotLineEndNone;
+ } else if (obj->isName("Square")) {
+ return annotLineEndSquare;
+ } else if (obj->isName("Circle")) {
+ return annotLineEndCircle;
+ } else if (obj->isName("Diamond")) {
+ return annotLineEndDiamond;
+ } else if (obj->isName("OpenArrow")) {
+ return annotLineEndOpenArrow;
+ } else if (obj->isName("ClosedArrow")) {
+ return annotLineEndClosedArrow;
+ } else if (obj->isName("Butt")) {
+ return annotLineEndButt;
+ } else if (obj->isName("ROpenArrow")) {
+ return annotLineEndROpenArrow;
+ } else if (obj->isName("RClosedArrow")) {
+ return annotLineEndRClosedArrow;
+ } else if (obj->isName("Slash")) {
+ return annotLineEndSlash;
+ } else {
+ return annotLineEndNone;
}
- *width = w;
+}
- // next line
- while (j < text->getLength() && text->getChar(j) == ' ') {
- ++j;
- }
- if (j < text->getLength() && text->getChar(j) == 0x0d) {
- ++j;
+void Annot::adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ w = 0;
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndOpenArrow:
+ w = 0;
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndButt:
+ w = 0;
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndSlash:
+ w = 0;
+ break;
}
- if (j < text->getLength() && text->getChar(j) == 0x0a) {
- ++j;
+ *tx = x + w * dx;
+ *ty = y + w * dy;
+}
+
+void Annot::drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*dx + 0.5*w*dy,
+ y + w*dy - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx - 0.5*w*dy,
+ y + w*dy + 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ drawCircle(x + 0.5*w*dx, y + 0.5*w*dy, 0.5*w, fill ? "b" : "s");
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx - 0.5*w*dy,
+ y + 0.5*w*dy + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx,
+ y + w*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx + 0.5*w*dy,
+ y + 0.5*w*dy - 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndOpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndButt:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndSlash:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*cos(lineArrowAngle)*dy
+ - 0.5*w*sin(lineArrowAngle)*dx,
+ y - 0.5*w*cos(lineArrowAngle)*dx
+ - 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*cos(lineArrowAngle)*dy
+ + 0.5*w*sin(lineArrowAngle)*dx,
+ y + 0.5*w*cos(lineArrowAngle)*dx
+ + 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->append("S\n");
+ break;
}
- *next = j;
}
// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
-// If <fill> is true, the circle is filled; otherwise it is stroked.
-void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
- appearBuf->appendf("{0:.2f} {1:.2f} m\n",
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void Annot::drawCircle(double cx, double cy, double r, const char *cmd) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx + r, cy);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + r, cy + bezierCircle * r,
cx + bezierCircle * r, cy + r,
cx, cy + r);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx - bezierCircle * r, cy + r,
cx - r, cy + bezierCircle * r,
cx - r, cy);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx - r, cy - bezierCircle * r,
cx - bezierCircle * r, cy - r,
cx, cy - r);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + bezierCircle * r, cy - r,
cx + r, cy - bezierCircle * r,
cx + r, cy);
- appearBuf->append(fill ? "f\n" : "s\n");
+ appearBuf->appendf("{0:s}\n", cmd);
}
// Draw the top-left half of an (approximate) circle of radius <r>
@@ -1417,16 +974,16 @@ void Annot::drawCircleTopLeft(double cx, double cy, double r) {
double r2;
r2 = r / sqrt(2.0);
- appearBuf->appendf("{0:.2f} {1:.2f} m\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx + r2, cy + r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + (1 - bezierCircle) * r2,
cy + (1 + bezierCircle) * r2,
cx - (1 - bezierCircle) * r2,
cy + (1 + bezierCircle) * r2,
cx - r2,
cy + r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx - (1 + bezierCircle) * r2,
cy + (1 - bezierCircle) * r2,
cx - (1 + bezierCircle) * r2,
@@ -1442,16 +999,16 @@ void Annot::drawCircleBottomRight(double cx, double cy, double r) {
double r2;
r2 = r / sqrt(2.0);
- appearBuf->appendf("{0:.2f} {1:.2f} m\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx - r2, cy - r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx - (1 - bezierCircle) * r2,
cy - (1 + bezierCircle) * r2,
cx + (1 - bezierCircle) * r2,
cy - (1 + bezierCircle) * r2,
cx + r2,
cy - r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + (1 + bezierCircle) * r2,
cy - (1 - bezierCircle) * r2,
cx + (1 + bezierCircle) * r2,
@@ -1461,32 +1018,7 @@ void Annot::drawCircleBottomRight(double cx, double cy, double r) {
appearBuf->append("S\n");
}
-// Look up an inheritable field dictionary entry.
-Object *Annot::fieldLookup(Dict *field, Dict *acroForm,
- const char *key, Object *obj) {
- Dict *dict;
- Object parent;
-
- dict = field;
- if (!dict->lookup(key, obj)->isNull()) {
- return obj;
- }
- obj->free();
- if (dict->lookup("Parent", &parent)->isDict()) {
- fieldLookup(parent.getDict(), acroForm, key, obj);
- } else if (acroForm) {
- // some fields don't specify a parent, so we check the AcroForm
- // dictionary just in case
- fieldLookup(acroForm, NULL, key, obj);
- } else {
- obj->initNull();
- }
- parent.free();
- return obj;
-}
-
void Annot::draw(Gfx *gfx, GBool printing) {
- Object obj;
GBool oc, isLink;
// check the flags
@@ -1503,10 +1035,8 @@ void Annot::draw(Gfx *gfx, GBool printing) {
// draw the appearance stream
isLink = type && !type->cmp("Link");
- appearance.fetch(doc->getXRef(), &obj);
- gfx->drawAnnot(&obj, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
+ gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
xMin, yMin, xMax, yMax);
- obj.free();
}
Object *Annot::getObject(Object *obj) {
@@ -1524,8 +1054,9 @@ Object *Annot::getObject(Object *obj) {
Annots::Annots(PDFDoc *docA, Object *annotsObj) {
Annot *annot;
- Object obj1;
+ Object obj1, obj2;
Ref ref;
+ GBool drawWidgetAnnots;
int size;
int i;
@@ -1535,6 +1066,13 @@ Annots::Annots(PDFDoc *docA, Object *annotsObj) {
nAnnots = 0;
if (annotsObj->isArray()) {
+ // Kludge: some PDF files define an empty AcroForm, but still
+ // include Widget-type annotations -- in that case, we want to
+ // draw the widgets (since the form code won't). This really
+ // ought to look for Widget-type annotations that are not included
+ // in any form field.
+ drawWidgetAnnots = !doc->getCatalog()->getForm() ||
+ doc->getCatalog()->getForm()->getNumFields() == 0;
for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
if (annotsObj->arrayGetNF(i, &obj1)->isRef()) {
ref = obj1.getRef();
@@ -1544,16 +1082,20 @@ Annots::Annots(PDFDoc *docA, Object *annotsObj) {
ref.num = ref.gen = -1;
}
if (obj1.isDict()) {
- annot = new Annot(doc, obj1.getDict(), &ref);
- if (annot->isOk()) {
- if (nAnnots >= size) {
- size += 16;
- annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ if (drawWidgetAnnots ||
+ !obj1.dictLookup("Subtype", &obj2)->isName("Widget")) {
+ annot = new Annot(doc, obj1.getDict(), &ref);
+ if (annot->isOk()) {
+ if (nAnnots >= size) {
+ size += 16;
+ annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ }
+ annots[nAnnots++] = annot;
+ } else {
+ delete annot;
}
- annots[nAnnots++] = annot;
- } else {
- delete annot;
}
+ obj2.free();
}
obj1.free();
}
@@ -1569,69 +1111,11 @@ Annots::~Annots() {
gfree(annots);
}
-void Annots::generateAppearances() {
- Dict *acroForm;
- Object obj1, obj2;
- Ref ref;
+void Annots::generateAnnotAppearances() {
int i;
- acroForm = doc->getCatalog()->getAcroForm()->isDict() ?
- doc->getCatalog()->getAcroForm()->getDict() : NULL;
- if (acroForm->lookup("Fields", &obj1)->isArray()) {
- for (i = 0; i < obj1.arrayGetLength(); ++i) {
- if (obj1.arrayGetNF(i, &obj2)->isRef()) {
- ref = obj2.getRef();
- obj2.free();
- obj1.arrayGet(i, &obj2);
- } else {
- ref.num = ref.gen = -1;
- }
- if (obj2.isDict()) {
- scanFieldAppearances(obj2.getDict(), &ref, NULL, acroForm);
- }
- obj2.free();
- }
- }
- obj1.free();
-}
-
-void Annots::scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
- Dict *acroForm) {
- Annot *annot;
- Object obj1, obj2;
- Ref ref2;
- int i;
-
- // non-terminal node: scan the children
- if (node->lookup("Kids", &obj1)->isArray()) {
- for (i = 0; i < obj1.arrayGetLength(); ++i) {
- if (obj1.arrayGetNF(i, &obj2)->isRef()) {
- ref2 = obj2.getRef();
- obj2.free();
- obj1.arrayGet(i, &obj2);
- } else {
- ref2.num = ref2.gen = -1;
- }
- if (obj2.isDict()) {
- scanFieldAppearances(obj2.getDict(), &ref2, node, acroForm);
- }
- obj2.free();
- }
- obj1.free();
- return;
- }
- obj1.free();
-
- // terminal node: this is either a combined annot/field dict, or an
- // annot dict whose parent is a field
- if ((annot = findAnnot(ref))) {
- node->lookupNF("Parent", &obj1);
- if (!parent || !obj1.isNull()) {
- annot->generateFieldAppearance(node, node, acroForm);
- } else {
- annot->generateFieldAppearance(parent, node, acroForm);
- }
- obj1.free();
+ for (i = 0; i < nAnnots; ++i) {
+ annots[i]->generateAnnotAppearance();
}
}
diff --git a/xpdf/Annot.h b/xpdf/Annot.h
index 987e80e..6f52ac4 100644
--- a/xpdf/Annot.h
+++ b/xpdf/Annot.h
@@ -38,15 +38,15 @@ public:
AnnotBorderStyle(AnnotBorderType typeA, double widthA,
double *dashA, int dashLengthA,
- double rA, double gA, double bA);
+ double *colorA, int nColorCompsA);
~AnnotBorderStyle();
AnnotBorderType getType() { return type; }
double getWidth() { return width; }
void getDash(double **dashA, int *dashLengthA)
{ *dashA = dash; *dashLengthA = dashLength; }
- void getColor(double *rA, double *gA, double *bA)
- { *rA = r; *gA = g; *bA = b; }
+ int getNumColorComps() { return nColorComps; }
+ double *getColor() { return color; }
private:
@@ -54,7 +54,23 @@ private:
double width;
double *dash;
int dashLength;
- double r, g, b;
+ double color[4];
+ int nColorComps;
+};
+
+//------------------------------------------------------------------------
+
+enum AnnotLineEndType {
+ annotLineEndNone,
+ annotLineEndSquare,
+ annotLineEndCircle,
+ annotLineEndDiamond,
+ annotLineEndOpenArrow,
+ annotLineEndClosedArrow,
+ annotLineEndButt,
+ annotLineEndROpenArrow,
+ annotLineEndRClosedArrow,
+ annotLineEndSlash
};
//------------------------------------------------------------------------
@@ -85,25 +101,26 @@ public:
GBool match(Ref *refA)
{ return ref.num == refA->num && ref.gen == refA->gen; }
- void generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm);
+ void generateAnnotAppearance();
private:
- void setColor(Array *a, GBool fill, int adjust);
- void drawText(GString *text, GString *da, GfxFontDict *fontDict,
- GBool multiline, int comb, int quadding,
- GBool txField, GBool forceZapfDingbats, int rot);
- void drawListBox(GString **text, GBool *selection,
- int nOptions, int topIdx,
- GString *da, GfxFontDict *fontDict, GBool quadding);
- void getNextLine(GString *text, int start,
- GfxFont *font, double fontSize, double wMax,
- int *end, double *width, int *next);
- void drawCircle(double cx, double cy, double r, GBool fill);
+ void generateLineAppearance();
+ void generatePolyLineAppearance();
+ void generatePolygonAppearance();
+ void setLineStyle(AnnotBorderStyle *bs, double *lineWidth);
+ void setStrokeColor(double *color, int nComps);
+ GBool setFillColor(Object *colorObj);
+ AnnotLineEndType parseLineEndType(Object *obj);
+ void adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty);
+ void drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill);
+ void drawCircle(double cx, double cy, double r, const char *cmd);
void drawCircleTopLeft(double cx, double cy, double r);
void drawCircleBottomRight(double cx, double cy, double r);
- Object *fieldLookup(Dict *field, Dict *acroForm,
- const char *key, Object *obj);
PDFDoc *doc;
XRef *xref; // the xref table for this PDF file
@@ -137,9 +154,9 @@ public:
int getNumAnnots() { return nAnnots; }
Annot *getAnnot(int i) { return annots[i]; }
- // (Re)generate the appearance streams for all annotations belonging
- // to a form field.
- void generateAppearances();
+ // Generate an appearance stream for any non-form-field annotation
+ // that is missing it.
+ void generateAnnotAppearances();
private:
diff --git a/xpdf/CMap.cc b/xpdf/CMap.cc
index bb80bdd..affdf93 100644
--- a/xpdf/CMap.cc
+++ b/xpdf/CMap.cc
@@ -283,34 +283,36 @@ void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
CMapVectorEntry *vec;
- CID cid;
- int byte;
- Guint i, j;
-
- vec = vector;
- for (i = nBytes - 1; i >= 1; --i) {
- byte = (start >> (8 * i)) & 0xff;
- if (!vec[byte].isVector) {
- vec[byte].isVector = gTrue;
- vec[byte].vector =
- (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
- for (j = 0; j < 256; ++j) {
- vec[byte].vector[j].isVector = gFalse;
- vec[byte].vector[j].cid = 0;
+ int byte, byte0, byte1;
+ Guint start1, end1, i, j, k;
+
+ start1 = start & 0xffffff00;
+ end1 = end & 0xffffff00;
+ for (i = start1; i <= end1; i += 0x100) {
+ vec = vector;
+ for (j = nBytes - 1; j >= 1; --j) {
+ byte = (i >> (8 * j)) & 0xff;
+ if (!vec[byte].isVector) {
+ vec[byte].isVector = gTrue;
+ vec[byte].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (k = 0; k < 256; ++k) {
+ vec[byte].vector[k].isVector = gFalse;
+ vec[byte].vector[k].cid = 0;
+ }
}
+ vec = vec[byte].vector;
}
- vec = vec[byte].vector;
- }
- cid = firstCID;
- for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
- if (vec[byte].isVector) {
- error(errSyntaxError, -1,
- "Invalid CID ({0:x} - {1:x} [{2:d} bytes]) in CMap",
- start, end, nBytes);
- } else {
- vec[byte].cid = cid;
+ byte0 = (i < start) ? (start & 0xff) : 0;
+ byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff;
+ for (byte = byte0; byte <= byte1; ++byte) {
+ if (vec[byte].isVector) {
+ error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap",
+ i, nBytes);
+ } else {
+ vec[byte].cid = firstCID + ((i + byte) - start);
+ }
}
- ++cid;
}
}
diff --git a/xpdf/Catalog.cc b/xpdf/Catalog.cc
index 6c8a703..e2e2a87 100644
--- a/xpdf/Catalog.cc
+++ b/xpdf/Catalog.cc
@@ -2,7 +2,7 @@
//
// Catalog.cc
//
-// Copyright 1996-2007 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -27,7 +27,8 @@
#include "Page.h"
#include "Error.h"
#include "Link.h"
-#include "PDFDocEncoding.h"
+#include "Form.h"
+#include "TextString.h"
#include "Catalog.h"
//------------------------------------------------------------------------
@@ -69,23 +70,20 @@ PageTreeNode::~PageTreeNode() {
class EmbeddedFile {
public:
- EmbeddedFile(Unicode *nameA, int nameLenA, Object *streamRefA);
+ EmbeddedFile(TextString *nameA, Object *streamRefA);
~EmbeddedFile();
- Unicode *name;
- int nameLen;
+ TextString *name;
Object streamRef;
};
-EmbeddedFile::EmbeddedFile(Unicode *nameA, int nameLenA,
- Object *streamRefA) {
+EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) {
name = nameA;
- nameLen = nameLenA;
streamRefA->copy(&streamRef);
}
EmbeddedFile::~EmbeddedFile() {
- gfree(name);
+ delete name;
streamRef.free();
}
@@ -105,6 +103,7 @@ Catalog::Catalog(PDFDoc *docA) {
pageRefs = NULL;
numPages = 0;
baseURI = NULL;
+ form = NULL;
embeddedFiles = NULL;
xref->getCatalog(&catDict);
@@ -165,6 +164,10 @@ Catalog::Catalog(PDFDoc *docA) {
// get the AcroForm dictionary
catDict.dictLookup("AcroForm", &acroForm);
+ if (!acroForm.isNull()) {
+ form = Form::load(doc, this, &acroForm);
+ }
+
// get the OCProperties dictionary
catDict.dictLookup("OCProperties", &ocProperties);
@@ -205,6 +208,9 @@ Catalog::~Catalog() {
structTreeRoot.free();
outline.free();
acroForm.free();
+ if (form) {
+ delete form;
+ }
ocProperties.free();
if (embeddedFiles) {
deleteGList(embeddedFiles, EmbeddedFile);
@@ -236,7 +242,8 @@ GString *Catalog::readMetadata() {
GString *s;
Dict *dict;
Object obj;
- int c;
+ char buf[4096];
+ int n;
if (!metadata.isStream()) {
return NULL;
@@ -249,8 +256,8 @@ GString *Catalog::readMetadata() {
obj.free();
s = new GString();
metadata.streamReset();
- while ((c = metadata.streamGetChar()) != EOF) {
- s->append(c);
+ while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) {
+ s->append(buf, n);
}
metadata.streamClose();
return s;
@@ -615,6 +622,12 @@ void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents;
int i;
+ // check for an invalid object reference (e.g., in a damaged PDF file)
+ if (pageNodeRef->getRefNum() < 0 ||
+ pageNodeRef->getRefNum() >= xref->getNumObjects()) {
+ return;
+ }
+
// check for a page tree loop
if (pageNodeRef->isRef()) {
if (touchedObjs[pageNodeRef->getRefNum()]) {
@@ -661,42 +674,22 @@ void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
Object name2, efObj, streamObj;
GString *s;
- Unicode *name;
- int nameLen, i;
+ TextString *name;
if (fileSpec->isDict()) {
if (fileSpec->dictLookup("UF", &name2)->isString()) {
- s = name2.getString();
+ name = new TextString(name2.getString());
} else {
name2.free();
if (fileSpec->dictLookup("F", &name2)->isString()) {
- s = name2.getString();
+ name = new TextString(name2.getString());
} else if (name1 && name1->isString()) {
- s = name1->getString();
- } else {
- s = NULL;
- }
- }
- if (s) {
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- nameLen = (s->getLength() - 2) / 2;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
+ name = new TextString(name1->getString());
} else {
- nameLen = s->getLength();
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
+ s = new GString("?");
+ name = new TextString(s);
+ delete s;
}
- } else {
- nameLen = 1;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- name[0] = '?';
}
name2.free();
if (fileSpec->dictLookup("EF", &efObj)->isDict()) {
@@ -704,13 +697,13 @@ void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
if (!embeddedFiles) {
embeddedFiles = new GList();
}
- embeddedFiles->append(new EmbeddedFile(name, nameLen, &streamObj));
+ embeddedFiles->append(new EmbeddedFile(name, &streamObj));
} else {
- gfree(name);
+ delete name;
}
streamObj.free();
} else {
- gfree(name);
+ delete name;
}
efObj.free();
}
@@ -721,11 +714,15 @@ int Catalog::getNumEmbeddedFiles() {
}
Unicode *Catalog::getEmbeddedFileName(int idx) {
- return ((EmbeddedFile *)embeddedFiles->get(idx))->name;
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode();
}
int Catalog::getEmbeddedFileNameLength(int idx) {
- return ((EmbeddedFile *)embeddedFiles->get(idx))->nameLen;
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength();
+}
+
+Object *Catalog::getEmbeddedFileStreamRef(int idx) {
+ return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef;
}
Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) {
diff --git a/xpdf/Catalog.h b/xpdf/Catalog.h
index efbbeda..7f064aa 100644
--- a/xpdf/Catalog.h
+++ b/xpdf/Catalog.h
@@ -26,6 +26,7 @@ class PageAttrs;
struct Ref;
class LinkDest;
class PageTreeNode;
+class Form;
//------------------------------------------------------------------------
// Catalog
@@ -82,12 +83,15 @@ public:
Object *getAcroForm() { return &acroForm; }
+ Form *getForm() { return form; }
+
Object *getOCProperties() { return &ocProperties; }
// Get the list of embedded files.
int getNumEmbeddedFiles();
Unicode *getEmbeddedFileName(int idx);
int getEmbeddedFileNameLength(int idx);
+ Object *getEmbeddedFileStreamRef(int idx);
Object *getEmbeddedFileStreamObj(int idx, Object *strObj);
private:
@@ -106,6 +110,7 @@ private:
Object structTreeRoot; // structure tree root dictionary
Object outline; // outline dictionary
Object acroForm; // AcroForm dictionary
+ Form *form; // parsed form
Object ocProperties; // OCProperties dictionary
GList *embeddedFiles; // embedded file list [EmbeddedFile]
GBool ok; // true if catalog is valid
diff --git a/xpdf/CharCodeToUnicode.cc b/xpdf/CharCodeToUnicode.cc
index ff40595..674b992 100644
--- a/xpdf/CharCodeToUnicode.cc
+++ b/xpdf/CharCodeToUnicode.cc
@@ -167,7 +167,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
while (getLine(buf, sizeof(buf), f)) {
++line;
if (!(tok = strtok(buf, " \t\r\n")) ||
- !parseHex(tok, strlen(tok), &u0)) {
+ !parseHex(tok, (int)strlen(tok), &u0)) {
error(errSyntaxWarning, -1,
"Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
line, fileName);
@@ -178,7 +178,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
if (!(tok = strtok(NULL, " \t\r\n"))) {
break;
}
- if (!parseHex(tok, strlen(tok), &uBuf[n])) {
+ if (!parseHex(tok, (int)strlen(tok), &uBuf[n])) {
error(errSyntaxWarning, -1,
"Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
line, fileName);
@@ -336,23 +336,21 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
if (code1 > maxCode || code2 > maxCode) {
error(errSyntaxWarning, -1,
"Invalid entry in bfrange block in ToUnicode CMap");
- if (code1 > maxCode) {
- code1 = maxCode;
- }
if (code2 > maxCode) {
code2 = maxCode;
}
}
if (!strcmp(tok3, "[")) {
i = 0;
- while (pst->getToken(tok1, sizeof(tok1), &n1) &&
- code1 + i <= code2) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
if (!strcmp(tok1, "]")) {
break;
}
if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
- tok1[n1 - 1] = '\0';
- addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ if (code1 + i <= code2) {
+ tok1[n1 - 1] = '\0';
+ addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ }
} else {
error(errSyntaxWarning, -1,
"Illegal entry in bfrange block in ToUnicode CMap");
diff --git a/xpdf/CharCodeToUnicode.h b/xpdf/CharCodeToUnicode.h
index b4ccd04..1b870ef 100644
--- a/xpdf/CharCodeToUnicode.h
+++ b/xpdf/CharCodeToUnicode.h
@@ -74,6 +74,8 @@ public:
// code supported by the mapping.
CharCode getLength() { return mapLen; }
+ GBool isIdentity() { return !map; }
+
private:
void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
diff --git a/xpdf/Decrypt.cc b/xpdf/Decrypt.cc
index afde3c3..bf0bfb7 100644
--- a/xpdf/Decrypt.cc
+++ b/xpdf/Decrypt.cc
@@ -16,13 +16,12 @@
#include "gmem.h"
#include "Decrypt.h"
-static void aesKeyExpansion(DecryptAESState *s,
- Guchar *objKey, int objKeyLen);
-static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
static void aes256KeyExpansion(DecryptAES256State *s,
Guchar *objKey, int objKeyLen);
static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
static void sha256(Guchar *msg, int msgLen, Guchar *hash);
+static void sha384(Guchar *msg, int msgLen, Guchar *hash);
+static void sha512(Guchar *msg, int msgLen, Guchar *hash);
static Guchar passwordPad[32] = {
0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
@@ -45,6 +44,7 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
DecryptAES256State state;
Guchar test[127 + 56], test2[32];
GString *userPassword2;
+ const char *userPW;
Guchar fState[256];
Guchar tmpKey[16];
Guchar fx, fy;
@@ -52,7 +52,7 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
*ownerPasswordOk = gFalse;
- if (encRevision == 5) {
+ if (encRevision == 5 || encRevision == 6) {
// check the owner password
if (ownerPassword) {
@@ -65,6 +65,10 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
memcpy(test + len, ownerKey->getCString() + 32, 8);
memcpy(test + len + 8, userKey->getCString(), 48);
sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
if (!memcmp(test, ownerKey->getCString(), 32)) {
// compute the file key from the owner password
@@ -72,6 +76,10 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
memcpy(test + len, ownerKey->getCString() + 40, 8);
memcpy(test + len + 8, userKey->getCString(), 48);
sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
aes256KeyExpansion(&state, test, 32);
for (i = 0; i < 16; ++i) {
state.cbc[i] = 0;
@@ -90,34 +98,45 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
// check the user password
if (userPassword) {
//~ this is supposed to convert the password to UTF-8 using "SASLprep"
+ userPW = userPassword->getCString();
len = userPassword->getLength();
if (len > 127) {
len = 127;
}
- memcpy(test, userPassword->getCString(), len);
- memcpy(test + len, userKey->getCString() + 32, 8);
- sha256(test, len + 8, test);
- if (!memcmp(test, userKey->getCString(), 32)) {
-
- // compute the file key from the user password
- memcpy(test, userPassword->getCString(), len);
- memcpy(test + len, userKey->getCString() + 40, 8);
- sha256(test, len + 8, test);
- aes256KeyExpansion(&state, test, 32);
- for (i = 0; i < 16; ++i) {
- state.cbc[i] = 0;
- }
- aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
- memcpy(fileKey, state.buf, 16);
- aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
- gFalse);
- memcpy(fileKey + 16, state.buf, 16);
+ } else {
+ userPW = "";
+ len = 0;
+ }
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 32, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
+ }
+ if (!memcmp(test, userKey->getCString(), 32)) {
- return gTrue;
+ // compute the file key from the user password
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 40, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
}
+ aes256KeyExpansion(&state, test, 32);
+ for (i = 0; i < 16; ++i) {
+ state.cbc[i] = 0;
+ }
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
+ memcpy(fileKey, state.buf, 16);
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
+ gFalse);
+ memcpy(fileKey + 16, state.buf, 16);
+
+ return gTrue;
}
return gFalse;
+
} else {
// try using the supplied owner password to generate the user password
@@ -172,6 +191,61 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
}
}
+void Decrypt::r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey) {
+ Guchar key1[64*(127+64+48)];
+ DecryptAESState state128;
+ int n, i, j, k;
+
+ i = 0;
+ while (1) {
+ memcpy(key1, pwd, pwdLen);
+ memcpy(key1 + pwdLen, key, keyLen);
+ n = pwdLen + keyLen;
+ if (userKey) {
+ memcpy(key1 + pwdLen + keyLen, userKey, 48);
+ n += 48;
+ }
+ for (j = 1; j < 64; ++j) {
+ memcpy(key1 + j * n, key1, n);
+ }
+ n *= 64;
+ aesKeyExpansion(&state128, key, 16, gFalse);
+ for (j = 0; j < 16; ++j) {
+ state128.cbc[j] = key[16+j];
+ }
+ for (j = 0; j < n; j += 16) {
+ aesEncryptBlock(&state128, key1 + j);
+ memcpy(key1 + j, state128.buf, 16);
+ }
+ k = 0;
+ for (j = 0; j < 16; ++j) {
+ k += key1[j] % 3;
+ }
+ k %= 3;
+ switch (k) {
+ case 0:
+ sha256(key1, n, key);
+ keyLen = 32;
+ break;
+ case 1:
+ sha384(key1, n, key);
+ keyLen = 48;
+ break;
+ case 2:
+ sha512(key1, n, key);
+ keyLen = 64;
+ break;
+ }
+ // from the spec, it appears that i should be incremented after
+ // the test, but that doesn't match what Adobe does
+ ++i;
+ if (i >= 64 && key1[n - 1] <= i - 32) {
+ break;
+ }
+ }
+}
+
GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
GString *ownerKey, GString *userKey,
int permissions, GString *fileID,
@@ -305,8 +379,6 @@ DecryptStream::~DecryptStream() {
}
void DecryptStream::reset() {
- int i;
-
str->reset();
switch (algo) {
case cryptRC4:
@@ -315,17 +387,13 @@ void DecryptStream::reset() {
state.rc4.buf = EOF;
break;
case cryptAES:
- aesKeyExpansion(&state.aes, objKey, objKeyLength);
- for (i = 0; i < 16; ++i) {
- state.aes.cbc[i] = str->getChar();
- }
+ aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue);
+ str->getBlock((char *)state.aes.cbc, 16);
state.aes.bufIdx = 16;
break;
case cryptAES256:
aes256KeyExpansion(&state.aes256, objKey, objKeyLength);
- for (i = 0; i < 16; ++i) {
- state.aes256.cbc[i] = str->getChar();
- }
+ str->getBlock((char *)state.aes256.cbc, 16);
state.aes256.bufIdx = 16;
break;
}
@@ -333,7 +401,7 @@ void DecryptStream::reset() {
int DecryptStream::getChar() {
Guchar in[16];
- int c, i;
+ int c;
c = EOF; // make gcc happy
switch (algo) {
@@ -350,11 +418,8 @@ int DecryptStream::getChar() {
break;
case cryptAES:
if (state.aes.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = (Guchar)c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
}
@@ -366,11 +431,8 @@ int DecryptStream::getChar() {
break;
case cryptAES256:
if (state.aes256.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = (Guchar)c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
}
@@ -386,7 +448,7 @@ int DecryptStream::getChar() {
int DecryptStream::lookChar() {
Guchar in[16];
- int c, i;
+ int c;
c = EOF; // make gcc happy
switch (algo) {
@@ -402,11 +464,8 @@ int DecryptStream::lookChar() {
break;
case cryptAES:
if (state.aes.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
}
@@ -418,11 +477,8 @@ int DecryptStream::lookChar() {
break;
case cryptAES256:
if (state.aes256.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
}
@@ -540,6 +596,14 @@ static inline Guint rotWord(Guint x) {
return ((x << 8) & 0xffffffff) | (x >> 24);
}
+static inline void subBytes(Guchar *state) {
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ state[i] = sbox[state[i]];
+ }
+}
+
static inline void invSubBytes(Guchar *state) {
int i;
@@ -548,6 +612,29 @@ static inline void invSubBytes(Guchar *state) {
}
}
+static inline void shiftRows(Guchar *state) {
+ Guchar t;
+
+ t = state[4];
+ state[4] = state[5];
+ state[5] = state[6];
+ state[6] = state[7];
+ state[7] = t;
+
+ t = state[8];
+ state[8] = state[10];
+ state[10] = t;
+ t = state[9];
+ state[9] = state[11];
+ state[11] = t;
+
+ t = state[15];
+ state[15] = state[14];
+ state[14] = state[13];
+ state[13] = state[12];
+ state[12] = t;
+}
+
static inline void invShiftRows(Guchar *state) {
Guchar t;
@@ -571,6 +658,22 @@ static inline void invShiftRows(Guchar *state) {
state[15] = t;
}
+// {02} \cdot s
+static inline Guchar mul02(Guchar s) {
+ Guchar s2;
+
+ s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+ return s2;
+}
+
+// {03} \cdot s
+static inline Guchar mul03(Guchar s) {
+ Guchar s2;
+
+ s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+ return s ^ s2;
+}
+
// {09} \cdot s
static inline Guchar mul09(Guchar s) {
Guchar s2, s4, s8;
@@ -611,6 +714,22 @@ static inline Guchar mul0e(Guchar s) {
return s2 ^ s4 ^ s8;
}
+static inline void mixColumns(Guchar *state) {
+ int c;
+ Guchar s0, s1, s2, s3;
+
+ for (c = 0; c < 4; ++c) {
+ s0 = state[c];
+ s1 = state[4+c];
+ s2 = state[8+c];
+ s3 = state[12+c];
+ state[c] = mul02(s0) ^ mul03(s1) ^ s2 ^ s3;
+ state[4+c] = s0 ^ mul02(s1) ^ mul03(s2) ^ s3;
+ state[8+c] = s0 ^ s1 ^ mul02(s2) ^ mul03(s3);
+ state[12+c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3);
+ }
+}
+
static inline void invMixColumns(Guchar *state) {
int c;
Guchar s0, s1, s2, s3;
@@ -654,8 +773,9 @@ static inline void addRoundKey(Guchar *state, Guint *w) {
}
}
-static void aesKeyExpansion(DecryptAESState *s,
- Guchar *objKey, int objKeyLen) {
+void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt) {
Guint temp;
int i, round;
@@ -672,12 +792,50 @@ static void aesKeyExpansion(DecryptAESState *s,
}
s->w[i] = s->w[i-4] ^ temp;
}
+ if (decrypt) {
+ for (round = 1; round <= 9; ++round) {
+ invMixColumnsW(&s->w[round * 4]);
+ }
+ }
+}
+
+void aesEncryptBlock(DecryptAESState *s, Guchar *in) {
+ int c, round;
+
+ // initial state + CBC
+ for (c = 0; c < 4; ++c) {
+ s->state[c] = in[4*c] ^ s->cbc[4*c];
+ s->state[4+c] = in[4*c+1] ^ s->cbc[4*c+1];
+ s->state[8+c] = in[4*c+2] ^ s->cbc[4*c+2];
+ s->state[12+c] = in[4*c+3] ^ s->cbc[4*c+3];
+ }
+
+ // round 0
+ addRoundKey(s->state, &s->w[0]);
+
+ // rounds 1 .. 9
for (round = 1; round <= 9; ++round) {
- invMixColumnsW(&s->w[round * 4]);
+ subBytes(s->state);
+ shiftRows(s->state);
+ mixColumns(s->state);
+ addRoundKey(s->state, &s->w[round * 4]);
+ }
+
+ // round 10
+ subBytes(s->state);
+ shiftRows(s->state);
+ addRoundKey(s->state, &s->w[10 * 4]);
+
+ // output + save for next CBC
+ for (c = 0; c < 4; ++c) {
+ s->buf[4*c] = s->cbc[4*c] = s->state[c];
+ s->buf[4*c+1] = s->cbc[4*c+1] = s->state[4+c];
+ s->buf[4*c+2] = s->cbc[4*c+2] = s->state[8+c];
+ s->buf[4*c+3] = s->cbc[4*c+3] = s->state[12+c];
}
}
-static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
+void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
int c, round, n, i;
// initial state
@@ -844,151 +1002,187 @@ static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
}
-void md5(Guchar *msg, int msgLen, Guchar *digest) {
+void md5Start(MD5State *state) {
+ state->a = 0x67452301;
+ state->b = 0xefcdab89;
+ state->c = 0x98badcfe;
+ state->d = 0x10325476;
+ state->bufLen = 0;
+ state->msgLen = 0;
+}
+
+static void md5ProcessBlock(MD5State *state) {
Gulong x[16];
- Gulong a, b, c, d, aa, bb, cc, dd;
- int n64;
- int i, j, k;
+ Gulong a, b, c, d;
+ int i;
- // sanity check
- if (msgLen < 0) {
- return;
+ for (i = 0; i < 16; ++i) {
+ x[i] = state->buf[4*i] | (state->buf[4*i+1] << 8) |
+ (state->buf[4*i+2] << 16) | (state->buf[4*i+3] << 24);
}
- // compute number of 64-byte blocks
- // (length + pad byte (0x80) + 8 bytes for length)
- n64 = (msgLen + 1 + 8 + 63) / 64;
-
- // initialize a, b, c, d
- a = 0x67452301;
- b = 0xefcdab89;
- c = 0x98badcfe;
- d = 0x10325476;
-
- // loop through blocks
- k = 0;
- for (i = 0; i < n64; ++i) {
-
- // grab a 64-byte block
- for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4)
- x[j] = (((((msg[k+3] << 8) + msg[k+2]) << 8) + msg[k+1]) << 8) + msg[k];
- if (i == n64 - 1) {
- if (k == msgLen - 3)
- x[j] = 0x80000000 + (((msg[k+2] << 8) + msg[k+1]) << 8) + msg[k];
- else if (k == msgLen - 2)
- x[j] = 0x800000 + (msg[k+1] << 8) + msg[k];
- else if (k == msgLen - 1)
- x[j] = 0x8000 + msg[k];
- else
- x[j] = 0x80;
- ++j;
- while (j < 16)
- x[j++] = 0;
- x[14] = msgLen << 3;
- }
+ a = state->a;
+ b = state->b;
+ c = state->c;
+ d = state->d;
+
+ // round 1
+ a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478);
+ d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756);
+ c = md5Round1(c, d, a, b, x[2], 17, 0x242070db);
+ b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee);
+ a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf);
+ d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a);
+ c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613);
+ b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501);
+ a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8);
+ d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af);
+ c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
+ b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
+ a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122);
+ d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
+ c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
+ b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);
+
+ // round 2
+ a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562);
+ d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340);
+ c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
+ b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa);
+ a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d);
+ d = md5Round2(d, a, b, c, x[10], 9, 0x02441453);
+ c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
+ b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8);
+ a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6);
+ d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6);
+ c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87);
+ b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed);
+ a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905);
+ d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8);
+ c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9);
+ b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+ // round 3
+ a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942);
+ d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681);
+ c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
+ b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
+ a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44);
+ d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9);
+ c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60);
+ b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
+ a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6);
+ d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa);
+ c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085);
+ b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05);
+ a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039);
+ d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
+ c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
+ b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665);
+
+ // round 4
+ a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244);
+ d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97);
+ c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
+ b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039);
+ a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3);
+ d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92);
+ c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
+ b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1);
+ a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f);
+ d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
+ c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314);
+ b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
+ a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82);
+ d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
+ c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb);
+ b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391);
+
+ // increment a, b, c, d
+ state->a += a;
+ state->b += b;
+ state->c += c;
+ state->d += d;
+
+ state->bufLen = 0;
+}
- // save a, b, c, d
- aa = a;
- bb = b;
- cc = c;
- dd = d;
-
- // round 1
- a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478);
- d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756);
- c = md5Round1(c, d, a, b, x[2], 17, 0x242070db);
- b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee);
- a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf);
- d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a);
- c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613);
- b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501);
- a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8);
- d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af);
- c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
- b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
- a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122);
- d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
- c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
- b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);
-
- // round 2
- a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562);
- d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340);
- c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
- b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa);
- a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d);
- d = md5Round2(d, a, b, c, x[10], 9, 0x02441453);
- c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
- b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8);
- a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6);
- d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6);
- c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87);
- b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed);
- a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905);
- d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8);
- c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9);
- b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
-
- // round 3
- a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942);
- d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681);
- c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
- b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
- a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44);
- d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9);
- c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60);
- b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
- a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6);
- d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa);
- c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085);
- b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05);
- a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039);
- d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
- c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
- b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665);
-
- // round 4
- a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244);
- d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97);
- c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
- b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039);
- a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3);
- d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92);
- c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
- b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1);
- a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f);
- d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
- c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314);
- b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
- a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82);
- d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
- c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb);
- b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391);
-
- // increment a, b, c, d
- a += aa;
- b += bb;
- c += cc;
- d += dd;
+void md5Append(MD5State *state, Guchar *data, int dataLen) {
+ Guchar *p;
+ int remain, k;
+
+ p = data;
+ remain = dataLen;
+ while (state->bufLen + remain >= 64) {
+ k = 64 - state->bufLen;
+ memcpy(state->buf + state->bufLen, p, k);
+ state->bufLen = 64;
+ md5ProcessBlock(state);
+ p += k;
+ remain -= k;
}
+ if (remain > 0) {
+ memcpy(state->buf + state->bufLen, p, remain);
+ state->bufLen += remain;
+ }
+ state->msgLen += dataLen;
+}
+
+void md5Finish(MD5State *state) {
+ // padding and length
+ state->buf[state->bufLen++] = 0x80;
+ if (state->bufLen > 56) {
+ while (state->bufLen < 64) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ md5ProcessBlock(state);
+ }
+ while (state->bufLen < 56) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ state->buf[56] = (Guchar)(state->msgLen << 3);
+ state->buf[57] = (Guchar)(state->msgLen >> 5);
+ state->buf[58] = (Guchar)(state->msgLen >> 13);
+ state->buf[59] = (Guchar)(state->msgLen >> 21);
+ state->buf[60] = (Guchar)(state->msgLen >> 29);
+ state->buf[61] = (Guchar)0;
+ state->buf[62] = (Guchar)0;
+ state->buf[63] = (Guchar)0;
+ state->bufLen = 64;
+ md5ProcessBlock(state);
// break digest into bytes
- digest[0] = (Guchar)(a & 0xff);
- digest[1] = (Guchar)((a >>= 8) & 0xff);
- digest[2] = (Guchar)((a >>= 8) & 0xff);
- digest[3] = (Guchar)((a >>= 8) & 0xff);
- digest[4] = (Guchar)(b & 0xff);
- digest[5] = (Guchar)((b >>= 8) & 0xff);
- digest[6] = (Guchar)((b >>= 8) & 0xff);
- digest[7] = (Guchar)((b >>= 8) & 0xff);
- digest[8] = (Guchar)(c & 0xff);
- digest[9] = (Guchar)((c >>= 8) & 0xff);
- digest[10] = (Guchar)((c >>= 8) & 0xff);
- digest[11] = (Guchar)((c >>= 8) & 0xff);
- digest[12] = (Guchar)(d & 0xff);
- digest[13] = (Guchar)((d >>= 8) & 0xff);
- digest[14] = (Guchar)((d >>= 8) & 0xff);
- digest[15] = (Guchar)((d >>= 8) & 0xff);
+ state->digest[0] = (Guchar)state->a;
+ state->digest[1] = (Guchar)(state->a >> 8);
+ state->digest[2] = (Guchar)(state->a >> 16);
+ state->digest[3] = (Guchar)(state->a >> 24);
+ state->digest[4] = (Guchar)state->b;
+ state->digest[5] = (Guchar)(state->b >> 8);
+ state->digest[6] = (Guchar)(state->b >> 16);
+ state->digest[7] = (Guchar)(state->b >> 24);
+ state->digest[8] = (Guchar)state->c;
+ state->digest[9] = (Guchar)(state->c >> 8);
+ state->digest[10] = (Guchar)(state->c >> 16);
+ state->digest[11] = (Guchar)(state->c >> 24);
+ state->digest[12] = (Guchar)state->d;
+ state->digest[13] = (Guchar)(state->d >> 8);
+ state->digest[14] = (Guchar)(state->d >> 16);
+ state->digest[15] = (Guchar)(state->d >> 24);
+}
+
+void md5(Guchar *msg, int msgLen, Guchar *digest) {
+ MD5State state;
+ int i;
+
+ if (msgLen < 0) {
+ return;
+ }
+ md5Start(&state);
+ md5Append(&state, msg, msgLen);
+ md5Finish(&state);
+ for (i = 0; i < 16; ++i) {
+ digest[i] = state.digest[i];
+ }
}
//------------------------------------------------------------------------
@@ -1042,7 +1236,7 @@ static inline Guint sha256sigma1(Guint x) {
return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
}
-void sha256HashBlock(Guchar *blk, Guint *H) {
+static void sha256HashBlock(Guchar *blk, Guint *H) {
Guint W[64];
Guint a, b, c, d, e, f, g, h;
Guint T1, T2;
@@ -1147,3 +1341,270 @@ static void sha256(Guchar *msg, int msgLen, Guchar *hash) {
hash[i*4 + 3] = (Guchar)H[i];
}
}
+
+//------------------------------------------------------------------------
+// SHA-384 and SHA-512 hashes
+//------------------------------------------------------------------------
+
+typedef unsigned long long SHA512Uint64;
+
+static SHA512Uint64 sha512K[80] = {
+ 0x428a2f98d728ae22LL, 0x7137449123ef65cdLL,
+ 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL,
+ 0x3956c25bf348b538LL, 0x59f111f1b605d019LL,
+ 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL,
+ 0xd807aa98a3030242LL, 0x12835b0145706fbeLL,
+ 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL,
+ 0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL,
+ 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL,
+ 0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL,
+ 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL,
+ 0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL,
+ 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL,
+ 0x983e5152ee66dfabLL, 0xa831c66d2db43210LL,
+ 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL,
+ 0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL,
+ 0x06ca6351e003826fLL, 0x142929670a0e6e70LL,
+ 0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL,
+ 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL,
+ 0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL,
+ 0x81c2c92e47edaee6LL, 0x92722c851482353bLL,
+ 0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL,
+ 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL,
+ 0xd192e819d6ef5218LL, 0xd69906245565a910LL,
+ 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL,
+ 0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL,
+ 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL,
+ 0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL,
+ 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL,
+ 0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL,
+ 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL,
+ 0x90befffa23631e28LL, 0xa4506cebde82bde9LL,
+ 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL,
+ 0xca273eceea26619cLL, 0xd186b8c721c0c207LL,
+ 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL,
+ 0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL,
+ 0x113f9804bef90daeLL, 0x1b710b35131c471bLL,
+ 0x28db77f523047d84LL, 0x32caab7b40c72493LL,
+ 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL,
+ 0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL,
+ 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL
+};
+
+static inline SHA512Uint64 rotr64(SHA512Uint64 x, Guint n) {
+ return (x >> n) | (x << (64 - n));
+}
+
+static inline SHA512Uint64 sha512Ch(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (~x & z);
+}
+
+static inline SHA512Uint64 sha512Maj(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline SHA512Uint64 sha512Sigma0(SHA512Uint64 x) {
+ return rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39);
+}
+
+static inline SHA512Uint64 sha512Sigma1(SHA512Uint64 x) {
+ return rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41);
+}
+
+static inline SHA512Uint64 sha512sigma0(SHA512Uint64 x) {
+ return rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7);
+}
+
+static inline SHA512Uint64 sha512sigma1(SHA512Uint64 x) {
+ return rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6);
+}
+
+static void sha512HashBlock(Guchar *blk, SHA512Uint64 *H) {
+ SHA512Uint64 W[80];
+ SHA512Uint64 a, b, c, d, e, f, g, h;
+ SHA512Uint64 T1, T2;
+ Guint t;
+
+ // 1. prepare the message schedule
+ for (t = 0; t < 16; ++t) {
+ W[t] = ((SHA512Uint64)blk[t*8] << 56) |
+ ((SHA512Uint64)blk[t*8 + 1] << 48) |
+ ((SHA512Uint64)blk[t*8 + 2] << 40) |
+ ((SHA512Uint64)blk[t*8 + 3] << 32) |
+ ((SHA512Uint64)blk[t*8 + 4] << 24) |
+ ((SHA512Uint64)blk[t*8 + 5] << 16) |
+ ((SHA512Uint64)blk[t*8 + 6] << 8) |
+ (SHA512Uint64)blk[t*8 + 7];
+ }
+ for (t = 16; t < 80; ++t) {
+ W[t] = sha512sigma1(W[t-2]) + W[t-7] + sha512sigma0(W[t-15]) + W[t-16];
+ }
+
+ // 2. initialize the eight working variables
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+ // 3.
+ for (t = 0; t < 80; ++t) {
+ T1 = h + sha512Sigma1(e) + sha512Ch(e,f,g) + sha512K[t] + W[t];
+ T2 = sha512Sigma0(a) + sha512Maj(a,b,c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ // 4. compute the intermediate hash value
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+static void sha512(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0x6a09e667f3bcc908LL;
+ H[1] = 0xbb67ae8584caa73bLL;
+ H[2] = 0x3c6ef372fe94f82bLL;
+ H[3] = 0xa54ff53a5f1d36f1LL;
+ H[4] = 0x510e527fade682d1LL;
+ H[5] = 0x9b05688c2b3e6c1fLL;
+ H[6] = 0x1f83d9abfb41bd6bLL;
+ H[7] = 0x5be0cd19137e2179LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 8; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
+
+static void sha384(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0xcbbb9d5dc1059ed8LL;
+ H[1] = 0x629a292a367cd507LL;
+ H[2] = 0x9159015a3070dd17LL;
+ H[3] = 0x152fecd8f70e5939LL;
+ H[4] = 0x67332667ffc00b31LL;
+ H[5] = 0x8eb44a8768581511LL;
+ H[6] = 0xdb0c2e0d64f98fa7LL;
+ H[7] = 0x47b5481dbefa4fa4LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 6; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
diff --git a/xpdf/Decrypt.h b/xpdf/Decrypt.h
index 156acec..5ddcfc8 100644
--- a/xpdf/Decrypt.h
+++ b/xpdf/Decrypt.h
@@ -42,6 +42,8 @@ public:
private:
+ static void r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey);
static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
GString *ownerKey, GString *userKey,
int permissions, GString *fileID,
@@ -104,8 +106,24 @@ private:
//------------------------------------------------------------------------
+struct MD5State {
+ Gulong a, b, c, d;
+ Guchar buf[64];
+ int bufLen;
+ int msgLen;
+ Guchar digest[16];
+};
+
extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+void md5Start(MD5State *state);
+void md5Append(MD5State *state, Guchar *data, int dataLen);
+void md5Finish(MD5State *state);
extern void md5(Guchar *msg, int msgLen, Guchar *digest);
+extern void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt);
+extern void aesEncryptBlock(DecryptAESState *s, Guchar *in);
+extern void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
#endif
diff --git a/xpdf/Dict.cc b/xpdf/Dict.cc
index 3ef804b..7e33fff 100644
--- a/xpdf/Dict.cc
+++ b/xpdf/Dict.cc
@@ -20,13 +20,24 @@
#include "Dict.h"
//------------------------------------------------------------------------
+
+struct DictEntry {
+ char *key;
+ Object val;
+ DictEntry *next;
+};
+
+//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------
Dict::Dict(XRef *xrefA) {
xref = xrefA;
- entries = NULL;
- size = length = 0;
+ size = 8;
+ length = 0;
+ entries = (DictEntry *)gmallocn(size, sizeof(DictEntry));
+ hashTab = (DictEntry **)gmallocn(2 * size - 1, sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
ref = 1;
}
@@ -38,32 +49,69 @@ Dict::~Dict() {
entries[i].val.free();
}
gfree(entries);
+ gfree(hashTab);
}
void Dict::add(char *key, Object *val) {
- if (length == size) {
- if (length == 0) {
- size = 8;
- } else {
- size *= 2;
+ DictEntry *e;
+ int h;
+
+ if ((e = find(key))) {
+ e->val.free();
+ e->val = *val;
+ gfree(key);
+ } else {
+ if (length == size) {
+ expand();
}
- entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ h = hash(key);
+ entries[length].key = key;
+ entries[length].val = *val;
+ entries[length].next = hashTab[h];
+ hashTab[h] = &entries[length];
+ ++length;
}
- entries[length].key = key;
- entries[length].val = *val;
- ++length;
}
-inline DictEntry *Dict::find(const char *key) {
- int i;
+void Dict::expand() {
+ int h, i;
+ size *= 2;
+ entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ hashTab = (DictEntry **)greallocn(hashTab, 2 * size - 1,
+ sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
for (i = 0; i < length; ++i) {
- if (!strcmp(key, entries[i].key))
- return &entries[i];
+ h = hash(entries[i].key);
+ entries[i].next = hashTab[h];
+ hashTab[h] = &entries[i];
+ }
+}
+
+inline DictEntry *Dict::find(const char *key) {
+ DictEntry *e;
+ int h;
+
+ h = hash(key);
+ for (e = hashTab[h]; e; e = e->next) {
+ if (!strcmp(key, e->key)) {
+ return e;
+ }
}
return NULL;
}
+int Dict::hash(const char *key) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = key; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % (2 * size - 1));
+}
+
GBool Dict::is(const char *type) {
DictEntry *e;
diff --git a/xpdf/Dict.h b/xpdf/Dict.h
index 1f3f8b5..301d0b8 100644
--- a/xpdf/Dict.h
+++ b/xpdf/Dict.h
@@ -17,15 +17,12 @@
#include "Object.h"
+struct DictEntry;
+
//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------
-struct DictEntry {
- char *key;
- Object val;
-};
-
class Dict {
public:
@@ -67,11 +64,14 @@ private:
XRef *xref; // the xref table for this PDF file
DictEntry *entries; // array of entries
+ DictEntry **hashTab; // hash table pointers
int size; // size of <entries> array
int length; // number of entries in dictionary
int ref; // reference count
DictEntry *find(const char *key);
+ void expand();
+ int hash(const char *key);
};
#endif
diff --git a/xpdf/Error.cc b/xpdf/Error.cc
index cb4f27c..600442e 100644
--- a/xpdf/Error.cc
+++ b/xpdf/Error.cc
@@ -2,7 +2,7 @@
//
// Error.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -41,9 +41,12 @@ void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
errorCbkData = data;
}
-void CDECL error(ErrorCategory category, int pos, const char *msg, ...) {
+void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...) {
va_list args;
- GString *s;
+ GString *s, *sanitized;
+ char c;
+ int i;
// NB: this can be called before the globalParams object is created
if (!errorCbk && globalParams && globalParams->getErrQuiet()) {
@@ -52,17 +55,32 @@ void CDECL error(ErrorCategory category, int pos, const char *msg, ...) {
va_start(args, msg);
s = GString::formatv(msg, args);
va_end(args);
+
+ // remove non-printable characters, just in case they might cause
+ // problems for the terminal program
+ sanitized = new GString();
+ for (i = 0; i < s->getLength(); ++i) {
+ c = s->getChar(i);
+ if (c >= 0x20 && c <= 0x7e) {
+ sanitized->append(c);
+ } else {
+ sanitized->appendf("<{0:02x}>", c & 0xff);
+ }
+ }
+
if (errorCbk) {
- (*errorCbk)(errorCbkData, category, pos, s->getCString());
+ (*errorCbk)(errorCbkData, category, (int)pos, sanitized->getCString());
} else {
if (pos >= 0) {
fprintf(stderr, "%s (%d): %s\n",
- errorCategoryNames[category], pos, s->getCString());
+ errorCategoryNames[category], (int)pos, sanitized->getCString());
} else {
fprintf(stderr, "%s: %s\n",
- errorCategoryNames[category], s->getCString());
+ errorCategoryNames[category], sanitized->getCString());
}
fflush(stderr);
}
+
delete s;
+ delete sanitized;
}
diff --git a/xpdf/Error.h b/xpdf/Error.h
index a4ae5c9..6c6afe3 100644
--- a/xpdf/Error.h
+++ b/xpdf/Error.h
@@ -17,11 +17,12 @@
#include <stdio.h>
#include "config.h"
+#include "gfile.h"
enum ErrorCategory {
errSyntaxWarning, // PDF syntax error which can be worked around;
// output will probably be correct
- errSyntaxError, // PDF syntax error which can be worked around;
+ errSyntaxError, // PDF syntax error which cannot be worked around;
// output will probably be incorrect
errConfig, // error in Xpdf config info (xpdfrc file, etc.)
errCommandLine, // error in user-supplied parameters, action not
@@ -37,6 +38,7 @@ extern void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
int pos, char *msg),
void *data);
-extern void CDECL error(ErrorCategory category, int pos, const char *msg, ...);
+extern void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...);
#endif
diff --git a/xpdf/Form.cc b/xpdf/Form.cc
new file mode 100644
index 0000000..f65a1e2
--- /dev/null
+++ b/xpdf/Form.cc
@@ -0,0 +1,67 @@
+//========================================================================
+//
+// Form.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#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, catDict, needsRenderingObj;
+
+ if (!acroFormObj->isDict()) {
+ error(errSyntaxError, -1, "AcroForm object is wrong type");
+ return NULL;
+ }
+ //~ temporary: create an XFAForm only for XFAF, not for dynamic XFA
+ acroFormObj->dictLookup("XFA", &xfaObj);
+ docA->getXRef()->getCatalog(&catDict);
+ catDict.dictLookup("NeedsRendering", &needsRenderingObj);
+ catDict.free();
+ if (globalParams->getEnableXFA() &&
+ !xfaObj.isNull() &&
+ !(needsRenderingObj.isBool() && needsRenderingObj.getBool())) {
+ form = XFAForm::load(docA, acroFormObj, &xfaObj);
+ } else {
+ form = AcroForm::load(docA, catalog, acroFormObj);
+ }
+ xfaObj.free();
+ needsRenderingObj.free();
+ return form;
+}
+
+Form::Form(PDFDoc *docA) {
+ doc = docA;
+}
+
+Form::~Form() {
+}
+
+//------------------------------------------------------------------------
+// FormField
+//------------------------------------------------------------------------
+
+FormField::FormField() {
+}
+
+FormField::~FormField() {
+}
diff --git a/xpdf/Form.h b/xpdf/Form.h
new file mode 100644
index 0000000..50c0f47
--- /dev/null
+++ b/xpdf/Form.h
@@ -0,0 +1,64 @@
+//========================================================================
+//
+// 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;
+
+protected:
+
+ Form(PDFDoc *docA);
+
+ PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+
+class FormField {
+public:
+
+ FormField();
+ virtual ~FormField();
+
+ virtual const char *getType() = 0;
+ virtual Unicode *getName(int *length) = 0;
+ virtual Unicode *getValue(int *length) = 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/xpdf/Function.cc b/xpdf/Function.cc
index 2659af1..a06d10d 100644
--- a/xpdf/Function.cc
+++ b/xpdf/Function.cc
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <math.h>
#include "gmem.h"
+#include "GList.h"
#include "Object.h"
#include "Dict.h"
#include "Stream.h"
@@ -350,7 +351,7 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
samples = (double *)gmallocn(nSamples, sizeof(double));
buf = 0;
bits = 0;
- bitMask = (1 << sampleBits) - 1;
+ bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU;
str->reset();
for (i = 0; i < nSamples; ++i) {
if (sampleBits == 8) {
@@ -778,55 +779,57 @@ void StitchingFunction::transform(double *in, double *out) {
// PostScriptFunction
//------------------------------------------------------------------------
-enum PSOp {
- psOpAbs,
- psOpAdd,
- psOpAnd,
- psOpAtan,
- psOpBitshift,
- psOpCeiling,
- psOpCopy,
- psOpCos,
- psOpCvi,
- psOpCvr,
- psOpDiv,
- psOpDup,
- psOpEq,
- psOpExch,
- psOpExp,
- psOpFalse,
- psOpFloor,
- psOpGe,
- psOpGt,
- psOpIdiv,
- psOpIndex,
- psOpLe,
- psOpLn,
- psOpLog,
- psOpLt,
- psOpMod,
- psOpMul,
- psOpNe,
- psOpNeg,
- psOpNot,
- psOpOr,
- psOpPop,
- psOpRoll,
- psOpRound,
- psOpSin,
- psOpSqrt,
- psOpSub,
- psOpTrue,
- psOpTruncate,
- psOpXor,
- psOpIf,
- psOpIfelse,
- psOpReturn
-};
+// This is not an enum, because we can't foreward-declare the enum
+// type in Function.h
+#define psOpAbs 0
+#define psOpAdd 1
+#define psOpAnd 2
+#define psOpAtan 3
+#define psOpBitshift 4
+#define psOpCeiling 5
+#define psOpCopy 6
+#define psOpCos 7
+#define psOpCvi 8
+#define psOpCvr 9
+#define psOpDiv 10
+#define psOpDup 11
+#define psOpEq 12
+#define psOpExch 13
+#define psOpExp 14
+#define psOpFalse 15
+#define psOpFloor 16
+#define psOpGe 17
+#define psOpGt 18
+#define psOpIdiv 19
+#define psOpIndex 20
+#define psOpLe 21
+#define psOpLn 22
+#define psOpLog 23
+#define psOpLt 24
+#define psOpMod 25
+#define psOpMul 26
+#define psOpNe 27
+#define psOpNeg 28
+#define psOpNot 29
+#define psOpOr 30
+#define psOpPop 31
+#define psOpRoll 32
+#define psOpRound 33
+#define psOpSin 34
+#define psOpSqrt 35
+#define psOpSub 36
+#define psOpTrue 37
+#define psOpTruncate 38
+#define psOpXor 39
+#define psOpPush 40
+#define psOpJ 41
+#define psOpJz 42
+
+#define nPSOps 43
// Note: 'if' and 'ifelse' are parsed separately.
// The rest are listed here in alphabetical order.
-// The index in this table is equivalent to the entry in PSOp.
+// The index in this table is equivalent to the psOpXXX defines.
static const char *psOpNames[] = {
"abs",
"add",
@@ -870,218 +873,22 @@ static const char *psOpNames[] = {
"xor"
};
-#define nPSOps (sizeof(psOpNames) / sizeof(char *))
-
-enum PSObjectType {
- psBool,
- psInt,
- psReal,
- psOperator,
- psBlock
-};
-
-// In the code array, 'if'/'ifelse' operators take up three slots
-// plus space for the code in the subclause(s).
-//
-// +---------------------------------+
-// | psOperator: psOpIf / psOpIfelse |
-// +---------------------------------+
-// | psBlock: ptr=<A> |
-// +---------------------------------+
-// | psBlock: ptr=<B> |
-// +---------------------------------+
-// | if clause |
-// | ... |
-// | psOperator: psOpReturn |
-// +---------------------------------+
-// <A> | else clause |
-// | ... |
-// | psOperator: psOpReturn |
-// +---------------------------------+
-// <B> | ... |
-//
-// For 'if', pointer <A> is present in the code stream but unused.
-
-struct PSObject {
- PSObjectType type;
+struct PSCode {
+ int op;
union {
- GBool booln; // boolean (stack only)
- int intg; // integer (stack and code)
- double real; // real (stack and code)
- PSOp op; // operator (code only)
- int blk; // if/ifelse block pointer (code only)
- };
+ double d;
+ int i;
+ } val;
};
#define psStackSize 100
-class PSStack {
-public:
-
- PSStack() { sp = psStackSize; }
- void pushBool(GBool booln);
- void pushInt(int intg);
- void pushReal(double real);
- GBool popBool();
- int popInt();
- double popNum();
- GBool empty() { return sp == psStackSize; }
- GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; }
- GBool topTwoAreInts()
- { return sp < psStackSize - 1 &&
- stack[sp].type == psInt &&
- stack[sp+1].type == psInt; }
- GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; }
- GBool topTwoAreNums()
- { return sp < psStackSize - 1 &&
- (stack[sp].type == psInt || stack[sp].type == psReal) &&
- (stack[sp+1].type == psInt || stack[sp+1].type == psReal); }
- void copy(int n);
- void roll(int n, int j);
- void index(int i);
- void pop();
-
-private:
-
- GBool checkOverflow(int n = 1);
- GBool checkUnderflow();
- GBool checkType(PSObjectType t1, PSObjectType t2);
-
- PSObject stack[psStackSize];
- int sp;
-};
-
-GBool PSStack::checkOverflow(int n) {
- if (sp - n < 0) {
- error(errSyntaxError, -1, "Stack overflow in PostScript function");
- return gFalse;
- }
- return gTrue;
-}
-
-GBool PSStack::checkUnderflow() {
- if (sp == psStackSize) {
- error(errSyntaxError, -1, "Stack underflow in PostScript function");
- return gFalse;
- }
- return gTrue;
-}
-
-GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) {
- if (stack[sp].type != t1 && stack[sp].type != t2) {
- error(errSyntaxError, -1, "Type mismatch in PostScript function");
- return gFalse;
- }
- return gTrue;
-}
-
-void PSStack::pushBool(GBool booln) {
- if (checkOverflow()) {
- stack[--sp].type = psBool;
- stack[sp].booln = booln;
- }
-}
-
-void PSStack::pushInt(int intg) {
- if (checkOverflow()) {
- stack[--sp].type = psInt;
- stack[sp].intg = intg;
- }
-}
-
-void PSStack::pushReal(double real) {
- if (checkOverflow()) {
- stack[--sp].type = psReal;
- stack[sp].real = real;
- }
-}
-
-GBool PSStack::popBool() {
- if (checkUnderflow() && checkType(psBool, psBool)) {
- return stack[sp++].booln;
- }
- return gFalse;
-}
-
-int PSStack::popInt() {
- if (checkUnderflow() && checkType(psInt, psInt)) {
- return stack[sp++].intg;
- }
- return 0;
-}
-
-double PSStack::popNum() {
- double ret;
-
- if (checkUnderflow() && checkType(psInt, psReal)) {
- ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real;
- ++sp;
- return ret;
- }
- return 0;
-}
-
-void PSStack::copy(int n) {
- int i;
-
- if (sp + n > psStackSize) {
- error(errSyntaxError, -1, "Stack underflow in PostScript function");
- return;
- }
- if (!checkOverflow(n)) {
- return;
- }
- for (i = sp + n - 1; i >= sp; --i) {
- stack[i - n] = stack[i];
- }
- sp -= n;
-}
-
-void PSStack::roll(int n, int j) {
- PSObject obj;
- int i, k;
-
- if (j >= 0) {
- j %= n;
- } else {
- j = -j % n;
- if (j != 0) {
- j = n - j;
- }
- }
- if (n <= 0 || j == 0 || n > psStackSize || sp + n > psStackSize) {
- return;
- }
- for (i = 0; i < j; ++i) {
- obj = stack[sp];
- for (k = sp; k < sp + n - 1; ++k) {
- stack[k] = stack[k+1];
- }
- stack[sp + n - 1] = obj;
- }
-}
-
-void PSStack::index(int i) {
- if (!checkOverflow()) {
- return;
- }
- --sp;
- stack[sp] = stack[sp + 1 + i];
-}
-
-void PSStack::pop() {
- if (!checkUnderflow()) {
- return;
- }
- ++sp;
-}
-
PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
Stream *str;
- int codePtr;
+ GList *tokens;
GString *tok;
double in[funcMaxInputs];
- int i;
+ int tokPtr, codePtr, i;
codeString = NULL;
code = NULL;
@@ -1104,22 +911,27 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
}
str = funcObj->getStream();
- //----- parse the function
+ //----- tokenize the function
codeString = new GString();
+ tokens = new GList();
str->reset();
- if (!(tok = getToken(str)) || tok->cmp("{")) {
+ while ((tok = getToken(str))) {
+ tokens->append(tok);
+ }
+ str->close();
+
+ //----- parse the function
+ if (tokens->getLength() < 1 ||
+ ((GString *)tokens->get(0))->cmp("{")) {
error(errSyntaxError, -1, "Expected '{' at start of PostScript function");
- if (tok) {
- delete tok;
- }
- goto err1;
+ goto err2;
}
- delete tok;
+ tokPtr = 1;
codePtr = 0;
- if (!parseCode(str, &codePtr)) {
+ if (!parseCode(tokens, &tokPtr, &codePtr)) {
goto err2;
}
- str->close();
+ codeLen = codePtr;
//----- set up the cache
for (i = 0; i < m; ++i) {
@@ -1131,16 +943,16 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
ok = gTrue;
err2:
- str->close();
+ deleteGList(tokens, GString);
err1:
return;
}
PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
memcpy(this, func, sizeof(PostScriptFunction));
- code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
- memcpy(code, func->code, codeSize * sizeof(PSObject));
codeString = func->codeString->copy();
+ code = (PSCode *)gmallocn(codeSize, sizeof(PSCode));
+ memcpy(code, func->code, codeSize * sizeof(PSCode));
}
PostScriptFunction::~PostScriptFunction() {
@@ -1151,8 +963,9 @@ PostScriptFunction::~PostScriptFunction() {
}
void PostScriptFunction::transform(double *in, double *out) {
- PSStack *stack;
- int i;
+ double stack[psStackSize];
+ double x;
+ int sp, i;
// check the cache
for (i = 0; i < m; ++i) {
@@ -1167,25 +980,28 @@ void PostScriptFunction::transform(double *in, double *out) {
return;
}
- stack = new PSStack();
for (i = 0; i < m; ++i) {
- //~ may need to check for integers here
- stack->pushReal(in[i]);
+ stack[psStackSize - 1 - i] = in[i];
}
- exec(stack, 0);
- for (i = n - 1; i >= 0; --i) {
- out[i] = stack->popNum();
- if (out[i] < range[i][0]) {
+ sp = exec(stack, psStackSize - m);
+ // if (sp < psStackSize - n) {
+ // error(errSyntaxWarning, -1,
+ // "Extra values on stack at end of PostScript function");
+ // }
+ if (sp > psStackSize - n) {
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ sp = psStackSize - n;
+ }
+ for (i = 0; i < n; ++i) {
+ x = stack[sp + n - 1 - i];
+ if (x < range[i][0]) {
out[i] = range[i][0];
- } else if (out[i] > range[i][1]) {
+ } else if (x > range[i][1]) {
out[i] = range[i][1];
+ } else {
+ out[i] = x;
}
}
- // if (!stack->empty()) {
- // error(errSyntaxWarning, -1,
- // "Extra values on stack at end of PostScript function");
- // }
- delete stack;
// save current result in the cache
for (i = 0; i < m; ++i) {
@@ -1196,101 +1012,71 @@ void PostScriptFunction::transform(double *in, double *out) {
}
}
-GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
+GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) {
GString *tok;
char *p;
- GBool isReal;
- int opPtr, elsePtr;
int a, b, mid, cmp;
+ int codePtr0, codePtr1;
while (1) {
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
+ tok = (GString *)tokens->get((*tokPtr)++);
p = tok->getCString();
if (isdigit(*p) || *p == '.' || *p == '-') {
- isReal = gFalse;
- for (; *p; ++p) {
- if (*p == '.') {
- isReal = gTrue;
- break;
- }
- }
- resizeCode(*codePtr);
- if (isReal) {
- code[*codePtr].type = psReal;
- code[*codePtr].real = atof(tok->getCString());
- } else {
- code[*codePtr].type = psInt;
- code[*codePtr].intg = atoi(tok->getCString());
- }
- ++*codePtr;
- delete tok;
+ addCodeD(codePtr, psOpPush, atof(tok->getCString()));
} else if (!tok->cmp("{")) {
- delete tok;
- opPtr = *codePtr;
- *codePtr += 3;
- resizeCode(opPtr + 2);
- if (!parseCode(str, codePtr)) {
+ codePtr0 = *codePtr;
+ addCodeI(codePtr, psOpJz, 0);
+ if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
- if (!tok->cmp("{")) {
- elsePtr = *codePtr;
- if (!parseCode(str, codePtr)) {
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("if")) {
+ code[codePtr0].val.i = *codePtr;
+ } else if (!tok->cmp("{")) {
+ codePtr1 = *codePtr;
+ addCodeI(codePtr, psOpJ, 0);
+ code[codePtr0].val.i = *codePtr;
+ if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
- delete tok;
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
- } else {
- elsePtr = -1;
- }
- if (!tok->cmp("if")) {
- if (elsePtr >= 0) {
- error(errSyntaxError, -1,
- "Got 'if' operator with two blocks in PostScript function");
- return gFalse;
- }
- code[opPtr].type = psOperator;
- code[opPtr].op = psOpIf;
- code[opPtr+2].type = psBlock;
- code[opPtr+2].blk = *codePtr;
- } else if (!tok->cmp("ifelse")) {
- if (elsePtr < 0) {
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("ifelse")) {
+ code[codePtr1].val.i = *codePtr;
+ } else {
error(errSyntaxError, -1,
- "Got 'ifelse' operator with one block in PostScript function");
+ "Expected 'ifelse' in PostScript function stream");
return gFalse;
}
- code[opPtr].type = psOperator;
- code[opPtr].op = psOpIfelse;
- code[opPtr+1].type = psBlock;
- code[opPtr+1].blk = elsePtr;
- code[opPtr+2].type = psBlock;
- code[opPtr+2].blk = *codePtr;
} else {
error(errSyntaxError, -1,
- "Expected if/ifelse operator in PostScript function");
- delete tok;
+ "Expected 'if' in PostScript function stream");
return gFalse;
}
- delete tok;
} else if (!tok->cmp("}")) {
- delete tok;
- resizeCode(*codePtr);
- code[*codePtr].type = psOperator;
- code[*codePtr].op = psOpReturn;
- ++*codePtr;
break;
+ } else if (!tok->cmp("if")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'if' in PostScript function stream");
+ return gFalse;
+ } else if (!tok->cmp("ifelse")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'ifelse' in PostScript function stream");
+ return gFalse;
} else {
a = -1;
b = nPSOps;
@@ -1311,19 +1097,55 @@ GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
error(errSyntaxError, -1,
"Unknown operator '{0:t}' in PostScript function",
tok);
- delete tok;
return gFalse;
}
- delete tok;
- resizeCode(*codePtr);
- code[*codePtr].type = psOperator;
- code[*codePtr].op = (PSOp)a;
- ++*codePtr;
+ addCode(codePtr, a);
}
}
return gTrue;
}
+void PostScriptFunction::addCode(int *codePtr, int op) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeI(int *codePtr, int op, int x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.i = x;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeD(int *codePtr, int op, double x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.d = x;
+ ++(*codePtr);
+}
+
GString *PostScriptFunction::getToken(Stream *str) {
GString *s;
int c;
@@ -1333,7 +1155,8 @@ GString *PostScriptFunction::getToken(Stream *str) {
comment = gFalse;
while (1) {
if ((c = str->getChar()) == EOF) {
- break;
+ delete s;
+ return NULL;
}
codeString->append(c);
if (comment) {
@@ -1372,323 +1195,362 @@ GString *PostScriptFunction::getToken(Stream *str) {
return s;
}
-void PostScriptFunction::resizeCode(int newSize) {
- if (newSize >= codeSize) {
- codeSize += 64;
- code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
- }
-}
-
-void PostScriptFunction::exec(PSStack *stack, int codePtr) {
- int i1, i2;
- double r1, r2;
- GBool b1, b2;
-
- while (1) {
- switch (code[codePtr].type) {
- case psInt:
- stack->pushInt(code[codePtr++].intg);
+int PostScriptFunction::exec(double *stack, int sp0) {
+ PSCode *c;
+ double tmp[psStackSize];
+ double t;
+ int sp, ip, nn, k, i;
+
+ sp = sp0;
+ ip = 0;
+ while (ip < codeLen) {
+ c = &code[ip++];
+ switch(c->op) {
+ case psOpAbs:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = fabs(stack[sp]);
break;
- case psReal:
- stack->pushReal(code[codePtr++].real);
+ case psOpAdd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] + stack[sp];
+ ++sp;
break;
- case psOperator:
- switch (code[codePtr++].op) {
- case psOpAbs:
- if (stack->topIsInt()) {
- stack->pushInt(abs(stack->popInt()));
- } else {
- stack->pushReal(fabs(stack->popNum()));
- }
- break;
- case psOpAdd:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 + i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(r1 + r2);
- }
- break;
- case psOpAnd:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 & i2);
- } else {
- b2 = stack->popBool();
- b1 = stack->popBool();
- stack->pushBool(b1 && b2);
- }
- break;
- case psOpAtan:
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(atan2(r1, r2));
- break;
- case psOpBitshift:
- i2 = stack->popInt();
- i1 = stack->popInt();
- if (i2 > 0) {
- stack->pushInt(i1 << i2);
- } else if (i2 < 0) {
- stack->pushInt((int)((Guint)i1 >> -i2));
- } else {
- stack->pushInt(i1);
- }
- break;
- case psOpCeiling:
- if (!stack->topIsInt()) {
- stack->pushReal(ceil(stack->popNum()));
- }
- break;
- case psOpCopy:
- stack->copy(stack->popInt());
- break;
- case psOpCos:
- stack->pushReal(cos(stack->popNum()));
- break;
- case psOpCvi:
- if (!stack->topIsInt()) {
- stack->pushInt((int)stack->popNum());
- }
- break;
- case psOpCvr:
- if (!stack->topIsReal()) {
- stack->pushReal(stack->popNum());
- }
- break;
- case psOpDiv:
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(r1 / r2);
- break;
- case psOpDup:
- stack->copy(1);
- break;
- case psOpEq:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 == i2);
- } else if (stack->topTwoAreNums()) {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 == r2);
- } else {
- b2 = stack->popBool();
- b1 = stack->popBool();
- stack->pushBool(b1 == b2);
- }
- break;
- case psOpExch:
- stack->roll(2, 1);
- break;
- case psOpExp:
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(pow(r1, r2));
- break;
- case psOpFalse:
- stack->pushBool(gFalse);
- break;
- case psOpFloor:
- if (!stack->topIsInt()) {
- stack->pushReal(floor(stack->popNum()));
- }
- break;
- case psOpGe:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 >= i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 >= r2);
- }
- break;
- case psOpGt:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 > i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 > r2);
- }
- break;
- case psOpIdiv:
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 / i2);
- break;
- case psOpIndex:
- stack->index(stack->popInt());
- break;
- case psOpLe:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 <= i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 <= r2);
- }
- break;
- case psOpLn:
- stack->pushReal(log(stack->popNum()));
- break;
- case psOpLog:
- stack->pushReal(log10(stack->popNum()));
- break;
- case psOpLt:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 < i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 < r2);
- }
- break;
- case psOpMod:
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 % i2);
- break;
- case psOpMul:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- //~ should check for out-of-range, and push a real instead
- stack->pushInt(i1 * i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(r1 * r2);
- }
- break;
- case psOpNe:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushBool(i1 != i2);
- } else if (stack->topTwoAreNums()) {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushBool(r1 != r2);
- } else {
- b2 = stack->popBool();
- b1 = stack->popBool();
- stack->pushBool(b1 != b2);
- }
- break;
- case psOpNeg:
- if (stack->topIsInt()) {
- stack->pushInt(-stack->popInt());
- } else {
- stack->pushReal(-stack->popNum());
- }
- break;
- case psOpNot:
- if (stack->topIsInt()) {
- stack->pushInt(~stack->popInt());
- } else {
- stack->pushBool(!stack->popBool());
- }
- break;
- case psOpOr:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 | i2);
- } else {
- b2 = stack->popBool();
- b1 = stack->popBool();
- stack->pushBool(b1 || b2);
- }
- break;
- case psOpPop:
- stack->pop();
- break;
- case psOpRoll:
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->roll(i1, i2);
- break;
- case psOpRound:
- if (!stack->topIsInt()) {
- r1 = stack->popNum();
- stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
- }
- break;
- case psOpSin:
- stack->pushReal(sin(stack->popNum()));
- break;
- case psOpSqrt:
- stack->pushReal(sqrt(stack->popNum()));
- break;
- case psOpSub:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 - i2);
- } else {
- r2 = stack->popNum();
- r1 = stack->popNum();
- stack->pushReal(r1 - r2);
- }
- break;
- case psOpTrue:
- stack->pushBool(gTrue);
- break;
- case psOpTruncate:
- if (!stack->topIsInt()) {
- r1 = stack->popNum();
- stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));
- }
- break;
- case psOpXor:
- if (stack->topTwoAreInts()) {
- i2 = stack->popInt();
- i1 = stack->popInt();
- stack->pushInt(i1 ^ i2);
- } else {
- b2 = stack->popBool();
- b1 = stack->popBool();
- stack->pushBool(b1 ^ b2);
- }
- break;
- case psOpIf:
- b1 = stack->popBool();
- if (b1) {
- exec(stack, codePtr + 2);
- }
- codePtr = code[codePtr + 1].blk;
- break;
- case psOpIfelse:
- b1 = stack->popBool();
- if (b1) {
- exec(stack, codePtr + 2);
- } else {
- exec(stack, code[codePtr].blk);
+ case psOpAnd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp];
+ ++sp;
+ break;
+ case psOpAtan:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = atan2(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpBitshift:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp + 1];
+ nn = (int)stack[sp];
+ if (nn > 0) {
+ stack[sp + 1] = k << nn;
+ } else if (nn < 0) {
+ stack[sp + 1] = k >> -nn;
+ } else {
+ stack[sp + 1] = k;
+ }
+ ++sp;
+ break;
+ case psOpCeiling:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = ceil(stack[sp]);
+ break;
+ case psOpCopy:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (sp - nn < 0) {
+ goto overflow;
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp - nn + i] = stack[sp + i];
+ }
+ sp -= nn;
+ break;
+ case psOpCos:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = cos(stack[sp]);
+ break;
+ case psOpCvi:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = (int)stack[sp];
+ break;
+ case psOpCvr:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ break;
+ case psOpDiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] / stack[sp];
+ ++sp;
+ break;
+ case psOpDup:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = stack[sp];
+ --sp;
+ break;
+ case psOpEq:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpExch:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = stack[sp + 1];
+ stack[sp + 1] = t;
+ break;
+ case psOpExp:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = pow(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpFalse:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 0;
+ --sp;
+ break;
+ case psOpFloor:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = floor(stack[sp]);
+ break;
+ case psOpGe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpGt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpIdiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp];
+ ++sp;
+ break;
+ case psOpIndex:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k < 0) {
+ goto invalidArg;
+ }
+ if (sp + 1 + k >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp + 1 + k];
+ break;
+ case psOpLe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpLn:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log(stack[sp]);
+ break;
+ case psOpLog:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log10(stack[sp]);
+ break;
+ case psOpLt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpMod:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp];
+ ++sp;
+ break;
+ case psOpMul:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] * stack[sp];
+ ++sp;
+ break;
+ case psOpNe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpNeg:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = -stack[sp];
+ break;
+ case psOpNot:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp] == 0 ? 1 : 0;
+ break;
+ case psOpOr:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPop:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ ++sp;
+ break;
+ case psOpRoll:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (k >= 0) {
+ k %= nn;
+ } else {
+ k = -k % nn;
+ if (k) {
+ k = nn - k;
}
- codePtr = code[codePtr + 1].blk;
- break;
- case psOpReturn:
- return;
+ }
+ for (i = 0; i < nn; ++i) {
+ tmp[i] = stack[sp + i];
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp + i] = tmp[(i + k) % nn];
}
break;
- default:
- error(errSyntaxError, -1,
- "Internal: bad object in PostScript function code");
+ case psOpRound:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5);
+ break;
+ case psOpSin:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sin(stack[sp]);
+ break;
+ case psOpSqrt:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sqrt(stack[sp]);
+ break;
+ case psOpSub:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] - stack[sp];
+ ++sp;
+ break;
+ case psOpTrue:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 1;
+ --sp;
+ break;
+ case psOpTruncate:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t) : ceil(t);
+ break;
+ case psOpXor:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPush:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[--sp] = c->val.d;
+ break;
+ case psOpJ:
+ ip = c->val.i;
+ break;
+ case psOpJz:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ if (k == 0) {
+ ip = c->val.i;
+ }
break;
}
}
+ return sp;
+
+ underflow:
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ return sp;
+ overflow:
+ error(errSyntaxError, -1, "Stack overflow in PostScript function");
+ return sp;
+ invalidArg:
+ error(errSyntaxError, -1, "Invalid arg in PostScript function");
+ return sp;
}
diff --git a/xpdf/Function.h b/xpdf/Function.h
index 8852dca..5cf4516 100644
--- a/xpdf/Function.h
+++ b/xpdf/Function.h
@@ -18,10 +18,10 @@
#include "gtypes.h"
#include "Object.h"
+class GList;
class Dict;
class Stream;
-struct PSObject;
-class PSStack;
+struct PSCode;
//------------------------------------------------------------------------
// Function
@@ -217,13 +217,16 @@ public:
private:
PostScriptFunction(PostScriptFunction *func);
- GBool parseCode(Stream *str, int *codePtr);
+ GBool parseCode(GList *tokens, int *tokPtr, int *codePtr);
+ void addCode(int *codePtr, int op);
+ void addCodeI(int *codePtr, int op, int x);
+ void addCodeD(int *codePtr, int op, double x);
GString *getToken(Stream *str);
- void resizeCode(int newSize);
- void exec(PSStack *stack, int codePtr);
+ int exec(double *stack, int sp0);
GString *codeString;
- PSObject *code;
+ PSCode *code;
+ int codeLen;
int codeSize;
double cacheIn[funcMaxInputs];
double cacheOut[funcMaxOutputs];
diff --git a/xpdf/Gfx.cc b/xpdf/Gfx.cc
index 1979f84..0fa48f8 100644
--- a/xpdf/Gfx.cc
+++ b/xpdf/Gfx.cc
@@ -2,7 +2,7 @@
//
// Gfx.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -36,7 +36,7 @@
#include "Annot.h"
#include "OptionalContent.h"
#include "Error.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Gfx.h"
// the MSVC math.h doesn't define this
@@ -80,11 +80,15 @@
// fill.
#define patchColorDelta (dblToCol(1 / 256.0))
+// Max errors (undefined operator, wrong number of args) allowed before
+// giving up on a content stream.
+#define contentStreamErrorLimit 500
+
//------------------------------------------------------------------------
// Operator table
//------------------------------------------------------------------------
-#ifdef WIN32 // this works around a bug in the VC7 compiler
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
# pragma optimize("",off)
#endif
@@ -257,7 +261,7 @@ Operator Gfx::opTab[] = {
&Gfx::opCurveTo2},
};
-#ifdef WIN32 // this works around a bug in the VC7 compiler
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
# pragma optimize("",on)
#endif
@@ -337,15 +341,31 @@ GfxFont *GfxResources::lookupFont(char *name) {
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->fonts) {
- if ((font = resPtr->fonts->lookup(name)))
+ if ((font = resPtr->fonts->lookup(name))) {
return font;
+ }
}
}
error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
return NULL;
}
-GBool GfxResources::lookupXObject(char *name, Object *obj) {
+GfxFont *GfxResources::lookupFontByRef(Ref ref) {
+ GfxFont *font;
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->fonts) {
+ if ((font = resPtr->fonts->lookupByRef(ref))) {
+ return font;
+ }
+ }
+ }
+ error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
+ return NULL;
+}
+
+GBool GfxResources::lookupXObject(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -359,7 +379,7 @@ GBool GfxResources::lookupXObject(char *name, Object *obj) {
return gFalse;
}
-GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
+GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -373,9 +393,16 @@ GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
return gFalse;
}
-void GfxResources::lookupColorSpace(char *name, Object *obj) {
+void GfxResources::lookupColorSpace(const char *name, Object *obj) {
GfxResources *resPtr;
+ //~ should also test for G, RGB, and CMYK - but only in inline images (?)
+ if (!strcmp(name, "DeviceGray") ||
+ !strcmp(name, "DeviceRGB") ||
+ !strcmp(name, "DeviceCMYK")) {
+ obj->initNull();
+ return;
+ }
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->colorSpaceDict.isDict()) {
if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
@@ -387,15 +414,19 @@ void GfxResources::lookupColorSpace(char *name, Object *obj) {
obj->initNull();
}
-GfxPattern *GfxResources::lookupPattern(char *name) {
+GfxPattern *GfxResources::lookupPattern(const char *name
+ ) {
GfxResources *resPtr;
GfxPattern *pattern;
- Object obj;
+ Object objRef, obj;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->patternDict.isDict()) {
if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
- pattern = GfxPattern::parse(&obj);
+ resPtr->patternDict.dictLookupNF(name, &objRef);
+ pattern = GfxPattern::parse(&objRef, &obj
+ );
+ objRef.free();
obj.free();
return pattern;
}
@@ -406,7 +437,8 @@ GfxPattern *GfxResources::lookupPattern(char *name) {
return NULL;
}
-GfxShading *GfxResources::lookupShading(char *name) {
+GfxShading *GfxResources::lookupShading(const char *name
+ ) {
GfxResources *resPtr;
GfxShading *shading;
Object obj;
@@ -414,7 +446,8 @@ GfxShading *GfxResources::lookupShading(char *name) {
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->shadingDict.isDict()) {
if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
- shading = GfxShading::parse(&obj);
+ shading = GfxShading::parse(&obj
+ );
obj.free();
return shading;
}
@@ -425,7 +458,7 @@ GfxShading *GfxResources::lookupShading(char *name) {
return NULL;
}
-GBool GfxResources::lookupGState(char *name, Object *obj) {
+GBool GfxResources::lookupGState(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -440,7 +473,7 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
return gFalse;
}
-GBool GfxResources::lookupPropertiesNF(char *name, Object *obj) {
+GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -491,6 +524,7 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
markedContentStack = new GList();
ocState = gTrue;
parser = NULL;
+ contentStreamStack = new GList();
abortCheckCbk = abortCheckCbkA;
abortCheckCbkData = abortCheckCbkDataA;
@@ -535,6 +569,7 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
markedContentStack = new GList();
ocState = gTrue;
parser = NULL;
+ contentStreamStack = new GList();
abortCheckCbk = abortCheckCbkA;
abortCheckCbkData = abortCheckCbkDataA;
@@ -563,41 +598,99 @@ Gfx::~Gfx() {
popResources();
}
deleteGList(markedContentStack, GfxMarkedContent);
+ delete contentStreamStack;
}
-void Gfx::display(Object *obj, GBool topLevel) {
- Object obj2;
+void Gfx::display(Object *objRef, GBool topLevel) {
+ Object obj1, obj2;
int i;
- if (obj->isArray()) {
- for (i = 0; i < obj->arrayGetLength(); ++i) {
- obj->arrayGet(i, &obj2);
+ objRef->fetch(xref, &obj1);
+ if (obj1.isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ if (checkForContentStreamLoop(&obj2)) {
+ obj2.free();
+ obj1.free();
+ return;
+ }
+ obj2.free();
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGet(i, &obj2);
if (!obj2.isStream()) {
- error(errSyntaxError, -1, "Weird page contents");
+ error(errSyntaxError, -1, "Invalid object type for content stream");
obj2.free();
+ obj1.free();
return;
}
obj2.free();
}
- } else if (!obj->isStream()) {
- error(errSyntaxError, -1, "Weird page contents");
+ contentStreamStack->append(&obj1);
+ } else if (obj1.isStream()) {
+ if (checkForContentStreamLoop(objRef)) {
+ obj1.free();
+ return;
+ }
+ contentStreamStack->append(objRef);
+ } else {
+ error(errSyntaxError, -1, "Invalid object type for content stream");
+ obj1.free();
return;
}
- parser = new Parser(xref, new Lexer(xref, obj), gFalse);
+ parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
go(topLevel);
delete parser;
parser = NULL;
+ contentStreamStack->del(contentStreamStack->getLength() - 1);
+ obj1.free();
+}
+
+// If <ref> is already on contentStreamStack, i.e., if there is a loop
+// in the content streams, report an error, and return true.
+GBool Gfx::checkForContentStreamLoop(Object *ref) {
+ Object *objPtr;
+ Object obj1;
+ int i, j;
+
+ if (ref->isRef()) {
+ for (i = 0; i < contentStreamStack->getLength(); ++i) {
+ objPtr = (Object *)contentStreamStack->get(i);
+ if (objPtr->isRef()) {
+ if (ref->getRefNum() == objPtr->getRefNum() &&
+ ref->getRefGen() == objPtr->getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ return gTrue;
+ }
+ } else if (objPtr->isArray()) {
+ for (j = 0; j < objPtr->arrayGetLength(); ++j) {
+ objPtr->arrayGetNF(j, &obj1);
+ if (obj1.isRef()) {
+ if (ref->getRefNum() == obj1.getRefNum() &&
+ ref->getRefGen() == obj1.getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ obj1.free();
+ return gTrue;
+ }
+ }
+ obj1.free();
+ }
+ }
+ }
+ }
+ return gFalse;
}
void Gfx::go(GBool topLevel) {
Object obj;
Object args[maxArgs];
int numArgs, i;
- int lastAbortCheck;
+ int lastAbortCheck, errCount;
// scan a sequence of objects
updateLevel = 1; // make sure even empty pages trigger a call to dump()
lastAbortCheck = 0;
+ errCount = 0;
numArgs = 0;
parser->getObj(&obj);
while (!obj.isEOF()) {
@@ -613,7 +706,9 @@ void Gfx::go(GBool topLevel) {
printf("\n");
fflush(stdout);
}
- execOp(&obj, args, numArgs);
+ if (!execOp(&obj, args, numArgs)) {
+ ++errCount;
+ }
obj.free();
for (i = 0; i < numArgs; ++i)
args[i].free();
@@ -635,6 +730,13 @@ void Gfx::go(GBool topLevel) {
}
}
+ // check for too many errors
+ if (errCount > contentStreamErrorLimit) {
+ error(errSyntaxError, -1,
+ "Too many errors - giving up on this content stream");
+ break;
+ }
+
// got an argument - save it
} else if (numArgs < maxArgs) {
args[numArgs++] = obj;
@@ -678,7 +780,8 @@ void Gfx::go(GBool topLevel) {
}
}
-void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
+// Returns true if successful, false on error.
+GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
Operator *op;
char *name;
Object *argPtr;
@@ -687,9 +790,11 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
// find operator
name = cmd->getCmd();
if (!(op = findOp(name))) {
- if (ignoreUndef == 0)
- error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
- return;
+ if (ignoreUndef > 0) {
+ return gTrue;
+ }
+ error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
+ return gFalse;
}
// type check args
@@ -698,7 +803,7 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
if (numArgs < op->numArgs) {
error(errSyntaxError, getPos(),
"Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
- return;
+ return gFalse;
}
if (numArgs > op->numArgs) {
#if 0
@@ -713,7 +818,7 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
error(errSyntaxError, getPos(),
"Too many ({0:d}) args to '{1:s}' operator",
numArgs, name);
- return;
+ return gFalse;
}
}
for (i = 0; i < numArgs; ++i) {
@@ -721,12 +826,14 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
error(errSyntaxError, getPos(),
"Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
i, name, argPtr[i].getTypeName());
- return;
+ return gFalse;
}
}
// do it
(this->*op->func)(argPtr, numArgs);
+
+ return gTrue;
}
Operator *Gfx::findOp(char *name) {
@@ -766,7 +873,7 @@ GBool Gfx::checkArg(Object *arg, TchkType type) {
return gFalse;
}
-int Gfx::getPos() {
+GFileOffset Gfx::getPos() {
return parser ? parser->getPos() : -1;
}
@@ -840,7 +947,7 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) {
}
void Gfx::opSetExtGState(Object args[], int numArgs) {
- Object obj1, obj2, obj3, obj4, obj5;
+ Object obj1, obj2, obj3, objRef3, obj4, obj5;
Object args2[2];
GfxBlendMode mode;
GBool haveFillOP;
@@ -895,22 +1002,21 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
args2[1].free();
}
obj2.free();
-#if 0 //~ need to add a new version of GfxResources::lookupFont() that
- //~ takes an indirect ref instead of a name
+ if (obj1.dictLookup("FL", &obj2)->isNum()) {
+ opSetFlat(&obj2, 1);
+ }
+ obj2.free();
+
+ // font
if (obj1.dictLookup("Font", &obj2)->isArray() &&
obj2.arrayGetLength() == 2) {
- obj2.arrayGet(0, &args2[0]);
- obj2.arrayGet(1, &args2[1]);
- if (args2[0].isDict() && args2[1].isNum()) {
- opSetFont(args2, 2);
+ obj2.arrayGetNF(0, &obj3);
+ obj2.arrayGetNF(1, &obj4);
+ if (obj3.isRef() && obj4.isNum()) {
+ doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
}
- args2[0].free();
- args2[1].free();
- }
- obj2.free();
-#endif
- if (obj1.dictLookup("FL", &obj2)->isNum()) {
- opSetFlat(&obj2, 1);
+ obj3.free();
+ obj4.free();
}
obj2.free();
@@ -1045,7 +1151,8 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
blendingColorSpace = NULL;
isolated = knockout = gFalse;
if (!obj4.dictLookup("CS", &obj5)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj5);
+ blendingColorSpace = GfxColorSpace::parse(&obj5
+ );
}
obj5.free();
if (obj4.dictLookup("I", &obj5)->isBool()) {
@@ -1066,8 +1173,10 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
}
}
}
- doSoftMask(&obj3, alpha, blendingColorSpace,
+ obj2.dictLookupNF("G", &objRef3);
+ doSoftMask(&obj3, &objRef3, alpha, blendingColorSpace,
isolated, knockout, funcs[0], &backdropColor);
+ objRef3.free();
if (funcs[0]) {
delete funcs[0];
}
@@ -1090,7 +1199,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
obj1.free();
}
-void Gfx::doSoftMask(Object *str, GBool alpha,
+void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
Function *transferFunc, GfxColor *backdropColor) {
@@ -1149,7 +1258,7 @@ void Gfx::doSoftMask(Object *str, GBool alpha,
// draw it
++formDepth;
- drawForm(str, resDict, m, bbox, gTrue, gTrue,
+ drawForm(strRef, resDict, m, bbox, gTrue, gTrue,
blendingColorSpace, isolated, knockout,
alpha, transferFunc, backdropColor);
--formDepth;
@@ -1171,7 +1280,7 @@ void Gfx::opSetFillGray(Object args[], int numArgs) {
GfxColor color;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateFillColorSpace(state);
color.c[0] = dblToCol(args[0].getNum());
state->setFillColor(&color);
@@ -1182,7 +1291,7 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) {
GfxColor color;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateStrokeColorSpace(state);
color.c[0] = dblToCol(args[0].getNum());
state->setStrokeColor(&color);
@@ -1194,7 +1303,7 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
int i;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
out->updateFillColorSpace(state);
for (i = 0; i < 4; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1208,7 +1317,7 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
int i;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
out->updateStrokeColorSpace(state);
for (i = 0; i < 4; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1222,7 +1331,7 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
int i;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceRGBColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
out->updateFillColorSpace(state);
for (i = 0; i < 3; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1236,7 +1345,7 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
int i;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
out->updateStrokeColorSpace(state);
for (i = 0; i < 3; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1253,9 +1362,11 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
state->setFillPattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull()) {
- colorSpace = GfxColorSpace::parse(&args[0]);
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
} else {
- colorSpace = GfxColorSpace::parse(&obj);
+ colorSpace = GfxColorSpace::parse(&obj
+ );
}
obj.free();
if (colorSpace) {
@@ -1277,9 +1388,11 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
state->setStrokePattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull()) {
- colorSpace = GfxColorSpace::parse(&args[0]);
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
} else {
- colorSpace = GfxColorSpace::parse(&obj);
+ colorSpace = GfxColorSpace::parse(&obj
+ );
}
obj.free();
if (colorSpace) {
@@ -1350,7 +1463,8 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) {
out->updateFillColor(state);
}
if (args[numArgs-1].isName() &&
- (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ (pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
state->setFillPattern(pattern);
}
@@ -1395,7 +1509,8 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
out->updateStrokeColor(state);
}
if (args[numArgs-1].isName() &&
- (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ (pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
state->setStrokePattern(pattern);
}
@@ -1751,11 +1866,11 @@ void Gfx::doPatternText() {
}
void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
- GBool invert, GBool inlineImg) {
+ GBool invert, GBool inlineImg, GBool interpolate) {
saveState();
out->setSoftMaskFromImageMask(state, ref, str,
- width, height, invert, inlineImg);
+ width, height, invert, inlineImg, interpolate);
state->clearPath();
state->moveTo(0, 0);
@@ -1773,11 +1888,11 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
GfxPatternColorSpace *patCS;
GfxColorSpace *cs;
GfxState *savedState;
- double xMin, yMin, xMax, yMax, x, y, x1, y1;
+ double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
double cxMin, cyMin, cxMax, cyMax;
int xi0, yi0, xi1, yi1, xi, yi;
double *ctm, *btm, *ptm;
- double m[6], ictm[6], m1[6], imb[6];
+ double bbox[4], m[6], ictm[6], m1[6], imb[6];
double det;
double xstep, ystep;
int i;
@@ -1791,7 +1906,12 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
btm = baseMatrix;
ptm = tPat->getMatrix();
// iCTM = invert CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
ictm[0] = ctm[3] * det;
ictm[1] = -ctm[1] * det;
ictm[2] = -ctm[2] * det;
@@ -1814,7 +1934,12 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
// construct a (device space) -> (pattern space) transform matrix
- det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
+ det = m1[0] * m1[3] - m1[1] * m1[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
imb[0] = m1[3] * det;
imb[1] = -m1[1] * det;
imb[2] = -m1[2] * det;
@@ -1839,9 +1964,9 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
out->updateFillColor(state);
out->updateStrokeColor(state);
} else {
- state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateFillColorSpace(state);
- state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateStrokeColorSpace(state);
}
if (!stroke) {
@@ -1912,21 +2037,31 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
// draw the pattern
//~ this should treat negative steps differently -- start at right/top
//~ edge instead of left/bottom (?)
+ bbox[0] = tPat->getBBox()[0];
+ bbox[1] = tPat->getBBox()[1];
+ bbox[2] = tPat->getBBox()[2];
+ bbox[3] = tPat->getBBox()[3];
+ if (bbox[0] > bbox[2]) {
+ t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
+ }
+ if (bbox[1] > bbox[3]) {
+ t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
+ }
xstep = fabs(tPat->getXStep());
ystep = fabs(tPat->getYStep());
- xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
- xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
- yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
- yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
+ xi0 = (int)ceil((xMin - bbox[2]) / xstep);
+ xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
+ yi0 = (int)ceil((yMin - bbox[3]) / ystep);
+ yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1;
for (i = 0; i < 4; ++i) {
m1[i] = m[i];
}
if (out->useTilingPatternFill()) {
m1[4] = m[4];
m1[5] = m[5];
- out->tilingPatternFill(state, this, tPat->getContentStream(),
+ out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
tPat->getPaintType(), tPat->getResDict(),
- m1, tPat->getBBox(),
+ m1, bbox,
xi0, yi0, xi1, yi1, xstep, ystep);
} else {
for (yi = yi0; yi < yi1; ++yi) {
@@ -1935,8 +2070,8 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
y = yi * ystep;
m1[4] = x * m[0] + y * m[2] + m[4];
m1[5] = x * m[1] + y * m[3] + m[5];
- drawForm(tPat->getContentStream(), tPat->getResDict(),
- m1, tPat->getBBox());
+ drawForm(tPat->getContentStreamRef(), tPat->getResDict(),
+ m1, bbox);
}
}
}
@@ -1979,7 +2114,12 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
btm = baseMatrix;
ptm = sPat->getMatrix();
// iCTM = invert CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
+ return;
+ }
+ det = 1 / det;
ictm[0] = ctm[3] * det;
ictm[1] = -ctm[1] * det;
ictm[2] = -ctm[2] * det;
@@ -2074,11 +2214,16 @@ void Gfx::opShFill(Object args[], int numArgs) {
GfxState *savedState;
double xMin, yMin, xMax, yMax;
+ if (!out->needNonText()) {
+ return;
+ }
+
if (!ocState) {
return;
}
- if (!(shading = res->lookupShading(args[0].getName()))) {
+ if (!(shading = res->lookupShading(args[0].getName()
+ ))) {
return;
}
@@ -2487,7 +2632,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
GfxColor colorA, colorB;
double xa, ya, xb, yb, ra, rb;
double ta, tb, sa, sb;
- double sz, sMin, sMax, h;
+ double sMin, sMax, h;
double sLeft, sRight, sTop, sBottom, sZero, sDiag;
GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
GBool haveSMin, haveSMax;
@@ -2513,18 +2658,14 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
if (h == 0) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else if (r1 - r0 == 0) {
enclosed = gFalse;
theta = 0;
- sz = 0; // make gcc happy
- } else if (fabs(r1 - r0) >= h) {
+ } else if (fabs(r1 - r0) >= h - 0.0001) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else {
enclosed = gFalse;
- sz = -r0 / (r1 - r0);
theta = asin((r1 - r0) / h);
}
if (enclosed) {
@@ -2598,7 +2739,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
haveSMin = gTrue;
}
}
- if (haveSZero && sZero < 0) {
+ if (haveSZero && sZero <= 0) {
if (!haveSMin || sZero > sMin) {
sMin = sZero;
}
@@ -2865,34 +3006,56 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
double x0, y0, x1, y1, x2, y2;
- GfxColor color0, color1, color2;
+ double color0[gfxColorMaxComps];
+ double color1[gfxColorMaxComps];
+ double color2[gfxColorMaxComps];
int i;
for (i = 0; i < shading->getNTriangles(); ++i) {
- shading->getTriangle(i, &x0, &y0, &color0,
- &x1, &y1, &color1,
- &x2, &y2, &color2);
- gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
- shading->getColorSpace()->getNComps(), 0);
+ shading->getTriangle(i, &x0, &y0, color0,
+ &x1, &y1, color1,
+ &x2, &y2, color2);
+ gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2,
+ shading, 0);
}
}
-void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
- double x1, double y1, GfxColor *color1,
- double x2, double y2, GfxColor *color2,
- int nComps, int depth) {
+void Gfx::gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth) {
+ double dx0, dy0, dx1, dy1, dx2, dy2;
double x01, y01, x12, y12, x20, y20;
- GfxColor color01, color12, color20;
- int i;
-
+ double color01[gfxColorMaxComps];
+ double color12[gfxColorMaxComps];
+ double color20[gfxColorMaxComps];
+ GfxColor c0, c1, c2;
+ int nComps, i;
+
+ // recursion ends when:
+ // (1) color difference is smaller than gouraudColorDelta; or
+ // (2) triangles are smaller than 0.5 pixel (note that "device
+ // space" is 72dpi when generating PostScript); or
+ // (3) max recursion depth (gouraudMaxDepth) is hit.
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(color0, &c0);
+ shading->getColor(color1, &c1);
+ shading->getColor(color2, &c2);
for (i = 0; i < nComps; ++i) {
- if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
- abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
+ if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta ||
+ abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) {
break;
}
}
- if (i == nComps || depth == gouraudMaxDepth) {
- state->setFillColor(color0);
+ state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0);
+ state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1);
+ state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2);
+ if (i == nComps ||
+ depth == gouraudMaxDepth ||
+ (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 &&
+ fabs(dx1) < 0.5 && fabs(dy1) < 0.5 &&
+ fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) {
+ state->setFillColor(&c0);
out->updateFillColor(state);
state->moveTo(x0, y0);
state->lineTo(x1, y1);
@@ -2907,21 +3070,19 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
y12 = 0.5 * (y1 + y2);
x20 = 0.5 * (x2 + x0);
y20 = 0.5 * (y2 + y0);
- //~ if the shading has a Function, this should interpolate on the
- //~ function parameter, not on the color components
- for (i = 0; i < nComps; ++i) {
- color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
- color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
- color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
- }
- gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
- x20, y20, &color20, nComps, depth + 1);
- gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
- x12, y12, &color12, nComps, depth + 1);
- gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
- x20, y20, &color20, nComps, depth + 1);
- gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
- x2, y2, color2, nComps, depth + 1);
+ for (i = 0; i < shading->getNComps(); ++i) {
+ color01[i] = 0.5 * (color0[i] + color1[i]);
+ color12[i] = 0.5 * (color1[i] + color2[i]);
+ color20[i] = 0.5 * (color2[i] + color0[i]);
+ }
+ gouraudFillTriangle(x0, y0, color0, x01, y01, color01,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x1, y1, color1,
+ x12, y12, color12, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x12, y12, color12,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x20, y20, color20, x12, y12, color12,
+ x2, y2, color2, shading, depth + 1);
}
}
@@ -2938,31 +3099,32 @@ void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
start = 0;
}
for (i = 0; i < shading->getNPatches(); ++i) {
- fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
- start);
+ fillPatch(shading->getPatch(i), shading, start);
}
}
-void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
GfxPatch patch00, patch01, patch10, patch11;
+ GfxColor c00, c01, c10, c11;
double xx[4][8], yy[4][8];
double xxm, yym;
- int i;
+ int nComps, i;
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(patch->color[0][0], &c00);
+ shading->getColor(patch->color[0][1], &c01);
+ shading->getColor(patch->color[1][0], &c10);
+ shading->getColor(patch->color[1][1], &c11);
for (i = 0; i < nComps; ++i) {
- if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
- > patchColorDelta ||
- abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
- > patchColorDelta ||
- abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
- > patchColorDelta ||
- abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
- > patchColorDelta) {
+ 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 || depth == patchMaxDepth) {
- state->setFillColor(&patch->color[0][0]);
+ state->setFillColor(&c00);
out->updateFillColor(state);
state->moveTo(patch->x[0][0], patch->y[0][0]);
state->curveTo(patch->x[0][1], patch->y[0][1],
@@ -3039,35 +3201,33 @@ void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
patch11.x[3][i-4] = xx[3][i];
patch11.y[3][i-4] = yy[3][i];
}
- //~ if the shading has a Function, this should interpolate on the
- //~ function parameter, not on the color components
- for (i = 0; i < nComps; ++i) {
- patch00.color[0][0].c[i] = patch->color[0][0].c[i];
- patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
- patch->color[0][1].c[i]) / 2;
- patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
- patch01.color[0][1].c[i] = patch->color[0][1].c[i];
- patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
- patch->color[1][1].c[i]) / 2;
- patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
- patch11.color[1][1].c[i] = patch->color[1][1].c[i];
- patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
- patch->color[1][0].c[i]) / 2;
- patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
- patch10.color[1][0].c[i] = patch->color[1][0].c[i];
- patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
- patch->color[0][0].c[i]) / 2;
- patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
- patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
- patch01.color[1][1].c[i]) / 2;
- patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
- patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
- patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
- }
- fillPatch(&patch00, nComps, depth + 1);
- fillPatch(&patch10, nComps, depth + 1);
- fillPatch(&patch01, nComps, depth + 1);
- fillPatch(&patch11, nComps, depth + 1);
+ 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]);
+ 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(&patch00, shading, depth + 1);
+ fillPatch(&patch10, shading, depth + 1);
+ fillPatch(&patch01, shading, depth + 1);
+ fillPatch(&patch11, shading, depth + 1);
}
}
@@ -3123,19 +3283,22 @@ void Gfx::opSetCharSpacing(Object args[], int numArgs) {
}
void Gfx::opSetFont(Object args[], int numArgs) {
- GfxFont *font;
+ doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
+}
- if (!(font = res->lookupFont(args[0].getName()))) {
+void Gfx::doSetFont(GfxFont *font, double size) {
+ if (!font) {
+ state->setFont(NULL, 0);
return;
}
if (printCommands) {
printf(" font: tag=%s name='%s' %g\n",
font->getTag()->getCString(),
font->getName() ? font->getName()->getCString() : "???",
- args[1].getNum());
+ size);
fflush(stdout);
}
- state->setFont(font, args[1].getNum());
+ state->setFont(font, size);
fontChanged = gTrue;
}
@@ -3343,7 +3506,7 @@ void Gfx::doShowText(GString *s) {
double x0, y0, x1, y1;
double oldCTM[6], newCTM[6];
double *mat;
- Object charProc;
+ Object charProcRef, charProc;
Dict *resDict;
Parser *oldParser;
GfxState *savedState;
@@ -3427,12 +3590,13 @@ void Gfx::doShowText(GString *s) {
state->transformDelta(dx, dy, &ddx, &ddy);
if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
code, u, uLen)) {
- ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
+ ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
+ charProcRef.fetch(xref, &charProc);
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
pushResources(resDict);
}
if (charProc.isStream()) {
- display(&charProc, gFalse);
+ display(&charProcRef, gFalse);
} else {
error(errSyntaxError, getPos(),
"Missing or bad Type3 CharProc entry");
@@ -3442,6 +3606,7 @@ void Gfx::doShowText(GString *s) {
popResources();
}
charProc.free();
+ charProcRef.free();
}
restoreStateStack(savedState);
curX += tdx;
@@ -3592,44 +3757,53 @@ void Gfx::opXObject(Object args[], int numArgs) {
obj1.free();
return;
}
+#if USE_EXCEPTIONS
+ try {
+#endif
#if OPI_SUPPORT
- obj1.streamGetDict()->lookup("OPI", &opiDict);
- if (opiDict.isDict()) {
- out->opiBegin(state, opiDict.getDict());
- }
+ obj1.streamGetDict()->lookup("OPI", &opiDict);
+ if (opiDict.isDict()) {
+ out->opiBegin(state, opiDict.getDict());
+ }
#endif
- obj1.streamGetDict()->lookup("Subtype", &obj2);
- if (obj2.isName("Image")) {
- if (out->needNonText()) {
+ obj1.streamGetDict()->lookup("Subtype", &obj2);
+ if (obj2.isName("Image")) {
+ if (out->needNonText()) {
+ res->lookupXObjectNF(name, &refObj);
+ doImage(&refObj, obj1.getStream(), gFalse);
+ refObj.free();
+ }
+ } else if (obj2.isName("Form")) {
res->lookupXObjectNF(name, &refObj);
- doImage(&refObj, obj1.getStream(), gFalse);
+ if (out->useDrawForm() && refObj.isRef()) {
+ out->drawForm(refObj.getRef());
+ } else {
+ doForm(&refObj, &obj1);
+ }
refObj.free();
- }
- } else if (obj2.isName("Form")) {
- res->lookupXObjectNF(name, &refObj);
- if (out->useDrawForm() && refObj.isRef()) {
- out->drawForm(refObj.getRef());
+ } else if (obj2.isName("PS")) {
+ obj1.streamGetDict()->lookup("Level1", &obj3);
+ out->psXObject(obj1.getStream(),
+ obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
+ } else if (obj2.isName()) {
+ error(errSyntaxError, getPos(),
+ "Unknown XObject subtype '{0:s}'", obj2.getName());
} else {
- doForm(&obj1);
- }
- refObj.free();
- } else if (obj2.isName("PS")) {
- obj1.streamGetDict()->lookup("Level1", &obj3);
- out->psXObject(obj1.getStream(),
- obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
- } else if (obj2.isName()) {
- error(errSyntaxError, getPos(),
- "Unknown XObject subtype '{0:s}'", obj2.getName());
- } else {
- error(errSyntaxError, getPos(),
- "XObject subtype is missing or wrong type");
- }
- obj2.free();
+ error(errSyntaxError, getPos(),
+ "XObject subtype is missing or wrong type");
+ }
+ obj2.free();
#if OPI_SUPPORT
- if (opiDict.isDict()) {
- out->opiEnd(state, opiDict.getDict());
+ if (opiDict.isDict()) {
+ out->opiEnd(state, opiDict.getDict());
+ }
+ opiDict.free();
+#endif
+#if USE_EXCEPTIONS
+ } catch (GMemException e) {
+ obj1.free();
+ throw;
}
- opiDict.free();
#endif
obj1.free();
}
@@ -3649,6 +3823,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
int maskWidth, maskHeight;
GBool maskInvert;
Stream *maskStr;
+ GBool interpolate;
Object obj1, obj2;
int i, n;
@@ -3710,6 +3885,9 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
}
if (obj1.isInt()) {
bits = obj1.getInt();
+ if (bits < 1 || bits > 16) {
+ goto err2;
+ }
} else if (mask) {
bits = 1;
} else {
@@ -3718,6 +3896,15 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
obj1.free();
}
+ // interpolate flag
+ dict->lookup("Interpolate", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("I", &obj1);
+ }
+ interpolate = obj1.isBool() && obj1.getBool();
+ obj1.free();
+
// display a mask
if (mask) {
@@ -3751,9 +3938,11 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
// draw it
} else {
if (state->getFillColorSpace()->getMode() == csPattern) {
- doPatternImageMask(ref, str, width, height, invert, inlineImg);
+ doPatternImageMask(ref, str, width, height, invert, inlineImg,
+ interpolate);
} else {
- out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+ out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
+ interpolate);
}
}
@@ -3775,13 +3964,14 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
}
}
if (!obj1.isNull()) {
- colorSpace = GfxColorSpace::parse(&obj1);
+ colorSpace = GfxColorSpace::parse(&obj1
+ );
} else if (csMode == streamCSDeviceGray) {
- colorSpace = new GfxDeviceGrayColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceGray);
} else if (csMode == streamCSDeviceRGB) {
- colorSpace = new GfxDeviceRGBColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceRGB);
} else if (csMode == streamCSDeviceCMYK) {
- colorSpace = new GfxDeviceCMYKColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceCMYK);
} else {
colorSpace = NULL;
}
@@ -3860,7 +4050,8 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
obj2.free();
}
}
- maskColorSpace = GfxColorSpace::parse(&obj1);
+ maskColorSpace = GfxColorSpace::parse(&obj1
+ );
obj1.free();
if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
goto err1;
@@ -3977,14 +4168,17 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
} else {
if (haveSoftMask) {
out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap);
+ maskStr, maskWidth, maskHeight, maskColorMap,
+ interpolate);
delete maskColorMap;
} else if (haveExplicitMask) {
out->drawMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskInvert);
+ maskStr, maskWidth, maskHeight, maskInvert,
+ interpolate);
} else {
out->drawImage(state, ref, str, width, height, colorMap,
- haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
+ haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
+ interpolate);
}
}
@@ -4006,7 +4200,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
error(errSyntaxError, getPos(), "Bad image parameters");
}
-void Gfx::doForm(Object *str) {
+void Gfx::doForm(Object *strRef, Object *str) {
Dict *dict;
GBool transpGroup, isolated, knockout;
GfxColorSpace *blendingColorSpace;
@@ -4087,7 +4281,8 @@ void Gfx::doForm(Object *str) {
if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
transpGroup = gTrue;
if (!obj1.dictLookup("CS", &obj3)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj3);
+ blendingColorSpace = GfxColorSpace::parse(&obj3
+ );
}
obj3.free();
if (obj1.dictLookup("I", &obj3)->isBool()) {
@@ -4105,7 +4300,7 @@ void Gfx::doForm(Object *str) {
// draw it
++formDepth;
- drawForm(str, resDict, m, bbox,
+ drawForm(strRef, resDict, m, bbox,
transpGroup, gFalse, blendingColorSpace, isolated, knockout);
--formDepth;
@@ -4117,7 +4312,8 @@ void Gfx::doForm(Object *str) {
ocState = ocSaved;
}
-void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
+void Gfx::drawForm(Object *strRef, Dict *resDict,
+ double *matrix, double *bbox,
GBool transpGroup, GBool softMask,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
@@ -4181,7 +4377,7 @@ void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
}
// draw the form
- display(str, gFalse);
+ display(strRef, gFalse);
if (softMask || transpGroup) {
out->endTransparencyGroup(state);
@@ -4210,13 +4406,17 @@ void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
return;
}
+void Gfx::takeContentStreamStack(Gfx *oldGfx) {
+ contentStreamStack->append(oldGfx->contentStreamStack);
+}
+
//------------------------------------------------------------------------
// in-line image operators
//------------------------------------------------------------------------
void Gfx::opBeginImage(Object args[], int numArgs) {
Stream *str;
- int c1, c2;
+ int c1, c2, c3;
// NB: this function is run even if ocState is false -- doImage() is
// responsible for skipping over the inline image data
@@ -4231,9 +4431,11 @@ void Gfx::opBeginImage(Object args[], int numArgs) {
// skip 'EI' tag
c1 = str->getUndecodedStream()->getChar();
c2 = str->getUndecodedStream()->getChar();
- while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
+ c3 = str->getUndecodedStream()->lookChar();
+ while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
c1 = c2;
c2 = str->getUndecodedStream()->getChar();
+ c3 = str->getUndecodedStream()->lookChar();
}
delete str;
}
@@ -4328,9 +4530,7 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
GfxMarkedContent *mc;
Object obj;
GBool ocStateNew;
- GString *s;
- Unicode *u;
- int uLen, i;
+ TextString *s;
GfxMarkedContentKind mcKind;
if (printCommands) {
@@ -4351,24 +4551,9 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
mcKind = gfxMCOptionalContent;
} else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) {
if (args[1].dictLookup("ActualText", &obj)->isString()) {
- s = obj.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- uLen = (s->getLength() - 2) / 2;
- u = (Unicode *)gmallocn(uLen, sizeof(Unicode));
- for (i = 0; i < uLen; ++i) {
- u[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- uLen = s->getLength();
- u = (Unicode *)gmallocn(uLen, sizeof(Unicode));
- for (i = 0; i < uLen; ++i) {
- u[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
- out->beginActualText(state, u, uLen);
- gfree(u);
+ s = new TextString(obj.getString());
+ out->beginActualText(state, s->getUnicode(), s->getLength());
+ delete s;
mcKind = gfxMCActualText;
}
obj.free();
@@ -4416,14 +4601,14 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
// misc
//------------------------------------------------------------------------
-void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
+void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
double xMin, double yMin, double xMax, double yMax) {
Dict *dict, *resDict;
- Object matrixObj, bboxObj, resObj, obj1;
+ Object str, matrixObj, bboxObj, resObj, obj1;
double formXMin, formYMin, formXMax, formYMax;
double x, y, sx, sy, tx, ty;
double m[6], bbox[4];
- double r, g, b;
+ double *borderColor;
GfxColor color;
double *dash, *dash2;
int dashLength;
@@ -4439,16 +4624,18 @@ void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
}
// draw the appearance stream (if there is one)
- if (str->isStream()) {
+ strRef->fetch(xref, &str);
+ if (str.isStream()) {
// get stream dict
- dict = str->streamGetDict();
+ dict = str.streamGetDict();
// get the form bounding box
dict->lookup("BBox", &bboxObj);
if (!bboxObj.isArray()) {
- bboxObj.free();
error(errSyntaxError, getPos(), "Bad form bounding box");
+ bboxObj.free();
+ str.free();
return;
}
for (i = 0; i < 4; ++i) {
@@ -4548,22 +4735,43 @@ void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
// draw it
- drawForm(str, resDict, m, bbox);
+ drawForm(strRef, resDict, m, bbox);
resObj.free();
}
+ str.free();
// draw the border
- if (borderStyle && borderStyle->getWidth() > 0) {
- if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
- state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
- out->updateStrokeColorSpace(state);
- }
- borderStyle->getColor(&r, &g, &b);
- color.c[0] = dblToCol(r);
- color.c[1] = dblToCol(g);
- color.c[2] = dblToCol(b);
+ if (borderStyle && borderStyle->getWidth() > 0 &&
+ borderStyle->getNumColorComps() > 0) {
+ borderColor = borderStyle->getColor();
+ switch (borderStyle->getNumColorComps()) {
+ case 1:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceGray) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 3:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 4:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ }
+ color.c[0] = dblToCol(borderColor[0]);
+ color.c[1] = dblToCol(borderColor[1]);
+ color.c[2] = dblToCol(borderColor[2]);
+ color.c[3] = dblToCol(borderColor[3]);
state->setStrokeColor(&color);
out->updateStrokeColor(state);
state->setLineWidth(borderStyle->getWidth());
diff --git a/xpdf/Gfx.h b/xpdf/Gfx.h
index 2753766..b734777 100644
--- a/xpdf/Gfx.h
+++ b/xpdf/Gfx.h
@@ -16,6 +16,7 @@
#endif
#include "gtypes.h"
+#include "gfile.h"
class GString;
class GList;
@@ -84,13 +85,16 @@ public:
~GfxResources();
GfxFont *lookupFont(char *name);
- GBool lookupXObject(char *name, Object *obj);
- GBool lookupXObjectNF(char *name, Object *obj);
- void lookupColorSpace(char *name, Object *obj);
- GfxPattern *lookupPattern(char *name);
- GfxShading *lookupShading(char *name);
- GBool lookupGState(char *name, Object *obj);
- GBool lookupPropertiesNF(char *name, Object *obj);
+ 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);
+ GfxPattern *lookupPattern(const char *name
+ );
+ GfxShading *lookupShading(const char *name
+ );
+ GBool lookupGState(const char *name, Object *obj);
+ GBool lookupPropertiesNF(const char *name, Object *obj);
GfxResources *getNext() { return next; }
@@ -152,12 +156,13 @@ public:
~Gfx();
- // Interpret a stream or array of streams.
- void display(Object *obj, GBool topLevel = gTrue);
+ // Interpret a stream or array of streams. <objRef> should be a
+ // reference wherever possible (for loop-checking).
+ void display(Object *objRef, GBool topLevel = gTrue);
// Display an annotation, given its appearance (a Form XObject),
// border style, and bounding box (in default user space).
- void drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
+ void drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
double xMin, double yMin, double xMax, double yMax);
// Save graphics state.
@@ -169,13 +174,21 @@ public:
// Get the current graphics state object.
GfxState *getState() { return state; }
- void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
+ 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);
+ // Take all of the content stream stack entries from <oldGfx>. This
+ // is useful when creating a new Gfx object to handle a pattern,
+ // etc., where it's useful to check for loops that span both Gfx
+ // objects. This function should be called immediately after the
+ // Gfx constructor, i.e., before processing any content streams with
+ // the new Gfx object.
+ void takeContentStreamStack(Gfx *oldGfx);
+
private:
PDFDoc *doc;
@@ -201,6 +214,8 @@ private:
GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent]
Parser *parser; // parser for page content stream(s)
+ GList *contentStreamStack; // stack of open content streams, used
+ // for loop-checking
GBool // callback to check for an abort
(*abortCheckCbk)(void *data);
@@ -208,11 +223,12 @@ private:
static Operator opTab[]; // table of operators
+ GBool checkForContentStreamLoop(Object *ref);
void go(GBool topLevel);
- void execOp(Object *cmd, Object args[], int numArgs);
+ GBool execOp(Object *cmd, Object args[], int numArgs);
Operator *findOp(char *name);
GBool checkArg(Object *arg, TchkType type);
- int getPos();
+ GFileOffset getPos();
// graphics state operators
void opSave(Object args[], int numArgs);
@@ -225,7 +241,7 @@ private:
void opSetMiterLimit(Object args[], int numArgs);
void opSetLineWidth(Object args[], int numArgs);
void opSetExtGState(Object args[], int numArgs);
- void doSoftMask(Object *str, GBool alpha,
+ void doSoftMask(Object *str, Object *strRef, GBool alpha,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
Function *transferFunc, GfxColor *backdropColor);
@@ -268,7 +284,7 @@ private:
void doPatternStroke();
void doPatternText();
void doPatternImageMask(Object *ref, Stream *str, int width, int height,
- GBool invert, GBool inlineImg);
+ GBool invert, GBool inlineImg, GBool interpolate);
void doTilingPatternFill(GfxTilingPattern *tPat,
GBool stroke, GBool eoFill, GBool text);
void doShadingPatternFill(GfxShadingPattern *sPat,
@@ -282,12 +298,12 @@ private:
void doAxialShFill(GfxAxialShading *shading);
void doRadialShFill(GfxRadialShading *shading);
void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
- void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
- double x1, double y1, GfxColor *color1,
- double x2, double y2, GfxColor *color2,
- int nComps, int depth);
+ void gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth);
void doPatchMeshShFill(GfxPatchMeshShading *shading);
- void fillPatch(GfxPatch *patch, int nComps, int depth);
+ void fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth);
void doEndPath();
// path clipping operators
@@ -301,6 +317,7 @@ private:
// text state operators
void opSetCharSpacing(Object args[], int numArgs);
void opSetFont(Object args[], int numArgs);
+ void doSetFont(GfxFont *font, double size);
void opSetTextLeading(Object args[], int numArgs);
void opSetTextRender(Object args[], int numArgs);
void opSetTextRise(Object args[], int numArgs);
@@ -324,7 +341,7 @@ private:
// XObject operators
void opXObject(Object args[], int numArgs);
void doImage(Object *ref, Stream *str, GBool inlineImg);
- void doForm(Object *str);
+ void doForm(Object *strRef, Object *str);
// in-line image operators
void opBeginImage(Object args[], int numArgs);
diff --git a/xpdf/GfxFont.cc b/xpdf/GfxFont.cc
index aa88e78..0ebb5b9 100644
--- a/xpdf/GfxFont.cc
+++ b/xpdf/GfxFont.cc
@@ -146,6 +146,7 @@ static int readFromStream(void *data) {
GfxFontLoc::GfxFontLoc() {
path = NULL;
fontNum = 0;
+ oblique = 0;
encoding = NULL;
substIdx = -1;
}
@@ -175,6 +176,8 @@ GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
fontDict->lookup("BaseFont", &obj1);
if (obj1.isName()) {
nameA = new GString(obj1.getName());
+ } else if (obj1.isString()) {
+ nameA = obj1.getString()->copy();
}
obj1.free();
@@ -451,7 +454,7 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
}
// some broken font descriptors set ascent and descent to 0;
// others set it to ridiculous values (e.g., 32768)
- if (t != 0 && t < 3) {
+ if (t != 0 && t < 1.9) {
ascent = t;
}
}
@@ -464,7 +467,7 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
t = -t;
}
// some broken font descriptors set ascent and descent to 0
- if (t != 0 && t > -3) {
+ if (t != 0 && t > -1.9) {
descent = t;
}
}
@@ -489,7 +492,8 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
CharCodeToUnicode *ctu) {
GString *buf;
Object obj1;
- int c;
+ char buf2[4096];
+ int n;
if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
obj1.free();
@@ -497,8 +501,8 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
}
buf = new GString();
obj1.streamReset();
- while ((c = obj1.streamGetChar()) != EOF) {
- buf->append(c);
+ while ((n = obj1.streamGetBlock(buf2, sizeof(buf2))) > 0) {
+ buf->append(buf2, n);
}
obj1.streamClose();
obj1.free();
@@ -518,6 +522,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
PSFontParam16 *psFont16;
Object refObj, embFontObj;
int substIdx, fontNum;
+ double oblique;
GBool embed;
if (type == fontType3) {
@@ -570,7 +575,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
}
//----- PS passthrough
- if (ps && !isCIDFont() && globalParams->getPSFontPassthrough()) {
+ if (ps && name && !isCIDFont() && globalParams->getPSFontPassthrough()) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocResident;
fontLoc->fontType = fontType1;
@@ -578,6 +583,13 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
return fontLoc;
}
+ //----- external font file (fontFile, fontDir)
+ if (name && (path = globalParams->findFontFile(name))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, isCIDFont()))) {
+ return fontLoc;
+ }
+ }
+
//----- PS resident Base-14 font
if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
fontLoc = new GfxFontLoc();
@@ -587,28 +599,19 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
return fontLoc;
}
- //----- external font file (fontFile, fontDir)
- if ((path = globalParams->findFontFile(name))) {
- if ((fontLoc = getExternalFont(path, isCIDFont()))) {
- return fontLoc;
- }
- }
-
//----- external font file for Base-14 font
if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name);
- if ((path = globalParams->findFontFile(base14Name))) {
- if ((fontLoc = getExternalFont(path, gFalse))) {
- delete base14Name;
- return fontLoc;
- }
- }
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
delete base14Name;
+ if (path && (fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
+ return fontLoc;
+ }
}
//----- system font
- if ((path = globalParams->findSystemFontFile(name, &sysFontType,
- &fontNum))) {
+ if (name && (path = globalParams->findSystemFontFile(name, &sysFontType,
+ &fontNum))) {
if (isCIDFont()) {
if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) {
fontLoc = new GfxFontLoc();
@@ -624,13 +627,13 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontTrueType;
fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
return fontLoc;
} else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontType1;
fontLoc->path = path;
- fontLoc->fontNum = fontNum;
return fontLoc;
}
}
@@ -641,7 +644,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
//----- 8-bit PS resident font
if (ps) {
- if ((path = globalParams->getPSResidentFont(name))) {
+ if (name && (path = globalParams->getPSResidentFont(name))) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocResident;
fontLoc->fontType = fontType1;
@@ -675,10 +678,10 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
fontLoc->substIdx = substIdx;
return fontLoc;
} else {
- path = globalParams->findFontFile(substName);
+ path = globalParams->findBase14FontFile(substName, &fontNum, &oblique);
delete substName;
if (path) {
- if ((fontLoc = getExternalFont(path, gFalse))) {
+ if ((fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'",
base14SubstFonts[substIdx], name);
fontLoc->substIdx = substIdx;
@@ -692,7 +695,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
}
//----- 16-bit PS resident font
- if (ps && ((psFont16 = globalParams->getPSResidentFont16(
+ if (ps && name && ((psFont16 = globalParams->getPSResidentFont16(
name,
((GfxCIDFont *)this)->getWMode())))) {
fontLoc = new GfxFontLoc();
@@ -720,7 +723,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
//----- CID font substitution
if ((path = globalParams->findCCFontFile(
((GfxCIDFont *)this)->getCollection()))) {
- if ((fontLoc = getExternalFont(path, gTrue))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, gTrue))) {
error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'",
fontLoc->path, name);
return fontLoc;
@@ -733,15 +736,18 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) {
GString *path;
+ int fontNum;
+ double oblique;
- path = globalParams->findFontFile(base14Name);
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
if (!path) {
return NULL;
}
- return getExternalFont(path, gFalse);
+ return getExternalFont(path, fontNum, oblique, gFalse);
}
-GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
+GfxFontLoc *GfxFont::getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid) {
FoFiIdentifierType fft;
GfxFontType fontType;
GfxFontLoc *fontLoc;
@@ -768,6 +774,9 @@ GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
case fofiIdOpenTypeCFFCID:
fontType = fontCIDType0COT;
break;
+ case fofiIdDfont:
+ fontType = cid ? fontCIDType2 : fontTrueType;
+ break;
case fofiIdUnknown:
case fofiIdError:
default:
@@ -784,6 +793,8 @@ GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontType;
fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
+ fontLoc->oblique = oblique;
return fontLoc;
}
@@ -791,8 +802,7 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
char *buf;
Object obj1, obj2;
Stream *str;
- int c;
- int size, i;
+ int size, n;
obj1.initRef(embFontID.num, embFontID.gen);
obj1.fetch(xref, &obj2);
@@ -805,21 +815,19 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
}
str = obj2.getStream();
+ size = 0;
buf = NULL;
- i = size = 0;
str->reset();
- while ((c = str->getChar()) != EOF) {
- if (i == size) {
- if (size > INT_MAX - 4096) {
- error(errSyntaxError, -1, "Embedded font file is too large");
- break;
- }
- size += 4096;
- buf = (char *)grealloc(buf, size);
- }
- buf[i++] = c;
- }
- *len = i;
+ do {
+ if (size > INT_MAX - 4096) {
+ error(errSyntaxError, -1, "Embedded font file is too large");
+ break;
+ }
+ buf = (char *)grealloc(buf, size + 4096);
+ n = str->getBlock(buf + size, 4096);
+ size += n;
+ } while (n == 4096);
+ *len = size;
str->close();
obj2.free();
@@ -908,8 +916,8 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
fontBBox[2] = 0.001 * builtinFont->bbox[2];
fontBBox[3] = 0.001 * builtinFont->bbox[3];
} else {
- ascent = 0.95;
- descent = -0.35;
+ ascent = 0.75;
+ descent = -0.25;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
}
@@ -1491,6 +1499,15 @@ Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
return proc;
}
+Object *Gfx8BitFont::getCharProcNF(int code, Object *proc) {
+ if (enc[code] && charProcs.isDict()) {
+ charProcs.dictLookupNF(enc[code], proc);
+ } else {
+ proc->initNull();
+ }
+ return proc;
+}
+
Dict *Gfx8BitFont::getResources() {
return resources.isDict() ? resources.getDict() : (Dict *)NULL;
}
@@ -1565,7 +1582,6 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
error(errSyntaxError, -1,
"Missing or empty DescendantFonts entry in Type 0 font");
obj1.free();
-
goto err1;
}
if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
@@ -1661,6 +1677,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
}
cidToGID[cidToGIDLen++] = (c1 << 8) + c2;
}
+ obj1.streamClose();
} else if (!obj1.isName("Identity") && !obj1.isNull()) {
error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font");
}
@@ -1836,7 +1853,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
err2:
obj1.free();
desFontDictObj.free();
- err1:;
+ err1:
+ error(errSyntaxError, -1, "Failed to parse font object for '{0:t}'", name);
}
GfxCIDFont::~GfxCIDFont() {
@@ -2016,3 +2034,16 @@ GfxFont *GfxFontDict::lookup(char *tag) {
}
return NULL;
}
+
+GfxFont *GfxFontDict::lookupByRef(Ref ref) {
+ int i;
+
+ for (i = 0; i < numFonts; ++i) {
+ if (fonts[i] &&
+ fonts[i]->getID()->num == ref.num &&
+ fonts[i]->getID()->gen == ref.gen) {
+ return fonts[i];
+ }
+ }
+ return NULL;
+}
diff --git a/xpdf/GfxFont.h b/xpdf/GfxFont.h
index db45ef0..1ec46ec 100644
--- a/xpdf/GfxFont.h
+++ b/xpdf/GfxFont.h
@@ -100,8 +100,11 @@ public:
// (if locType == gfxFontLocExternal)
// PS font name
// (if locType == gfxFontLocResident)
- int fontNum; // for TrueType collections
+ int fontNum; // for TrueType collections and Mac dfonts
// (if locType == gfxFontLocExternal)
+ double oblique; // sheer factor to oblique this font
+ // (used when substituting a plain
+ // font for an oblique font)
GString *encoding; // PS font encoding, only for 16-bit fonts
// (if locType == gfxFontLocResident)
int wMode; // writing mode, only for 16-bit fonts
@@ -207,7 +210,8 @@ protected:
void readFontDescriptor(XRef *xref, Dict *fontDict);
CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
CharCodeToUnicode *ctu);
- static GfxFontLoc *getExternalFont(GString *path, GBool cid);
+ static GfxFontLoc *getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid);
GString *tag; // PDF font tag
Ref id; // reference (used as unique ID)
@@ -267,6 +271,7 @@ public:
// Return the Type 3 CharProc for the character associated with <code>.
Object *getCharProc(int code, Object *proc);
+ Object *getCharProcNF(int code, Object *proc);
// Return the Type 3 Resources dictionary, or NULL if none.
Dict *getResources();
@@ -347,6 +352,7 @@ public:
// Get the specified font.
GfxFont *lookup(char *tag);
+ GfxFont *lookupByRef(Ref ref);
// Iterative access.
int getNumFonts() { return numFonts; }
diff --git a/xpdf/GfxState.cc b/xpdf/GfxState.cc
index cf5e7c2..bdaa5e6 100644
--- a/xpdf/GfxState.cc
+++ b/xpdf/GfxState.cc
@@ -20,6 +20,7 @@
#include "Object.h"
#include "Array.h"
#include "Page.h"
+#include "XRef.h"
#include "GfxState.h"
//------------------------------------------------------------------------
@@ -28,7 +29,6 @@
// loops in the color space object structure.
#define colorSpaceRecursionLimit 8
-
//------------------------------------------------------------------------
static inline GfxColorComp clip01(GfxColorComp x) {
@@ -101,7 +101,8 @@ GfxColorSpace::GfxColorSpace() {
GfxColorSpace::~GfxColorSpace() {
}
-GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
+GfxColorSpace *GfxColorSpace::parse(Object *csObj,
+ int recursion) {
GfxColorSpace *cs;
Object obj1;
@@ -112,11 +113,11 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
cs = NULL;
if (csObj->isName()) {
if (csObj->isName("DeviceGray") || csObj->isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (csObj->isName("Pattern")) {
cs = new GfxPatternColorSpace(NULL);
} else {
@@ -125,11 +126,11 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
} else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
csObj->arrayGet(0, &obj1);
if (obj1.isName("DeviceGray") || obj1.isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (obj1.isName("CalGray")) {
cs = GfxCalGrayColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("CalRGB")) {
@@ -137,15 +138,20 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
} else if (obj1.isName("Lab")) {
cs = GfxLabColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("ICCBased")) {
- cs = GfxICCBasedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxICCBasedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Indexed") || obj1.isName("I")) {
- cs = GfxIndexedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxIndexedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Separation")) {
- cs = GfxSeparationColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxSeparationColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("DeviceN")) {
- cs = GfxDeviceNColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxDeviceNColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Pattern")) {
- cs = GfxPatternColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxPatternColorSpace::parse(csObj->getArray(),
+ recursion);
} else {
error(errSyntaxError, -1, "Bad color space");
}
@@ -156,6 +162,20 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
return cs;
}
+GfxColorSpace *GfxColorSpace::create(GfxColorSpaceMode mode) {
+ GfxColorSpace *cs;
+
+ cs = NULL;
+ if (mode == csDeviceGray) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (mode == csDeviceRGB) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (mode == csDeviceCMYK) {
+ cs = new GfxDeviceCMYKColorSpace();
+ }
+ return cs;
+}
+
void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
int maxImgPixel) {
int i;
@@ -185,9 +205,13 @@ GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
}
GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
- return new GfxDeviceGrayColorSpace();
+ GfxDeviceGrayColorSpace *cs;
+
+ cs = new GfxDeviceGrayColorSpace();
+ return cs;
}
+
void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01(color->c[0]);
}
@@ -233,6 +257,7 @@ GfxColorSpace *GfxCalGrayColorSpace::copy() {
return cs;
}
+
GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) {
GfxCalGrayColorSpace *cs;
Object obj1, obj2, obj3;
@@ -311,9 +336,13 @@ GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
}
GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
- return new GfxDeviceRGBColorSpace();
+ GfxDeviceRGBColorSpace *cs;
+
+ cs = new GfxDeviceRGBColorSpace();
+ return cs;
}
+
void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.3 * color->c[0] +
0.59 * color->c[1] +
@@ -456,6 +485,7 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.299 * color->c[0] +
0.587 * color->c[1] +
@@ -505,9 +535,13 @@ GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
}
GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
- return new GfxDeviceCMYKColorSpace();
+ GfxDeviceCMYKColorSpace *cs;
+
+ cs = new GfxDeviceCMYKColorSpace();
+ return cs;
}
+
void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
- 0.3 * color->c[0]
@@ -706,6 +740,7 @@ GfxColorSpace *GfxLabColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
GfxRGB rgb;
@@ -720,6 +755,7 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
double t1, t2;
double r, g, b;
+
// convert L*a*b* to CIE 1931 XYZ color space
t1 = (colToDbl(color->c[0]) + 16) / 116;
t2 = t1 + colToDbl(color->c[1]) / 500;
@@ -756,6 +792,7 @@ void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
GfxRGB rgb;
GfxColorComp c, m, y, k;
+
getRGB(color, &rgb);
c = clip01(gfxColorComp1 - rgb.r);
m = clip01(gfxColorComp1 - rgb.g);
@@ -831,7 +868,8 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() {
return cs;
}
-GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr,
+ int recursion) {
GfxICCBasedColorSpace *cs;
Ref iccProfileStreamA;
int nCompsA;
@@ -874,16 +912,17 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
nCompsA = 4;
}
if (dict->lookup("Alternate", &obj2)->isNull() ||
- !(altA = GfxColorSpace::parse(&obj2, recursion + 1))) {
+ !(altA = GfxColorSpace::parse(&obj2,
+ recursion + 1))) {
switch (nCompsA) {
case 1:
- altA = new GfxDeviceGrayColorSpace();
+ altA = GfxColorSpace::create(csDeviceGray);
break;
case 3:
- altA = new GfxDeviceRGBColorSpace();
+ altA = GfxColorSpace::create(csDeviceRGB);
break;
case 4:
- altA = new GfxDeviceCMYKColorSpace();
+ altA = GfxColorSpace::create(csDeviceCMYK);
break;
default:
error(errSyntaxError, -1, "Bad ICCBased color space - invalid N");
@@ -910,6 +949,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
alt->getGray(color, gray);
}
@@ -981,7 +1021,8 @@ GfxColorSpace *GfxIndexedColorSpace::copy() {
return cs;
}
-GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr,
+ int recursion) {
GfxIndexedColorSpace *cs;
GfxColorSpace *baseA;
int indexHighA;
@@ -995,7 +1036,8 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
goto err1;
}
arr->get(1, &obj1);
- if (!(baseA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(baseA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1, "Bad Indexed color space (base color space)");
goto err2;
}
@@ -1060,6 +1102,7 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
GfxColor *baseColor) {
Guchar *p;
@@ -1146,12 +1189,16 @@ GfxSeparationColorSpace::~GfxSeparationColorSpace() {
}
GfxColorSpace *GfxSeparationColorSpace::copy() {
- return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxSeparationColorSpace *cs;
+
+ cs = new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'All' and 'None' colorants
-GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr,
+ int recursion) {
GfxSeparationColorSpace *cs;
GString *nameA;
GfxColorSpace *altA;
@@ -1169,7 +1216,8 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
nameA = new GString(obj1.getName());
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Separation color space (alternate color space)");
goto err3;
@@ -1193,6 +1241,7 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x;
double c[gfxColorMaxComps];
@@ -1303,12 +1352,16 @@ GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
}
GfxColorSpace *GfxDeviceNColorSpace::copy() {
- return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxDeviceNColorSpace *cs;
+
+ cs = new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'None' colorant
-GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr,
+ int recursion) {
GfxDeviceNColorSpace *cs;
int nCompsA;
GString *namesA[gfxColorMaxComps];
@@ -1343,7 +1396,8 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
}
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad DeviceN color space (alternate color space)");
goto err3;
@@ -1369,6 +1423,7 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x[gfxColorMaxComps], c[gfxColorMaxComps];
GfxColor color2;
@@ -1438,11 +1493,15 @@ GfxPatternColorSpace::~GfxPatternColorSpace() {
}
GfxColorSpace *GfxPatternColorSpace::copy() {
- return new GfxPatternColorSpace(under ? under->copy() :
- (GfxColorSpace *)NULL);
+ GfxPatternColorSpace *cs;
+
+ cs = new GfxPatternColorSpace(under ? under->copy() :
+ (GfxColorSpace *)NULL);
+ return cs;
}
-GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr,
+ int recursion) {
GfxPatternColorSpace *cs;
GfxColorSpace *underA;
Object obj1;
@@ -1454,7 +1513,8 @@ GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
underA = NULL;
if (arr->getLength() == 2) {
arr->get(1, &obj1);
- if (!(underA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(underA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Pattern color space (underlying color space)");
obj1.free();
@@ -1466,6 +1526,7 @@ GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = 0;
}
@@ -1495,24 +1556,26 @@ GfxPattern::GfxPattern(int typeA) {
GfxPattern::~GfxPattern() {
}
-GfxPattern *GfxPattern::parse(Object *obj) {
+GfxPattern *GfxPattern::parse(Object *objRef, Object *obj
+ ) {
GfxPattern *pattern;
- Object obj1;
+ Object typeObj;
if (obj->isDict()) {
- obj->dictLookup("PatternType", &obj1);
+ obj->dictLookup("PatternType", &typeObj);
} else if (obj->isStream()) {
- obj->streamGetDict()->lookup("PatternType", &obj1);
+ obj->streamGetDict()->lookup("PatternType", &typeObj);
} else {
return NULL;
}
pattern = NULL;
- if (obj1.isInt() && obj1.getInt() == 1) {
- pattern = GfxTilingPattern::parse(obj);
- } else if (obj1.isInt() && obj1.getInt() == 2) {
- pattern = GfxShadingPattern::parse(obj);
+ if (typeObj.isInt() && typeObj.getInt() == 1) {
+ pattern = GfxTilingPattern::parse(objRef, obj);
+ } else if (typeObj.isInt() && typeObj.getInt() == 2) {
+ pattern = GfxShadingPattern::parse(obj
+ );
}
- obj1.free();
+ typeObj.free();
return pattern;
}
@@ -1520,7 +1583,7 @@ GfxPattern *GfxPattern::parse(Object *obj) {
// GfxTilingPattern
//------------------------------------------------------------------------
-GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) {
GfxTilingPattern *pat;
Dict *dict;
int paintTypeA, tilingTypeA;
@@ -1597,7 +1660,7 @@ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
obj1.free();
pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
- &resDictA, matrixA, patObj);
+ &resDictA, matrixA, patObjRef);
resDictA.free();
return pat;
}
@@ -1605,7 +1668,7 @@ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
double *bboxA, double xStepA, double yStepA,
Object *resDictA, double *matrixA,
- Object *contentStreamA):
+ Object *contentStreamRefA):
GfxPattern(1)
{
int i;
@@ -1621,24 +1684,25 @@ GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
for (i = 0; i < 6; ++i) {
matrix[i] = matrixA[i];
}
- contentStreamA->copy(&contentStream);
+ contentStreamRefA->copy(&contentStreamRef);
}
GfxTilingPattern::~GfxTilingPattern() {
resDict.free();
- contentStream.free();
+ contentStreamRef.free();
}
GfxPattern *GfxTilingPattern::copy() {
return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
- &resDict, matrix, &contentStream);
+ &resDict, matrix, &contentStreamRef);
}
//------------------------------------------------------------------------
// GfxShadingPattern
//------------------------------------------------------------------------
-GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj
+ ) {
Dict *dict;
GfxShading *shadingA;
double matrixA[6];
@@ -1651,7 +1715,8 @@ GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
dict = patObj->getDict();
dict->lookup("Shading", &obj1);
- shadingA = GfxShading::parse(&obj1);
+ shadingA = GfxShading::parse(&obj1
+ );
obj1.free();
if (!shadingA) {
return NULL;
@@ -1724,7 +1789,8 @@ GfxShading::~GfxShading() {
}
}
-GfxShading *GfxShading::parse(Object *obj) {
+GfxShading *GfxShading::parse(Object *obj
+ ) {
GfxShading *shading;
Dict *dict;
int typeA;
@@ -1748,17 +1814,21 @@ GfxShading *GfxShading::parse(Object *obj) {
switch (typeA) {
case 1:
- shading = GfxFunctionShading::parse(dict);
+ shading = GfxFunctionShading::parse(dict
+ );
break;
case 2:
- shading = GfxAxialShading::parse(dict);
+ shading = GfxAxialShading::parse(dict
+ );
break;
case 3:
- shading = GfxRadialShading::parse(dict);
+ shading = GfxRadialShading::parse(dict
+ );
break;
case 4:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 4 shading object");
goto err1;
@@ -1766,7 +1836,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 5:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 5 shading object");
goto err1;
@@ -1774,7 +1845,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 6:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(6, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 6 shading object");
goto err1;
@@ -1782,7 +1854,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 7:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(7, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 7 shading object");
goto err1;
@@ -1799,12 +1872,14 @@ GfxShading *GfxShading::parse(Object *obj) {
return NULL;
}
-GBool GfxShading::init(Dict *dict) {
+GBool GfxShading::init(Dict *dict
+ ) {
Object obj1, obj2;
int i;
dict->lookup("ColorSpace", &obj1);
- if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+ if (!(colorSpace = GfxColorSpace::parse(&obj1
+ ))) {
error(errSyntaxError, -1, "Bad color space in shading dictionary");
obj1.free();
return gFalse;
@@ -1901,7 +1976,8 @@ GfxFunctionShading::~GfxFunctionShading() {
}
}
-GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict
+ ) {
GfxFunctionShading *shading;
double x0A, y0A, x1A, y1A;
double matrixA[6];
@@ -1916,9 +1992,9 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
obj1.arrayGetLength() == 4) {
x0A = obj1.arrayGet(0, &obj2)->getNum();
obj2.free();
- y0A = obj1.arrayGet(1, &obj2)->getNum();
+ x1A = obj1.arrayGet(1, &obj2)->getNum();
obj2.free();
- x1A = obj1.arrayGet(2, &obj2)->getNum();
+ y0A = obj1.arrayGet(2, &obj2)->getNum();
obj2.free();
y1A = obj1.arrayGet(3, &obj2)->getNum();
obj2.free();
@@ -1970,7 +2046,8 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2060,7 +2137,8 @@ GfxAxialShading::~GfxAxialShading() {
}
}
-GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+GfxAxialShading *GfxAxialShading::parse(Dict *dict
+ ) {
GfxAxialShading *shading;
double x0A, y0A, x1A, y1A;
double t0A, t1A;
@@ -2137,7 +2215,8 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2226,7 +2305,8 @@ GfxRadialShading::~GfxRadialShading() {
}
}
-GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+GfxRadialShading *GfxRadialShading::parse(Dict *dict
+ ) {
GfxRadialShading *shading;
double x0A, y0A, r0A, x1A, y1A, r1A;
double t0A, t1A;
@@ -2307,7 +2387,8 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2413,7 +2494,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA):
+ int nCompsA, Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
int i;
@@ -2422,6 +2503,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
nVertices = nVerticesA;
triangles = trianglesA;
nTriangles = nTrianglesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2440,6 +2522,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
nTriangles = shading->nTriangles;
triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+ nComps = shading->nComps;
nFuncs = shading->nFuncs;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = shading->funcs[i]->copy();
@@ -2456,9 +2539,9 @@ GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
}
}
-GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
- Dict *dict,
- Stream *str) {
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
+ int typeA, Dict *dict, Stream *str
+ ) {
GfxGouraudTriangleShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2469,7 +2552,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
double cMul[gfxColorMaxComps];
GfxGouraudVertex *verticesA;
int (*trianglesA)[3];
- int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+ int nCompsA, nVerticesA, nTrianglesA, vertSize, triSize;
Guint x, y, flag;
Guint c[gfxColorMaxComps];
GfxShadingBitBuf *bitBuf;
@@ -2531,7 +2614,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2585,12 +2668,12 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
!bitBuf->getBits(coordBits, &y)) {
break;
}
- for (i = 0; i < nComps; ++i) {
+ for (i = 0; i < nCompsA; ++i) {
if (!bitBuf->getBits(compBits, &c[i])) {
break;
}
}
- if (i < nComps) {
+ if (i < nCompsA) {
break;
}
if (nVerticesA == vertSize) {
@@ -2600,9 +2683,8 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
}
verticesA[nVerticesA].x = xMin + xMul * (double)x;
verticesA[nVerticesA].y = yMin + yMul * (double)y;
- for (i = 0; i < nComps; ++i) {
- verticesA[nVerticesA].color.c[i] =
- dblToCol(cMin[i] + cMul[i] * (double)c[i]);
+ for (i = 0; i < nCompsA; ++i) {
+ verticesA[nVerticesA].color[i] = cMin[i] + cMul[i] * (double)c[i];
}
++nVerticesA;
bitBuf->flushBits();
@@ -2657,8 +2739,9 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
trianglesA, nTrianglesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2676,54 +2759,46 @@ GfxShading *GfxGouraudTriangleShading::copy() {
void GfxGouraudTriangleShading::getTriangle(
int i,
- double *x0, double *y0, GfxColor *color0,
- double *x1, double *y1, GfxColor *color1,
- double *x2, double *y2, GfxColor *color2) {
- double in;
- double out[gfxColorMaxComps];
+ double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2) {
int v, j;
v = triangles[i][0];
*x0 = vertices[v].x;
*y0 = vertices[v].y;
- if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
- }
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color0->c[j] = dblToCol(out[j]);
- }
- } else {
- *color0 = vertices[v].color;
+ for (j = 0; j < nComps; ++j) {
+ color0[j] = vertices[v].color[j];
}
v = triangles[i][1];
*x1 = vertices[v].x;
*y1 = vertices[v].y;
- if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
- }
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color1->c[j] = dblToCol(out[j]);
- }
- } else {
- *color1 = vertices[v].color;
+ for (j = 0; j < nComps; ++j) {
+ color1[j] = vertices[v].color[j];
}
v = triangles[i][2];
*x2 = vertices[v].x;
*y2 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color2[j] = vertices[v].color[j];
+ }
+}
+
+void GfxGouraudTriangleShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
}
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color2->c[j] = dblToCol(out[j]);
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
}
} else {
- *color2 = vertices[v].color;
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
}
}
@@ -2733,6 +2808,7 @@ void GfxGouraudTriangleShading::getTriangle(
GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
GfxPatch *patchesA, int nPatchesA,
+ int nCompsA,
Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
@@ -2740,6 +2816,7 @@ GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
patches = patchesA;
nPatches = nPatchesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2754,6 +2831,7 @@ GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
nPatches = shading->nPatches;
patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+ nComps = shading->nComps;
nFuncs = shading->nFuncs;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = shading->funcs[i]->copy();
@@ -2770,7 +2848,8 @@ GfxPatchMeshShading::~GfxPatchMeshShading() {
}
GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
- Stream *str) {
+ Stream *str
+ ) {
GfxPatchMeshShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2780,11 +2859,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
double xMul, yMul;
double cMul[gfxColorMaxComps];
GfxPatch *patchesA, *p;
- int nComps, nPatchesA, patchesSize, nPts, nColors;
+ int nCompsA, nPatchesA, patchesSize, nPts, nColors;
Guint flag;
double x[16], y[16];
Guint xi, yi;
- GfxColorComp c[4][gfxColorMaxComps];
+ double c[4][gfxColorMaxComps];
Guint ci;
GfxShadingBitBuf *bitBuf;
Object obj1, obj2;
@@ -2833,7 +2912,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2907,13 +2986,13 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
break;
}
for (i = 0; i < nColors; ++i) {
- for (j = 0; j < nComps; ++j) {
+ for (j = 0; j < nCompsA; ++j) {
if (!bitBuf->getBits(compBits, &ci)) {
break;
}
- c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci);
+ c[i][j] = cMin[j] + cMul[j] * (double)ci;
}
- if (j < nComps) {
+ if (j < nCompsA) {
break;
}
}
@@ -2953,11 +3032,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][0] = y[10];
p->x[1][0] = x[11];
p->y[1][0] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = c[0][j];
- p->color[0][1].c[j] = c[1][j];
- p->color[1][1].c[j] = c[2][j];
- p->color[1][0].c[j] = c[3][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -2985,11 +3064,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3017,11 +3096,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3049,11 +3128,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3092,11 +3171,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][2] = y[14];
p->x[2][1] = x[15];
p->y[2][1] = y[15];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = c[0][j];
- p->color[0][1].c[j] = c[1][j];
- p->color[1][1].c[j] = c[2][j];
- p->color[1][0].c[j] = c[3][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -3132,11 +3211,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3172,11 +3251,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3212,11 +3291,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3273,8 +3352,9 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
}
shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -3290,6 +3370,24 @@ GfxShading *GfxPatchMeshShading::copy() {
return new GfxPatchMeshShading(this);
}
+void GfxPatchMeshShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
+ if (nFuncs > 0) {
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
+ }
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
+ }
+}
+
//------------------------------------------------------------------------
// GfxImageColorMap
//------------------------------------------------------------------------
@@ -3310,7 +3408,11 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
// bits per component and color space
bits = bitsA;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
colorSpace = colorSpaceA;
// initialize
@@ -3431,7 +3533,11 @@ GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
lookup[k] = NULL;
lookup2[k] = NULL;
}
- n = 1 << bits;
+ if (bits <= 8) {
+ n = 1 << bits;
+ } else {
+ n = 256;
+ }
for (k = 0; k < nComps; ++k) {
lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
@@ -3521,7 +3627,11 @@ void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
int maxPixel, i;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
for (i = 0; i < nComps; ++i) {
color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
}
@@ -3811,7 +3921,8 @@ void GfxPath::offset(double dx, double dy) {
//------------------------------------------------------------------------
GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
- int rotateA, GBool upsideDown) {
+ int rotateA, GBool upsideDown
+ ) {
double kx, ky;
hDPI = hDPIA;
@@ -3861,8 +3972,8 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
pageHeight = ky * (py2 - py1);
}
- fillColorSpace = new GfxDeviceGrayColorSpace();
- strokeColorSpace = new GfxDeviceGrayColorSpace();
+ fillColorSpace = GfxColorSpace::create(csDeviceGray);
+ strokeColorSpace = GfxColorSpace::create(csDeviceGray);
fillColor.c[0] = 0;
strokeColor.c[0] = 0;
fillPattern = NULL;
diff --git a/xpdf/GfxState.h b/xpdf/GfxState.h
index 5d57de9..74ab5eb 100644
--- a/xpdf/GfxState.h
+++ b/xpdf/GfxState.h
@@ -140,7 +140,13 @@ public:
virtual GfxColorSpaceMode getMode() = 0;
// Construct a color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Object *csObj, int recursion = 0);
+ static GfxColorSpace *parse(Object *csObj,
+ int recursion = 0);
+
+ // Construct a simple color space. The <mode> argument can be
+ // csDeviceGray, csDeviceRGB, or csDeviceCMYK.
+ static GfxColorSpace *create(GfxColorSpaceMode mode);
+
// Convert to gray, RGB, or CMYK.
virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
@@ -381,7 +387,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csICCBased; }
// Construct an ICCBased color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -418,7 +425,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csIndexed; }
// Construct an Indexed color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -457,7 +465,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csSeparation; }
// Construct a Separation color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -499,7 +508,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csDeviceN; }
// Construct a DeviceN color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -542,7 +552,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csPattern; }
// Construct a Pattern color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -570,7 +581,8 @@ public:
GfxPattern(int typeA);
virtual ~GfxPattern();
- static GfxPattern *parse(Object *obj);
+ static GfxPattern *parse(Object *objRef, Object *obj
+ );
virtual GfxPattern *copy() = 0;
@@ -588,7 +600,7 @@ private:
class GfxTilingPattern: public GfxPattern {
public:
- static GfxTilingPattern *parse(Object *patObj);
+ static GfxTilingPattern *parse(Object *patObjRef, Object *patObj);
virtual ~GfxTilingPattern();
virtual GfxPattern *copy();
@@ -601,7 +613,7 @@ public:
Dict *getResDict()
{ return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
double *getMatrix() { return matrix; }
- Object *getContentStream() { return &contentStream; }
+ Object *getContentStreamRef() { return &contentStreamRef; }
private:
@@ -616,7 +628,7 @@ private:
double xStep, yStep;
Object resDict;
double matrix[6];
- Object contentStream;
+ Object contentStreamRef;
};
//------------------------------------------------------------------------
@@ -626,7 +638,8 @@ private:
class GfxShadingPattern: public GfxPattern {
public:
- static GfxShadingPattern *parse(Object *patObj);
+ static GfxShadingPattern *parse(Object *patObj
+ );
virtual ~GfxShadingPattern();
virtual GfxPattern *copy();
@@ -653,7 +666,8 @@ public:
GfxShading(GfxShading *shading);
virtual ~GfxShading();
- static GfxShading *parse(Object *obj);
+ static GfxShading *parse(Object *obj
+ );
virtual GfxShading *copy() = 0;
@@ -667,7 +681,8 @@ public:
protected:
- GBool init(Dict *dict);
+ GBool init(Dict *dict
+ );
int type;
GfxColorSpace *colorSpace;
@@ -691,7 +706,8 @@ public:
GfxFunctionShading(GfxFunctionShading *shading);
virtual ~GfxFunctionShading();
- static GfxFunctionShading *parse(Dict *dict);
+ static GfxFunctionShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -725,7 +741,8 @@ public:
GfxAxialShading(GfxAxialShading *shading);
virtual ~GfxAxialShading();
- static GfxAxialShading *parse(Dict *dict);
+ static GfxAxialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -763,7 +780,8 @@ public:
GfxRadialShading(GfxRadialShading *shading);
virtual ~GfxRadialShading();
- static GfxRadialShading *parse(Dict *dict);
+ static GfxRadialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -793,7 +811,7 @@ private:
struct GfxGouraudVertex {
double x, y;
- GfxColor color;
+ double color[gfxColorMaxComps];
};
class GfxGouraudTriangleShading: public GfxShading {
@@ -802,18 +820,21 @@ public:
GfxGouraudTriangleShading(int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
virtual ~GfxGouraudTriangleShading();
- static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
int getNTriangles() { return nTriangles; }
- void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
- double *x1, double *y1, GfxColor *color1,
- double *x2, double *y2, GfxColor *color2);
+ void getTriangle(int i, double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2);
+ void getColor(double *in, GfxColor *out);
private:
@@ -822,6 +843,7 @@ private:
int (*triangles)[3];
int nTriangles;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -832,29 +854,33 @@ private:
struct GfxPatch {
double x[4][4];
double y[4][4];
- GfxColor color[2][2];
+ double color[2][2][gfxColorMaxComps];
};
class GfxPatchMeshShading: public GfxShading {
public:
GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxPatchMeshShading(GfxPatchMeshShading *shading);
virtual ~GfxPatchMeshShading();
- static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
int getNPatches() { return nPatches; }
GfxPatch *getPatch(int i) { return &patches[i]; }
+ void getColor(double *in, GfxColor *out);
private:
GfxPatch *patches;
int nPatches;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -1040,7 +1066,8 @@ public:
// x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
// coordinate system specified by <upsideDown>.
GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
- int rotateA, GBool upsideDown);
+ int rotateA, GBool upsideDown
+ );
// Destructor.
~GfxState();
diff --git a/xpdf/GlobalParams.cc b/xpdf/GlobalParams.cc
index 63e932b..ea7e9fd 100644
--- a/xpdf/GlobalParams.cc
+++ b/xpdf/GlobalParams.cc
@@ -12,15 +12,19 @@
#pragma implementation
#endif
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0500 // for GetSystemWindowsDirectory
+# include <windows.h>
+#endif
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef ENABLE_PLUGINS
-# ifndef WIN32
+# ifndef _WIN32
# include <dlfcn.h>
# endif
#endif
-#ifdef WIN32
+#ifdef _WIN32
# include <shlobj.h>
#endif
#if HAVE_PAPER_H
@@ -31,6 +35,7 @@
#include "GList.h"
#include "GHash.h"
#include "gfile.h"
+#include "FoFiIdentifier.h"
#include "Error.h"
#include "NameToCharCode.h"
#include "CharCodeToUnicode.h"
@@ -43,8 +48,9 @@
#endif
#include "GlobalParams.h"
-#ifdef WIN32
+#ifdef _WIN32
# define strcasecmp stricmp
+# define strncasecmp strnicmp
#endif
#if MULTITHREADED
@@ -68,7 +74,7 @@
#include "UTF8.h"
#ifdef ENABLE_PLUGINS
-# ifdef WIN32
+# ifdef _WIN32
extern XpdfPluginVecTable xpdfPluginVecTable;
# endif
#endif
@@ -84,25 +90,29 @@ static struct {
const char *name;
const char *t1FileName;
const char *ttFileName;
+ const char *macFileName; // may be .dfont, .ttf, or .ttc
+ const char *macFontName; // font name inside .dfont or .ttc
+ const char *obliqueFont; // name of font to oblique
+ double obliqueFactor; // oblique sheer factor
} displayFontTab[] = {
- {"Courier", "n022003l.pfb", "cour.ttf"},
- {"Courier-Bold", "n022004l.pfb", "courbd.ttf"},
- {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf"},
- {"Courier-Oblique", "n022023l.pfb", "couri.ttf"},
- {"Helvetica", "n019003l.pfb", "arial.ttf"},
- {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf"},
- {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
- {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf"},
- {"Symbol", "s050000l.pfb", NULL},
- {"Times-Bold", "n021004l.pfb", "timesbd.ttf"},
- {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf"},
- {"Times-Italic", "n021023l.pfb", "timesi.ttf"},
- {"Times-Roman", "n021003l.pfb", "times.ttf"},
- {"ZapfDingbats", "d050000l.pfb", NULL},
+ {"Courier", "n022003l.pfb", "cour.ttf", "Courier", "Courier", NULL, 0},
+ {"Courier-Bold", "n022004l.pfb", "courbd.ttf", "Courier", "Courier Bold", NULL, 0},
+ {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", "Courier", "Courier Bold Oblique", "Courier-Bold", 0.212557},
+ {"Courier-Oblique", "n022023l.pfb", "couri.ttf", "Courier", "Courier Oblique", "Courier", 0.212557},
+ {"Helvetica", "n019003l.pfb", "arial.ttf", "Helvetica", "Helvetica", NULL, 0},
+ {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica-Bold", NULL, 0},
+ {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", "Helvetica", "Helvetica Bold Oblique", "Helvetica-Bold", 0.212557},
+ {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", "Helvetica", "Helvetica Oblique", "Helvetica", 0.212557},
+ {"Symbol", "s050000l.pfb", NULL, "Symbol", "Symbol", NULL, 0},
+ {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times-Bold", NULL, 0},
+ {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times-BoldItalic", NULL, 0},
+ {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times-Italic", NULL, 0},
+ {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times-Roman", NULL, 0},
+ {"ZapfDingbats", "d050000l.pfb", NULL, "ZapfDingbats", "Zapf Dingbats", NULL, 0},
{NULL}
};
-#ifdef WIN32
+#ifdef _WIN32
static const char *displayFontDirs[] = {
"c:/windows/fonts",
"c:/winnt/fonts",
@@ -115,10 +125,31 @@ static const char *displayFontDirs[] = {
"/usr/share/fonts/default/Type1",
"/usr/share/fonts/default/ghostscript",
"/usr/share/fonts/type1/gsfonts",
+#if defined(__sun) && defined(__SVR4)
+ "/usr/sfw/share/ghostscript/fonts",
+#endif
NULL
};
#endif
+#ifdef __APPLE__
+static const char *macSystemFontPath = "/System/Library/Fonts";
+#endif
+
+struct Base14FontInfo {
+ Base14FontInfo(GString *fileNameA, int fontNumA, double obliqueA) {
+ fileName = fileNameA;
+ fontNum = fontNumA;
+ oblique = obliqueA;
+ }
+ ~Base14FontInfo() {
+ delete fileName;
+ }
+ GString *fileName;
+ int fontNum;
+ double oblique;
+};
+
//------------------------------------------------------------------------
GlobalParams *globalParams = NULL;
@@ -198,13 +229,13 @@ public:
~SysFontList();
SysFontInfo *find(GString *name);
-#ifdef WIN32
+#ifdef _WIN32
void scanWindowsFonts(char *winFontDir);
#endif
private:
-#ifdef WIN32
+#ifdef _WIN32
SysFontInfo *makeWindowsFont(char *name, int fontNum,
char *path);
#endif
@@ -241,40 +272,36 @@ SysFontInfo *SysFontList::find(GString *name) {
}
n = name2->getLength();
- // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
- name2->del(n - 2, 2);
- n -= 2;
- }
+ // font names like "Arial-BoldMT,Bold" are occasionally used,
+ // so run this loop twice
+ bold = italic = gFalse;
+ for (i = 0; i < 2; ++i) {
- // look for "Regular"
- if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) {
- name2->del(n - 7, 7);
- n -= 7;
- }
+ // 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 "Italic"
- if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) {
- name2->del(n - 6, 6);
- italic = gTrue;
- n -= 6;
- } else {
- italic = gFalse;
- }
+ // look for "Regular"
+ if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) {
+ name2->del(n - 7, 7);
+ n -= 7;
+ }
- // look for "Bold"
- if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) {
- name2->del(n - 4, 4);
- bold = gTrue;
- n -= 4;
- } else {
- bold = gFalse;
- }
+ // look for "Italic"
+ if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) {
+ name2->del(n - 6, 6);
+ italic = gTrue;
+ n -= 6;
+ }
- // remove trailing "MT" (FooMT-Bold, etc.)
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
- name2->del(n - 2, 2);
- n -= 2;
+ // look for "Bold"
+ if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) {
+ name2->del(n - 4, 4);
+ bold = gTrue;
+ n -= 4;
+ }
}
// remove trailing "PS"
@@ -323,7 +350,7 @@ SysFontInfo *SysFontList::find(GString *name) {
return fi;
}
-#ifdef WIN32
+#ifdef _WIN32
void SysFontList::scanWindowsFonts(char *winFontDir) {
OSVERSIONINFO version;
char *path;
@@ -341,15 +368,15 @@ void SysFontList::scanWindowsFonts(char *winFontDir) {
} else {
path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
}
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0,
- KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
- &regKey) == ERROR_SUCCESS) {
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &regKey) == ERROR_SUCCESS) {
idx = 0;
while (1) {
valNameLen = sizeof(valName) - 1;
dataLen = sizeof(data) - 1;
- if (RegEnumValue(regKey, idx, valName, &valNameLen, NULL,
- &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
+ if (RegEnumValueA(regKey, idx, valName, &valNameLen, NULL,
+ &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
break;
}
if (type == REG_SZ &&
@@ -357,7 +384,7 @@ void SysFontList::scanWindowsFonts(char *winFontDir) {
dataLen > 0 && dataLen < sizeof(data)) {
valName[valNameLen] = '\0';
data[dataLen] = '\0';
- n = strlen(data);
+ n = (int)strlen(data);
if (!strcasecmp(data + n - 4, ".ttf") ||
!strcasecmp(data + n - 4, ".ttc")) {
fontPath = new GString(data);
@@ -398,7 +425,7 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
int i;
SysFontType type;
- n = strlen(name);
+ n = (int)strlen(name);
bold = italic = gFalse;
// remove trailing ' (TrueType)'
@@ -419,7 +446,7 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
}
// remove trailing ' Regular'
- if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) {
+ if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) {
n -= 8;
}
@@ -490,7 +517,7 @@ public:
private:
-#ifdef WIN32
+#ifdef _WIN32
Plugin(HMODULE libA);
HMODULE lib;
#else
@@ -504,7 +531,7 @@ Plugin *Plugin::load(char *type, char *name) {
Plugin *plugin;
XpdfPluginVecTable *vt;
XpdfBool (*xpdfInitPlugin)(void);
-#ifdef WIN32
+#ifdef _WIN32
HMODULE libA;
#else
void *dlA;
@@ -515,9 +542,9 @@ Plugin *Plugin::load(char *type, char *name) {
appendToPath(path, type);
appendToPath(path, name);
-#ifdef WIN32
+#ifdef _WIN32
path->append(".dll");
- if (!(libA = LoadLibrary(path->getCString()))) {
+ if (!(libA = LoadLibraryA(path->getCString()))) {
error(errIO, -1, "Failed to load plugin '{0:t}'", path);
goto err1;
}
@@ -548,7 +575,7 @@ Plugin *Plugin::load(char *type, char *name) {
}
memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));
-#ifdef WIN32
+#ifdef _WIN32
if (!(xpdfInitPlugin = (XpdfBool (*)(void))
GetProcAddress(libA, "xpdfInitPlugin"))) {
error(errIO, -1, "Failed to find xpdfInitPlugin in plugin '{0:t}'",
@@ -568,7 +595,7 @@ Plugin *Plugin::load(char *type, char *name) {
goto err2;
}
-#ifdef WIN32
+#ifdef _WIN32
plugin = new Plugin(libA);
#else
plugin = new Plugin(dlA);
@@ -578,7 +605,7 @@ Plugin *Plugin::load(char *type, char *name) {
return plugin;
err2:
-#ifdef WIN32
+#ifdef _WIN32
FreeLibrary(libA);
#else
dlclose(dlA);
@@ -588,7 +615,7 @@ Plugin *Plugin::load(char *type, char *name) {
return NULL;
}
-#ifdef WIN32
+#ifdef _WIN32
Plugin::Plugin(HMODULE libA) {
lib = libA;
}
@@ -601,7 +628,7 @@ Plugin::Plugin(void *dlA) {
Plugin::~Plugin() {
void (*xpdfFreePlugin)(void);
-#ifdef WIN32
+#ifdef _WIN32
if ((xpdfFreePlugin = (void (*)(void))
GetProcAddress(lib, "xpdfFreePlugin"))) {
(*xpdfFreePlugin)();
@@ -621,7 +648,7 @@ Plugin::~Plugin() {
// parsing
//------------------------------------------------------------------------
-GlobalParams::GlobalParams(char *cfgFileName) {
+GlobalParams::GlobalParams(const char *cfgFileName) {
UnicodeMap *map;
GString *fileName;
FILE *f;
@@ -644,7 +671,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
}
}
-#ifdef WIN32
+#ifdef _WIN32
// baseDir will be set by a call to setBaseDir
baseDir = new GString();
#else
@@ -660,6 +687,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
fontFiles = new GHash(gTrue);
fontDirs = new GList();
ccFontFiles = new GHash(gTrue);
+ base14SysFonts = new GHash(gTrue);
sysFonts = new SysFontList();
#if HAVE_PAPER_H
char *paperName;
@@ -683,6 +711,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
psImageableURX = psPaperWidth;
psImageableURY = psPaperHeight;
psCrop = gTrue;
+ psUseCropBoxAsPage = gFalse;
psExpandSmaller = gFalse;
psShrinkLarger = gTrue;
psCenter = gTrue;
@@ -700,12 +729,15 @@ GlobalParams::GlobalParams(char *cfgFileName) {
psPreload = gFalse;
psOPI = gFalse;
psASCIIHex = gFalse;
+ psLZW = gTrue;
psUncompressPreloadedImages = gFalse;
+ psMinLineWidth = 0;
psRasterResolution = 300;
psRasterMono = gFalse;
+ psRasterSliceSize = 20000000;
psAlwaysRasterize = gFalse;
textEncoding = new GString("Latin1");
-#if defined(WIN32)
+#if defined(_WIN32)
textEOL = eolDOS;
#elif defined(MACOS)
textEOL = eolMac;
@@ -713,10 +745,9 @@ GlobalParams::GlobalParams(char *cfgFileName) {
textEOL = eolUnix;
#endif
textPageBreaks = gTrue;
- textKeepTinyChars = gFalse;
+ textKeepTinyChars = gTrue;
initialZoom = new GString("125");
continuousView = gFalse;
- enableT1lib = gTrue;
enableFreeType = gTrue;
disableFreeTypeHinting = gFalse;
antialias = gTrue;
@@ -737,6 +768,8 @@ GlobalParams::GlobalParams(char *cfgFileName) {
movieCommand = NULL;
mapNumericCharNames = gTrue;
mapUnknownCharNames = gFalse;
+ mapExtTrueTypeFontsViaUnicode = gTrue;
+ enableXFA = gTrue;
createDefaultKeyBindings();
printCommands = gFalse;
errQuiet = gFalse;
@@ -791,9 +824,9 @@ GlobalParams::GlobalParams(char *cfgFileName) {
}
}
if (!f) {
-#ifdef WIN32
+#ifdef _WIN32
char buf[512];
- i = GetModuleFileName(NULL, buf, sizeof(buf));
+ i = GetModuleFileNameA(NULL, buf, sizeof(buf));
if (i <= 0 || i >= sizeof(buf)) {
// error or path too long for buffer - just use the current dir
buf[0] = '\0';
@@ -928,7 +961,7 @@ void GlobalParams::createDefaultKeyBindings() {
keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
xpdfKeyContextAny, "redraw"));
keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
- xpdfKeyContextAny, "closeWindow"));
+ xpdfKeyContextAny, "closeWindowOrQuit"));
keyBindings->append(new KeyBinding('?', xpdfKeyModNone,
xpdfKeyContextAny, "about"));
keyBindings->append(new KeyBinding('q', xpdfKeyModNone,
@@ -1017,6 +1050,9 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
parsePSImageableArea(tokens, fileName, line);
} else if (!cmd->cmp("psCrop")) {
parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+ } else if (!cmd->cmp("psUseCropBoxAsPage")) {
+ parseYesNo("psUseCropBoxAsPage", &psUseCropBoxAsPage,
+ tokens, fileName, line);
} else if (!cmd->cmp("psExpandSmaller")) {
parseYesNo("psExpandSmaller", &psExpandSmaller,
tokens, fileName, line);
@@ -1054,14 +1090,22 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
parseYesNo("psOPI", &psOPI, tokens, fileName, line);
} else if (!cmd->cmp("psASCIIHex")) {
parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLZW")) {
+ parseYesNo("psLZW", &psLZW, tokens, fileName, line);
} else if (!cmd->cmp("psUncompressPreloadedImages")) {
parseYesNo("psUncompressPreloadedImages", &psUncompressPreloadedImages,
tokens, fileName, line);
+ } else if (!cmd->cmp("psMinLineWidth")) {
+ parseFloat("psMinLineWidth", &psMinLineWidth,
+ tokens, fileName, line);
} else if (!cmd->cmp("psRasterResolution")) {
parseFloat("psRasterResolution", &psRasterResolution,
tokens, fileName, line);
} else if (!cmd->cmp("psRasterMono")) {
parseYesNo("psRasterMono", &psRasterMono, tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterSliceSize")) {
+ parseInteger("psRasterSliceSize", &psRasterSliceSize,
+ tokens, fileName, line);
} else if (!cmd->cmp("psAlwaysRasterize")) {
parseYesNo("psAlwaysRasterize", &psAlwaysRasterize,
tokens, fileName, line);
@@ -1079,8 +1123,6 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
parseInitialZoom(tokens, fileName, line);
} else if (!cmd->cmp("continuousView")) {
parseYesNo("continuousView", &continuousView, tokens, fileName, line);
- } else if (!cmd->cmp("enableT1lib")) {
- parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
} else if (!cmd->cmp("enableFreeType")) {
parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
} else if (!cmd->cmp("disableFreeTypeHinting")) {
@@ -1133,6 +1175,12 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
} else if (!cmd->cmp("mapUnknownCharNames")) {
parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
tokens, fileName, line);
+ } else if (!cmd->cmp("mapExtTrueTypeFontsViaUnicode")) {
+ parseYesNo("mapExtTrueTypeFontsViaUnicode",
+ &mapExtTrueTypeFontsViaUnicode,
+ 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")) {
@@ -1148,6 +1196,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
!cmd->cmp("displayNamedCIDFontX") ||
!cmd->cmp("displayCIDFontX")) {
error(errConfig, -1, "Xpdf no longer supports X fonts");
+ } else if (!cmd->cmp("enableT1lib")) {
+ error(errConfig, -1, "Xpdf no longer uses t1lib");
} else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
error(errConfig, -1,
"The t1libControl and freetypeControl options have been replaced by the enableT1lib, enableFreeType, and antialias options");
@@ -1520,6 +1570,7 @@ void GlobalParams::parseScreenType(GList *tokens, GString *fileName,
}
}
+
void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
KeyBinding *binding;
GList *cmds;
@@ -1836,6 +1887,7 @@ GlobalParams::~GlobalParams() {
deleteGHash(fontFiles, GString);
deleteGList(fontDirs, GString);
deleteGHash(ccFontFiles, GString);
+ deleteGHash(base14SysFonts, Base14FontInfo);
delete sysFonts;
if (psFile) {
delete psFile;
@@ -1886,35 +1938,77 @@ void GlobalParams::setBaseDir(char *dir) {
baseDir = new GString(dir);
}
-void GlobalParams::setupBaseFonts(char *dir) {
- GString *fontName;
- GString *fileName;
-#ifdef WIN32
+#ifdef _WIN32
+static void getWinFontDir(char *winFontDir) {
HMODULE shell32Lib;
BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
- LPTSTR lpszPath,
+ LPSTR lpszPath,
int nFolder,
BOOL fCreate);
- char winFontDir[MAX_PATH];
-#endif
- FILE *f;
- int i, j;
+ char *p;
+ int i;
-#ifdef WIN32
// SHGetSpecialFolderPath isn't available in older versions of
// shell32.dll (Win95 and WinNT4), so do a dynamic load
winFontDir[0] = '\0';
- if ((shell32Lib = LoadLibrary("shell32.dll"))) {
+ if ((shell32Lib = LoadLibraryA("shell32.dll"))) {
if ((SHGetSpecialFolderPathFunc =
- (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
+ (BOOL (__stdcall *)(HWND hwndOwner, LPSTR lpszPath,
int nFolder, BOOL fCreate))
GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
CSIDL_FONTS, FALSE)) {
winFontDir[0] = '\0';
}
+ // kludge: Terminal Server changes CSIDL_FONTS to something like
+ // "C:\Users\whatever\Windows\Fonts", which doesn't actually
+ // contain any fonts -- kill that, so we hit the fallback code
+ // below.
+ for (p = winFontDir; *p; ++p) {
+ if (!strncasecmp(p, "\\Users\\", 7)) {
+ winFontDir[0] = '\0';
+ break;
+ }
+ }
+ }
+ }
+ // if something went wrong, or we're on a Terminal Server, try using
+ // %SYSTEMROOT%\Fonts
+ if (!winFontDir[0]) {
+ GetSystemWindowsDirectoryA(winFontDir, MAX_PATH - 6);
+ winFontDir[MAX_PATH - 7] = '\0';
+ i = (int)strlen(winFontDir);
+ if (winFontDir[i-1] != '\\') {
+ winFontDir[i++] = '\\';
}
+ strcpy(winFontDir + i, "Fonts");
}
+}
+#endif
+
+void GlobalParams::setupBaseFonts(char *dir) {
+ GString *fontName;
+ GString *fileName;
+ int fontNum;
+ const char *s;
+ Base14FontInfo *base14;
+#ifdef _WIN32
+ char winFontDir[MAX_PATH];
+#endif
+#ifdef __APPLE__
+ static const char *macFontExts[3] = { "dfont", "ttc", "ttf" };
+ GList *dfontFontNames;
+ GBool found;
+ int k;
+#endif
+ FILE *f;
+ int i, j;
+
+#ifdef _WIN32
+ getWinFontDir(winFontDir);
+#endif
+#ifdef __APPLE__
+ dfontFontNames = NULL;
#endif
for (i = 0; displayFontTab[i].name; ++i) {
if (fontFiles->lookup(displayFontTab[i].name)) {
@@ -1922,6 +2016,7 @@ void GlobalParams::setupBaseFonts(char *dir) {
}
fontName = new GString(displayFontTab[i].name);
fileName = NULL;
+ fontNum = 0;
if (dir) {
fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
if ((f = fopen(fileName->getCString(), "rb"))) {
@@ -1931,7 +2026,7 @@ void GlobalParams::setupBaseFonts(char *dir) {
fileName = NULL;
}
}
-#ifdef WIN32
+#ifdef _WIN32
if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
fileName = appendToPath(new GString(winFontDir),
displayFontTab[i].ttFileName);
@@ -1942,13 +2037,67 @@ void GlobalParams::setupBaseFonts(char *dir) {
fileName = NULL;
}
}
- // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
- // or Win2003 Server, or with older versions of shell32.dll, so check
- // the "standard" directories
- if (displayFontTab[i].ttFileName) {
+#endif
+#ifdef __APPLE__
+ // Check for Mac OS X system fonts.
+ s = displayFontTab[i].macFileName;
+ if (dfontFontNames && i > 0 &&
+ (!s || strcmp(s, displayFontTab[i-1].macFileName))) {
+ deleteGList(dfontFontNames, GString);
+ dfontFontNames = NULL;
+ }
+ if (!fileName && s) {
+ for (j = 0; j < 3; ++j) {
+ fileName = GString::format("{0:s}/{1:s}.{2:s}",
+ macSystemFontPath, s, macFontExts[j]);
+ if (!(f = fopen(fileName->getCString(), "rb"))) {
+ delete fileName;
+ fileName = NULL;
+ } else {
+ fclose(f);
+ found = gFalse;
+ // for .dfont or .ttc, we need to scan the font list
+ if (j < 2) {
+ if (!dfontFontNames) {
+ dfontFontNames =
+ FoFiIdentifier::getFontList(fileName->getCString());
+ }
+ if (dfontFontNames) {
+ for (k = 0; k < dfontFontNames->getLength(); ++k) {
+ if (!((GString *)dfontFontNames->get(k))
+ ->cmp(displayFontTab[i].macFontName)) {
+ fontNum = k;
+ found = gTrue;
+ break;
+ }
+ }
+ }
+ // for .ttf, we just use the font
+ } else {
+ found = gTrue;
+ }
+ if (!found) {
+ delete fileName;
+ fileName = NULL;
+ }
+ break;
+ }
+ }
+ }
+#endif // __APPLE__
+ // On Linux, this checks the "standard" ghostscript font
+ // directories. On Windows, it checks the "standard" system font
+ // directories (because SHGetSpecialFolderPath(CSIDL_FONTS)
+ // doesn't work on Win 2k Server or Win2003 Server, or with older
+ // versions of shell32.dll).
+#ifdef _WIN32
+ s = displayFontTab[i].ttFileName;
+#else
+ s = displayFontTab[i].t1FileName;
+#endif
+ if (!fileName && s) {
for (j = 0; !fileName && displayFontDirs[j]; ++j) {
- fileName = appendToPath(new GString(displayFontDirs[j]),
- displayFontTab[i].ttFileName);
+ fileName = appendToPath(new GString(displayFontDirs[j]), s);
if ((f = fopen(fileName->getCString(), "rb"))) {
fclose(f);
} else {
@@ -1957,28 +2106,35 @@ void GlobalParams::setupBaseFonts(char *dir) {
}
}
}
-#else // WIN32
- for (j = 0; !fileName && displayFontDirs[j]; ++j) {
- fileName = appendToPath(new GString(displayFontDirs[j]),
- displayFontTab[i].t1FileName);
- if ((f = fopen(fileName->getCString(), "rb"))) {
- fclose(f);
- } else {
- delete fileName;
- fileName = NULL;
- }
- }
-#endif // WIN32
if (!fileName) {
- error(errConfig, -1, "No display font for '{0:s}'",
- displayFontTab[i].name);
delete fontName;
continue;
}
- addFontFile(fontName, fileName);
+ base14SysFonts->add(fontName, new Base14FontInfo(fileName, fontNum, 0));
}
-
-#ifdef WIN32
+#ifdef __APPLE__
+ if (dfontFontNames) {
+ deleteGList(dfontFontNames, GString);
+ }
+#endif
+ for (i = 0; displayFontTab[i].name; ++i) {
+ if (!base14SysFonts->lookup(displayFontTab[i].name) &&
+ !fontFiles->lookup(displayFontTab[i].name)) {
+ if (displayFontTab[i].obliqueFont &&
+ ((base14 = (Base14FontInfo *)base14SysFonts
+ ->lookup(displayFontTab[i].obliqueFont)))) {
+ base14SysFonts->add(
+ new GString(displayFontTab[i].name),
+ new Base14FontInfo(base14->fileName->copy(),
+ base14->fontNum,
+ displayFontTab[i].obliqueFactor));
+ } else {
+ error(errConfig, -1, "No display font for '{0:s}'",
+ displayFontTab[i].name);
+ }
+ }
+ }
+#ifdef _WIN32
if (winFontDir[0]) {
sysFonts->scanWindowsFonts(winFontDir);
}
@@ -2083,7 +2239,7 @@ FILE *GlobalParams::findToUnicodeFile(GString *name) {
GString *GlobalParams::findFontFile(GString *fontName) {
static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc" };
GString *path, *dir;
-#ifdef WIN32
+#ifdef _WIN32
GString *fontNameU;
#endif
const char *ext;
@@ -2100,7 +2256,7 @@ GString *GlobalParams::findFontFile(GString *fontName) {
dir = (GString *)fontDirs->get(i);
for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) {
ext = exts[j];
-#ifdef WIN32
+#ifdef _WIN32
fontNameU = fileNameToUTF8(fontName->getCString());
path = appendToPath(dir->copy(), fontNameU->getCString());
delete fontNameU;
@@ -2120,6 +2276,25 @@ GString *GlobalParams::findFontFile(GString *fontName) {
return NULL;
}
+GString *GlobalParams::findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique) {
+ Base14FontInfo *fi;
+ GString *path;
+
+ lockGlobalParams;
+ if ((fi = (Base14FontInfo *)base14SysFonts->lookup(fontName))) {
+ path = fi->fileName->copy();
+ *fontNum = fi->fontNum;
+ *oblique = fi->oblique;
+ unlockGlobalParams;
+ return path;
+ }
+ unlockGlobalParams;
+ *fontNum = 0;
+ *oblique = 0;
+ return findFontFile(fontName);
+}
+
GString *GlobalParams::findSystemFontFile(GString *fontName,
SysFontType *type,
int *fontNum) {
@@ -2193,6 +2368,15 @@ GBool GlobalParams::getPSCrop() {
return f;
}
+GBool GlobalParams::getPSUseCropBoxAsPage() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psUseCropBoxAsPage;
+ unlockGlobalParams;
+ return f;
+}
+
GBool GlobalParams::getPSExpandSmaller() {
GBool f;
@@ -2242,7 +2426,9 @@ GString *GlobalParams::getPSResidentFont(GString *fontName) {
GString *psName;
lockGlobalParams;
- psName = (GString *)psResidentFonts->lookup(fontName);
+ if ((psName = (GString *)psResidentFonts->lookup(fontName))) {
+ psName = psName->copy();
+ }
unlockGlobalParams;
return psName;
}
@@ -2371,6 +2557,15 @@ GBool GlobalParams::getPSASCIIHex() {
return ah;
}
+GBool GlobalParams::getPSLZW() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psLZW;
+ unlockGlobalParams;
+ return ah;
+}
+
GBool GlobalParams::getPSUncompressPreloadedImages() {
GBool ah;
@@ -2380,6 +2575,15 @@ GBool GlobalParams::getPSUncompressPreloadedImages() {
return ah;
}
+double GlobalParams::getPSMinLineWidth() {
+ double w;
+
+ lockGlobalParams;
+ w = psMinLineWidth;
+ unlockGlobalParams;
+ return w;
+}
+
double GlobalParams::getPSRasterResolution() {
double res;
@@ -2398,6 +2602,15 @@ GBool GlobalParams::getPSRasterMono() {
return mono;
}
+int GlobalParams::getPSRasterSliceSize() {
+ int slice;
+
+ lockGlobalParams;
+ slice = psRasterSliceSize;
+ unlockGlobalParams;
+ return slice;
+}
+
GBool GlobalParams::getPSAlwaysRasterize() {
GBool rast;
@@ -2461,15 +2674,6 @@ GBool GlobalParams::getContinuousView() {
return f;
}
-GBool GlobalParams::getEnableT1lib() {
- GBool f;
-
- lockGlobalParams;
- f = enableT1lib;
- unlockGlobalParams;
- return f;
-}
-
GBool GlobalParams::getEnableFreeType() {
GBool f;
@@ -2597,6 +2801,7 @@ GBool GlobalParams::getDrawAnnotations() {
return draw;
}
+
GBool GlobalParams::getMapNumericCharNames() {
GBool map;
@@ -2615,6 +2820,24 @@ GBool GlobalParams::getMapUnknownCharNames() {
return map;
}
+GBool GlobalParams::getMapExtTrueTypeFontsViaUnicode() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapExtTrueTypeFontsViaUnicode;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getEnableXFA() {
+ GBool enable;
+
+ lockGlobalParams;
+ enable = enableXFA;
+ unlockGlobalParams;
+ return enable;
+}
+
GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
KeyBinding *binding;
GList *cmds;
@@ -2804,6 +3027,12 @@ void GlobalParams::setPSCrop(GBool crop) {
unlockGlobalParams;
}
+void GlobalParams::setPSUseCropBoxAsPage(GBool crop) {
+ lockGlobalParams;
+ psUseCropBoxAsPage = crop;
+ unlockGlobalParams;
+}
+
void GlobalParams::setPSExpandSmaller(GBool expand) {
lockGlobalParams;
psExpandSmaller = expand;
@@ -2882,7 +3111,7 @@ void GlobalParams::setPSASCIIHex(GBool hex) {
unlockGlobalParams;
}
-void GlobalParams::setTextEncoding(char *encodingName) {
+void GlobalParams::setTextEncoding(const char *encodingName) {
lockGlobalParams;
delete textEncoding;
textEncoding = new GString(encodingName);
@@ -2930,15 +3159,6 @@ void GlobalParams::setContinuousView(GBool cont) {
unlockGlobalParams;
}
-GBool GlobalParams::setEnableT1lib(char *s) {
- GBool ok;
-
- lockGlobalParams;
- ok = parseYesNo2(s, &enableT1lib);
- unlockGlobalParams;
- return ok;
-}
-
GBool GlobalParams::setEnableFreeType(char *s) {
GBool ok;
@@ -3015,6 +3235,18 @@ void GlobalParams::setMapUnknownCharNames(GBool map) {
unlockGlobalParams;
}
+void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) {
+ lockGlobalParams;
+ mapExtTrueTypeFontsViaUnicode = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setEnableXFA(GBool enable) {
+ lockGlobalParams;
+ enableXFA = enable;
+ unlockGlobalParams;
+}
+
void GlobalParams::setPrintCommands(GBool printCommandsA) {
lockGlobalParams;
printCommands = printCommandsA;
diff --git a/xpdf/GlobalParams.h b/xpdf/GlobalParams.h
index e2da5ca..07a6f83 100644
--- a/xpdf/GlobalParams.h
+++ b/xpdf/GlobalParams.h
@@ -173,7 +173,7 @@ public:
// Initialize the global parameters by attempting to read a config
// file.
- GlobalParams(char *cfgFileName);
+ GlobalParams(const char *cfgFileName);
~GlobalParams();
@@ -193,6 +193,8 @@ public:
FILE *findCMapFile(GString *collection, GString *cMapName);
FILE *findToUnicodeFile(GString *name);
GString *findFontFile(GString *fontName);
+ GString *findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique);
GString *findSystemFontFile(GString *fontName, SysFontType *type,
int *fontNum);
GString *findCCFontFile(GString *collection);
@@ -202,6 +204,7 @@ public:
void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
GBool getPSDuplex();
GBool getPSCrop();
+ GBool getPSUseCropBoxAsPage();
GBool getPSExpandSmaller();
GBool getPSShrinkLarger();
GBool getPSCenter();
@@ -218,9 +221,12 @@ public:
GBool getPSPreload();
GBool getPSOPI();
GBool getPSASCIIHex();
+ GBool getPSLZW();
GBool getPSUncompressPreloadedImages();
+ double getPSMinLineWidth();
double getPSRasterResolution();
GBool getPSRasterMono();
+ int getPSRasterSliceSize();
GBool getPSAlwaysRasterize();
GString *getTextEncodingName();
EndOfLineKind getTextEOL();
@@ -228,7 +234,6 @@ public:
GBool getTextKeepTinyChars();
GString *getInitialZoom();
GBool getContinuousView();
- GBool getEnableT1lib();
GBool getEnableFreeType();
GBool getDisableFreeTypeHinting();
GBool getAntialias();
@@ -249,6 +254,8 @@ public:
GString *getMovieCommand() { return movieCommand; }
GBool getMapNumericCharNames();
GBool getMapUnknownCharNames();
+ GBool getMapExtTrueTypeFontsViaUnicode();
+ GBool getEnableXFA();
GList *getKeyBinding(int code, int mods, int context);
GBool getPrintCommands();
GBool getErrQuiet();
@@ -269,6 +276,7 @@ public:
void setPSImageableArea(int llx, int lly, int urx, int ury);
void setPSDuplex(GBool duplex);
void setPSCrop(GBool crop);
+ void setPSUseCropBoxAsPage(GBool crop);
void setPSExpandSmaller(GBool expand);
void setPSShrinkLarger(GBool shrink);
void setPSCenter(GBool center);
@@ -281,13 +289,12 @@ public:
void setPSPreload(GBool preload);
void setPSOPI(GBool opi);
void setPSASCIIHex(GBool hex);
- void setTextEncoding(char *encodingName);
+ void setTextEncoding(const char *encodingName);
GBool setTextEOL(char *s);
void setTextPageBreaks(GBool pageBreaks);
void setTextKeepTinyChars(GBool keep);
void setInitialZoom(char *s);
void setContinuousView(GBool cont);
- GBool setEnableT1lib(char *s);
GBool setEnableFreeType(char *s);
GBool setAntialias(char *s);
GBool setVectorAntialias(char *s);
@@ -299,6 +306,8 @@ public:
void setScreenWhiteThreshold(double thresh);
void setMapNumericCharNames(GBool map);
void setMapUnknownCharNames(GBool map);
+ void setMapExtTrueTypeFontsViaUnicode(GBool map);
+ void setEnableXFA(GBool enable);
void setPrintCommands(GBool printCommandsA);
void setErrQuiet(GBool errQuietA);
@@ -379,6 +388,8 @@ private:
GList *fontDirs; // list of font dirs [GString]
GHash *ccFontFiles; // character collection font files:
// collection name mapped to path [GString]
+ GHash *base14SysFonts; // Base-14 system font files: font name
+ // mapped to path [Base14FontInfo]
SysFontList *sysFonts; // system fonts
GString *psFile; // PostScript file or command (for xpdf)
int psPaperWidth; // paper size, in PostScript points, for
@@ -388,6 +399,7 @@ private:
psImageableURX,
psImageableURY;
GBool psCrop; // crop PS output to CropBox
+ GBool psUseCropBoxAsPage; // use CropBox as page size
GBool psExpandSmaller; // expand smaller pages to fill paper
GBool psShrinkLarger; // shrink larger pages to fit paper
GBool psCenter; // center pages on the paper
@@ -411,11 +423,15 @@ private:
// memory
GBool psOPI; // generate PostScript OPI comments?
GBool psASCIIHex; // use ASCIIHex instead of ASCII85?
+ GBool psLZW; // false to use RLE instead of LZW
GBool psUncompressPreloadedImages; // uncompress all preloaded images
+ double psMinLineWidth; // minimum line width for PostScript output
double psRasterResolution; // PostScript rasterization resolution (dpi)
GBool psRasterMono; // true to do PostScript rasterization
// in monochrome (gray); false to do it
// in color (RGB/CMYK)
+ int psRasterSliceSize; // maximum size (pixels) of PostScript
+ // rasterization slice
GBool psAlwaysRasterize; // force PostScript rasterization
GString *textEncoding; // encoding (unicodeMap) to use for text
// output
@@ -425,7 +441,6 @@ private:
GBool textKeepTinyChars; // keep all characters in text output
GString *initialZoom; // initial zoom level
GBool continuousView; // continuous view mode
- GBool enableT1lib; // t1lib enable flag
GBool enableFreeType; // FreeType enable flag
GBool disableFreeTypeHinting; // FreeType hinting disable flag
GBool antialias; // font anti-aliasing enable flag
@@ -446,6 +461,9 @@ private:
GString *movieCommand; // command executed for movie annotations
GBool mapNumericCharNames; // map numeric char names (from font subsets)?
GBool mapUnknownCharNames; // map unknown char names?
+ GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode
+ // for external TrueType fonts?
+ GBool enableXFA; // enable XFA form rendering
GList *keyBindings; // key & mouse button bindings [KeyBinding]
GBool printCommands; // print the drawing commands
GBool errQuiet; // suppress error messages?
diff --git a/xpdf/HTMLGen.cc b/xpdf/HTMLGen.cc
new file mode 100644
index 0000000..6372e13
--- /dev/null
+++ b/xpdf/HTMLGen.cc
@@ -0,0 +1,583 @@
+//========================================================================
+//
+// HTMLGen.cc
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+//~ to do:
+//~ - fonts
+//~ - underlined? (underlines are present in the background image)
+//~ - include the original font name in the CSS entry (before the
+//~ generic serif/sans-serif/monospace name)
+//~ - check that htmlDir exists and is a directory
+//~ - links:
+//~ - links to pages
+//~ - links to named destinations
+//~ - links to URLs
+//~ - rotated text should go in the background image
+//~ - metadata
+//~ - PDF outline
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <png.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "SplashBitmap.h"
+#include "PDFDoc.h"
+#include "TextOutputDev.h"
+#include "SplashOutputDev.h"
+#include "ErrorCodes.h"
+#if EVAL_MODE
+# include "SplashMath.h"
+# include "Splash.h"
+# include "BuiltinFontTables.h"
+# include "FontEncodingTables.h"
+#endif
+#include "HTMLGen.h"
+
+#ifdef _WIN32
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+//------------------------------------------------------------------------
+
+struct FontStyleTagInfo {
+ const char *tag;
+ int tagLen;
+ GBool bold;
+ GBool italic;
+};
+
+// NB: these are compared, in order, against the tail of the font
+// name, so "BoldItalic" must come before "Italic", etc.
+static FontStyleTagInfo fontStyleTags[] = {
+ {"Roman", 5, gFalse, gFalse},
+ {"Regular", 7, gFalse, gFalse},
+ {"Condensed", 9, gFalse, gFalse},
+ {"CondensedBold", 13, gTrue, gFalse},
+ {"CondensedLight", 14, gFalse, gFalse},
+ {"SemiBold", 8, gTrue, gFalse},
+ {"BoldItalic", 10, gTrue, gTrue},
+ {"Bold_Italic", 11, gTrue, gTrue},
+ {"BoldOblique", 11, gTrue, gTrue},
+ {"Bold_Oblique", 12, gTrue, gTrue},
+ {"Bold", 4, gTrue, gFalse},
+ {"Italic", 6, gFalse, gTrue},
+ {"Oblique", 7, gFalse, gTrue},
+ {NULL, 0, gFalse, gFalse}
+};
+
+struct StandardFontInfo {
+ const char *name;
+ GBool fixedWidth;
+ GBool serif;
+};
+
+static StandardFontInfo standardFonts[] = {
+ {"Arial", gFalse, gFalse},
+ {"Courier", gTrue, gFalse},
+ {"Futura", gFalse, gFalse},
+ {"Helvetica", gFalse, gFalse},
+ {"Minion", gFalse, gTrue},
+ {"NewCenturySchlbk", gFalse, gTrue},
+ {"Times", gFalse, gTrue},
+ {"TimesNew", gFalse, gTrue},
+ {"Times_New", gFalse, gTrue},
+ {"Verdana", gFalse, gFalse},
+ {"LucidaSans", gFalse, gFalse},
+ {NULL, gFalse, gFalse}
+};
+
+struct SubstFontInfo {
+ double mWidth;
+};
+
+// index: {fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static SubstFontInfo substFonts[16] = {
+ {0.833},
+ {0.833},
+ {0.889},
+ {0.889},
+ {0.788},
+ {0.722},
+ {0.833},
+ {0.778},
+ {0.600},
+ {0.600},
+ {0.600},
+ {0.600}
+};
+
+// Map Unicode indexes from the private use area, following the Adobe
+// Glyph list.
+#define privateUnicodeMapStart 0xf6f9
+#define privateUnicodeMapEnd 0xf7ff
+static int
+privateUnicodeMap[privateUnicodeMapEnd - privateUnicodeMapStart + 1] = {
+ 0x0141, 0x0152, 0, 0, 0x0160, 0, 0x017d, // f6f9
+ 0, 0, 0, 0, 0, 0, 0, 0, // f700
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f710
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0021, 0, 0, 0x0024, 0, 0x0026, 0, // f720
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // f730
+ 0x0038, 0x0039, 0, 0, 0, 0, 0, 0x003f,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f740
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f750
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // f760
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // f770
+ 0x0058, 0x0059, 0x005a, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f780
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f790
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x00a1, 0x00a2, 0, 0, 0, 0, 0, // f7a0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7b0
+ 0, 0, 0, 0, 0, 0, 0, 0x00bf,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7c0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7d0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // f7e0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0, // f7f0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178
+};
+
+//------------------------------------------------------------------------
+
+#if EVAL_MODE
+
+#define EVAL_MODE_MSG "XpdfHTML evaluation - www.glyphandcog.com"
+
+static void drawEvalModeMsg(SplashOutputDev *out) {
+ BuiltinFont *bf;
+ SplashFont *font;
+ GString *fontName;
+ char *msg;
+ SplashCoord mat[4], ident[6];
+ SplashCoord diag, size, textW, x, y;
+ Gushort cw;
+ int w, h, n, i;
+
+ // get the Helvetica font info
+ bf = builtinFontSubst[4];
+
+ msg = EVAL_MODE_MSG;
+ n = strlen(msg);
+
+ w = out->getBitmap()->getWidth();
+ h = out->getBitmap()->getHeight();
+
+ ident[0] = 1; ident[1] = 0;
+ ident[2] = 0; ident[3] = -1;
+ ident[4] = 0; ident[5] = h;
+ out->getSplash()->setMatrix(ident);
+
+ diag = splashSqrt((SplashCoord)(w*w + h*h));
+ size = diag / (0.67 * n);
+ if (size < 8) {
+ size = 8;
+ }
+ mat[0] = size * (SplashCoord)w / diag;
+ mat[3] = mat[0];
+ mat[1] = size * (SplashCoord)h / diag;
+ mat[2] = -mat[1];
+ fontName = new GString(bf->name);
+ font = out->getFont(fontName, mat);
+ delete fontName;
+ if (!font) {
+ return;
+ }
+
+ textW = 0;
+ for (i = 0; i < n; ++i) {
+ bf->widths->getWidth(winAnsiEncoding[msg[i] & 0xff], &cw);
+ textW += size * cw * 0.001;
+ }
+
+ out->setFillColor(255, 0, 0);
+ x = 0.5 * (diag - textW) * (SplashCoord)w / diag;
+ y = 0.5 * (diag - textW) * (SplashCoord)h / diag;
+ for (i = 0; i < n; ++i) {
+ out->getSplash()->fillChar(x, y, msg[i], font);
+ bf->widths->getWidth(winAnsiEncoding[msg[i] & 0xff], &cw);
+ x += mat[0] * cw * 0.001;
+ y += mat[1] * cw * 0.001;
+ }
+}
+#endif
+
+//------------------------------------------------------------------------
+
+HTMLGen::HTMLGen(double backgroundResolutionA) {
+ TextOutputControl textOutControl;
+ SplashColor paperColor;
+
+ ok = gTrue;
+
+ backgroundResolution = backgroundResolutionA;
+ drawInvisibleText = gTrue;
+
+ // set up the TextOutputDev
+ textOutControl.mode = textOutReadingOrder;
+ textOutControl.html = gTrue;
+ textOut = new TextOutputDev(NULL, &textOutControl, gFalse);
+ if (!textOut->isOk()) {
+ ok = gFalse;
+ }
+
+ // set up the SplashOutputDev
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+ splashOut->setSkipText(gTrue, gFalse);
+}
+
+HTMLGen::~HTMLGen() {
+ delete textOut;
+ delete splashOut;
+}
+
+void HTMLGen::startDoc(PDFDoc *docA) {
+ doc = docA;
+ splashOut->startDoc(doc->getXRef());
+}
+
+static inline int pr(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *data) {
+ return writeFunc(stream, data, (int)strlen(data));
+}
+
+static int pf(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *fmt, ...) {
+ va_list args;
+ GString *s;
+ int ret;
+
+ va_start(args, fmt);
+ s = GString::formatv(fmt, args);
+ va_end(args);
+ ret = writeFunc(stream, s->getCString(), s->getLength());
+ delete s;
+ return ret;
+}
+
+struct PNGWriteInfo {
+ int (*writePNG)(void *stream, const char *data, int size);
+ void *pngStream;
+};
+
+static void pngWriteFunc(png_structp png, png_bytep data, png_size_t size) {
+ PNGWriteInfo *info;
+
+ info = (PNGWriteInfo *)png_get_progressive_ptr(png);
+ info->writePNG(info->pngStream, (char *)data, (int)size);
+}
+
+int HTMLGen::convertPage(
+ int pg, const char *pngURL,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream) {
+ png_structp png;
+ png_infop pngInfo;
+ PNGWriteInfo writeInfo;
+ SplashBitmap *bitmap;
+ Guchar *p;
+ double pageW, pageH;
+ TextPage *text;
+ GList *fonts, *cols, *pars, *lines, *words;
+ double *fontScales;
+ TextFontInfo *font;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word0, *word1;
+ GString *s;
+ double base, base1;
+ int subSuper0, subSuper1;
+ double r0, g0, b0, r1, g1, b1;
+ int colIdx, parIdx, lineIdx, wordIdx;
+ int y, i, u;
+
+ // generate the background bitmap
+ doc->displayPage(splashOut, pg, backgroundResolution, backgroundResolution,
+ 0, gFalse, gTrue, gFalse);
+#if EVAL_MODE
+ drawEvalModeMsg(splashOut);
+#endif
+ bitmap = splashOut->getBitmap();
+ if (!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(pngInfo = png_create_info_struct(png))) {
+ return errFileIO;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ return errFileIO;
+ }
+ writeInfo.writePNG = writePNG;
+ writeInfo.pngStream = pngStream;
+ png_set_write_fn(png, &writeInfo, pngWriteFunc, NULL);
+ png_set_IHDR(png, pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png, pngInfo);
+ p = bitmap->getDataPtr();
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+ png_write_end(png, pngInfo);
+ png_destroy_write_struct(&png, &pngInfo);
+
+ // page size
+ pageW = doc->getPageCropWidth(pg);
+ pageH = doc->getPageCropHeight(pg);
+
+ // get the PDF text
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ doc->processLinks(textOut, pg);
+ text = textOut->takeText();
+
+ // HTML header
+ pr(writeHTML, htmlStream, "<html>\n");
+ pr(writeHTML, htmlStream, "<head>\n");
+ pr(writeHTML, htmlStream, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+ pr(writeHTML, htmlStream, "<style type=\"text/css\">\n");
+ pr(writeHTML, htmlStream, ".txt { white-space:nowrap; }\n");
+ fonts = text->getFonts();
+ fontScales = (double *)gmallocn(fonts->getLength(), sizeof(double));
+ for (i = 0; i < fonts->getLength(); ++i) {
+ font = (TextFontInfo *)fonts->get(i);
+ s = getFontDefn(font, &fontScales[i]);
+ pf(writeHTML, htmlStream, "#f{0:d} {{ {1:t} }}\n", i, s);
+ delete s;
+ }
+ pr(writeHTML, htmlStream, "</style>\n");
+ pr(writeHTML, htmlStream, "</head>\n");
+ pr(writeHTML, htmlStream, "<body onload=\"start()\">\n");
+ pf(writeHTML, htmlStream, "<img id=\"background\" style=\"position:absolute; left:0px; top:0px;\" width=\"{0:d}\" height=\"{1:d}\" src=\"{2:s}\">\n",
+ (int)pageW, (int)pageH, pngURL);
+
+ // generate the HTML text
+ cols = text->makeColumns();
+ for (colIdx = 0; colIdx < cols->getLength(); ++colIdx) {
+ col = (TextColumn *)cols->get(colIdx);
+ pars = col->getParagraphs();
+ for (parIdx = 0; parIdx < pars->getLength(); ++parIdx) {
+ par = (TextParagraph *)pars->get(parIdx);
+ lines = par->getLines();
+ for (lineIdx = 0; lineIdx < lines->getLength(); ++lineIdx) {
+ line = (TextLine *)lines->get(lineIdx);
+ if (line->getRotation() != 0) {
+ continue;
+ }
+ words = line->getWords();
+ base = line->getBaseline();
+ s = new GString();
+ word0 = NULL;
+ subSuper0 = 0; // make gcc happy
+ r0 = g0 = b0 = 0; // make gcc happy
+ for (wordIdx = 0; wordIdx < words->getLength(); ++wordIdx) {
+ word1 = (TextWord *)words->get(wordIdx);
+ if (!drawInvisibleText && word1->isInvisible()) {
+ continue;
+ }
+ word1->getColor(&r1, &g1, &b1);
+ base1 = word1->getBaseline();
+ if (base1 - base < -1) {
+ subSuper1 = -1; // superscript
+ } else if (base1 - base > 1) {
+ subSuper1 = 1; // subscript
+ } else {
+ subSuper1 = 0;
+ }
+ if (!word0 ||
+ word1->getFontInfo() != word0->getFontInfo() ||
+ word1->getFontSize() != word0->getFontSize() ||
+ subSuper1 != subSuper0 ||
+ r1 != r0 || g1 != g0 || b1 != b0) {
+ if (word0) {
+ s->append("</span>");
+ }
+ for (i = 0; i < fonts->getLength(); ++i) {
+ if (word1->getFontInfo() == (TextFontInfo *)fonts->get(i)) {
+ break;
+ }
+ }
+ s->appendf("<span id=\"f{0:d}\" style=\"font-size:{1:d}px;vertical-align:{2:s};color:#{3:02x}{4:02x}{5:02x};\">",
+ i, (int)(fontScales[i] * word1->getFontSize()),
+ subSuper1 < 0 ? "super"
+ : subSuper1 > 0 ? "sub"
+ : "baseline",
+ (int)(r1 * 255), (int)(g1 * 255), (int)(b1 * 255));
+ }
+ for (i = 0; i < word1->getLength(); ++i) {
+ u = word1->getChar(i);
+ if (u >= privateUnicodeMapStart &&
+ u <= privateUnicodeMapEnd &&
+ privateUnicodeMap[u - privateUnicodeMapStart]) {
+ u = privateUnicodeMap[u - privateUnicodeMapStart];
+ }
+ if (u <= 0x7f) {
+ if (u == '&') {
+ s->append("&amp;");
+ } else if (u == '<') {
+ s->append("&lt;");
+ } else if (u == '>') {
+ s->append("&gt;");
+ } else {
+ s->append((char)u);
+ }
+ } else if (u <= 0x7ff) {
+ s->append((char)(0xc0 + (u >> 6)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0xffff) {
+ s->append((char)0xe0 + (u >> 12));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x1fffff) {
+ s->append((char)0xf0 + (u >> 18));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x3ffffff) {
+ s->append((char)0xf8 + (u >> 24));
+ s->append((char)0x80 + ((u >> 18) & 0x3f));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x7fffffff) {
+ s->append((char)0xfc + (u >> 30));
+ s->append((char)0x80 + ((u >> 24) & 0x3f));
+ s->append((char)0x80 + ((u >> 18) & 0x3f));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ }
+ }
+ if (word1->getSpaceAfter()) {
+ s->append(' ');
+ }
+ word0 = word1;
+ subSuper0 = subSuper1;
+ r0 = r1;
+ g0 = g1;
+ b0 = b1;
+ }
+ s->append("</span>");
+ pf(writeHTML, htmlStream, "<div class=\"txt\" style=\"position:absolute; left:{0:d}px; top:{1:d}px;\">{2:t}</div>\n",
+ (int)line->getXMin(), (int)line->getYMin(), s);
+ delete s;
+ }
+ }
+ }
+ gfree(fontScales);
+ delete text;
+ deleteGList(cols, TextColumn);
+
+ // HTML trailer
+ pr(writeHTML, htmlStream, "</body>\n");
+ pr(writeHTML, htmlStream, "</html>\n");
+
+ return errNone;
+}
+
+GString *HTMLGen::getFontDefn(TextFontInfo *font, double *scale) {
+ GString *fontName;
+ char *fontName2;
+ FontStyleTagInfo *fst;
+ StandardFontInfo *sf;
+ GBool fixedWidth, serif, bold, italic;
+ double s;
+ int n, i;
+
+ // get the font name, remove any subset tag
+ fontName = font->getFontName();
+ if (fontName) {
+ fontName2 = fontName->getCString();
+ n = fontName->getLength();
+ for (i = 0; i < n && i < 7; ++i) {
+ if (fontName2[i] < 'A' || fontName2[i] > 'Z') {
+ break;
+ }
+ }
+ if (i == 6 && n > 7 && fontName2[6] == '+') {
+ fontName2 += 7;
+ n -= 7;
+ }
+ } else {
+ fontName2 = NULL;
+ n = 0;
+ }
+
+ // get the style info from the font descriptor flags
+ fixedWidth = font->isFixedWidth();
+ serif = font->isSerif();
+ bold = font->isBold();
+ italic = font->isItalic();
+
+ if (fontName2) {
+
+ // look for a style tag at the end of the font name -- this
+ // overrides the font descriptor bold/italic flags
+ for (fst = fontStyleTags; fst->tag; ++fst) {
+ if (n > fst->tagLen &&
+ !strcasecmp(fontName2 + n - fst->tagLen, fst->tag)) {
+ bold = fst->bold;
+ italic = fst->italic;
+ n -= fst->tagLen;
+ if (n > 1 && (fontName2[n-1] == '-' ||
+ fontName2[n-1] == ',' ||
+ fontName2[n-1] == '.' ||
+ fontName2[n-1] == '_')) {
+ --n;
+ }
+ break;
+ }
+ }
+
+ // look for a known font name -- this overrides the font descriptor
+ // fixedWidth/serif flags
+ for (sf = standardFonts; sf->name; ++sf) {
+ if (!strncasecmp(fontName2, sf->name, n)) {
+ fixedWidth = sf->fixedWidth;
+ serif = sf->serif;
+ break;
+ }
+ }
+ }
+
+ // compute the scaling factor
+ *scale = 1;
+ if ((s = font->getMWidth())) {
+ i = (fixedWidth ? 8 : serif ? 4 : 0) + (bold ? 2 : 0) + (italic ? 1 : 0);
+ if (s < substFonts[i].mWidth) {
+ *scale = s / substFonts[i].mWidth;
+ }
+ }
+
+ // generate the CSS markup
+ return GString::format("font-family:{0:s}; font-weight:{1:s}; font-style:{2:s};",
+ fixedWidth ? "monospace"
+ : serif ? "serif"
+ : "sans-serif",
+ bold ? "bold" : "normal",
+ italic ? "italic" : "normal");
+}
diff --git a/xpdf/HTMLGen.h b/xpdf/HTMLGen.h
new file mode 100644
index 0000000..7271c67
--- /dev/null
+++ b/xpdf/HTMLGen.h
@@ -0,0 +1,63 @@
+//========================================================================
+//
+// HTMLGen.h
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef HTMLGEN_H
+#define HTMLGEN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString;
+class PDFDoc;
+class TextOutputDev;
+class TextFontInfo;
+class SplashOutputDev;
+
+//------------------------------------------------------------------------
+
+class HTMLGen {
+public:
+
+ HTMLGen(double backgroundResolutionA);
+ ~HTMLGen();
+
+ GBool isOk() { return ok; }
+
+ double getBackgroundResolution() { return backgroundResolution; }
+ void setBackgroundResolution(double backgroundResolutionA)
+ { backgroundResolution = backgroundResolutionA; }
+
+ GBool getDrawInvisibleText() { return drawInvisibleText; }
+ void setDrawInvisibleText(GBool drawInvisibleTextA)
+ { drawInvisibleText = drawInvisibleTextA; }
+
+ void startDoc(PDFDoc *docA);
+ int convertPage(int pg, const char *pngURL,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream);
+
+private:
+
+ GString *getFontDefn(TextFontInfo *font, double *scale);
+
+ double backgroundResolution;
+ GBool drawInvisibleText;
+
+ PDFDoc *doc;
+ TextOutputDev *textOut;
+ SplashOutputDev *splashOut;
+
+ GBool ok;
+};
+
+#endif
diff --git a/xpdf/ImageOutputDev.cc b/xpdf/ImageOutputDev.cc
index a5c9314..3f6b1a1 100644
--- a/xpdf/ImageOutputDev.cc
+++ b/xpdf/ImageOutputDev.cc
@@ -37,7 +37,8 @@ ImageOutputDev::~ImageOutputDev() {
gfree(fileRoot);
}
-void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -47,16 +48,16 @@ void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
FILE *f;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -68,8 +69,9 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -78,7 +80,7 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -92,8 +94,14 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar(), f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -104,14 +112,15 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
FILE *f;
ImageStream *imgStr;
Guchar *p;
GfxRGB rgb;
int x, y;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT &&
@@ -120,7 +129,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
!inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -132,8 +141,9 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -143,7 +153,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
colorMap->getBits() == 1) {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -157,8 +167,14 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar() ^ 0xff, f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -168,7 +184,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PPM header
- sprintf(fileName, "%s-%03d.ppm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -203,8 +219,36 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
}
}
}
+
+ imgStr->close();
delete imgStr;
fclose(f);
}
}
+
+void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ gFalse, interpolate);
+}
+
+void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap,
+ NULL, gFalse, interpolate);
+}
diff --git a/xpdf/ImageOutputDev.h b/xpdf/ImageOutputDev.h
index 5496225..2984686 100644
--- a/xpdf/ImageOutputDev.h
+++ b/xpdf/ImageOutputDev.h
@@ -62,7 +62,7 @@ public:
virtual GBool useDrawChar() { return gFalse; }
//----- path painting
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -71,10 +71,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
private:
diff --git a/xpdf/JArithmeticDecoder.cc b/xpdf/JArithmeticDecoder.cc
index 56ddc31..92db326 100644
--- a/xpdf/JArithmeticDecoder.cc
+++ b/xpdf/JArithmeticDecoder.cc
@@ -92,10 +92,18 @@ JArithmeticDecoder::JArithmeticDecoder() {
dataLen = 0;
limitStream = gFalse;
nBytesRead = 0;
+ readBuf = -1;
}
inline Guint JArithmeticDecoder::readByte() {
+ Guint x;
+
if (limitStream) {
+ if (readBuf >= 0) {
+ x = (Guint)readBuf;
+ readBuf = -1;
+ return x;
+ }
--dataLen;
if (dataLen < 0) {
return 0xff;
@@ -162,9 +170,15 @@ void JArithmeticDecoder::restart(int dataLenA) {
void JArithmeticDecoder::cleanup() {
if (limitStream) {
+ // This saves one extra byte of data from the end of packet i, to
+ // be used in packet i+1. It's not clear from the JPEG 2000 spec
+ // exactly how this should work, but this kludge does seem to fix
+ // decode of some problematic JPEG 2000 streams. It may actually
+ // be necessary to buffer an arbitrary number of bytes (not just
+ // one byte), but I haven't run into that case yet.
while (dataLen > 0) {
- buf0 = buf1;
- buf1 = readByte();
+ readBuf = -1;
+ readBuf = readByte();
}
}
}
diff --git a/xpdf/JArithmeticDecoder.h b/xpdf/JArithmeticDecoder.h
index 7217984..c0f773f 100644
--- a/xpdf/JArithmeticDecoder.h
+++ b/xpdf/JArithmeticDecoder.h
@@ -108,6 +108,7 @@ private:
Guint nBytesRead;
int dataLen;
GBool limitStream;
+ int readBuf;
};
#endif
diff --git a/xpdf/JBIG2Stream.cc b/xpdf/JBIG2Stream.cc
index eb2719a..8588931 100644
--- a/xpdf/JBIG2Stream.cc
+++ b/xpdf/JBIG2Stream.cc
@@ -623,11 +623,11 @@ Guint JBIG2MMRDecoder::get24Bits() {
}
void JBIG2MMRDecoder::skipTo(Guint length) {
- while (nBytesRead < length) {
- str->getChar();
- ++nBytesRead;
- ++byteCounter;
- }
+ int n;
+
+ n = str->discardChars(length - nBytesRead);
+ nBytesRead += n;
+ byteCounter += n;
}
//------------------------------------------------------------------------
@@ -1310,10 +1310,9 @@ void JBIG2Stream::readSegments() {
}
refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
nRefSegs = refFlags & 0x1fffffff;
- for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError1;
- }
+ i = (nRefSegs + 9) >> 3;
+ if (curStr->discardChars(i) != i) {
+ goto eofError1;
}
}
@@ -1436,10 +1435,8 @@ void JBIG2Stream::readSegments() {
break;
default:
error(errSyntaxError, getPos(), "Unknown segment type in JBIG2 stream");
- for (i = 0; i < segLength; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError2;
- }
+ if (curStr->discardChars(segLength) != segLength) {
+ goto eofError2;
}
break;
}
@@ -1460,12 +1457,7 @@ void JBIG2Stream::readSegments() {
gfree(refSegs);
break;
}
- while (byteCounter < segLength) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(segLength - byteCounter);
}
gfree(refSegs);
@@ -1502,9 +1494,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
Guint symHeight, symWidth, totalWidth, x, symID;
int dh, dw, refAggNum, refDX, refDY, bmSize;
GBool ex;
- int run, cnt, c;
+ int run, cnt;
Guint i, j, k;
- Guchar *p;
symWidths = NULL;
@@ -1814,14 +1805,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
if (bmSize == 0) {
collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
bmSize = symHeight * ((totalWidth + 7) >> 3);
- p = collBitmap->getDataPtr();
- for (k = 0; k < (Guint)bmSize; ++k) {
- if ((c = curStr->getChar()) == EOF) {
- break;
- }
- *p++ = (Guchar)c;
- ++byteCounter;
- }
+ byteCounter += curStr->getBlock((char *)collBitmap->getDataPtr(),
+ bmSize);
} else {
collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
0, gFalse, gFalse, NULL, NULL, NULL,
@@ -2781,7 +2766,6 @@ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
Guchar mask;
int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
-
bitmap = new JBIG2Bitmap(0, w, h);
bitmap->clearToZero();
@@ -2994,7 +2978,7 @@ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
ltpCX = 0x0e3; // 001 1100 01 1
break;
case 3:
- ltpCX = 0x18a; // 01100 0101 1
+ ltpCX = 0x18b; // 01100 0101 1
break;
}
}
@@ -3821,27 +3805,13 @@ void JBIG2Stream::readPageInfoSeg(Guint length) {
}
void JBIG2Stream::readEndOfStripeSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readProfilesSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
@@ -3909,15 +3879,8 @@ void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
}
void JBIG2Stream::readExtensionSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
diff --git a/xpdf/JPXStream.cc b/xpdf/JPXStream.cc
index 98dcfef..7e2bc6b 100644
--- a/xpdf/JPXStream.cc
+++ b/xpdf/JPXStream.cc
@@ -42,7 +42,7 @@
#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup
#define jpxContextSign 9 // 9 - 13: sign
-#define jpxContextMagRef 14 // 14 -16: magnitude refinement
+#define jpxContextMagRef 14 // 14 - 16: magnitude refinement
#define jpxContextRunLength 17 // cleanup: run length
#define jpxContextUniform 18 // cleanup: first signif coeff
@@ -152,9 +152,10 @@ static Guint signContext[5][5][2] = {
#define idwtKappa 1.230174104914001
#define idwtIKappa (1.0 / idwtKappa)
-// number of bits to the right of the decimal point for the fixed
-// point arithmetic used in the IDWT
-#define fracBits 16
+// sum of the sample size (number of bits) and the number of bits to
+// the right of the decimal point for the fixed point arithmetic used
+// in the IDWT
+#define fracBits 24
//------------------------------------------------------------------------
@@ -233,12 +234,25 @@ JPXStream::JPXStream(Stream *strA):
nComps = 0;
bpc = NULL;
width = height = 0;
+ reduction = 0;
haveCS = gFalse;
+
+ palette.bpc = NULL;
+ palette.c = NULL;
havePalette = gFalse;
+
+ compMap.comp = NULL;
+ compMap.type = NULL;
+ compMap.pComp = NULL;
haveCompMap = gFalse;
+
+ channelDefn.idx = NULL;
+ channelDefn.type = NULL;
+ channelDefn.assoc = NULL;
haveChannelDefn = gFalse;
img.tiles = NULL;
+
bitBuf = 0;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -252,13 +266,13 @@ JPXStream::~JPXStream() {
void JPXStream::reset() {
bufStr->reset();
- if (readBoxes()) {
- curY = img.yOffset;
- } else {
+ if (readBoxes() == jpxDecodeFatalError) {
// readBoxes reported an error, so we go immediately to EOF
- curY = img.ySize;
+ curY = img.ySizeR;
+ } else {
+ curY = img.yOffsetR;
}
- curX = img.xOffset;
+ curX = img.xOffsetR;
curComp = 0;
readBufLen = 0;
}
@@ -387,23 +401,25 @@ int JPXStream::lookChar() {
void JPXStream::fillReadBuf() {
JPXTileComp *tileComp;
Guint tileIdx, tx, ty;
- int pix, pixBits;
+ int pix, pixBits, k;
+ GBool eol;
do {
- if (curY >= img.ySize) {
+ if (curY >= img.ySizeR) {
return;
}
- tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
- + (curX - img.xTileOffset) / img.xTileSize;
+ tileIdx = ((curY - img.yTileOffsetR) / img.yTileSizeR) * img.nXTiles
+ + (curX - img.xTileOffsetR) / img.xTileSizeR;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
tileComp = &img.tiles[tileIdx].tileComps[curComp];
#else
tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
#endif
- tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
- ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
- pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
+ tx = jpxCeilDiv((curX - img.xTileOffsetR) % img.xTileSizeR, tileComp->hSep);
+ ty = jpxCeilDiv((curY - img.yTileOffsetR) % img.yTileSizeR, tileComp->vSep);
+ pix = (int)tileComp->data[ty * tileComp->w + tx];
pixBits = tileComp->prec;
+ eol = gFalse;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
if (++curComp == img.nComps) {
#else
@@ -418,13 +434,10 @@ void JPXStream::fillReadBuf() {
if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
#endif
curComp = 0;
- if (++curX == img.xSize) {
- curX = img.xOffset;
+ if (++curX == img.xSizeR) {
+ curX = img.xOffsetR;
++curY;
- if (pixBits < 8) {
- pix <<= 8 - pixBits;
- pixBits = 8;
- }
+ eol = gTrue;
}
}
if (pixBits == 8) {
@@ -433,6 +446,10 @@ void JPXStream::fillReadBuf() {
readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
}
readBufLen += pixBits;
+ if (eol && (k = readBufLen & 7)) {
+ readBuf <<= 8 - k;
+ readBufLen += 8 - k;
+ }
} while (readBufLen < 8);
}
@@ -447,7 +464,7 @@ GBool JPXStream::isBinary(GBool last) {
void JPXStream::getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
Guint boxType, boxLen, dataLen, csEnum;
- Guint bpc1, dummy, i;
+ Guint bpc1, dummy;
int csMeth, csPrec, csPrec1, dummy2;
StreamColorSpaceMode csMode1;
GBool haveBPC, haveCSMode;
@@ -498,13 +515,13 @@ void JPXStream::getImageParams(int *bitsPerComponent,
csPrec = csPrec1;
haveCSMode = gTrue;
}
- for (i = 0; i < dataLen - 7; ++i) {
- bufStr->getChar();
+ if (dataLen > 7) {
+ bufStr->discardChars(dataLen - 7);
}
}
} else {
- for (i = 0; i < dataLen - 3; ++i) {
- bufStr->getChar();
+ if (dataLen > 3) {
+ bufStr->discardChars(dataLen - 3);
}
}
}
@@ -516,9 +533,7 @@ void JPXStream::getImageParams(int *bitsPerComponent,
break;
} else {
cover(4);
- for (i = 0; i < dataLen; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(dataLen);
}
}
}
@@ -529,7 +544,7 @@ void JPXStream::getImageParams(int *bitsPerComponent,
void JPXStream::getImageParams2(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
int segType;
- Guint segLen, nComps1, bpc1, dummy, i;
+ Guint segLen, nComps1, bpc1, dummy;
while (readMarkerHdr(&segType, &segLen)) {
if (segType == 0x51) { // SIZ - image and tile size
@@ -559,15 +574,14 @@ void JPXStream::getImageParams2(int *bitsPerComponent,
} else {
cover(6);
if (segLen > 2) {
- for (i = 0; i < segLen - 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(segLen - 2);
}
}
}
}
-GBool JPXStream::readBoxes() {
+JPXDecodeResult JPXStream::readBoxes() {
+ JPXDecodeResult result;
Guint boxType, boxLen, dataLen;
Guint bpc1, compression, unknownColorspace, ipr;
Guint i, j;
@@ -581,8 +595,8 @@ GBool JPXStream::readBoxes() {
cover(7);
error(errSyntaxWarning, getPos(),
"Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
- if (!readCodestream(0)) {
- return gFalse;
+ if ((result = readCodestream(0)) == jpxDecodeFatalError) {
+ return result;
}
nComps = img.nComps;
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
@@ -591,7 +605,7 @@ GBool JPXStream::readBoxes() {
}
width = img.xSize - img.xOffset;
height = img.ySize - img.yOffset;
- return gTrue;
+ return result;
}
while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
@@ -614,12 +628,12 @@ GBool JPXStream::readBoxes() {
!readUByte(&unknownColorspace) ||
!readUByte(&ipr)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (compression != 7) {
error(errSyntaxError, getPos(),
"Unknown compression type in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
for (i = 0; i < nComps; ++i) {
@@ -632,24 +646,24 @@ GBool JPXStream::readBoxes() {
if (!haveImgHdr) {
error(errSyntaxError, getPos(),
"Found bits per component box before image header box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (dataLen != nComps) {
error(errSyntaxError, getPos(),
"Invalid bits per component box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < nComps; ++i) {
if (!readUByte(&bpc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
break;
case 0x636F6C72: // color specification
cover(11);
if (!readColorSpecBox(dataLen)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
break;
case 0x70636c72: // palette
@@ -657,15 +671,16 @@ GBool JPXStream::readBoxes() {
if (!readUWord(&palette.nEntries) ||
!readUByte(&palette.nComps)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ havePalette = gTrue;
palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
palette.c =
(int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
for (i = 0; i < palette.nComps; ++i) {
if (!readUByte(&palette.bpc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
++palette.bpc[i];
}
@@ -675,14 +690,14 @@ GBool JPXStream::readBoxes() {
(palette.bpc[j] & 0x80) ? gTrue : gFalse,
&palette.c[i * palette.nComps + j])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
}
- havePalette = gTrue;
break;
case 0x636d6170: // component mapping
cover(13);
+ haveCompMap = gTrue;
compMap.nChannels = dataLen / 4;
compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
@@ -692,17 +707,17 @@ GBool JPXStream::readBoxes() {
!readUByte(&compMap.type[i]) ||
!readUByte(&compMap.pComp[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveCompMap = gTrue;
break;
case 0x63646566: // channel definition
cover(14);
if (!readUWord(&channelDefn.nChannels)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ haveChannelDefn = gTrue;
channelDefn.idx =
(Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
channelDefn.type =
@@ -714,10 +729,9 @@ GBool JPXStream::readBoxes() {
!readUWord(&channelDefn.type[i]) ||
!readUWord(&channelDefn.assoc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveChannelDefn = gTrue;
break;
case 0x6A703263: // contiguous codestream
cover(15);
@@ -729,28 +743,25 @@ GBool JPXStream::readBoxes() {
error(errSyntaxError, getPos(),
"JPX stream has no supported color spec");
}
- if (!readCodestream(dataLen)) {
- return gFalse;
+ if ((result = readCodestream(dataLen)) != jpxDecodeOk) {
+ return result;
}
break;
default:
cover(16);
- for (i = 0; i < dataLen; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
- }
+ if (bufStr->discardChars(dataLen) != dataLen) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
}
break;
}
}
- return gTrue;
+ return jpxDecodeOk;
}
GBool JPXStream::readColorSpecBox(Guint dataLen) {
JPXColorSpec newCS;
Guint csApprox, csEnum;
- Guint i;
GBool ok;
ok = gFalse;
@@ -852,10 +863,9 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
case 3: // any ICC profile (JPX)
case 4: // vendor color (JPX)
cover(18);
- for (i = 0; i < dataLen - 3; ++i) {
- if (bufStr->getChar() == EOF) {
- goto err;
- }
+ if (dataLen > 3 &&
+ bufStr->discardChars(dataLen - 3) != dataLen - 3) {
+ goto err;
}
break;
}
@@ -872,11 +882,11 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
return gFalse;
}
-GBool JPXStream::readCodestream(Guint len) {
+JPXDecodeResult JPXStream::readCodestream(Guint len) {
JPXTile *tile;
JPXTileComp *tileComp;
int segType;
- GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+ GBool haveSIZ, haveCOD, haveQCD, haveSOT, ok;
Guint precinctSize, style;
Guint segLen, capabilities, comp, i, j, r;
@@ -885,7 +895,7 @@ GBool JPXStream::readCodestream(Guint len) {
do {
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
switch (segType) {
case 0x4f: // SOC - start of codestream
@@ -897,7 +907,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (haveSIZ) {
error(errSyntaxError, getPos(),
"Duplicate SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUWord(&capabilities) ||
!readULong(&img.xSize) ||
@@ -910,12 +920,12 @@ GBool JPXStream::readCodestream(Guint len) {
!readULong(&img.yTileOffset) ||
!readUWord(&img.nComps)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (haveImgHdr && img.nComps != nComps) {
error(errSyntaxError, getPos(),
"Different number of components in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.xSize == 0 || img.ySize == 0 ||
img.xOffset >= img.xSize || img.yOffset >= img.ySize ||
@@ -925,8 +935,16 @@ GBool JPXStream::readCodestream(Guint len) {
img.xTileSize + img.xTileOffset <= img.xOffset ||
img.yTileSize + img.yTileOffset <= img.yOffset) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
- }
+ return jpxDecodeFatalError;
+ }
+ img.xSizeR = img.xSize >> reduction;
+ img.ySizeR = img.ySize >> reduction;
+ img.xOffsetR = img.xOffset >> reduction;
+ img.yOffsetR = img.yOffset >> reduction;
+ img.xTileSizeR = img.xTileSize >> reduction;
+ img.yTileSizeR = img.yTileSize >> reduction;
+ img.xTileOffsetR = img.xTileOffset >> reduction;
+ img.yTileOffsetR = img.yTileOffset >> reduction;
img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
/ img.xTileSize;
img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
@@ -936,12 +954,16 @@ GBool JPXStream::readCodestream(Guint len) {
img.nXTiles >= INT_MAX / img.nYTiles) {
error(errSyntaxError, getPos(),
"Bad tile count in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,
sizeof(JPXTile));
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].init = gFalse;
+ img.tiles[i].nextTilePart = 0;
+ img.tiles[i].tileComps = NULL;
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
sizeof(JPXTileComp));
for (comp = 0; comp < img.nComps; ++comp) {
@@ -956,12 +978,12 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[comp].hSep) ||
!readUByte(&img.tiles[0].tileComps[comp].vSep)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].hSep == 0 ||
img.tiles[0].tileComps[comp].vSep == 0) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].sgned =
(img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
@@ -978,7 +1000,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX COD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].style) ||
!readUByte(&img.tiles[0].progOrder) ||
@@ -990,14 +1012,21 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[0].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[0].nDecompLevels > 32 ||
img.tiles[0].tileComps[0].codeBlockW > 8 ||
img.tiles[0].tileComps[0].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
+#if 1 //~ progression orders 2-4 are unimplemented
+ if (img.tiles[0].progOrder >= 2) {
+ error(errUnimplemented, -1,
+ "JPX progression order {0:d} is unimplemented",
+ img.tiles[0].progOrder);
+ }
+#endif
img.tiles[0].tileComps[0].codeBlockW += 2;
img.tiles[0].tileComps[0].codeBlockH += 2;
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
@@ -1035,7 +1064,7 @@ GBool JPXStream::readCodestream(Guint len) {
cover(91);
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1065,7 +1094,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveCOD) {
error(errSyntaxError, getPos(),
"JPX COC marker segment before COD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
@@ -1077,13 +1106,13 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[comp].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].nDecompLevels > 32 ||
img.tiles[0].tileComps[comp].codeBlockW > 8 ||
img.tiles[0].tileComps[comp].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].style =
(img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
@@ -1117,7 +1146,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (img.tiles[0].tileComps[comp].style & 0x01) {
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1142,16 +1171,16 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX QCD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
if (segLen <= 3) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
img.tiles[0].tileComps[0].quantSteps =
@@ -1161,7 +1190,7 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
@@ -1172,12 +1201,12 @@ GBool JPXStream::readCodestream(Guint len) {
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
if (segLen < 5) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
img.tiles[0].tileComps[0].quantSteps =
@@ -1187,12 +1216,12 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1219,19 +1248,19 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveQCD) {
error(errSyntaxError, getPos(),
"JPX QCC marker segment before QCD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
comp >= img.nComps ||
!readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
if (segLen <= (img.nComps > 256 ? 5U : 4U)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
segLen - (img.nComps > 256 ? 5 : 4);
@@ -1242,7 +1271,7 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
@@ -1253,12 +1282,12 @@ GBool JPXStream::readCodestream(Guint len) {
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
(segLen - (img.nComps > 256 ? 5 : 4)) / 2;
@@ -1269,12 +1298,12 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].tileComps[comp].quantStyle =
@@ -1295,11 +1324,10 @@ GBool JPXStream::readCodestream(Guint len) {
cover(25);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return jpxDecodeFatalError;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1308,7 +1336,7 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&compInfo[comp].defROI.style) ||
!readUByte(&compInfo[comp].defROI.shift)) {
error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
#endif
break;
@@ -1316,11 +1344,10 @@ GBool JPXStream::readCodestream(Guint len) {
cover(26);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return jpxDecodeFatalError;
}
#else
nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1335,7 +1362,7 @@ GBool JPXStream::readCodestream(Guint len) {
!(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
!readUByte(&progs[i].progOrder)) {
error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
#endif
@@ -1344,52 +1371,47 @@ GBool JPXStream::readCodestream(Guint len) {
cover(27);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPM segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
+ return jpxDecodeFatalError;
}
#endif
break;
case 0x55: // TLM - tile-part lengths
// skipped
cover(28);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x57: // PLM - packet length, main header
// skipped
cover(29);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x63: // CRG - component registration
// skipped
cover(30);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x64: // COM - comment
// skipped
cover(31);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x90: // SOT - start of tile
@@ -1400,10 +1422,8 @@ GBool JPXStream::readCodestream(Guint len) {
cover(33);
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX stream", segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1412,27 +1432,30 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"Missing SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveCOD) {
error(errSyntaxError, getPos(),
"Missing COD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveQCD) {
error(errSyntaxError, getPos(),
"Missing QCD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
//----- read the tile-parts
+ ok = gTrue;
while (1) {
if (!readTilePart()) {
- return gFalse;
+ ok = gFalse;
+ break;
}
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ ok = gFalse;
+ break;
}
if (segType != 0x90) { // SOT - start of tile
break;
@@ -1441,7 +1464,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (segType != 0xd9) { // EOC - end of codestream
error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream");
- return gFalse;
+ ok = gFalse;
}
//----- finish decoding the image
@@ -1449,20 +1472,20 @@ GBool JPXStream::readCodestream(Guint len) {
tile = &img.tiles[i];
if (!tile->init) {
error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (comp = 0; comp < img.nComps; ++comp) {
tileComp = &tile->tileComps[comp];
inverseTransform(tileComp);
}
if (!inverseMultiCompAndDC(tile)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
}
//~ can free memory below tileComps here, and also tileComp.buf
- return gTrue;
+ return ok ? jpxDecodeOk : jpxDecodeNonFatalError;
}
GBool JPXStream::readTilePart() {
@@ -1490,11 +1513,16 @@ GBool JPXStream::readTilePart() {
return gFalse;
}
- if ((tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
- tileIdx >= img.nXTiles * img.nYTiles) {
- error(errSyntaxError, getPos(), "Weird tile index in JPX stream");
+ // check tileIdx and tilePartIdx
+ // (this ignores nTileParts, because some encoders get it wrong)
+ if (tileIdx >= img.nXTiles * img.nYTiles ||
+ tilePartIdx != img.tiles[tileIdx].nextTilePart ||
+ (tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
+ (tilePartIdx == 0 && img.tiles[tileIdx].init)) {
+ error(errSyntaxError, getPos(), "Weird tile-part header in JPX stream");
return gFalse;
}
+ ++img.tiles[tileIdx].nextTilePart;
tilePartToEOC = tilePartLen == 0;
tilePartLen -= 12; // subtract size of SOT segment
@@ -1527,6 +1555,13 @@ 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
img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1760,11 +1795,10 @@ GBool JPXStream::readTilePart() {
cover(38);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "Got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1781,11 +1815,10 @@ GBool JPXStream::readTilePart() {
cover(39);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "Got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return gFalse;
}
#else
nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1809,31 +1842,28 @@ GBool JPXStream::readTilePart() {
cover(40);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPT segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
+ return gFalse;
}
#endif
case 0x58: // PLT - packet length, tile-part header
// skipped
cover(41);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
+ return gFalse;
}
break;
case 0x64: // COM - comment
// skipped
cover(42);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return gFalse;
}
break;
case 0x93: // SOD - start of data
@@ -1845,10 +1875,8 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX tile-part stream",
segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1886,12 +1914,13 @@ 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->w = tileComp->x1 - tileComp->x0;
tileComp->cbW = 1 << tileComp->codeBlockW;
tileComp->cbH = 1 << tileComp->codeBlockH;
- tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
- (tileComp->y1 - tileComp->y0),
- sizeof(int));
+ tileComp->w = (tileComp->x1 - tileComp->x0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->h = (tileComp->y1 - tileComp->y0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->data = (int *)gmallocn(tileComp->w * tileComp->h, sizeof(int));
if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
n = tileComp->x1 - tileComp->x0;
} else {
@@ -1927,6 +1956,9 @@ GBool JPXStream::readTilePart() {
}
resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
for (pre = 0; pre < 1; ++pre) {
+ resLevel->precincts[pre].subbands = NULL;
+ }
+ for (pre = 0; pre < 1; ++pre) {
precinct = &resLevel->precincts[pre];
precinct->x0 = resLevel->x0;
precinct->y0 = resLevel->y0;
@@ -1936,6 +1968,11 @@ GBool JPXStream::readTilePart() {
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];
@@ -1973,6 +2010,12 @@ GBool JPXStream::readTilePart() {
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
@@ -2013,21 +2056,25 @@ GBool JPXStream::readTilePart() {
cb->nZeroBitPlanes = 0;
cb->dataLenSize = 1;
cb->dataLen = (Guint *)gmalloc(sizeof(Guint));
- 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;
+ 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;
+ }
}
+ memset(cb->touched, 0,
+ (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
+ } else {
+ cb->coeffs = NULL;
+ cb->touched = NULL;
+ cb->len = 0;
}
- memset(cb->touched, 0,
- (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
- cb->arithDecoder = NULL;
- cb->stats = NULL;
++cb;
}
}
@@ -2381,7 +2428,21 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
Guint horiz, vert, diag, all, cx, xorBit;
int horizSign, vertSign, bit;
int segSym;
- Guint i, x, y0, y1;
+ Guint n, i, x, y0, y1;
+
+ if (res > tileComp->nDecompLevels - reduction) {
+ // skip the codeblock data
+ if (tileComp->codeBlockStyle & 0x04) {
+ n = 0;
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ n += cb->dataLen[i];
+ }
+ } else {
+ n = cb->dataLen[0];
+ }
+ bufStr->discardChars(n);
+ return gTrue;
+ }
if (cb->arithDecoder) {
cover(63);
@@ -2735,7 +2796,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
}
if (tileComp->transform == 0) {
cover(71);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// do fixed point adjustment and dequantization on (NL)LL
@@ -2766,7 +2827,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
cover(96);
if (tileComp->transform == 0) {
cover(97);
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(98);
@@ -2782,7 +2843,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
//----- IDWT for each level
- for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+ for (r = 1; r <= tileComp->nDecompLevels - reduction; ++r) {
resLevel = &tileComp->resLevels[r];
// (n)LL is already in the upper-left corner of the
@@ -2837,7 +2898,7 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
}
if (tileComp->transform == 0) {
cover(103);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// fixed point adjustment and dequantization
@@ -2868,7 +2929,7 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
if (qStyle == 0) {
cover(76);
if (tileComp->transform == 0) {
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(77);
@@ -3103,8 +3164,8 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
if (tile->tileComps[0].transform == 0) {
cover(87);
j = 0;
- for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
- for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3120,8 +3181,8 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
} else {
cover(88);
j = 0;
- for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
- for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3144,12 +3205,12 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
minVal = -(1 << (tileComp->prec - 1));
maxVal = (1 << (tileComp->prec - 1)) - 1;
dataPtr = tileComp->data;
- for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
- for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(109);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
if (coeff < minVal) {
cover(110);
@@ -3168,12 +3229,12 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
maxVal = (1 << tileComp->prec) - 1;
zeroVal = 1 << (tileComp->prec - 1);
dataPtr = tileComp->data;
- for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
- for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(112);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
coeff += zeroVal;
if (coeff < 0) {
@@ -3339,16 +3400,12 @@ GBool JPXStream::readBits(int nBits, Guint *x) {
}
void JPXStream::skipSOP() {
- int i;
-
// SOP occurs at the start of the packet header, so we don't need to
// worry about bit-stuff prior to it
if (byteCount >= 6 &&
bufStr->lookChar(0) == 0xff &&
bufStr->lookChar(1) == 0x91) {
- for (i = 0; i < 6; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(6);
byteCount -= 6;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -3356,15 +3413,13 @@ void JPXStream::skipSOP() {
}
void JPXStream::skipEPH() {
- int i, k;
+ int k;
k = bitBufSkip ? 1 : 0;
if (byteCount >= (Guint)(k + 2) &&
bufStr->lookChar(k) == 0xff &&
bufStr->lookChar(k + 1) == 0x92) {
- for (i = 0; i < k + 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(k + 2);
byteCount -= k + 2;
bitBufLen = 0;
bitBufSkip = gFalse;
diff --git a/xpdf/JPXStream.h b/xpdf/JPXStream.h
index 2c46ca0..d00a55e 100644
--- a/xpdf/JPXStream.h
+++ b/xpdf/JPXStream.h
@@ -199,7 +199,7 @@ struct JPXTileComp {
//----- computed
Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
- Guint w; // x1 - x0
+ Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction
Guint cbW; // code-block width
Guint cbH; // code-block height
@@ -234,6 +234,9 @@ struct JPXTile {
Guint precinct; // precinct
Guint layer; // layer
+ //----- tile part info
+ Guint nextTilePart; // next expected tile-part
+
//----- children
JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps)
};
@@ -247,6 +250,11 @@ struct JPXImage {
Guint xTileSize, yTileSize; // size of tiles
Guint xTileOffset, // offset of first tile
yTileOffset;
+ Guint xSizeR, ySizeR; // size of reference grid >> reduction
+ Guint xOffsetR, yOffsetR; // image offset >> reduction
+ Guint xTileSizeR, yTileSizeR; // size of tiles >> reduction
+ Guint xTileOffsetR, // offset of first tile >> reduction
+ yTileOffsetR;
Guint nComps; // number of components
//----- computed
@@ -259,6 +267,14 @@ struct JPXImage {
//------------------------------------------------------------------------
+enum JPXDecodeResult {
+ jpxDecodeOk,
+ jpxDecodeNonFatalError,
+ jpxDecodeFatalError
+};
+
+//------------------------------------------------------------------------
+
class JPXStream: public FilterStream {
public:
@@ -273,14 +289,15 @@ public:
virtual GBool isBinary(GBool last = gTrue);
virtual void getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode);
+ void reduceResolution(int reductionA) { reduction = reductionA; }
private:
void fillReadBuf();
void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
- GBool readBoxes();
+ JPXDecodeResult readBoxes();
GBool readColorSpecBox(Guint dataLen);
- GBool readCodestream(Guint len);
+ JPXDecodeResult readCodestream(Guint len);
GBool readTilePart();
GBool readTilePartData(Guint tileIdx,
Guint tilePartLen, GBool tilePartToEOC);
@@ -314,6 +331,7 @@ private:
Guint nComps; // number of components
Guint *bpc; // bits per component, for each component
Guint width, height; // image size
+ int reduction; // log2(reduction in resolution)
GBool haveImgHdr; // set if a JP2/JPX image header has been
// found
JPXColorSpec cs; // color specification
diff --git a/xpdf/Lexer.h b/xpdf/Lexer.h
index f6ad9ce..3f46689 100644
--- a/xpdf/Lexer.h
+++ b/xpdf/Lexer.h
@@ -53,13 +53,12 @@ public:
Stream *getStream()
{ return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
- // Get current position in file. This is only used for error
- // messages, so it returns an int instead of a Guint.
- int getPos()
- { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }
+ // Get current position in file.
+ GFileOffset getPos()
+ { return curStr.isNone() ? -1 : curStr.streamGetPos(); }
// Set position in file.
- void setPos(Guint pos, int dir = 0)
+ void setPos(GFileOffset pos, int dir = 0)
{ if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
// Returns true if <c> is a whitespace character.
diff --git a/xpdf/Link.cc b/xpdf/Link.cc
index 56a2963..86a3c41 100644
--- a/xpdf/Link.cc
+++ b/xpdf/Link.cc
@@ -39,7 +39,7 @@ LinkAction *LinkAction::parseDest(Object *obj) {
LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
LinkAction *action;
- Object obj2, obj3, obj4;
+ Object obj2, obj3, obj4, obj5;
if (!obj->isDict()) {
error(errSyntaxWarning, -1, "Bad annotation action");
@@ -86,6 +86,30 @@ LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
obj3.free();
obj4.free();
+ // JavaScript action
+ } else if (obj2.isName("JavaScript")) {
+ obj->dictLookup("JS", &obj3);
+ action = new LinkJavaScript(&obj3);
+ obj3.free();
+
+ // SubmitForm action
+ } else if (obj2.isName("SubmitForm")) {
+ obj->dictLookup("F", &obj3);
+ obj->dictLookup("Fields", &obj4);
+ obj->dictLookup("Flags", &obj5);
+ action = new LinkSubmitForm(&obj3, &obj4, &obj5);
+ obj3.free();
+ obj4.free();
+ obj5.free();
+
+ // Hide action
+ } else if (obj2.isName("Hide")) {
+ obj->dictLookupNF("T", &obj3);
+ obj->dictLookup("H", &obj4);
+ action = new LinkHide(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
// unknown action
} else if (obj2.isName()) {
action = new LinkUnknown(obj2.getName());
@@ -117,7 +141,7 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
// dictionary
} else if (fileSpecObj->isDict()) {
-#ifdef WIN32
+#ifdef _WIN32
if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
#else
if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
@@ -139,7 +163,7 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
// system-dependent path manipulation
if (name) {
-#ifdef WIN32
+#ifdef _WIN32
int i, j;
// "//...." --> "\...."
@@ -517,7 +541,7 @@ LinkLaunch::LinkLaunch(Object *actionObj) {
fileName = getFileSpecName(&obj1);
} else {
obj1.free();
-#ifdef WIN32
+#ifdef _WIN32
if (actionObj->dictLookup("Win", &obj1)->isDict()) {
obj1.dictLookup("F", &obj2);
fileName = getFileSpecName(&obj2);
@@ -644,6 +668,98 @@ LinkMovie::~LinkMovie() {
}
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+ char buf[4096];
+ int n;
+
+ if (jsObj->isString()) {
+ js = jsObj->getString()->copy();
+ } else if (jsObj->isStream()) {
+ js = new GString();
+ jsObj->streamReset();
+ while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ js->append(buf, n);
+ }
+ jsObj->streamClose();
+ } else {
+ error(errSyntaxError, -1, "JavaScript action JS key is wrong type");
+ js = NULL;
+ }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+ if (js) {
+ delete js;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj,
+ Object *flagsObj) {
+ if (urlObj->isString()) {
+ url = urlObj->getString()->copy();
+ } else {
+ error(errSyntaxError, -1, "SubmitForm action URL is wrong type");
+ url = NULL;
+ }
+
+ if (fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ if (!fieldsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type");
+ }
+ fields.initNull();
+ }
+
+ if (flagsObj->isInt()) {
+ flags = flagsObj->getInt();
+ } else {
+ if (!flagsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type");
+ }
+ flags = 0;
+ }
+}
+
+LinkSubmitForm::~LinkSubmitForm() {
+ if (url) {
+ delete url;
+ }
+ fields.free();
+}
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) {
+ if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ error(errSyntaxError, -1, "Hide action T value is wrong type");
+ fields.initNull();
+ }
+
+ if (hideFlagObj->isBool()) {
+ hideFlag = hideFlagObj->getBool();
+ } else {
+ error(errSyntaxError, -1, "Hide action H value is wrong type");
+ hideFlag = gFalse;
+ }
+}
+
+LinkHide::~LinkHide() {
+ fields.free();
+}
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
@@ -745,7 +861,7 @@ Link::~Link() {
Links::Links(Object *annots, GString *baseURI) {
Link *link;
- Object obj1, obj2;
+ Object obj1, obj2, obj3;
int size;
int i;
@@ -756,7 +872,10 @@ Links::Links(Object *annots, GString *baseURI) {
if (annots->isArray()) {
for (i = 0; i < annots->arrayGetLength(); ++i) {
if (annots->arrayGet(i, &obj1)->isDict()) {
- if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+ obj1.dictLookup("Subtype", &obj2);
+ obj1.dictLookup("FT", &obj3);
+ if (obj2.isName("Link") ||
+ (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) {
link = new Link(obj1.getDict(), baseURI);
if (link->isOk()) {
if (numLinks >= size) {
@@ -768,6 +887,7 @@ Links::Links(Object *annots, GString *baseURI) {
delete link;
}
}
+ obj3.free();
obj2.free();
}
obj1.free();
diff --git a/xpdf/Link.h b/xpdf/Link.h
index 55fbfa1..480496a 100644
--- a/xpdf/Link.h
+++ b/xpdf/Link.h
@@ -32,6 +32,9 @@ enum LinkActionKind {
actionURI, // URI
actionNamed, // named action
actionMovie, // movie action
+ actionJavaScript, // run JavaScript
+ actionSubmitForm, // submit form
+ actionHide, // hide annotation
actionUnknown // anything else
};
@@ -244,8 +247,10 @@ public:
virtual ~LinkNamed();
+ // Was the LinkNamed created successfully?
virtual GBool isOk() { return name != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionNamed; }
GString *getName() { return name; }
@@ -265,8 +270,10 @@ public:
virtual ~LinkMovie();
+ // Was the LinkMovie created successfully?
virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionMovie; }
GBool hasAnnotRef() { return annotRef.num >= 0; }
Ref *getAnnotRef() { return &annotRef; }
@@ -279,6 +286,81 @@ private:
};
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+ LinkJavaScript(Object *jsObj);
+
+ virtual ~LinkJavaScript();
+
+ // Was the LinkJavaScript created successfully?
+ virtual GBool isOk() { return js != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionJavaScript; }
+ GString *getJS() { return js; }
+
+private:
+
+ GString *js;
+};
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+class LinkSubmitForm: public LinkAction {
+public:
+
+ LinkSubmitForm(Object *urlObj, Object *fieldsObj, Object *flagsObj);
+
+ virtual ~LinkSubmitForm();
+
+ // Was the LinkSubmitForm created successfully?
+ virtual GBool isOk() { return url != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionSubmitForm; }
+ GString *getURL() { return url; }
+ Object *getFields() { return &fields; }
+ int getFlags() { return flags; }
+
+private:
+
+ GString *url;
+ Object fields;
+ int flags;
+};
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+class LinkHide: public LinkAction {
+public:
+
+ LinkHide(Object *fieldsObj, Object *hideFlagObj);
+
+ virtual ~LinkHide();
+
+ // Was the LinkHide created successfully?
+ virtual GBool isOk() { return !fields.isNull(); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionHide; }
+ Object *getFields() { return &fields; }
+ GBool getHideFlag() { return hideFlag; }
+
+private:
+
+ Object fields;
+ GBool hideFlag;
+};
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
diff --git a/xpdf/Makefile.in b/xpdf/Makefile.in
index 5b48eff..de3e676 100644
--- a/xpdf/Makefile.in
+++ b/xpdf/Makefile.in
@@ -19,18 +19,19 @@ FOFILIBDIR = ../fofi
SPLASHSRCDIR = $(srcdir)/../splash
SPLASHLIBDIR = ../splash
-CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @t1_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @libpng_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@ @EXTRA_CFLAGS@
LDFLAGS = @LDFLAGS@
-T1LIBS = @t1_LIBS@
-FTLIBS = @freetype2_LIBS@
+FTLIBS = @freetype2_LIBS@ -lz
XLIBS = @Sgm_LIBS@ @Xm_LIBS@ @Xt_LIBS@ @Xp_LIBS@ @Xext_LIBS@ @Xpm_LIBS@ @X_PRE_LIBS@ @X_LIBS@ -lX11 @X_EXTRA_LIBS@
+PNGLIBS = @libpng_LIBS@
+
SPLASHLIBS = -L$(SPLASHLIBDIR) -lsplash
-OTHERLIBS = @LIBS@ @libpaper_LIBS@ \
+OTHERLIBS = @LIBS@ @libpaper_LIBS@ @EXTRA_LIBS@ \
-L$(FOFILIBDIR) -lfofi \
-L$(GOOLIBDIR) -lGoo
@@ -49,6 +50,7 @@ EXE = @EXE@
#------------------------------------------------------------------------
CXX_SRC = \
+ $(srcdir)/AcroForm.cc \
$(srcdir)/Annot.cc \
$(srcdir)/Array.cc \
$(srcdir)/BuiltinFont.cc \
@@ -61,11 +63,13 @@ CXX_SRC = \
$(srcdir)/Dict.cc \
$(srcdir)/Error.cc \
$(srcdir)/FontEncodingTables.cc \
+ $(srcdir)/Form.cc \
$(srcdir)/Function.cc \
$(srcdir)/Gfx.cc \
$(srcdir)/GfxFont.cc \
$(srcdir)/GfxState.cc \
$(srcdir)/GlobalParams.cc \
+ $(srcdir)/HTMLGen.cc \
$(srcdir)/ImageOutputDev.cc \
$(srcdir)/JArithmeticDecoder.cc \
$(srcdir)/JBIG2Stream.cc \
@@ -89,44 +93,94 @@ CXX_SRC = \
$(srcdir)/SplashOutputDev.cc \
$(srcdir)/Stream.cc \
$(srcdir)/TextOutputDev.cc \
+ $(srcdir)/TextString.cc \
$(srcdir)/UnicodeMap.cc \
$(srcdir)/UnicodeTypeTable.cc \
+ $(srcdir)/XFAForm.cc \
$(srcdir)/XPDFApp.cc \
$(srcdir)/XPDFCore.cc \
$(srcdir)/XPDFTree.cc \
$(srcdir)/XPDFViewer.cc \
$(srcdir)/XpdfPluginAPI.cc \
$(srcdir)/XRef.cc \
+ $(srcdir)/Zoox.cc \
$(srcdir)/pdftops.cc \
$(srcdir)/pdftotext.cc \
+ $(srcdir)/pdftohtml.cc \
$(srcdir)/pdfinfo.cc \
$(srcdir)/pdffonts.cc \
$(srcdir)/pdfdetach.cc \
$(srcdir)/pdftoppm.cc \
+ $(srcdir)/pdftopng.cc \
$(srcdir)/pdfimages.cc \
$(srcdir)/xpdf.cc
#------------------------------------------------------------------------
-all: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) \
- pdffonts$(EXE) pdfdetach$(EXE) pdftoppm$(EXE) pdfimages$(EXE)
+all: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdftohtml$(EXE) \
+ pdfinfo$(EXE) pdffonts$(EXE) pdfdetach$(EXE) pdftoppm$(EXE) \
+ pdftopng$(EXE) pdfimages$(EXE)
-all-no-x: pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) pdffonts$(EXE) \
- pdfdetach$(EXE) pdfimages$(EXE)
+all-no-x: pdftops$(EXE) pdftotext$(EXE) pdftohtml$(EXE) pdfinfo$(EXE) \
+ pdffonts$(EXE) pdfdetach$(EXE) pdfimages$(EXE)
#------------------------------------------------------------------------
-XPDF_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o Catalog.o \
- CharCodeToUnicode.o CMap.o CoreOutputDev.o Decrypt.o Dict.o \
- Error.o FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFCore.o \
- PDFDoc.o PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o \
- PSTokenizer.o SecurityHandler.o SplashOutputDev.o Stream.o \
- TextOutputDev.o UnicodeMap.o UnicodeTypeTable.o XPDFApp.o \
- XPDFCore.o XPDFTree.o XPDFViewer.o XpdfPluginAPI.o XRef.o xpdf.o
-XPDF_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+XPDF_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ CoreOutputDev.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFCore.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PreScanOutputDev.o \
+ PSOutputDev.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XPDFApp.o \
+ XPDFCore.o \
+ XPDFTree.o \
+ XPDFViewer.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ xpdf.o
+XPDF_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(XLIBS) $(OTHERLIBS) -lm
xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -134,16 +188,53 @@ xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFTOPS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o OptionalContent.o \
- Outline.o Object.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o PSTokenizer.o \
- SecurityHandler.o SplashOutputDev.o Stream.o UnicodeMap.o \
- XpdfPluginAPI.o XRef.o pdftops.o
-PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+PDFTOPS_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ OptionalContent.o \
+ Outline.o \
+ Object.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PreScanOutputDev.o \
+ PSOutputDev.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftops.o
+PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -152,15 +243,51 @@ pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFTOTEXT_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- TextOutputDev.o UnicodeMap.o UnicodeTypeTable.o XpdfPluginAPI.o \
- XRef.o pdftotext.o
+PDFTOTEXT_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftotext.o
PDFTOTEXT_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -169,14 +296,105 @@ pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFINFO_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfinfo.o
+PDFTOHTML_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ HTMLGen.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftohtml.o
+PDFTOHTML_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftohtml$(EXE): $(PDFTOHTML_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftohtml$(EXE) $(PDFTOHTML_OBJS) \
+ $(PDFTOHTML_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFINFO_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfinfo.o
PDFINFO_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -185,14 +403,49 @@ pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFFONTS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdffonts.o
+PDFFONTS_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdffonts.o
PDFFONTS_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -201,14 +454,49 @@ pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFDETACH_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfdetach.o
+PDFDETACH_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfdetach.o
PDFDETACH_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -217,16 +505,53 @@ pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFTOPPM_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o GfxState.o \
- GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o JPXStream.o \
- Lexer.o Link.o NameToCharCode.o Object.o OptionalContent.o \
- Outline.o OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o \
- PSTokenizer.o SecurityHandler.o SplashOutputDev.o Stream.o \
- TextOutputDev.o UnicodeMap.o UnicodeTypeTable.o XpdfPluginAPI.o \
- XRef.o pdftoppm.o
-PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+PDFTOPPM_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftoppm.o
+PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -235,14 +560,105 @@ pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFIMAGES_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o GfxState.o \
- GlobalParams.o ImageOutputDev.o JArithmeticDecoder.o \
- JBIG2Stream.o JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfimages.o
+PDFTOPNG_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftopng.o
+PDFTOPNG_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftopng$(EXE): $(PDFTOPNG_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftopng$(EXE) $(PDFTOPNG_OBJS) \
+ $(PDFTOPNG_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFIMAGES_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ ImageOutputDev.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfimages.o
PDFIMAGES_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfimages$(EXE): $(PDFIMAGES_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -255,10 +671,12 @@ clean:
rm -f $(XPDF_OBJS) xpdf$(EXE)
rm -f $(PDFTOPS_OBJS) pdftops$(EXE)
rm -f $(PDFTOTEXT_OBJS) pdftotext$(EXE)
+ rm -f $(PDFTOHTML_OBJS) pdftohtml$(EXE)
rm -f $(PDFINFO_OBJS) pdfinfo$(EXE)
rm -f $(PDFFONTS_OBJS) pdffonts$(EXE)
rm -f $(PDFDETACH_OBJS) pdfdetach$(EXE)
rm -f $(PDFTOPPM_OBJS) pdftoppm$(EXE)
+ rm -f $(PDFTOPNG_OBJS) pdftopng$(EXE)
rm -f $(PDFIMAGES_OBJS) pdfimages$(EXE)
#------------------------------------------------------------------------
@@ -266,4 +684,4 @@ clean:
depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff --git a/xpdf/Object.h b/xpdf/Object.h
index e1abed9..64c75f7 100644
--- a/xpdf/Object.h
+++ b/xpdf/Object.h
@@ -19,6 +19,7 @@
#include <string.h>
#include "gtypes.h"
#include "gmem.h"
+#include "gfile.h"
#include "GString.h"
class XRef;
@@ -179,9 +180,10 @@ public:
void streamClose();
int streamGetChar();
int streamLookChar();
+ int streamGetBlock(char *blk, int size);
char *streamGetLine(char *buf, int size);
- Guint streamGetPos();
- void streamSetPos(Guint pos, int dir = 0);
+ GFileOffset streamGetPos();
+ void streamSetPos(GFileOffset pos, int dir = 0);
Dict *streamGetDict();
// Output.
@@ -288,13 +290,16 @@ inline int Object::streamGetChar()
inline int Object::streamLookChar()
{ return stream->lookChar(); }
+inline int Object::streamGetBlock(char *blk, int size)
+ { return stream->getBlock(blk, size); }
+
inline char *Object::streamGetLine(char *buf, int size)
{ return stream->getLine(buf, size); }
-inline Guint Object::streamGetPos()
+inline GFileOffset Object::streamGetPos()
{ return stream->getPos(); }
-inline void Object::streamSetPos(Guint pos, int dir)
+inline void Object::streamSetPos(GFileOffset pos, int dir)
{ stream->setPos(pos, dir); }
inline Dict *Object::streamGetDict()
diff --git a/xpdf/OptionalContent.cc b/xpdf/OptionalContent.cc
index 589719c..bebb674 100644
--- a/xpdf/OptionalContent.cc
+++ b/xpdf/OptionalContent.cc
@@ -2,7 +2,7 @@
//
// OptionalContent.cc
//
-// Copyright 2008 Glyph & Cog, LLC
+// Copyright 2008-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include "Error.h"
#include "Object.h"
#include "PDFDoc.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "OptionalContent.h"
//------------------------------------------------------------------------
@@ -150,70 +150,79 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
}
}
obj->fetch(xref, &obj2);
- if (obj2.isDict("OCMD")) {
- if (obj2.dictLookup("VE", &obj3)->isArray()) {
- *visible = evalOCVisibilityExpr(&obj3, 0);
- obj3.free();
- } else {
- obj3.free();
- policy = ocPolicyAnyOn;
- if (obj2.dictLookup("P", &obj3)->isName()) {
- if (obj3.isName("AllOn")) {
- policy = ocPolicyAllOn;
- } else if (obj3.isName("AnyOn")) {
- policy = ocPolicyAnyOn;
- } else if (obj3.isName("AnyOff")) {
- policy = ocPolicyAnyOff;
- } else if (obj3.isName("AllOff")) {
- policy = ocPolicyAllOff;
- }
+ if (!obj2.isDict("OCMD")) {
+ obj2.free();
+ return gFalse;
+ }
+ if (obj2.dictLookup("VE", &obj3)->isArray()) {
+ *visible = evalOCVisibilityExpr(&obj3, 0);
+ obj3.free();
+ } else {
+ obj3.free();
+ policy = ocPolicyAnyOn;
+ if (obj2.dictLookup("P", &obj3)->isName()) {
+ if (obj3.isName("AllOn")) {
+ policy = ocPolicyAllOn;
+ } else if (obj3.isName("AnyOn")) {
+ policy = ocPolicyAnyOn;
+ } else if (obj3.isName("AnyOff")) {
+ policy = ocPolicyAnyOff;
+ } else if (obj3.isName("AllOff")) {
+ policy = ocPolicyAllOff;
}
- obj3.free();
- obj2.dictLookupNF("OCGs", &obj3);
- ocg = NULL;
- if (obj3.isRef()) {
- ref = obj3.getRef();
- ocg = findOCG(&ref);
+ }
+ obj3.free();
+ obj2.dictLookupNF("OCGs", &obj3);
+ ocg = NULL;
+ if (obj3.isRef()) {
+ ref = obj3.getRef();
+ ocg = findOCG(&ref);
+ }
+ if (ocg) {
+ *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
+ ocg->getState() : !ocg->getState();
+ } else {
+ *visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
+ if (!obj3.fetch(xref, &obj4)->isArray()) {
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
}
- if (ocg) {
- *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
- ocg->getState() : !ocg->getState();
- } else {
- *visible = gTrue;
- if (obj3.fetch(xref, &obj4)->isArray()) {
- for (i = 0; i < obj4.arrayGetLength(); ++i) {
- obj4.arrayGetNF(i, &obj5);
- if (obj5.isRef()) {
- ref = obj5.getRef();
- if ((ocg = findOCG(&ref))) {
- 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;
- }
- }
- }
+ 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;
}
}
- obj4.free();
+ obj5.free();
}
- obj3.free();
+ obj4.free();
}
- obj2.free();
- return gTrue;
+ obj3.free();
}
obj2.free();
- return gFalse;
+ return gTrue;
}
GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
@@ -279,12 +288,9 @@ GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
//------------------------------------------------------------------------
OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
- Unicode *nameA;
- int nameLenA;
+ TextString *nameA;
Object obj1, obj2, obj3;
- GString *s;
OCUsageState viewStateA, printStateA;
- int i;
if (!obj->isDict()) {
return NULL;
@@ -294,22 +300,7 @@ OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
obj1.free();
return NULL;
}
- s = obj1.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- nameLenA = (s->getLength() - 2) / 2;
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLenA = s->getLength();
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
+ nameA = new TextString(obj1.getString());
obj1.free();
viewStateA = printStateA = ocUsageUnset;
@@ -339,30 +330,35 @@ OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
}
obj1.free();
- return new OptionalContentGroup(refA, nameA, nameLenA,
- viewStateA, printStateA);
+ return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
}
-OptionalContentGroup::OptionalContentGroup(Ref *refA, Unicode *nameA,
- int nameLenA,
+OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA,
OCUsageState printStateA) {
ref = *refA;
name = nameA;
- nameLen = nameLenA;
viewState = viewStateA;
printState = printStateA;
state = gTrue;
}
OptionalContentGroup::~OptionalContentGroup() {
- gfree(name);
+ delete name;
}
GBool OptionalContentGroup::matches(Ref *refA) {
return refA->num == ref.num && refA->gen == ref.gen;
}
+Unicode *OptionalContentGroup::getName() {
+ return name->getUnicode();
+}
+
+int OptionalContentGroup::getNameLength() {
+ return name->getLength();
+}
+
//------------------------------------------------------------------------
OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
@@ -404,8 +400,10 @@ OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
obj2.arrayGetNF(i, &obj3);
if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
if (!child->ocg && !child->name && node->getNumChildren() > 0) {
- node->getChild(node->getNumChildren() - 1)->
- addChildren(child->takeChildren());
+ if (child->getNumChildren() > 0) {
+ node->getChild(node->getNumChildren() - 1)->
+ addChildren(child->takeChildren());
+ }
delete child;
} else {
node->addChild(child);
@@ -418,42 +416,19 @@ OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
}
OCDisplayNode::OCDisplayNode() {
- name = NULL;
- nameLen = 0;
+ name = new TextString();
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(GString *nameA) {
- int i;
-
- if ((nameA->getChar(0) & 0xff) == 0xfe &&
- (nameA->getChar(1) & 0xff) == 0xff) {
- nameLen = (nameA->getLength() - 2) / 2;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = ((nameA->getChar(2 + 2*i) & 0xff) << 8) |
- (nameA->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLen = nameA->getLength();
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = pdfDocEncoding[nameA->getChar(i) & 0xff];
- }
- }
+ name = new TextString(nameA);
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
- nameLen = ocgA->getNameLength();
- if (nameLen) {
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- memcpy(name, ocgA->getName(), nameLen * sizeof(Unicode));
- } else {
- name = NULL;
- }
+ name = new TextString(ocgA->name);
ocg = ocgA;
children = NULL;
}
@@ -482,12 +457,20 @@ GList *OCDisplayNode::takeChildren() {
}
OCDisplayNode::~OCDisplayNode() {
- gfree(name);
+ delete name;
if (children) {
deleteGList(children, OCDisplayNode);
}
}
+Unicode *OCDisplayNode::getName() {
+ return name->getUnicode();
+}
+
+int OCDisplayNode::getNameLength() {
+ return name->getLength();
+}
+
int OCDisplayNode::getNumChildren() {
if (!children) {
return 0;
diff --git a/xpdf/OptionalContent.h b/xpdf/OptionalContent.h
index 82d3e0e..e2ea244 100644
--- a/xpdf/OptionalContent.h
+++ b/xpdf/OptionalContent.h
@@ -2,7 +2,7 @@
//
// OptionalContent.h
//
-// Copyright 2008 Glyph & Cog, LLC
+// Copyright 2008-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -22,6 +22,7 @@
class GString;
class GList;
class PDFDoc;
+class TextString;
class XRef;
class OptionalContentGroup;
class OCDisplayNode;
@@ -78,8 +79,8 @@ public:
GBool matches(Ref *refA);
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OCUsageState getViewState() { return viewState; }
OCUsageState getPrintState() { return printState; }
GBool getState() { return state; }
@@ -87,15 +88,16 @@ public:
private:
- OptionalContentGroup(Ref *refA, Unicode *nameA, int nameLenA,
+ OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA, OCUsageState printStateA);
Ref ref;
- Unicode *name;
- int nameLen;
+ TextString *name;
OCUsageState viewState, // suggested state when viewing
printState; // suggested state when printing
GBool state; // current state (on/off)
+
+ friend class OCDisplayNode;
};
//------------------------------------------------------------------------
@@ -108,8 +110,8 @@ public:
OCDisplayNode();
~OCDisplayNode();
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OptionalContentGroup *getOCG() { return ocg; }
int getNumChildren();
OCDisplayNode *getChild(int idx);
@@ -122,8 +124,7 @@ private:
void addChildren(GList *childrenA);
GList *takeChildren();
- Unicode *name; // display name (may be NULL)
- int nameLen;
+ TextString *name; // display name
OptionalContentGroup *ocg; // NULL for display labels
GList *children; // NULL if there are no children
// [OCDisplayNode]
diff --git a/xpdf/Outline.cc b/xpdf/Outline.cc
index 30ca85d..e87f61e 100644
--- a/xpdf/Outline.cc
+++ b/xpdf/Outline.cc
@@ -2,7 +2,7 @@
//
// Outline.cc
//
-// Copyright 2002-2003 Glyph & Cog, LLC
+// Copyright 2002-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include "GList.h"
#include "Error.h"
#include "Link.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Outline.h"
//------------------------------------------------------------------------
@@ -32,7 +32,7 @@ Outline::Outline(Object *outlineObj, XRef *xref) {
outlineObj->dictLookupNF("First", &first);
outlineObj->dictLookupNF("Last", &last);
if (first.isRef() && last.isRef()) {
- items = OutlineItem::readItemList(&first, &last, xref);
+ items = OutlineItem::readItemList(&first, &last, NULL, xref);
}
first.free();
last.free();
@@ -46,35 +46,18 @@ Outline::~Outline() {
//------------------------------------------------------------------------
-OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+OutlineItem::OutlineItem(Object *itemRefA, Dict *dict,
+ OutlineItem *parentA, XRef *xrefA) {
Object obj1;
- GString *s;
- int i;
xref = xrefA;
title = NULL;
action = NULL;
kids = NULL;
+ parent = parentA;
if (dict->lookup("Title", &obj1)->isString()) {
- s = obj1.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- titleLen = (s->getLength() - 2) / 2;
- title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
- for (i = 0; i < titleLen; ++i) {
- title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- titleLen = s->getLength();
- title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
- for (i = 0; i < titleLen; ++i) {
- title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
- } else {
- titleLen = 0;
+ title = new TextString(obj1.getString());
}
obj1.free();
@@ -88,6 +71,7 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
}
obj1.free();
+ itemRefA->copy(&itemRef);
dict->lookupNF("First", &firstRef);
dict->lookupNF("Last", &lastRef);
dict->lookupNF("Next", &nextRef);
@@ -104,22 +88,24 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
OutlineItem::~OutlineItem() {
close();
if (title) {
- gfree(title);
+ delete title;
}
if (action) {
delete action;
}
+ itemRef.free();
firstRef.free();
lastRef.free();
nextRef.free();
}
GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA) {
+ OutlineItem *parentA, XRef *xrefA) {
GList *items;
- OutlineItem *item;
+ OutlineItem *item, *sibling;
Object obj;
- Object *p, *refObj;
+ Object *p;
+ OutlineItem *ancestor;
int i;
items = new GList();
@@ -132,8 +118,36 @@ GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
obj.free();
break;
}
- item = new OutlineItem(obj.getDict(), xrefA);
+ item = new OutlineItem(p, obj.getDict(), parentA, xrefA);
obj.free();
+
+ // check for loops with parents
+ for (ancestor = parentA; ancestor; ancestor = ancestor->parent) {
+ if (p->getRefNum() == ancestor->itemRef.getRefNum() &&
+ p->getRefGen() == ancestor->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (ancestor) {
+ delete item;
+ break;
+ }
+
+ // check for loops with siblings
+ for (i = 0; i < items->getLength(); ++i) {
+ sibling = (OutlineItem *)items->get(i);
+ if (p->getRefNum() == sibling->itemRef.getRefNum() &&
+ p->getRefGen() == sibling->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (i < items->getLength()) {
+ delete item;
+ break;
+ }
+
items->append(item);
if (p->getRefNum() == lastItemRef->getRef().num &&
p->getRefGen() == lastItemRef->getRef().gen) {
@@ -143,23 +157,13 @@ GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
if (!p->isRef()) {
break;
}
- for (i = 0; i < items->getLength(); ++i) {
- refObj = (i == 0) ? firstItemRef
- : &((OutlineItem *)items->get(i - 1))->nextRef;
- if (refObj->getRefNum() == p->getRefNum() &&
- refObj->getRefGen() == p->getRefGen()) {
- error(errSyntaxError, -1, "Loop detected in outline item list");
- p = NULL;
- break;
- }
- }
} while (p);
return items;
}
void OutlineItem::open() {
if (!kids) {
- kids = readItemList(&firstRef, &lastRef, xref);
+ kids = readItemList(&firstRef, &lastRef, this, xref);
}
}
@@ -169,3 +173,11 @@ void OutlineItem::close() {
kids = NULL;
}
}
+
+Unicode *OutlineItem::getTitle() {
+ return title ? title->getUnicode() : (Unicode *)NULL;
+}
+
+int OutlineItem::getTitleLength() {
+ return title ? title->getLength() : 0;
+}
diff --git a/xpdf/Outline.h b/xpdf/Outline.h
index f38f8d1..a9c2089 100644
--- a/xpdf/Outline.h
+++ b/xpdf/Outline.h
@@ -2,7 +2,7 @@
//
// Outline.h
//
-// Copyright 2002-2003 Glyph & Cog, LLC
+// Copyright 2002-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -22,6 +22,7 @@ class GString;
class GList;
class XRef;
class LinkAction;
+class TextString;
//------------------------------------------------------------------------
@@ -44,17 +45,18 @@ private:
class OutlineItem {
public:
- OutlineItem(Dict *dict, XRef *xrefA);
+ OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA);
~OutlineItem();
static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA);
+ OutlineItem *parentA, XRef *xrefA);
void open();
void close();
- Unicode *getTitle() { return title; }
- int getTitleLength() { return titleLen; }
+ Unicode *getTitle();
+ int getTitleLength();
+ TextString *getTitleTextString() { return title; }
LinkAction *getAction() { return action; }
GBool isOpen() { return startsOpen; }
GBool hasKids() { return firstRef.isRef(); }
@@ -63,14 +65,15 @@ public:
private:
XRef *xref;
- Unicode *title;
- int titleLen;
+ TextString *title; // may be NULL
LinkAction *action;
+ Object itemRef;
Object firstRef;
Object lastRef;
Object nextRef;
GBool startsOpen;
GList *kids; // NULL unless this item is open [OutlineItem]
+ OutlineItem *parent;
};
#endif
diff --git a/xpdf/OutputDev.cc b/xpdf/OutputDev.cc
index d2ef286..2d17547 100644
--- a/xpdf/OutputDev.cc
+++ b/xpdf/OutputDev.cc
@@ -43,6 +43,11 @@ void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) {
*uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
}
+void OutputDev::cvtUserToDev(double ux, double uy, double *dx, double *dy) {
+ *dx = defCTM[0] * ux + defCTM[2] * uy + defCTM[4];
+ *dy = defCTM[1] * ux + defCTM[3] * uy + defCTM[5];
+}
+
void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
*dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
*dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
@@ -78,14 +83,10 @@ GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- int i, j;
-
+ GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -93,21 +94,17 @@ void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void OutputDev::setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- drawImageMask(state, ref, str, width, height, invert, inlineImg);
+ GBool inlineImg, GBool interpolate) {
+ drawImageMask(state, ref, str, width, height, invert, inlineImg, interpolate);
}
void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
- int i, j;
-
+ int *maskColors, GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -117,8 +114,9 @@ void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
- drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
}
void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
@@ -126,8 +124,10 @@ void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
- drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
}
#if OPI_SUPPORT
diff --git a/xpdf/OutputDev.h b/xpdf/OutputDev.h
index 138aa34..759e356 100644
--- a/xpdf/OutputDev.h
+++ b/xpdf/OutputDev.h
@@ -112,6 +112,7 @@ public:
// Convert between device and user coordinates.
virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+ virtual void cvtUserToDev(double ux, double uy, double *dx, double *dy);
virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
double *getDefCTM() { return defCTM; }
@@ -161,7 +162,7 @@ public:
virtual void stroke(GfxState *state) {}
virtual void fill(GfxState *state) {}
virtual void eoFill(GfxState *state) {}
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -201,25 +202,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
diff --git a/xpdf/PDFCore.cc b/xpdf/PDFCore.cc
index 6435815..34b6483 100644
--- a/xpdf/PDFCore.cc
+++ b/xpdf/PDFCore.cc
@@ -2,7 +2,7 @@
//
// PDFCore.cc
//
-// Copyright 2004 Glyph & Cog, LLC
+// Copyright 2004-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -100,7 +100,8 @@ PDFCore::PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,
selectULY = selectLRY = 0;
dragging = gFalse;
lastDragLeft = lastDragTop = gTrue;
- selectXorColor[0] = selectXorColor[1] = selectXorColor[2] = 0;
+ selectXorColor[0] = selectXorColor[1] = selectXorColor[2] =
+ reverseVideoA ? 0xff : 0x00;
splashColorXor(selectXorColor, paperColorA);
historyCur = pdfHistorySize - 1;
@@ -128,7 +129,11 @@ PDFCore::~PDFCore() {
}
for (i = 0; i < pdfHistorySize; ++i) {
if (history[i].fileName) {
+#ifdef _WIN32
+ delete[] history[i].fileName;
+#else
delete history[i].fileName;
+#endif
}
}
gfree(pageY);
@@ -147,7 +152,7 @@ int PDFCore::loadFile(GString *fileName, GString *ownerPassword,
return err;
}
-#ifdef WIN32
+#ifdef _WIN32
int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
GString *ownerPassword, GString *userPassword) {
int err;
@@ -423,6 +428,7 @@ void PDFCore::update(int topPageA, int scrollXA, int scrollYA,
// check for changes to the PDF file
if ((force || (!continuousMode && topPage != topPageA)) &&
+ doc->getFileName() &&
checkForNewFile()) {
if (loadFile(doc->getFileName()) == errNone) {
if (topPageA > doc->getNumPages()) {
@@ -758,13 +764,30 @@ void PDFCore::update(int topPageA, int scrollXA, int scrollYA,
}
hist = &history[historyCur];
if (hist->fileName) {
+#ifdef _WIN32
+ delete[] hist->fileName;
+#else
delete hist->fileName;
+#endif
+ }
+#ifdef _WIN32
+ if (doc->getFileNameU()) {
+ hist->fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
+ if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
+ hist->fileName, NULL) == 0) {
+ delete[] hist->fileName;
+ hist->fileName = NULL;
+ }
+ } else {
+ hist->fileName = NULL;
}
+#else
if (doc->getFileName()) {
hist->fileName = doc->getFileName()->copy();
} else {
hist->fileName = NULL;
}
+#endif
hist->page = topPage;
if (historyBLen < pdfHistorySize) {
++historyBLen;
@@ -807,6 +830,7 @@ void PDFCore::addPage(int pg, int rot) {
void PDFCore::needTile(PDFCorePage *page, int x, int y) {
PDFCoreTile *tile;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int xDest, yDest, sliceW, sliceH;
int i;
@@ -893,7 +917,8 @@ void PDFCore::needTile(PDFCorePage *page, int x, int y) {
page->links = doc->getLinks(page->page);
}
if (!page->text) {
- if ((textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse))) {
+ textOutCtrl.mode = textOutPhysLayout;
+ if ((textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse))) {
doc->displayPage(textOut, page->page, dpi, dpi, rotate,
gFalse, gTrue, gFalse);
page->text = textOut->takeText();
@@ -977,11 +1002,27 @@ GBool PDFCore::goForward() {
}
--historyFLen;
++historyBLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -999,11 +1040,27 @@ GBool PDFCore::goBackward() {
}
--historyBLen;
++historyFLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -1615,6 +1672,7 @@ GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
GString *PDFCore::extractText(int pg, double xMin, double yMin,
double xMax, double yMax) {
PDFCorePage *page;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int x0, y0, x1, y1, t;
GString *s;
@@ -1633,7 +1691,8 @@ GString *PDFCore::extractText(int pg, double xMin, double yMin,
}
s = page->text->getText(x0, y0, x1, y1);
} else {
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (textOut->isOk()) {
doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
textOut->cvtUserToDev(xMin, yMin, &x0, &y0);
@@ -1675,10 +1734,10 @@ GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
GBool next, GBool backward, GBool wholeWord,
GBool onePageOnly) {
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
double xMin, yMin, xMax, yMax;
PDFCorePage *page;
- PDFCoreTile *tile;
int pg;
GBool startAtTop, startAtLast, stopAtLast;
@@ -1721,7 +1780,8 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
if (!onePageOnly) {
// search following/previous pages
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (!textOut->isOk()) {
delete textOut;
goto notFound;
@@ -1791,7 +1851,6 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
// found: change the selection
found:
- tile = (PDFCoreTile *)page->tiles->get(0);
setSelection(pg, (int)floor(xMin), (int)floor(yMin),
(int)ceil(xMax), (int)ceil(yMax));
diff --git a/xpdf/PDFCore.h b/xpdf/PDFCore.h
index 264756f..1be1010 100644
--- a/xpdf/PDFCore.h
+++ b/xpdf/PDFCore.h
@@ -100,7 +100,11 @@ public:
//------------------------------------------------------------------------
struct PDFHistory {
+#ifdef _WIN32
+ wchar_t *fileName;
+#else
GString *fileName;
+#endif
int page;
};
@@ -125,7 +129,7 @@ public:
virtual int loadFile(GString *fileName, GString *ownerPassword = NULL,
GString *userPassword = NULL);
-#ifdef WIN32
+#ifdef _WIN32
// Load a new file. Returns pdfOk or error code.
virtual int loadFile(wchar_t *fileName, int fileNameLen,
GString *ownerPassword = NULL,
diff --git a/xpdf/PDFDoc.cc b/xpdf/PDFDoc.cc
index b20ef2c..5126289 100644
--- a/xpdf/PDFDoc.cc
+++ b/xpdf/PDFDoc.cc
@@ -16,7 +16,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
-#ifdef WIN32
+#ifdef _WIN32
# include <windows.h>
#endif
#include "GString.h"
@@ -52,7 +52,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
Object obj;
GString *fileName1, *fileName2;
-#ifdef WIN32
+#ifdef _WIN32
int n, i;
#endif
@@ -71,7 +71,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
optContent = NULL;
fileName = fileNameA;
-#ifdef WIN32
+#ifdef _WIN32
n = fileName->getLength();
fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
for (i = 0; i < n; ++i) {
@@ -114,7 +114,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
ok = setup(ownerPassword, userPassword);
}
-#ifdef WIN32
+#ifdef _WIN32
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
OSVERSIONINFO version;
@@ -169,7 +169,7 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
-#ifdef WIN32
+#ifdef _WIN32
int n, i;
#endif
@@ -178,7 +178,7 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
core = coreA;
if (strA->getFileName()) {
fileName = strA->getFileName()->copy();
-#ifdef WIN32
+#ifdef _WIN32
n = fileName->getLength();
fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
for (i = 0; i < n; ++i) {
@@ -188,7 +188,7 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
#endif
} else {
fileName = NULL;
-#ifdef WIN32
+#ifdef _WIN32
fileNameU = NULL;
#endif
}
@@ -231,6 +231,7 @@ GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
// read the optional content info
optContent = new OptionalContent(this);
+
// done
return gTrue;
}
@@ -294,7 +295,7 @@ PDFDoc::~PDFDoc() {
if (fileName) {
delete fileName;
}
-#ifdef WIN32
+#ifdef _WIN32
if (fileNameU) {
gfree(fileNameU);
}
@@ -309,10 +310,8 @@ void PDFDoc::checkHeader() {
int i;
pdfVersion = 0;
- for (i = 0; i < headerSearchSize; ++i) {
- hdrBuf[i] = str->getChar();
- }
- hdrBuf[headerSearchSize] = '\0';
+ memset(hdrBuf, 0, headerSearchSize + 1);
+ str->getBlock(hdrBuf, headerSearchSize);
for (i = 0; i < headerSearchSize - 5; ++i) {
if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
break;
@@ -455,15 +454,16 @@ GBool PDFDoc::isLinearized() {
GBool PDFDoc::saveAs(GString *name) {
FILE *f;
- int c;
+ char buf[4096];
+ int n;
if (!(f = fopen(name->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open file '{0:t}'", name);
return gFalse;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
str->close();
fclose(f);
@@ -482,7 +482,7 @@ GBool PDFDoc::saveEmbeddedFile(int idx, char *path) {
return ret;
}
-#ifdef WIN32
+#ifdef _WIN32
GBool PDFDoc::saveEmbeddedFile(int idx, wchar_t *path, int pathLen) {
FILE *f;
OSVERSIONINFO version;
@@ -518,14 +518,15 @@ GBool PDFDoc::saveEmbeddedFile(int idx, wchar_t *path, int pathLen) {
GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
Object strObj;
- int c;
+ char buf[4096];
+ int n;
if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
return gFalse;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, f);
+ while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
strObj.streamClose();
strObj.free();
@@ -535,24 +536,28 @@ GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
char *PDFDoc::getEmbeddedFileMem(int idx, int *size) {
Object strObj;
char *buf;
- int bufSize, len, c;
+ int bufSize, sizeInc, n;
if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
return NULL;
}
strObj.streamReset();
- bufSize = 1024;
- buf = (char *)gmalloc(bufSize);
- len = 0;
- while ((c = strObj.streamGetChar()) != EOF) {
- if (len == bufSize) {
- bufSize *= 2;
- buf = (char *)grealloc(buf, bufSize);
+ bufSize = 0;
+ buf = NULL;
+ do {
+ sizeInc = bufSize ? bufSize : 1024;
+ if (bufSize > INT_MAX - sizeInc) {
+ error(errIO, -1, "embedded file is too large");
+ *size = 0;
+ return NULL;
}
- buf[len++] = (char)c;
- }
+ buf = (char *)grealloc(buf, bufSize + sizeInc);
+ n = strObj.streamGetBlock(buf + bufSize, sizeInc);
+ bufSize += n;
+ } while (n == sizeInc);
strObj.streamClose();
strObj.free();
- *size = len;
+ *size = bufSize;
return buf;
}
+
diff --git a/xpdf/PDFDoc.h b/xpdf/PDFDoc.h
index 94fcfb7..509c1d5 100644
--- a/xpdf/PDFDoc.h
+++ b/xpdf/PDFDoc.h
@@ -39,7 +39,7 @@ public:
PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
GString *userPassword = NULL, PDFCore *coreA = NULL);
-#ifdef WIN32
+#ifdef _WIN32
PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
GString *userPassword = NULL, PDFCore *coreA = NULL);
#endif
@@ -55,7 +55,7 @@ public:
// Get file name.
GString *getFileName() { return fileName; }
-#ifdef WIN32
+#ifdef _WIN32
wchar_t *getFileNameU() { return fileNameU; }
#endif
@@ -172,11 +172,12 @@ public:
int getEmbeddedFileNameLength(int idx)
{ return catalog->getEmbeddedFileNameLength(idx); }
GBool saveEmbeddedFile(int idx, char *path);
-#ifdef WIN32
+#ifdef _WIN32
GBool saveEmbeddedFile(int idx, wchar_t *path, int pathLen);
#endif
char *getEmbeddedFileMem(int idx, int *size);
+
private:
GBool setup(GString *ownerPassword, GString *userPassword);
@@ -187,7 +188,7 @@ private:
GBool saveEmbeddedFile2(int idx, FILE *f);
GString *fileName;
-#ifdef WIN32
+#ifdef _WIN32
wchar_t *fileNameU;
#endif
FILE *file;
diff --git a/xpdf/PSOutputDev.cc b/xpdf/PSOutputDev.cc
index 55e576b..9521f6c 100644
--- a/xpdf/PSOutputDev.cc
+++ b/xpdf/PSOutputDev.cc
@@ -2,7 +2,7 @@
//
// PSOutputDev.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -39,6 +39,8 @@
#include "XRef.h"
#include "PreScanOutputDev.h"
#include "CharCodeToUnicode.h"
+#include "Form.h"
+#include "TextString.h"
#if HAVE_SPLASH
# include "Splash.h"
# include "SplashBitmap.h"
@@ -57,11 +59,6 @@
#endif
//------------------------------------------------------------------------
-
-// Max size of a slice when rasterizing pages, in pixels.
-#define rasterizationSliceSize 20000000
-
-//------------------------------------------------------------------------
// PostScript prolog and setup
//------------------------------------------------------------------------
@@ -85,24 +82,29 @@ static const char *prolog[] = {
" } for",
"~123sn",
"/pdfSetup {",
+ " /pdfDuplex exch def",
" /setpagedevice where {",
" pop 2 dict begin",
" /Policies 1 dict dup begin /PageSize 6 def end def",
- " { /Duplex true def } if",
+ " pdfDuplex { /Duplex true def } if",
" currentdict end setpagedevice",
- " } {",
- " pop",
- " } ifelse",
+ " } if",
+ " /pdfPageW 0 def",
+ " /pdfPageH 0 def",
"} def",
"/pdfSetupPaper {",
- " 2 array astore",
- " /setpagedevice where {",
- " pop 2 dict begin",
- " /PageSize exch def",
- " /ImagingBBox null def",
- " currentdict end setpagedevice",
+ " 2 copy pdfPageH ne exch pdfPageW ne or {",
+ " /pdfPageH exch def",
+ " /pdfPageW exch def",
+ " /setpagedevice where {",
+ " pop 3 dict begin",
+ " /PageSize [pdfPageW pdfPageH] def",
+ " pdfDuplex { /Duplex true def } if",
+ " /ImagingBBox null def",
+ " currentdict end setpagedevice",
+ " } if",
" } {",
- " pop",
+ " pop pop",
" } ifelse",
"} def",
"~1sn",
@@ -571,17 +573,14 @@ static const char *prolog[] = {
"} def",
"~23sn",
"/pdfImM { fCol imagemask skipEOD } def",
- "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
- "/pdfImClip {",
- " gsave",
- " 0 2 4 index length 1 sub {",
- " dup 4 index exch 2 copy",
- " get 5 index div put",
- " 1 add 3 index exch 2 copy",
- " get 3 index div put",
- " } for",
- " pop pop rectclip",
+ "/pr {",
+ " 4 2 roll exch 5 index div exch 4 index div moveto",
+ " exch 3 index div dup 0 rlineto",
+ " exch 2 index div 0 exch rlineto",
+ " neg 0 rlineto",
+ " closepath",
"} def",
+ "/pdfImClip { gsave clip } def",
"/pdfImClipEnd { grestore } def",
"~23sn",
"% shading operators",
@@ -736,6 +735,19 @@ static const char *prolog[] = {
NULL
};
+static const char *minLineWidthProlog[] = {
+ "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfMinLineDist pdfMinLineWidth pdfDist def",
+ "/setlinewidth {",
+ " dup pdfDist pdfMinLineDist lt {",
+ " pop pdfMinLineDist pdfIDist",
+ " } if",
+ " setlinewidth",
+ "} bind def",
+ NULL
+};
+
static const char *cmapProlog[] = {
"/CIDInit /ProcSet findresource begin",
"10 dict begin",
@@ -809,25 +821,68 @@ static PSSubstFont psBase14SubstFonts[14] = {
{"ZapfDingbats", 0}
};
-// Mapping from Type 1/1C font file to PS font name.
-struct PST1FontName {
- Ref fontFileID;
- GString *psName; // PostScript font name used for this
- // embedded font file
-};
+class PSFontInfo {
+public:
+
+ PSFontInfo(Ref fontIDA)
+ { fontID = fontIDA; ff = NULL; }
-// Info for 8-bit fonts
-struct PSFont8Info {
Ref fontID;
- int *codeToGID; // code-to-GID mapping for TrueType fonts
+ PSFontFileInfo *ff; // pointer to font file info; NULL indicates
+ // font mapping failed
};
-// Encoding info for substitute 16-bit font
-struct PSFont16Enc {
- Ref fontID;
- GString *enc; // NULL means font wasn't correctly substituted
+enum PSFontFileLocation {
+ psFontFileResident,
+ psFontFileEmbedded,
+ psFontFileExternal
};
+class PSFontFileInfo {
+public:
+
+ PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA);
+ ~PSFontFileInfo();
+
+ GString *psName; // name under which font is defined
+ GfxFontType type; // font type
+ PSFontFileLocation loc; // font location
+ Ref embFontID; // object ID for the embedded font file
+ // (for all embedded fonts)
+ GString *extFileName; // external font file path
+ // (for all external fonts)
+ GString *encoding; // encoding name (for resident CID fonts)
+ int *codeToGID; // mapping from code/CID to GID
+ // (for TrueType, OpenType-TrueType, and
+ // CID OpenType-CFF fonts)
+ int codeToGIDLen; // length of codeToGID array
+};
+
+PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA) {
+ psName = psNameA;
+ type = typeA;
+ loc = locA;
+ embFontID.num = embFontID.gen = -1;
+ extFileName = NULL;
+ encoding = NULL;
+ codeToGID = NULL;
+}
+
+PSFontFileInfo::~PSFontFileInfo() {
+ delete psName;
+ if (extFileName) {
+ delete extFileName;
+ }
+ if (encoding) {
+ delete encoding;
+ }
+ if (codeToGID) {
+ gfree(codeToGID);
+ }
+}
+
//------------------------------------------------------------------------
// process colors
//------------------------------------------------------------------------
@@ -998,11 +1053,8 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
customCodeCbk = customCodeCbkA;
customCodeCbkData = customCodeCbkDataA;
- fontIDs = NULL;
- fontNames = new GHash(gTrue);
- t1FontNames = NULL;
- font8Info = NULL;
- font16Enc = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
imgIDs = NULL;
formIDs = NULL;
xobjStack = NULL;
@@ -1019,7 +1071,7 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
} else if (fileName[0] == '|') {
fileTypeA = psPipe;
#ifdef HAVE_POPEN
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, (SignalFunc)SIG_IGN);
#endif
if (!(f = popen(fileName + 1, "w"))) {
@@ -1060,11 +1112,8 @@ PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
customCodeCbk = customCodeCbkA;
customCodeCbkData = customCodeCbkDataA;
- fontIDs = NULL;
- fontNames = new GHash(gTrue);
- t1FontNames = NULL;
- font8Info = NULL;
- font16Enc = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
imgIDs = NULL;
formIDs = NULL;
xobjStack = NULL;
@@ -1088,6 +1137,7 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
Page *page;
PDFRectangle *box;
PSOutPaperSize *size;
+ PSFontFileInfo *ff;
GList *names;
int pg, w, h, i;
@@ -1118,8 +1168,13 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
pg <= lastPage && pg <= catalog->getNumPages();
++pg) {
page = catalog->getPage(pg);
- w = (int)ceil(page->getMediaWidth());
- h = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ w = (int)ceil(page->getCropWidth());
+ h = (int)ceil(page->getCropHeight());
+ } else {
+ w = (int)ceil(page->getMediaWidth());
+ h = (int)ceil(page->getMediaHeight());
+ }
for (i = 0; i < paperSizes->getLength(); ++i) {
size = (PSOutPaperSize *)paperSizes->get(i);
if (size->w == w && size->h == h) {
@@ -1160,25 +1215,21 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
clipLLX0 = clipLLY0 = 0;
clipURX0 = clipURY0 = -1;
- // initialize fontIDs and fontNames lists
- fontIDSize = 64;
- fontIDLen = 0;
- fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
+ // initialize font lists, etc.
for (i = 0; i < 14; ++i) {
- fontNames->add(new GString(psBase14SubstFonts[i].psName), 1);
+ ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName),
+ fontType1, psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
}
names = globalParams->getPSResidentFonts();
for (i = 0; i < names->getLength(); ++i) {
- fontNames->add((GString *)names->get(i), 1);
+ if (!fontFileInfo->lookup((GString *)names->get(i))) {
+ ff = new PSFontFileInfo((GString *)names->get(i), fontType1,
+ psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
+ }
}
delete names;
- t1FontNameSize = 64;
- t1FontNameLen = 0;
- t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName));
- font8InfoLen = 0;
- font8InfoSize = 0;
- font16EncLen = 0;
- font16EncSize = 0;
imgIDLen = 0;
imgIDSize = 0;
formIDLen = 0;
@@ -1224,7 +1275,6 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
PSOutputDev::~PSOutputDev() {
PSOutCustomColor *cc;
- int i;
if (ok) {
if (!manualCtrl) {
@@ -1243,7 +1293,7 @@ PSOutputDev::~PSOutputDev() {
#ifdef HAVE_POPEN
else if (fileType == psPipe) {
pclose((FILE *)outputStream);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, (SignalFunc)SIG_DFL);
#endif
}
@@ -1255,30 +1305,8 @@ PSOutputDev::~PSOutputDev() {
if (embFontList) {
delete embFontList;
}
- if (fontIDs) {
- gfree(fontIDs);
- }
- delete fontNames;
- if (t1FontNames) {
- for (i = 0; i < t1FontNameLen; ++i) {
- delete t1FontNames[i].psName;
- }
- gfree(t1FontNames);
- }
- if (font8Info) {
- for (i = 0; i < font8InfoLen; ++i) {
- gfree(font8Info[i].codeToGID);
- }
- gfree(font8Info);
- }
- if (font16Enc) {
- for (i = 0; i < font16EncLen; ++i) {
- if (font16Enc[i].enc) {
- delete font16Enc[i].enc;
- }
- }
- gfree(font16Enc);
- }
+ deleteGList(fontInfo, PSFontInfo);
+ deleteGHash(fontFileInfo, PSFontFileInfo);
gfree(imgIDs);
gfree(formIDs);
if (xobjStack) {
@@ -1291,6 +1319,16 @@ PSOutputDev::~PSOutputDev() {
}
}
+GBool PSOutputDev::checkIO() {
+ if (fileType == psFile || fileType == psPipe || fileType == psStdout) {
+ if (ferror((FILE *)outputStream)) {
+ error(errIO, -1, "Error writing to PostScript file");
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
void PSOutputDev::writeHeader(int firstPage, int lastPage,
PDFRectangle *mediaBox, PDFRectangle *cropBox,
int pageRotate) {
@@ -1395,6 +1433,7 @@ void PSOutputDev::writeXpdfProcset() {
GBool lev1, lev2, lev3, sep, nonSep;
const char **p;
const char *q;
+ double w;
writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
@@ -1420,6 +1459,12 @@ void PSOutputDev::writeXpdfProcset() {
writePSFmt("{0:s}\n", *p);
}
}
+ if ((w = globalParams->getPSMinLineWidth()) > 0) {
+ writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w);
+ for (p = minLineWidthProlog; *p; ++p) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
writePS("%%EndResource\n");
if (level >= psLevel3) {
@@ -1434,10 +1479,10 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
Page *page;
Dict *resDict;
Annots *annots;
- Object *acroForm;
+ Form *form;
Object obj1, obj2, obj3;
GString *s;
- int pg, i;
+ int pg, i, j;
if (mode == psModeForm) {
// swap the form and xpdf dicts
@@ -1464,23 +1509,22 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
}
delete annots;
}
- if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) {
- if (acroForm->dictLookup("DR", &obj1)->isDict()) {
- setupResources(obj1.getDict());
- }
- obj1.free();
- if (acroForm->dictLookup("Fields", &obj1)->isArray()) {
- for (i = 0; i < obj1.arrayGetLength(); ++i) {
- if (obj1.arrayGet(i, &obj2)->isDict()) {
- if (obj2.dictLookup("DR", &obj3)->isDict()) {
- setupResources(obj3.getDict());
+ if ((form = catalog->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGet(j, &obj2);
+ if (obj2.isDict()) {
+ setupResources(obj2.getDict());
}
- obj3.free();
+ obj2.free();
}
- obj2.free();
+ } else if (obj1.isDict()) {
+ setupResources(obj1.getDict());
}
+ obj1.free();
}
- obj1.free();
}
if (mode != psModeForm) {
if (mode != psModeEPS && !manualCtrl) {
@@ -1503,6 +1547,9 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
delete s;
}
}
+ if (mode != psModeForm) {
+ writePS("end\n");
+ }
}
void PSOutputDev::writePageTrailer() {
@@ -1517,7 +1564,6 @@ void PSOutputDev::writeTrailer() {
if (mode == psModeForm) {
writePS("/Foo exch /Form defineresource pop\n");
} else {
- writePS("end\n");
writePS("%%DocumentSuppliedResources:\n");
writePS(embFontList->getCString());
if (level == psLevel1Sep || level == psLevel2Sep ||
@@ -1554,14 +1600,14 @@ void PSOutputDev::writeTrailer() {
}
void PSOutputDev::setupResources(Dict *resDict) {
- Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
+ Object xObjDict, xObjRef, xObj, patDict, patRef, pat;
+ Object gsDict, gsRef, gs, smask, smaskGroup, resObj;
Ref ref0, ref1;
GBool skip;
int i, j;
setupFonts(resDict);
setupImages(resDict);
- setupForms(resDict);
//----- recursively scan XObjects
resDict->lookup("XObject", &xObjDict);
@@ -1648,6 +1694,55 @@ void PSOutputDev::setupResources(Dict *resDict) {
inType3Char = gFalse;
}
patDict.free();
+
+ //----- recursively scan SMask transparency groups in ExtGState dicts
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+
+ // avoid infinite recursion on ExtGStates
+ skip = gFalse;
+ if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) {
+ ref0 = gsRef.getRef();
+ for (j = 0; j < xobjStack->getLength(); ++j) {
+ ref1 = *(Ref *)xobjStack->get(j);
+ if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
+ skip = gTrue;
+ break;
+ }
+ }
+ if (!skip) {
+ xobjStack->append(&ref0);
+ }
+ }
+ if (!skip) {
+
+ // process the ExtGState's SMask's transparency group's resource dict
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+
+ if (gsRef.isRef() && !skip) {
+ xobjStack->del(xobjStack->getLength() - 1);
+ }
+ gsRef.free();
+ }
+ }
+ gsDict.free();
+
+ setupForms(resDict);
}
void PSOutputDev::setupFonts(Dict *resDict) {
@@ -1681,8 +1776,8 @@ void PSOutputDev::setupFonts(Dict *resDict) {
}
void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
+ PSFontInfo *fi;
GfxFontLoc *fontLoc;
- GString *psName;
GBool subst;
char buf[16];
UnicodeMap *uMap;
@@ -1693,123 +1788,101 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
int i, j;
// check if font is already set up
- for (i = 0; i < fontIDLen; ++i) {
- if (fontIDs[i].num == font->getID()->num &&
- fontIDs[i].gen == font->getID()->gen) {
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
return;
}
}
- // add entry to fontIDs list
- if (fontIDLen >= fontIDSize) {
- fontIDSize += 64;
- fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
- }
- fontIDs[fontIDLen++] = *font->getID();
+ // add fontInfo entry
+ fi = new PSFontInfo(*font->getID());
+ fontInfo->append(fi);
- psName = NULL;
xs = ys = 1;
subst = gFalse;
if (font->getType() == fontType3) {
- psName = GString::format("T3_{0:d}_{1:d}",
- font->getID()->num, font->getID()->gen);
- setupType3Font(font, psName, parentResDict);
+ fi->ff = setupType3Font(font, parentResDict);
} else {
- fontLoc = font->locateFont(xref, gTrue);
- switch (fontLoc->locType) {
- case gfxFontLocEmbedded:
- switch (fontLoc->fontType) {
- case fontType1:
- // this assumes that the PS font name matches the PDF font name
- psName = font->getEmbeddedFontName()->copy();
- setupEmbeddedType1Font(&fontLoc->embFontID, psName);
- break;
- case fontType1C:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName);
- break;
- case fontType1COT:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName);
- break;
- case fontTrueType:
- case fontTrueTypeOT:
- psName = makePSFontName(font, font->getID());
- setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName);
- break;
- case fontCIDType0C:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName);
- break;
- case fontCIDType2:
- case fontCIDType2OT:
- psName = makePSFontName(font, font->getID());
- //~ should check to see if font actually uses vertical mode
- setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, gTrue);
- break;
- case fontCIDType0COT:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName);
- break;
- default:
- break;
- }
- break;
- case gfxFontLocExternal:
- //~ add cases for external 16-bit fonts
- switch (fontLoc->fontType) {
- case fontType1:
- if (font->getName()) {
- // this assumes that the PS font name matches the PDF font name
- psName = font->getName()->copy();
- } else {
- //~ this won't work -- the PS font name won't match
- psName = makePSFontName(font, font->getID());
+ if ((fontLoc = font->locateFont(xref, gTrue))) {
+ switch (fontLoc->locType) {
+ case gfxFontLocEmbedded:
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID);
+ break;
+ case fontType1C:
+ fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontType1COT:
+ fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType0C:
+ fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID,
+ gTrue);
+ break;
+ case fontCIDType0COT:
+ fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID);
+ break;
+ default:
+ break;
}
- setupExternalType1Font(fontLoc->path, psName);
- break;
- case fontTrueType:
- case fontTrueTypeOT:
- psName = makePSFontName(font, font->getID());
- setupExternalTrueTypeFont(font, fontLoc->path, psName);
break;
- case fontCIDType2:
- case fontCIDType2OT:
- psName = makePSFontName(font, font->getID());
- //~ should check to see if font actually uses vertical mode
- setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, gTrue);
+ case gfxFontLocExternal:
+ //~ add cases for other external 16-bit fonts
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupExternalType1Font(font, fontLoc->path);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupExternalTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum, gTrue);
+ break;
+ default:
+ break;
+ }
break;
- default:
+ case gfxFontLocResident:
+ if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) {
+ // handle psFontPassthrough
+ fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType,
+ psFontFileResident);
+ fontFileInfo->add(fi->ff->psName, fi->ff);
+ }
break;
}
- break;
- case gfxFontLocResident:
- psName = fontLoc->path->copy();
- break;
}
- if (!psName) {
+ if (!fi->ff) {
if (font->isCIDFont()) {
error(errSyntaxError, -1,
- "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)",
+ "Couldn't find a font for '{0:s}' ('{1:s}' character collection)",
font->getName() ? font->getName()->getCString()
: "(unnamed)",
((GfxCIDFont *)font)->getCollection()
? ((GfxCIDFont *)font)->getCollection()->getCString()
: "(unknown)");
- if (font16EncLen >= font16EncSize) {
- font16EncSize += 16;
- font16Enc = (PSFont16Enc *)greallocn(font16Enc,
- font16EncSize,
- sizeof(PSFont16Enc));
- }
- font16Enc[font16EncLen].fontID = *font->getID();
- font16Enc[font16EncLen].enc = NULL;
- ++font16EncLen;
} else {
error(errSyntaxError, -1,
- "Couldn't find a font to substitute for '{0:s}'",
+ "Couldn't find a font for '{0:s}'",
font->getName() ? font->getName()->getCString()
: "(unnamed)");
}
@@ -1843,23 +1916,14 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
if (fontLoc->locType == gfxFontLocResident &&
fontLoc->fontType >= fontCIDType0) {
subst = gTrue;
- if (font16EncLen >= font16EncSize) {
- font16EncSize += 16;
- font16Enc = (PSFont16Enc *)greallocn(font16Enc,
- font16EncSize,
- sizeof(PSFont16Enc));
- }
- font16Enc[font16EncLen].fontID = *font->getID();
if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
- font16Enc[font16EncLen].enc = fontLoc->encoding->copy();
+ 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);
- font16Enc[font16EncLen].enc = NULL;
}
- ++font16EncLen;
}
delete fontLoc;
@@ -1869,16 +1933,16 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
if (font->isCIDFont()) {
if (level == psLevel3 || level == psLevel3Sep) {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
- font->getID()->num, font->getID()->gen, psName,
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
- font->getID()->num, font->getID()->gen, psName,
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
}
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
- font->getID()->num, font->getID()->gen, psName, xs, ys);
+ font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys);
for (i = 0; i < 256; i += 8) {
writePS((char *)((i == 0) ? "[ " : " "));
for (j = 0; j < 8; ++j) {
@@ -1903,25 +1967,29 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
}
writePS("pdfMakeFont\n");
}
-
- delete psName;
}
-void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) {
static char hexChar[17] = "0123456789abcdef";
- Object refObj, strObj, obj1, obj2, obj3;
+ GString *psName;
+ PSFontFileInfo *ff;
+ Object refObj, strObj, obj1, obj2;
Dict *dict;
- int length1, length2, length3;
+ int length1, length2;
int c;
- int start[4];
+ int start[6];
GBool binMode;
- int i;
+ int n, i;
// check if font is already embedded
- if (fontNames->lookupInt(psName)) {
- return;
+ if ((ff = (PSFontFileInfo *)
+ fontFileInfo->lookup(font->getEmbeddedFontName()))) {
+ return ff;
}
- fontNames->add(psName->copy(), 1);
+
+ // generate name
+ // (this assumes that the PS font name matches the PDF font name)
+ psName = font->getEmbeddedFontName()->copy();
// get the font stream and info
refObj.initRef(id->num, id->gen);
@@ -1938,21 +2006,17 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
}
dict->lookup("Length1", &obj1);
dict->lookup("Length2", &obj2);
- dict->lookup("Length3", &obj3);
- if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
+ if (!obj1.isInt() || !obj2.isInt()) {
error(errSyntaxError, -1,
"Missing length fields in embedded font stream dictionary");
obj1.free();
obj2.free();
- obj3.free();
goto err1;
}
length1 = obj1.getInt();
length2 = obj2.getInt();
- length3 = obj3.getInt();
obj1.free();
obj2.free();
- obj3.free();
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -1960,91 +2024,164 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
embFontList->append(psName->getCString());
embFontList->append("\n");
- // copy ASCII portion of font
+ // check for PFB format
strObj.streamReset();
- for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
- writePSChar(c);
- }
+ start[0] = strObj.streamGetChar();
+ start[1] = strObj.streamGetChar();
+ if (start[0] == 0x80 && start[1] == 0x01) {
+ error(errSyntaxWarning, -1, "Embedded Type 1 font is in PFB format");
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ start[i] = strObj.streamGetChar();
+ }
+ if (start[2] == EOF || start[3] == EOF ||
+ start[4] == EOF || start[5] == EOF) {
+ break;
+ }
+ n = start[2] + (start[3] << 8) + (start[4] << 16) + (start[5] << 24);
+ if (start[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ start[0] = strObj.streamGetChar();
+ start[1] = strObj.streamGetChar();
+ if (start[0] == EOF || start[1] == EOF ||
+ (start[0] == 0x80 && start[1] == 0x03)) {
+ break;
+ } else if (!(start[0] == 0x80 &&
+ (start[1] == 0x01 || start[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in embedded font stream");
+ break;
+ }
+ }
+ writePSChar('\n');
- // figure out if encrypted portion is binary or ASCII
- binMode = gFalse;
- for (i = 0; i < 4; ++i) {
- start[i] = strObj.streamGetChar();
- if (start[i] == EOF) {
- error(errSyntaxError, -1,
- "Unexpected end of file in embedded font stream");
- goto err1;
+ // plain text (PFA) format
+ } else {
+
+ // copy ASCII portion of font
+ writePSChar(start[0]);
+ writePSChar(start[1]);
+ for (i = 2; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
+ writePSChar(c);
}
- if (!((start[i] >= '0' && start[i] <= '9') ||
- (start[i] >= 'A' && start[i] <= 'F') ||
- (start[i] >= 'a' && start[i] <= 'f')))
- binMode = gTrue;
- }
- // convert binary data to ASCII
- if (binMode) {
+ // figure out if encrypted portion is binary or ASCII
+ binMode = gFalse;
for (i = 0; i < 4; ++i) {
- writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
- writePSChar(hexChar[start[i] & 0x0f]);
+ start[i] = strObj.streamGetChar();
+ if (start[i] == EOF) {
+ error(errSyntaxError, -1,
+ "Unexpected end of file in embedded font stream");
+ goto err1;
+ }
+ if (!((start[i] >= '0' && start[i] <= '9') ||
+ (start[i] >= 'A' && start[i] <= 'F') ||
+ (start[i] >= 'a' && start[i] <= 'f')))
+ binMode = gTrue;
}
+
+ // convert binary data to ASCII
+ if (binMode) {
+ for (i = 0; i < 4; ++i) {
+ writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
+ writePSChar(hexChar[start[i] & 0x0f]);
+ }
#if 0 // this causes trouble for various PostScript printers
- // if Length2 is incorrect (too small), font data gets chopped, so
- // we take a few extra characters from the trailer just in case
- length2 += length3 >= 8 ? 8 : length3;
+ // if Length2 is incorrect (too small), font data gets chopped, so
+ // we take a few extra characters from the trailer just in case
+ length2 += length3 >= 8 ? 8 : length3;
#endif
- while (i < length2) {
- if ((c = strObj.streamGetChar()) == EOF) {
- break;
+ while (i < length2) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (++i % 32 == 0) {
+ writePSChar('\n');
+ }
}
- writePSChar(hexChar[(c >> 4) & 0x0f]);
- writePSChar(hexChar[c & 0x0f]);
- if (++i % 32 == 0) {
+ if (i % 32 > 0) {
writePSChar('\n');
}
- }
- if (i % 32 > 0) {
- writePSChar('\n');
- }
- // already in ASCII format -- just copy it
- } else {
- for (i = 0; i < 4; ++i) {
- writePSChar(start[i]);
- }
- for (i = 4; i < length2; ++i) {
- if ((c = strObj.streamGetChar()) == EOF) {
- break;
+ // already in ASCII format -- just copy it
+ } else {
+ for (i = 0; i < 4; ++i) {
+ writePSChar(start[i]);
+ }
+ for (i = 4; i < length2; ++i) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(c);
}
- writePSChar(c);
}
- }
- // write padding and "cleartomark"
- for (i = 0; i < 8; ++i) {
- writePS("00000000000000000000000000000000"
- "00000000000000000000000000000000\n");
+ // write padding and "cleartomark"
+ for (i = 0; i < 8; ++i) {
+ writePS("00000000000000000000000000000000"
+ "00000000000000000000000000000000\n");
+ }
+ writePS("cleartomark\n");
}
- writePS("cleartomark\n");
// ending comment
writePS("%%EndResource\n");
+ strObj.streamClose();
+ strObj.free();
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+
err1:
strObj.streamClose();
strObj.free();
+ delete psName;
+ return NULL;
}
-//~ This doesn't handle .pfb files or binary eexec data (which only
-//~ happens in pfb files?).
-void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
+PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font,
+ GString *fileName) {
+ static char hexChar[17] = "0123456789abcdef";
+ GString *psName;
+ PSFontFileInfo *ff;
FILE *fontFile;
- int c;
+ int buf[6];
+ int c, n, i;
- // check if font is already embedded
- if (fontNames->lookupInt(psName)) {
- return;
+ if (font->getName()) {
+ // check if font is already embedded
+ if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) {
+ return ff;
+ }
+ // this assumes that the PS font name matches the PDF font name
+ psName = font->getName()->copy();
+ } else {
+ // generate name
+ //~ this won't work -- the PS font name won't match
+ psName = makePSFontName(font, font->getID());
}
- fontNames->add(psName->copy(), 1);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2052,44 +2189,98 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
embFontList->append(psName->getCString());
embFontList->append("\n");
- // copy the font file
+ // open the font file
if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
error(errIO, -1, "Couldn't open external font file");
- return;
+ return NULL;
}
- while ((c = fgetc(fontFile)) != EOF) {
- writePSChar(c);
+
+ // check for PFB format
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == 0x80 && buf[1] == 0x01) {
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ buf[i] = fgetc(fontFile);
+ }
+ if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) {
+ break;
+ }
+ n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24);
+ if (buf[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == EOF || buf[1] == EOF ||
+ (buf[0] == 0x80 && buf[1] == 0x03)) {
+ break;
+ } else if (!(buf[0] == 0x80 &&
+ (buf[1] == 0x01 || buf[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in external font file");
+ break;
+ }
+ }
+ writePSChar('\n');
+
+ // plain text (PFA) format
+ } else {
+ writePSChar(buf[0]);
+ writePSChar(buf[1]);
+ while ((c = fgetc(fontFile)) != EOF) {
+ writePSChar(c);
+ }
}
+
fclose(fontFile);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2109,32 +2300,35 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2144,7 +2338,7 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
// convert it to a Type 1 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
ffTT->convertToType1(psName->getCString(), NULL, gTrue,
outputFunc, outputStream);
@@ -2156,14 +2350,50 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ return NULL;
+ }
+ if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
+ gfree(fontBuf);
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ gfree(fontBuf);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2172,38 +2402,57 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
embFontList->append("\n");
// convert it to a Type 42 font
- if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
- ffTT->convertToType42(psName->getCString(),
- ((Gfx8BitFont *)font)->getHasEncoding()
- ? ((Gfx8BitFont *)font)->getEncoding()
- : (char **)NULL,
- codeToGID, outputFunc, outputStream);
- if (codeToGID) {
- if (font8InfoLen >= font8InfoSize) {
- font8InfoSize += 16;
- font8Info = (PSFont8Info *)greallocn(font8Info,
- font8InfoSize,
- sizeof(PSFont8Info));
- }
- font8Info[font8InfoLen].fontID = *font->getID();
- font8Info[font8InfoLen].codeToGID = codeToGID;
- ++font8InfoLen;
- }
- delete ffTT;
- }
- gfree(fontBuf);
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
+ gfree(fontBuf);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum) {
+ GString *psName;
+ PSFontFileInfo *ff;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2212,55 +2461,45 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
embFontList->append("\n");
// convert it to a Type 42 font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
- ffTT->convertToType42(psName->getCString(),
- ((Gfx8BitFont *)font)->getHasEncoding()
- ? ((Gfx8BitFont *)font)->getEncoding()
- : (char **)NULL,
- codeToGID, outputFunc, outputStream);
- if (codeToGID) {
- if (font8InfoLen >= font8InfoSize) {
- font8InfoSize += 16;
- font8Info = (PSFont8Info *)greallocn(font8Info,
- font8InfoSize,
- sizeof(PSFont8Info));
- }
- font8Info[font8InfoLen].fontID = *font->getID();
- font8Info[font8InfoLen].codeToGID = codeToGID;
- ++font8InfoLen;
- }
- delete ffTT;
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2287,14 +2526,46 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName,
- GBool needVerticalMetrics) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont(
+ GfxFont *font, Ref *id,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
+ int *codeToGID;
+ int codeToGIDLen;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ codeToGID = ((GfxCIDFont *)font)->getCIDToGID();
+ codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ((!ff->codeToGID && !codeToGID) ||
+ (ff->codeToGID && codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2304,19 +2575,17 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
ffTT->convertToCIDType2(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
} else {
// otherwise: use a non-CID composite font
ffTT->convertToType0(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
}
@@ -2327,18 +2596,104 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if (codeToGIDLen) {
+ ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int));
+ ff->codeToGIDLen = codeToGIDLen;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font,
- GString *fileName,
- GString *psName,
- GBool needVerticalMetrics) {
+PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont(
+ GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
FoFiTrueType *ffTT;
int *codeToGID;
int codeToGIDLen;
CharCodeToUnicode *ctu;
Unicode uBuf[8];
int cmap, code;
+ GHashIter *iter;
+
+ // create a code-to-GID mapping, via Unicode
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ delete ffTT;
+ return NULL;
+ }
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
+ if ((ffTT->getCmapPlatform(cmap) == 3 &&
+ ffTT->getCmapEncoding(cmap) == 1) ||
+ ffTT->getCmapPlatform(cmap) == 0) {
+ break;
+ }
+ }
+ if (cmap >= ffTT->getNumCmaps()) {
+ error(errSyntaxError, -1,
+ "Couldn't find a Unicode cmap in font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ ctu->decRefCnt();
+ delete ffTT;
+ return NULL;
+ }
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ codeToGIDLen = 65536;
+ } else {
+ codeToGIDLen = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ for (code = 0; code < codeToGIDLen; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = 0;
+ }
+ }
+ ctu->decRefCnt();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ff->codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // check for embedding permission
+ if (ffTT->getEmbeddingRights() < 1) {
+ error(errSyntaxError, -1,
+ "TrueType font '{0:s}' does not allow embedding",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ gfree(codeToGID);
+ delete ffTT;
+ return NULL;
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2348,90 +2703,55 @@ void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font,
// convert it to a Type 0 font
//~ this should use fontNum to load the correct font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
-
- // check for embedding permission
- if (ffTT->getEmbeddingRights() >= 1) {
-
- // create a CID-to-GID mapping, via Unicode
- if ((ctu = ((GfxCIDFont *)font)->getToUnicode())) {
- // look for a Unicode cmap
- for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
- if ((ffTT->getCmapPlatform(cmap) == 3 &&
- ffTT->getCmapEncoding(cmap) == 1) ||
- ffTT->getCmapPlatform(cmap) == 0) {
- break;
- }
- }
- if (cmap < ffTT->getNumCmaps()) {
- // map CID -> Unicode -> GID
- codeToGIDLen = ctu->getLength();
- codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
- for (code = 0; code < codeToGIDLen; ++code) {
- if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
- codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
- } else {
- codeToGID[code] = 0;
- }
- }
- if (globalParams->getPSLevel() >= psLevel3) {
- // Level 3: use a CID font
- ffTT->convertToCIDType2(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- } else {
- // otherwise: use a non-CID composite font
- ffTT->convertToType0(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- }
- gfree(codeToGID);
- }
- ctu->decRefCnt();
- } else {
- error(errSyntaxError, -1,
- "Couldn't find a mapping to Unicode for font '{0:s}'",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
- }
- } else {
- error(errSyntaxError, -1,
- "TrueType font '%s' does not allow embedding",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
-
- }
- delete ffTT;
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType2(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
}
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = codeToGIDLen;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
+ int n;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2441,7 +2761,7 @@ void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
@@ -2464,10 +2784,22 @@ void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) {
+ ff->codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int));
+ ff->codeToGIDLen = n;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
- Dict *parentResDict) {
+PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font,
+ Dict *parentResDict) {
+ PSFontFileInfo *ff;
+ GString *psName;
Dict *resDict;
Dict *charProcs;
Object charProc;
@@ -2477,6 +2809,10 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
GString *buf;
int i;
+ // generate name
+ psName = GString::format("T3_{0:d}_{1:d}",
+ font->getID()->num, font->getID()->gen);
+
// set up resources used by font
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
inType3Char = gTrue;
@@ -2528,7 +2864,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
writePS("/");
writePSName(charProcs->getKey(i));
writePS(" {\n");
- gfx->display(charProcs->getVal(i, &charProc));
+ gfx->display(charProcs->getValNF(i, &charProc));
charProc.free();
if (t3String) {
if (t3Cacheable) {
@@ -2558,6 +2894,10 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
// Make a unique PS font name, based on the names given in the PDF
@@ -2567,16 +2907,14 @@ GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
if ((s = font->getEmbeddedFontName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
}
if ((s = font->getName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
@@ -2591,7 +2929,6 @@ GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
psName->append('_')->append(s);
delete s;
}
- fontNames->add(psName->copy(), 1);
return psName;
}
@@ -2651,7 +2988,7 @@ void PSOutputDev::setupImages(Dict *resDict) {
}
void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
- GBool useRLE, useCompressed, useASCIIHex;
+ GBool useLZW, useRLE, useCompressed, useASCIIHex;
GString *s;
int c;
int size, line, col, i;
@@ -2660,21 +2997,27 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
//~ this does not correctly handle the DeviceN color space
//~ -- need to use DeviceNRecoder
if (level < psLevel2) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCIIHex = gTrue;
} else {
if (globalParams->getPSUncompressPreloadedImages()) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
} else {
s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
if (s) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gTrue;
delete s;
} else {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useCompressed = gFalse;
}
}
@@ -2683,7 +3026,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
if (useCompressed) {
str = str->getUndecodedStream();
}
- if (useRLE) {
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCIIHex) {
@@ -2722,9 +3067,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
// add one entry for the final line of data; add another entry
- // because the RunLengthDecode filter may read past the end
+ // because the LZWDecode/RunLengthDecode filter may read past the end
++size;
- if (useRLE) {
+ if (useLZW || useRLE) {
++size;
}
writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
@@ -2771,7 +3116,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
- if (useRLE) {
+ if (useLZW || useRLE) {
++line;
writePSFmt("{0:d} <> put\n", line);
} else {
@@ -2799,7 +3144,7 @@ void PSOutputDev::setupForms(Dict *resDict) {
xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
if (subtypeObj.isName("Form")) {
if (xObjRef.isRef()) {
- setupForm(xObjRef.getRef(), &xObj);
+ setupForm(&xObjRef, &xObj);
} else {
error(errSyntaxError, -1,
"Form in resource dict is not an indirect reference");
@@ -2814,7 +3159,7 @@ void PSOutputDev::setupForms(Dict *resDict) {
xObjDict.free();
}
-void PSOutputDev::setupForm(Ref id, Object *strObj) {
+void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
Dict *dict, *resDict;
Object matrixObj, bboxObj, resObj, obj1;
double m[6], bbox[4];
@@ -2824,7 +3169,8 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
// check if form is already defined
for (i = 0; i < formIDLen; ++i) {
- if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) {
+ if (formIDs[i].num == strRef->getRefNum() &&
+ formIDs[i].gen == strRef->getRefGen()) {
return;
}
}
@@ -2838,7 +3184,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
}
formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
}
- formIDs[formIDLen++] = id;
+ formIDs[formIDLen++] = strRef->getRef();
dict = strObj->streamGetDict();
@@ -2875,7 +3221,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
dict->lookup("Resources", &resObj);
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
- writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
+ writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen());
writePS("q\n");
writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -2885,7 +3231,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx = new Gfx(doc, this, resDict, &box, &box);
- gfx->display(strObj);
+ gfx->display(strRef);
delete gfx;
writePS("Q\n");
@@ -2905,6 +3251,7 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
GBool rasterize;
#if HAVE_SPLASH
GBool mono;
+ GBool useLZW;
double dpi;
SplashOutputDev *splashOut;
SplashColor paperColor;
@@ -2915,10 +3262,11 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
Object obj;
Guchar *p;
Guchar col[4];
+ char buf[4096];
double hDPI2, vDPI2;
double m0, m1, m2, m3, m4, m5;
int nStripes, stripeH, stripeY;
- int c, w, h, x, y, comp, i;
+ int w, h, x, y, comp, i, n;
#endif
if (globalParams->getPSAlwaysRasterize()) {
@@ -2939,6 +3287,7 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
// get the rasterization parameters
dpi = globalParams->getPSRasterResolution();
mono = globalParams->getPSRasterMono();
+ useLZW = globalParams->getPSLZW();
// start the PS page
page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse,
@@ -2987,8 +3336,8 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
}
- nStripes = (int)ceil((double)(sliceW * sliceH) /
- (double)rasterizationSliceSize);
+ nStripes = (int)ceil(((double)sliceW * (double)sliceH) /
+ (double)globalParams->getPSRasterSliceSize());
stripeH = (sliceH + nStripes - 1) / nStripes;
// render the stripes
@@ -3094,21 +3443,29 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
} else {
writePS(" /ASCII85Decode filter\n");
}
- writePS(" /RunLengthDecode filter\n");
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else {
+ writePS(" /RunLengthDecode filter\n");
+ }
writePS(">>\n");
writePS("image\n");
obj.initNull();
p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj);
- str = new RunLengthEncoder(str0);
+ if (useLZW) {
+ str = new LZWEncoder(str0);
+ } else {
+ str = new RunLengthEncoder(str0);
+ }
if (globalParams->getPSASCIIHex()) {
str = new ASCIIHexEncoder(str);
} else {
str = new ASCII85Encoder(str);
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
delete str;
@@ -3148,8 +3505,13 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
if (paperMatch) {
page = doc->getCatalog()->getPage(pageNum);
imgLLX = imgLLY = 0;
- imgURX = (int)ceil(page->getMediaWidth());
- imgURY = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ imgURX = (int)ceil(page->getCropWidth());
+ imgURY = (int)ceil(page->getCropHeight());
+ } else {
+ imgURX = (int)ceil(page->getMediaWidth());
+ imgURY = (int)ceil(page->getMediaHeight());
+ }
if (state->getRotate() == 90 || state->getRotate() == 270) {
t = imgURX;
imgURX = imgURY;
@@ -3160,6 +3522,9 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
}
writePS("%%BeginPageSetup\n");
}
+ if (mode != psModeForm) {
+ writePS("xpdf begin\n");
+ }
// underlays
if (underlayCbk) {
@@ -3356,6 +3721,7 @@ void PSOutputDev::endPage() {
}
writePS("%%PageTrailer\n");
writePageTrailer();
+ writePS("end\n");
}
}
@@ -3371,8 +3737,13 @@ void PSOutputDev::restoreState(GfxState *state) {
void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
double m21, double m22, double m31, double m32) {
- writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
- m11, m12, m21, m22, m31, m32);
+ if (fabs(m11 * m22 - m12 * m21) < 0.00001) {
+ // avoid a singular (or close-to-singular) matrix
+ writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", m31, m32);
+ } else {
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
+ m11, m12, m21, m22, m31, m32);
+ }
}
void PSOutputDev::updateLineDash(GfxState *state) {
@@ -3737,7 +4108,7 @@ void PSOutputDev::eoFill(GfxState *state) {
writePS("f*\n");
}
-void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -3770,6 +4141,7 @@ void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx2 = new Gfx(doc, this, resDict, &box, NULL);
+ gfx2->takeContentStreamStack(gfx);
writePS("/x {\n");
if (paintType == 2) {
writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
@@ -3785,7 +4157,7 @@ void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
}
inType3Char = gTrue;
++numTilingPatterns;
- gfx2->display(str);
+ gfx2->display(strRef);
--numTilingPatterns;
inType3Char = gFalse;
writePS("} def\n");
@@ -3944,7 +4316,7 @@ GBool PSOutputDev::radialShadedFill(GfxState *state,
double xMin, yMin, xMax, yMax;
double x0, y0, r0, x1, y1, r1, t0, t1;
double xa, ya, ra;
- double sz, sMin, sMax, h, ta;
+ double sMin, sMax, h, ta;
double sLeft, sRight, sTop, sBottom, sZero, sDiag;
GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
GBool haveSMin, haveSMax;
@@ -3970,18 +4342,14 @@ GBool PSOutputDev::radialShadedFill(GfxState *state,
if (h == 0) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else if (r1 - r0 == 0) {
enclosed = gFalse;
theta = 0;
- sz = 0; // make gcc happy
} else if (fabs(r1 - r0) >= h) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else {
enclosed = gFalse;
- sz = -r0 / (r1 - r0);
theta = asin((r1 - r0) / h);
}
if (enclosed) {
@@ -4267,8 +4635,9 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
int wMode;
int *codeToGID;
GString *s2;
- double dx, dy, originX, originY;
+ double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0;
char *p;
+ PSFontInfo *fi;
UnicodeMap *uMap;
CharCode code;
Unicode u[8];
@@ -4292,30 +4661,32 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
}
wMode = font->getWMode();
+ fi = NULL;
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
+ break;
+ }
+ fi = NULL;
+ }
+
// check for a subtitute 16-bit font
uMap = NULL;
codeToGID = NULL;
if (font->isCIDFont()) {
- for (i = 0; i < font16EncLen; ++i) {
- if (font->getID()->num == font16Enc[i].fontID.num &&
- font->getID()->gen == font16Enc[i].fontID.gen) {
- if (!font16Enc[i].enc) {
- // font substitution failed, so don't output any text
- return;
- }
- uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
- break;
- }
+ if (!(fi && fi->ff)) {
+ // font substitution failed, so don't output any text
+ return;
+ }
+ if (fi->ff->encoding) {
+ uMap = globalParams->getUnicodeMap(fi->ff->encoding);
}
- // check for a code-to-GID map
+ // check for an 8-bit code-to-GID map
} else {
- for (i = 0; i < font8InfoLen; ++i) {
- if (font->getID()->num == font8Info[i].fontID.num &&
- font->getID()->gen == font8Info[i].fontID.gen) {
- codeToGID = font8Info[i].codeToGID;
- break;
- }
+ if (fi && fi->ff) {
+ codeToGID = fi->ff->codeToGID;
}
}
@@ -4326,10 +4697,18 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
s2 = new GString();
dxdySize = font->isCIDFont() ? 8 : s->getLength();
dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
+ originX0 = originY0 = 0; // make gcc happy
while (len > 0) {
n = font->getNextChar(p, len, &code,
u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
&dx, &dy, &originX, &originY);
+ //~ this doesn't handle the case where the origin offset changes
+ //~ within a string of characters -- which could be fixed by
+ //~ modifying dx,dy as needed for each character
+ if (p == s->getCString()) {
+ originX0 = originX;
+ originY0 = originY;
+ }
dx *= state->getFontSize();
dy *= state->getFontSize();
if (wMode) {
@@ -4389,8 +4768,14 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
if (uMap) {
uMap->decRefCnt();
}
+ originX0 *= state->getFontSize();
+ originY0 *= state->getFontSize();
+ state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0);
if (nChars > 0) {
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0);
+ }
writePSString(s2);
writePS("\n[");
for (i = 0; i < 2 * nChars; ++i) {
@@ -4400,6 +4785,9 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
writePSFmt("{0:.6g}", dxdy[i]);
}
writePS("] Tj\n");
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0);
+ }
}
gfree(dxdy);
delete s2;
@@ -4418,7 +4806,7 @@ void PSOutputDev::endTextObject(GfxState *state) {
void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
int len;
len = height * ((width + 7) / 8);
@@ -4442,7 +4830,8 @@ void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4474,7 +4863,7 @@ void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4684,10 +5073,11 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
GBool emitRect, addRect, extendRect;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i, j, x0, x1, y, maskXor;
@@ -4821,14 +5211,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
rectsOut[i].x0, rectsOut[i].y0,
rectsOut[i].x1 - rectsOut[i].x0,
rectsOut[i].y1 - rectsOut[i].y0);
}
- writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4923,14 +5313,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
rectsOut[i].x0, rectsOut[i].y0,
rectsOut[i].x1 - rectsOut[i].x0,
rectsOut[i].y1 - rectsOut[i].y0);
}
- writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4951,7 +5341,11 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -4994,8 +5388,8 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5065,7 +5459,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5073,11 +5467,17 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5087,7 +5487,9 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5119,8 +5521,10 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5141,12 +5545,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
n = 0;
} else {
// need to read the stream to count characters -- the length
- // is data-dependent (because of ASCII and RLE filters)
+ // is data-dependent (because of ASCII and LZW/RunLength
+ // filters)
str->reset();
n = 0;
- while ((c = str->getChar()) != EOF) {
- ++n;
- }
+ do {
+ i = str->discardChars(4096);
+ n += i;
+ } while (i == 4096);
str->close();
}
// +6/7 for "pdfIm\n" / "pdfImM\n"
@@ -5170,8 +5576,8 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5185,7 +5591,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
#endif
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -5204,18 +5610,20 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
Stream *str2;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
- GBool maskUseRLE, maskUseASCII, maskUseCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
GString *maskFilters;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i;
useASCIIHex = globalParams->getPSASCIIHex();
- useRLE = useASCII = useCompressed = gFalse; // make gcc happy
- maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy
+ useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy
+ maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy
+ maskUseCompressed = gFalse; // make gcc happy
maskFilters = NULL; // make gcc happy
// explicit masking
@@ -5225,17 +5633,23 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseCompressed = gFalse;
maskUseASCII = gFalse;
} else {
s = maskStr->getPSFilter(3, " ");
if (!s) {
- maskUseRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
maskUseASCII = !(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gFalse;
} else {
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseASCII = maskStr->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gTrue;
@@ -5246,7 +5660,9 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
maskFilters->appendf(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskFilters->append(" /LZWDecode filter\n");
+ } else if (maskUseRLE) {
maskFilters->append(" /RunLengthDecode filter\n");
}
if (maskUseCompressed) {
@@ -5263,11 +5679,13 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePS(maskFilters->getCString());
writePS("pdfMask\n");
- // add RunLengthEncode and ASCIIHex/85 encode filters
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
if (maskUseCompressed) {
maskStr = maskStr->getUndecodedStream();
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskStr = new LZWEncoder(maskStr);
+ } else if (maskUseRLE) {
maskStr = new RunLengthEncoder(maskStr);
}
if (maskUseASCII) {
@@ -5280,15 +5698,15 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
maskStr->reset();
- while ((c = maskStr->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
maskStr->close();
writePSChar('\n');
writePS("%-EOD-\n");
// delete encoders
- if (maskUseRLE || maskUseASCII) {
+ if (maskUseLZW || maskUseRLE || maskUseASCII) {
delete maskStr;
}
}
@@ -5305,7 +5723,11 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -5348,8 +5770,8 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5436,7 +5858,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5444,11 +5866,17 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5458,7 +5886,9 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5538,8 +5968,10 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5552,8 +5984,8 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5562,7 +5994,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePS("%-EOD-\n");
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -6267,6 +6699,10 @@ void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
double llx, double lly, double urx, double ury) {
+ if (t3String) {
+ error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc");
+ return;
+ }
t3WX = wx;
t3WY = wy;
t3LLX = llx;
@@ -6286,7 +6722,8 @@ void PSOutputDev::drawForm(Ref id) {
void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
Stream *str;
- int c;
+ char buf[4096];
+ int n;
if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
str = level1Stream;
@@ -6294,8 +6731,8 @@ void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
str = psStream;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
}
@@ -6462,6 +6899,14 @@ void PSOutputDev::writePSChar(char c) {
}
}
+void PSOutputDev::writePSBlock(char *s, int len) {
+ if (t3String) {
+ t3String->append(s, len);
+ } else {
+ (*outputFunc)(outputStream, s, len);
+ }
+}
+
void PSOutputDev::writePS(const char *s) {
if (t3String) {
t3String->append(s);
@@ -6564,7 +7009,9 @@ GString *PSOutputDev::filterPSName(GString *name) {
// Write a DSC-compliant <textline>.
void PSOutputDev::writePSTextLine(GString *s) {
- int i, j, step;
+ TextString *ts;
+ Unicode *u;
+ int i, j;
int c;
// - DSC comments must be printable ASCII; control chars and
@@ -6574,17 +7021,10 @@ void PSOutputDev::writePSTextLine(GString *s) {
// for the keyword, which was emitted by the caller)
// - lines that start with a left paren are treated as <text>
// instead of <textline>, so we escape a leading paren
- if (s->getLength() >= 2 &&
- (s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- i = 3;
- step = 2;
- } else {
- i = 0;
- step = 1;
- }
- for (j = 0; i < s->getLength() && j < 200; i += step) {
- c = s->getChar(i) & 0xff;
+ ts = new TextString(s);
+ u = ts->getUnicode();
+ for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) {
+ c = u[i] & 0xff;
if (c == '\\') {
writePS("\\\\");
j += 2;
@@ -6597,4 +7037,5 @@ void PSOutputDev::writePSTextLine(GString *s) {
}
}
writePS("\n");
+ delete ts;
}
diff --git a/xpdf/PSOutputDev.h b/xpdf/PSOutputDev.h
index e528f8c..7118830 100644
--- a/xpdf/PSOutputDev.h
+++ b/xpdf/PSOutputDev.h
@@ -30,11 +30,9 @@ class GfxFont;
class GfxColorSpace;
class GfxSeparationColorSpace;
class PDFRectangle;
-struct PST1FontName;
-struct PSFont8Info;
-struct PSFont16Enc;
class PSOutCustomColor;
class PSOutputDev;
+class PSFontFileInfo;
//------------------------------------------------------------------------
// PSOutputDev
@@ -92,6 +90,9 @@ public:
// Check if file was successfully created.
virtual GBool isOk() { return ok; }
+ // Returns false if there have been any errors on the output stream.
+ GBool checkIO();
+
//---- get info about output device
// Does this device use upside-down coordinates?
@@ -196,7 +197,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -218,15 +219,15 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
@@ -262,6 +263,7 @@ public:
{ overlayCbk = cbk; overlayCbkData = data; }
void writePSChar(char c);
+ void writePSBlock(char *s, int len);
void writePS(const char *s);
void writePSFmt(const char *fmt, ...);
void writePSString(GString *s);
@@ -277,27 +279,27 @@ private:
void setupResources(Dict *resDict);
void setupFonts(Dict *resDict);
void setupFont(GfxFont *font, Dict *parentResDict);
- void setupEmbeddedType1Font(Ref *id, GString *psName);
- void setupExternalType1Font(GString *fileName, GString *psName);
- void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName);
- void setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
- GString *psName);
- void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName,
- GBool needVerticalMetrics);
- void setupExternalCIDTrueTypeFont(GfxFont *font,
- GString *fileName,
- GString *psName,
- GBool needVerticalMetrics);
- void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName);
- void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);
+ PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName);
+ PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
+ int fontNum);
+ PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict);
GString *makePSFontName(GfxFont *font, Ref *id);
void setupImages(Dict *resDict);
void setupImage(Ref id, Stream *str, GBool mask);
void setupForms(Dict *resDict);
- void setupForm(Ref id, Object *strObj);
+ void setupForm(Object *strRef, Object *strObj);
void addProcessColor(double c, double m, double y, double k);
void addCustomColor(GfxSeparationColorSpace *sepCS);
void doPath(GfxPath *path);
@@ -358,19 +360,8 @@ private:
PDFDoc *doc;
XRef *xref; // the xref table for this PDF file
- Ref *fontIDs; // list of object IDs of all used fonts
- int fontIDLen; // number of entries in fontIDs array
- int fontIDSize; // size of fontIDs array
- GHash *fontNames; // all used font names
- PST1FontName *t1FontNames; // font names for Type 1/1C fonts
- int t1FontNameLen; // number of entries in t1FontNames array
- int t1FontNameSize; // size of t1FontNames array
- PSFont8Info *font8Info; // info for 8-bit fonts
- int font8InfoLen; // number of entries in font8Info array
- int font8InfoSize; // size of font8Info array
- PSFont16Enc *font16Enc; // encodings for substitute 16-bit fonts
- int font16EncLen; // number of entries in font16Enc array
- int font16EncSize; // size of font16Enc array
+ GList *fontInfo; // info for each font [PSFontInfo]
+ GHash *fontFileInfo; // info for each font file [PSFontFileInfo]
Ref *imgIDs; // list of image IDs for in-memory images
int imgIDLen; // number of entries in imgIDs array
int imgIDSize; // size of imgIDs array
diff --git a/xpdf/Page.cc b/xpdf/Page.cc
index 189d2a2..635ee7a 100644
--- a/xpdf/Page.cc
+++ b/xpdf/Page.cc
@@ -25,6 +25,7 @@
#include "Gfx.h"
#include "GfxState.h"
#include "Annot.h"
+#include "Form.h"
#endif
#include "Error.h"
#include "Catalog.h"
@@ -123,6 +124,15 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
dict->lookup("Metadata", &metadata);
dict->lookup("PieceInfo", &pieceInfo);
dict->lookup("SeparationInfo", &separationInfo);
+ if (dict->lookup("UserUnit", &obj1)->isNum()) {
+ userUnit = obj1.getNum();
+ if (userUnit < 1) {
+ userUnit = 1;
+ }
+ } else {
+ userUnit = 1;
+ }
+ obj1.free();
// resource dictionary
dict->lookup("Resources", &obj1);
@@ -312,7 +322,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
Gfx *gfx;
Object obj;
Annots *annotList;
- Dict *acroForm;
+ Form *form;
int i;
if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
@@ -347,8 +357,10 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
contents.fetch(xref, &obj);
if (!obj.isNull()) {
gfx->saveState();
- gfx->display(&obj);
- gfx->restoreState();
+ gfx->display(&contents);
+ while (gfx->getState()->hasSaves()) {
+ gfx->restoreState();
+ }
} else {
// empty pages need to call dump to do any setup required by the
// OutputDev
@@ -356,20 +368,11 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
}
obj.free();
- // draw annotations
+ // draw (non-form) annotations
if (globalParams->getDrawAnnotations()) {
annotList = new Annots(doc, getAnnots(&obj));
obj.free();
- acroForm = doc->getCatalog()->getAcroForm()->isDict() ?
- doc->getCatalog()->getAcroForm()->getDict() : NULL;
- if (acroForm) {
- if (acroForm->lookup("NeedAppearances", &obj)) {
- if (obj.isBool() && obj.getBool()) {
- annotList->generateAppearances();
- }
- }
- obj.free();
- }
+ annotList->generateAnnotAppearances();
if (annotList->getNumAnnots() > 0) {
if (globalParams->getPrintCommands()) {
printf("***** Annotations\n");
@@ -382,6 +385,12 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
delete annotList;
}
+ // draw form fields
+ if ((form = doc->getCatalog()->getForm())) {
+ form->draw(num, gfx, printing);
+ out->dump();
+ }
+
delete gfx;
#endif
}
@@ -459,6 +468,7 @@ void Page::processLinks(OutputDev *out) {
delete links;
}
+#ifndef PDF_PARSER_ONLY
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
int rotate, GBool useMediaBox, GBool upsideDown) {
GfxState *state;
@@ -478,3 +488,4 @@ void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
}
delete state;
}
+#endif
diff --git a/xpdf/Page.h b/xpdf/Page.h
index 102eca8..0e7729e 100644
--- a/xpdf/Page.h
+++ b/xpdf/Page.h
@@ -77,6 +77,7 @@ public:
Dict *getSeparationInfo()
{ return separationInfo.isDict()
? separationInfo.getDict() : (Dict *)NULL; }
+ double getUserUnit() { return userUnit; }
Dict *getResourceDict()
{ return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
@@ -100,6 +101,7 @@ private:
Object metadata;
Object pieceInfo;
Object separationInfo;
+ double userUnit;
Object resources;
};
@@ -146,6 +148,7 @@ public:
Stream *getMetadata() { return attrs->getMetadata(); }
Dict *getPieceInfo() { return attrs->getPieceInfo(); }
Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
+ double getUserUnit() { return attrs->getUserUnit(); }
// Get resource dictionary.
Dict *getResourceDict() { return attrs->getResourceDict(); }
diff --git a/xpdf/Parser.cc b/xpdf/Parser.cc
index c43da26..dd37470 100644
--- a/xpdf/Parser.cc
+++ b/xpdf/Parser.cc
@@ -153,7 +153,7 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
Object obj;
BaseStream *baseStr;
Stream *str;
- Guint pos, endPos, length;
+ GFileOffset pos, endPos, length;
// get stream start position
lexer->skipToNextLine();
@@ -162,20 +162,21 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
}
pos = str->getPos();
- // get length
- dict->dictLookup("Length", &obj, recursion);
- if (obj.isInt()) {
- length = (Guint)obj.getInt();
- obj.free();
- } else {
- error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
- obj.free();
- return NULL;
- }
-
// check for length in damaged file
if (xref && xref->getStreamEnd(pos, &endPos)) {
length = endPos - pos;
+
+ // get length from the stream object
+ } else {
+ dict->dictLookup("Length", &obj, recursion);
+ if (obj.isInt()) {
+ length = (GFileOffset)(Guint)obj.getInt();
+ obj.free();
+ } else {
+ error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
+ obj.free();
+ return NULL;
+ }
}
// in badly damaged PDF files, we can run off the end of the input
@@ -210,7 +211,7 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
}
// get filters
- str = str->addFilters(dict);
+ str = str->addFilters(dict, recursion);
return str;
}
diff --git a/xpdf/Parser.h b/xpdf/Parser.h
index b25d749..d5403d0 100644
--- a/xpdf/Parser.h
+++ b/xpdf/Parser.h
@@ -42,7 +42,7 @@ public:
Stream *getStream() { return lexer->getStream(); }
// Get current position in file.
- int getPos() { return lexer->getPos(); }
+ GFileOffset getPos() { return lexer->getPos(); }
private:
diff --git a/xpdf/PreScanOutputDev.cc b/xpdf/PreScanOutputDev.cc
index bf48df0..ced188e 100644
--- a/xpdf/PreScanOutputDev.cc
+++ b/xpdf/PreScanOutputDev.cc
@@ -61,13 +61,13 @@ void PreScanOutputDev::eoFill(GfxState *state) {
}
void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
- Object *str,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep) {
if (paintType == 1) {
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
} else {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
@@ -174,9 +174,7 @@ void PreScanOutputDev::endType3Char(GfxState *state) {
void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- int i, j;
-
+ GBool inlineImg, GBool interpolate) {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
if (state->getFillColorSpace()->getMode() == csPattern) {
@@ -186,9 +184,7 @@ void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -196,9 +192,9 @@ void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
- int i, j;
colorSpace = colorMap->getColorSpace();
if (colorSpace->getMode() == csIndexed) {
@@ -221,10 +217,8 @@ void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -235,7 +229,7 @@ void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
@@ -264,7 +258,8 @@ void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
diff --git a/xpdf/PreScanOutputDev.h b/xpdf/PreScanOutputDev.h
index 3889cfc..6ca8b46 100644
--- a/xpdf/PreScanOutputDev.h
+++ b/xpdf/PreScanOutputDev.h
@@ -67,7 +67,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -92,21 +92,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- transparency groups and soft masks
virtual void beginTransparencyGroup(GfxState *state, double *bbox,
diff --git a/xpdf/SecurityHandler.cc b/xpdf/SecurityHandler.cc
index 52607c2..88f2b65 100644
--- a/xpdf/SecurityHandler.cc
+++ b/xpdf/SecurityHandler.cc
@@ -158,7 +158,7 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
if ((encRevision <= 4 &&
ownerKeyObj.getString()->getLength() == 32 &&
userKeyObj.getString()->getLength() == 32) ||
- (encRevision == 5 &&
+ ((encRevision == 5 || encRevision == 6) &&
// the spec says 48 bytes, but Acrobat pads them out longer
ownerKeyObj.getString()->getLength() >= 48 &&
userKeyObj.getString()->getLength() >= 48 &&
@@ -180,7 +180,7 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
//~ doesn't handle the case where StmF, StrF, and EFF are not all the
//~ same)
if ((encVersion == 4 || encVersion == 5) &&
- (encRevision == 4 || encRevision == 5)) {
+ (encRevision == 4 || encRevision == 5 || encRevision == 6)) {
encryptDictA->dictLookup("CF", &cryptFiltersObj);
encryptDictA->dictLookup("StmF", &streamFilterObj);
encryptDictA->dictLookup("StrF", &stringFilterObj);
@@ -216,7 +216,9 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
cfLengthObj.free();
} else if (cfmObj.isName("AESV3")) {
encVersion = 5;
- encRevision = 5;
+ if (encRevision != 5 && encRevision != 6) {
+ encRevision = 6;
+ }
encAlgorithm = cryptAES256;
if (cryptFilterObj.dictLookup("Length",
&cfLengthObj)->isInt()) {
@@ -254,15 +256,15 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
} else {
fileID = new GString();
}
- if (fileKeyLength > 16 || fileKeyLength < 0) {
+ if (fileKeyLength > 16 || fileKeyLength <= 0) {
fileKeyLength = 16;
}
ok = gTrue;
- } else if (encVersion == 5 && encRevision == 5) {
+ } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) {
fileID = new GString(); // unused for V=R=5
ownerEnc = ownerEncObj.getString()->copy();
userEnc = userEncObj.getString()->copy();
- if (fileKeyLength > 32 || fileKeyLength < 0) {
+ if (fileKeyLength > 32 || fileKeyLength <= 0) {
fileKeyLength = 32;
}
ok = gTrue;
diff --git a/xpdf/SplashOutputDev.cc b/xpdf/SplashOutputDev.cc
index 50e4801..d9befec 100644
--- a/xpdf/SplashOutputDev.cc
+++ b/xpdf/SplashOutputDev.cc
@@ -2,7 +2,7 @@
//
// SplashOutputDev.cc
//
-// Copyright 2003 Glyph & Cog, LLC
+// Copyright 2003-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -27,6 +27,7 @@
#include "BuiltinFont.h"
#include "BuiltinFontTables.h"
#include "FoFiTrueType.h"
+#include "JPXStream.h"
#include "SplashBitmap.h"
#include "SplashGlyphBitmap.h"
#include "SplashPattern.h"
@@ -118,7 +119,9 @@ static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 255) {
+ if (dest[i] == 0) {
+ blend[i] = 0;
+ } else if (src[i] == 255) {
blend[i] = 255;
} else {
x = (dest[i] * 255) / (255 - src[i]);
@@ -132,7 +135,9 @@ static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 0) {
+ if (dest[i] == 255) {
+ blend[i] = 255;
+ } else if (src[i] == 0) {
blend[i] = 0;
} else {
x = ((255 - dest[i]) * 255) / src[i];
@@ -284,7 +289,10 @@ static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend, SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -317,7 +325,10 @@ static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend,
SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -435,7 +446,11 @@ SplashBlendFunc splashOutBlendFuncs[] = {
class SplashOutFontFileID: public SplashFontFileID {
public:
- SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
+ SplashOutFontFileID(Ref *rA) {
+ r = *rA;
+ substIdx = -1;
+ oblique = 0;
+ }
~SplashOutFontFileID() {}
@@ -444,12 +459,15 @@ public:
((SplashOutFontFileID *)id)->r.gen == r.gen;
}
+ void setOblique(double obliqueA) { oblique = obliqueA; }
+ double getOblique() { return oblique; }
void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
int getSubstIdx() { return substIdx; }
private:
Ref r;
+ double oblique;
int substIdx;
};
@@ -536,6 +554,10 @@ T3FontCache::~T3FontCache() {
struct T3GlyphStack {
Gushort code; // character code
+ GBool haveDx; // set after seeing a d0/d1 operator
+ GBool doNotCache; // set if we see a gsave/grestore before
+ // the d0/d1
+
//----- cache info
T3FontCache *cache; // font cache for the current font
T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
@@ -580,6 +602,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
bitmapRowPad = bitmapRowPadA;
bitmapTopDown = bitmapTopDownA;
bitmapUpsideDown = gFalse;
+ noComposite = gFalse;
allowAntialias = allowAntialiasA;
vectorAntialias = allowAntialias &&
globalParams->getVectorAntialias() &&
@@ -596,6 +619,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
colorMode != splashModeMono1, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, &screenParams);
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
splash->clear(paperColor, 0);
fontEngine = NULL;
@@ -688,9 +712,6 @@ void SplashOutputDev::startDoc(XRef *xrefA) {
delete fontEngine;
}
fontEngine = new SplashFontEngine(
-#if HAVE_T1LIB_H
- globalParams->getEnableT1lib(),
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
globalParams->getEnableFreeType(),
globalParams->getDisableFreeTypeHinting()
@@ -777,18 +798,28 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) {
}
void SplashOutputDev::endPage() {
- if (colorMode != splashModeMono1) {
+ if (colorMode != splashModeMono1 && !noComposite) {
splash->compositeBackground(paperColor);
}
}
void SplashOutputDev::saveState(GfxState *state) {
splash->saveState();
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Save (q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::restoreState(GfxState *state) {
splash->restoreState();
needFontUpdate = gTrue;
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Restore (Q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::updateAll(GfxState *state) {
@@ -976,10 +1007,19 @@ void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
if (overprintFlag && globalParams->getOverprintPreview()) {
mask = colorSpace->getOverprintMask();
+ // The OPM (overprintMode) setting is only relevant when the color
+ // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
+ // Per the PDF spec, this happens with ICCBased color spaces only
+ // if the profile matches the output device -- Acrobat's output
+ // preview mode does NOT honor OPM=1 for ICCBased CMYK color
+ // spaces. To change the behavior here, use:
+ // if (singleColor && overprintMode &&
+ // (colorSpace->getMode() == csDeviceCMYK ||
+ // (colorSpace->getMode() == csICCBased &&
+ // colorSpace->getNComps() == 4 &&
+ // <...the profile matches...>)))
if (singleColor && overprintMode &&
- (colorSpace->getMode() == csDeviceCMYK ||
- (colorSpace->getMode() == csICCBased &&
- colorSpace->getNComps() == 4))) {
+ colorSpace->getMode() == csDeviceCMYK) {
colorSpace->getCMYK(singleColor, &cmyk);
if (cmyk.c == 0) {
mask &= ~1;
@@ -1072,22 +1112,33 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
FoFiTrueType *ff;
Ref embRef;
Object refObj, strObj;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+#else
GString *tmpFileName, *fileName;
FILE *tmpFile;
+#endif
+ char blk[4096];
int *codeToGID;
CharCodeToUnicode *ctu;
double *textMat;
- double m11, m12, m21, m22, fontSize;
- double w, fontScaleMin, fontScaleAvg, fontScale;
+ double m11, m12, m21, m22, fontSize, oblique;
+ double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
Gushort ww;
SplashCoord mat[4];
char *name;
Unicode uBuf[8];
- int c, substIdx, n, code, cmap, i;
+ int substIdx, n, code, cmap, i;
needFontUpdate = gFalse;
font = NULL;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+#else
tmpFileName = NULL;
+ fileName = NULL;
+#endif
substIdx = -1;
if (!(gfxFont = state->getFont())) {
@@ -1098,10 +1149,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
goto err1;
}
- // sanity-check the font size - skip anything larger than 10 inches
+ // sanity-check the font size - skip anything larger than 20 inches
// (this avoids problems allocating memory for the font cache)
- if (state->getTransformedFontSize()
- > 10 * (state->getHDPI() + state->getVDPI())) {
+ 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()) {
goto err1;
}
@@ -1112,7 +1166,6 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
} else {
- fileName = NULL;
fontNum = 0;
if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
@@ -1125,6 +1178,24 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
// embedded font
if (fontLoc->locType == gfxFontLocEmbedded) {
gfxFont->getEmbeddedFontID(&embRef);
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = new GString();
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ strObj.free();
+ delete fontLoc;
+ goto err2;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fontBuf->append(blk, n);
+ }
+ strObj.streamClose();
+ strObj.free();
+#else
if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
error(errIO, -1, "Couldn't create temporary font file");
delete fontLoc;
@@ -1141,21 +1212,39 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
goto err2;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, tmpFile);
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fwrite(blk, 1, n, tmpFile);
}
strObj.streamClose();
strObj.free();
fclose(tmpFile);
fileName = tmpFileName;
+#endif
// external font
} else { // gfxFontLocExternal
+#if LOAD_FONTS_FROM_MEM
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
+ fontLoc->path);
+ delete fontLoc;
+ goto err2;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+#else
fileName = fontLoc->path;
+#endif
fontNum = fontLoc->fontNum;
if (fontLoc->substIdx >= 0) {
id->setSubstIdx(fontLoc->substIdx);
}
+ if (fontLoc->oblique != 0) {
+ id->setOblique(fontLoc->oblique);
+ }
}
// load the font file
@@ -1163,8 +1252,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1:
if (!(fontFile = fontEngine->loadType1Font(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1176,8 +1269,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1C:
if (!(fontFile = fontEngine->loadType1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1189,8 +1286,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1COT:
if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1201,7 +1302,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
break;
case fontTrueType:
case fontTrueTypeOT:
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
n = 256;
delete ff;
@@ -1222,9 +1328,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1239,8 +1349,14 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontCIDType0C:
if (!(fontFile = fontEngine->loadCIDFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf
+#else
fileName->getCString(),
- fileName == tmpFileName))) {
+ fileName == tmpFileName
+#endif
+ ))) {
+
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
@@ -1260,8 +1376,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
codeToGID, n))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1281,11 +1401,18 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
n * sizeof(int));
}
+ } else if (!globalParams->getMapExtTrueTypeFontsViaUnicode()) {
+ codeToGID = NULL;
+ n = 0;
} else {
// create a CID-to-GID mapping, via Unicode
if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
- //~ this should use fontNum to load the correct font
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(), fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
// look for a Unicode cmap
for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
if ((ff->getCmapPlatform(cmap) == 3 &&
@@ -1296,7 +1423,11 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (cmap < ff->getNumCmaps()) {
// map CID -> Unicode -> GID
- n = ctu->getLength();
+ if (ctu->isIdentity()) {
+ n = 65536;
+ } else {
+ n = ctu->getLength();
+ }
codeToGID = (int *)gmallocn(n, sizeof(int));
for (code = 0; code < n; ++code) {
if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
@@ -1318,9 +1449,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1342,10 +1477,15 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
// get the font matrix
textMat = state->getTextMat();
fontSize = state->getFontSize();
- m11 = textMat[0] * fontSize * state->getHorizScaling();
- m12 = textMat[1] * fontSize * state->getHorizScaling();
- m21 = textMat[2] * fontSize;
- m22 = textMat[3] * fontSize;
+ oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
+ m11 = state->getHorizScaling() * textMat[0];
+ m12 = state->getHorizScaling() * textMat[1];
+ m21 = oblique * m11 + textMat[2];
+ m22 = oblique * m12 + textMat[3];
+ m11 *= fontSize;
+ m12 *= fontSize;
+ m21 *= fontSize;
+ m22 *= fontSize;
// for substituted fonts: adjust the font matrix -- compare the
// widths of letters and digits (A-Z, a-z, 0-9) in the original font
@@ -1393,18 +1533,26 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
mat[2] = m21; mat[3] = m22;
font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
+#if !LOAD_FONTS_FROM_MEM
if (tmpFileName) {
delete tmpFileName;
}
+#endif
return;
err2:
delete id;
err1:
+#if LOAD_FONTS_FROM_MEM
+ if (fontBuf) {
+ delete fontBuf;
+ }
+#else
if (tmpFileName) {
unlink(tmpFileName->getCString());
delete tmpFileName;
}
+#endif
return;
}
@@ -1447,7 +1595,8 @@ void SplashOutputDev::eoFill(GfxState *state) {
delete path;
}
-void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -1529,7 +1678,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
ya = y * yStep;
mat1[4] = xa * mat[0] + ya * mat[2] + mat[4];
mat1[5] = xa * mat[1] + ya * mat[3] + mat[5];
- gfx->drawForm(str, resDict, mat1, bbox);
+ gfx->drawForm(strRef, resDict, mat1, bbox);
}
}
return;
@@ -1542,6 +1691,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
colorMode, gTrue, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
for (i = 0; i < splashMaxColorComps; ++i) {
color[i] = 0;
}
@@ -1556,7 +1706,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
// render the tile
state->shiftCTM(-tileX0, -tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
state->shiftCTM(tileX0, tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -1889,8 +2039,8 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
t3GlyphStack->cache = t3Font;
t3GlyphStack->cacheTag = NULL;
t3GlyphStack->cacheData = NULL;
-
- haveT3Dx = gFalse;
+ t3GlyphStack->haveDx = gFalse;
+ t3GlyphStack->doNotCache = gFalse;
return gFalse;
}
@@ -1920,7 +2070,7 @@ void SplashOutputDev::endType3Char(GfxState *state) {
}
void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
- haveT3Dx = gTrue;
+ t3GlyphStack->haveDx = gTrue;
}
void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
@@ -1932,10 +2082,14 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
int i, j;
// ignore multiple d0/d1 operators
- if (haveT3Dx) {
+ if (t3GlyphStack->haveDx) {
+ return;
+ }
+ t3GlyphStack->haveDx = gTrue;
+ // don't cache if we got a gsave/grestore before the d1
+ if (t3GlyphStack->doNotCache) {
return;
}
- haveT3Dx = gTrue;
t3Font = t3GlyphStack->cache;
@@ -2026,6 +2180,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
color[0] = 0xff;
}
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
splash->setFillPattern(new SplashSolidColor(color));
splash->setStrokePattern(new SplashSolidColor(color));
//~ this should copy other state from t3GlyphStack->origSplash?
@@ -2072,10 +2227,9 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
SplashColorPtr q;
int x;
- if (imgMaskData->y == imgMaskData->height) {
- return gFalse;
- }
- if (!(p = imgMaskData->imgStr->getLine())) {
+ if (imgMaskData->y == imgMaskData->height ||
+ !(p = imgMaskData->imgStr->getLine())) {
+ memset(line, 0, imgMaskData->width);
return gFalse;
}
for (x = 0, q = line; x < imgMaskData->width; ++x) {
@@ -2087,7 +2241,7 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2106,6 +2260,8 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2114,7 +2270,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
imgMaskData.y = 0;
splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
- t3GlyphStack != NULL);
+ t3GlyphStack != NULL, interpolate);
if (inlineImg) {
while (imgMaskData.y < height) {
imgMaskData.imgStr->getLine();
@@ -2130,7 +2286,8 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height,
GBool invert,
- GBool inlineImg) {
+ GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2145,6 +2302,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
mat[3] = -ctm[3];
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2154,12 +2312,12 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, gTrue);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- width, height, mat, gFalse);
+ width, height, mat, gFalse, interpolate);
delete imgMaskData.imgStr;
str->close();
delete maskSplash;
@@ -2180,17 +2338,12 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
SplashOutImageData *imgData = (SplashOutImageData *)data;
Guchar *p;
SplashColorPtr q, col;
- GfxRGB rgb;
- GfxGray gray;
-#if SPLASH_CMYK
- GfxCMYK cmyk;
-#endif
int nComps, x;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
return gFalse;
}
@@ -2229,29 +2382,15 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
switch (imgData->colorMode) {
case splashModeMono1:
case splashModeMono8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getGray(p, &gray);
- *q++ = colToByte(gray);
- }
+ imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width);
break;
case splashModeRGB8:
case splashModeBGR8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getRGB(p, &rgb);
- *q++ = colToByte(rgb.r);
- *q++ = colToByte(rgb.g);
- *q++ = colToByte(rgb.b);
- }
+ imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width);
break;
#if SPLASH_CMYK
case splashModeCMYK8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getCMYK(p, &cmyk);
- *q++ = colToByte(cmyk.c);
- *q++ = colToByte(cmyk.m);
- *q++ = colToByte(cmyk.y);
- *q++ = colToByte(cmyk.k);
- }
+ imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width);
break;
#endif
}
@@ -2274,10 +2413,11 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
Guchar alpha;
int nComps, x, i;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2353,7 +2493,8 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2378,6 +2519,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgData.imgStr = new ImageStream(str, width,
colorMap->getNumPixelComps(),
colorMap->getBits());
@@ -2433,12 +2576,14 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
src = maskColors ? &alphaImageSrc : &imageSrc;
splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
- width, height, mat);
+ width, height, mat, interpolate);
if (inlineImg) {
while (imgData.y < height) {
imgData.imgStr->getLine();
@@ -2470,15 +2615,17 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
+ static Guchar bitToByte[2] = {0x00, 0xff};
Guchar alpha;
Guchar *maskPtr;
- int maskBit;
+ int maskShift;
int nComps, x;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2486,15 +2633,13 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
maskPtr = imgData->mask->getDataPtr() +
imgData->y * imgData->mask->getRowSize();
- maskBit = 0x80;
+ maskShift = 7;
for (x = 0, q = colorLine, aq = alphaLine;
x < imgData->width;
++x, p += nComps) {
- alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
- if (!(maskBit >>= 1)) {
- ++maskPtr;
- maskBit = 0x80;
- }
+ alpha = bitToByte[(*maskPtr >> maskShift) & 1];
+ maskPtr += (8 - maskShift) >> 3;
+ maskShift = (maskShift - 1) & 7;
if (imgData->lookup) {
switch (imgData->colorMode) {
case splashModeMono1:
@@ -2555,7 +2700,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
Stream *str, int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth,
- int maskHeight, GBool maskInvert) {
+ int maskHeight, GBool maskInvert,
+ GBool interpolate) {
GfxImageColorMap *maskColorMap;
Object maskDecode, decodeLow, decodeHigh;
double *ctm;
@@ -2577,6 +2723,10 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
state->getOverprintMode(), NULL);
+ ctm = state->getCTM();
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
// If the mask is higher resolution than the image, use
// drawSoftMaskedImage() instead.
if (maskWidth > width || maskHeight > height) {
@@ -2589,7 +2739,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
new GfxDeviceGrayColorSpace());
maskDecode.free();
drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap);
+ maskStr, maskWidth, maskHeight, maskColorMap,
+ interpolate);
delete maskColorMap;
} else {
@@ -2610,19 +2761,20 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
imgMaskData.y = 0;
maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
maskSplash = new Splash(maskBitmap, gFalse);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
maskColor[0] = 0;
maskSplash->clear(maskColor);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+ // use "glyph mode" here to get the correct scaled size
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- maskWidth, maskHeight, mat, gFalse);
+ maskWidth, maskHeight, mat, gTrue, interpolate);
delete imgMaskData.imgStr;
maskStr->close();
delete maskSplash;
//----- draw the source image
- ctm = state->getCTM();
mat[0] = ctm[0];
mat[1] = ctm[1];
mat[2] = -ctm[2];
@@ -2685,11 +2837,13 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
- width, height, mat);
+ width, height, mat, interpolate);
delete maskBitmap;
gfree(imgData.lookup);
@@ -2703,7 +2857,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2711,7 +2866,6 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
SplashColorMode srcMode;
SplashBitmap *maskBitmap;
Splash *maskSplash;
- SplashColor maskColor;
GfxGray gray;
GfxRGB rgb;
#if SPLASH_CMYK
@@ -2731,6 +2885,9 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
//----- set up the soft mask
imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
@@ -2753,10 +2910,10 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, vectorAntialias);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
- maskWidth, maskHeight, mat);
+ maskWidth, maskHeight, mat, interpolate);
delete imgMaskData.imgStr;
maskStr->close();
gfree(imgMaskData.lookup);
@@ -2820,10 +2977,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
- splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
+ splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat,
+ interpolate);
splash->setSoftMask(NULL);
gfree(imgData.lookup);
@@ -2831,6 +2991,98 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
str->close();
}
+void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
+ int *width, int *height) {
+ double sw, sh;
+ int reduction;
+
+ if (str->getKind() == strJPX &&
+ *width * *height > 10000000) {
+ sw = (double)*width / (fabs(ctm[2]) + fabs(ctm[3]));
+ sh = (double)*height / (fabs(ctm[0]) + fabs(ctm[1]));
+ if (sw > 8 && sh > 8) {
+ reduction = 3;
+ } else if (sw > 4 && sh > 4) {
+ reduction = 2;
+ } else if (sw > 2 && sh > 2) {
+ reduction = 1;
+ } else {
+ reduction = 0;
+ }
+ if (reduction > 0) {
+ ((JPXStream *)str)->reduceResolution(reduction);
+ *width >>= reduction;
+ *height >>= reduction;
+ }
+ }
+}
+
+void SplashOutputDev::clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ SplashBitmap *maskBitmap;
+ double xxMin, yyMin, xxMax, yyMax, xx, yy;
+ int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
+ Guchar *p;
+
+ maskBitmap = maskSplash->getBitmap();
+ xxMin = maskBitmap->getWidth();
+ xxMax = 0;
+ yyMin = maskBitmap->getHeight();
+ yyMax = 0;
+ state->transform(xMin, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMin, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ xxMinI = (int)floor(xxMin);
+ if (xxMinI < 0) {
+ xxMinI = 0;
+ }
+ xxMaxI = (int)ceil(xxMax);
+ if (xxMaxI > maskBitmap->getWidth()) {
+ xxMaxI = maskBitmap->getWidth();
+ }
+ yyMinI = (int)floor(yyMin);
+ if (yyMinI < 0) {
+ yyMinI = 0;
+ }
+ yyMaxI = (int)ceil(yyMax);
+ if (yyMaxI > maskBitmap->getHeight()) {
+ yyMaxI = maskBitmap->getHeight();
+ }
+ p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
+ if (maskBitmap->getMode() == splashModeMono1) {
+ n = (xxMaxI + 7) / 8 - xxMinI / 8;
+ p += xxMinI / 8;
+ } else {
+ n = xxMaxI - xxMinI;
+ p += xxMinI;
+ }
+ if (xxMaxI > xxMinI) {
+ for (y = yyMinI; y < yyMaxI; ++y) {
+ memset(p, 0, n);
+ p += maskBitmap->getRowSize();
+ }
+ }
+}
+
void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
@@ -2921,7 +3173,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
//~ not yet for transparency groups
// switch to the blending color space
- if (forSoftMask && isolated && blendingColorSpace) {
+ if (forSoftMask && isolated && !knockout && blendingColorSpace) {
if (blendingColorSpace->getMode() == csDeviceGray ||
blendingColorSpace->getMode() == csCalGray ||
(blendingColorSpace->getMode() == csICCBased &&
@@ -2948,6 +3200,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
splash = new Splash(bitmap, vectorAntialias,
transpGroup->origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
//~ Acrobat apparently copies at least the fill and stroke colors, and
//~ maybe other state(?) -- but not the clipping path (and not sure
//~ what else)
@@ -2962,8 +3215,9 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
splash->clear(color, 0);
} else {
splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
- splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
}
+ splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty,
+ !isolated, knockout);
transpGroup->tBitmap = bitmap;
state->shiftCTM(-tx, -ty);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -3022,7 +3276,7 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
- double lum, lum2;
+ double backdrop, backdrop2, lum, lum2;
int tx, ty, x, y;
tx = transpGroupStack->tx;
@@ -3030,11 +3284,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
tBitmap = transpGroupStack->tBitmap;
// composite with backdrop color
+ backdrop = 0;
if (!alpha && tBitmap->getMode() != splashModeMono1) {
//~ need to correctly handle the case where no blending color
//~ space is given
tSplash = new Splash(tBitmap, vectorAntialias,
transpGroupStack->origSplash->getScreen());
+ tSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
if (transpGroupStack->blendingColorSpace) {
switch (tBitmap->getMode()) {
case splashModeMono1:
@@ -3042,12 +3298,16 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
break;
case splashModeMono8:
transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
+ backdrop = colToDbl(gray);
color[0] = colToByte(gray);
tSplash->compositeBackground(color);
break;
case splashModeRGB8:
case splashModeBGR8:
transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
+ backdrop = 0.3 * colToDbl(rgb.r) +
+ 0.59 * colToDbl(rgb.g) +
+ 0.11 * colToDbl(rgb.b);
color[0] = colToByte(rgb.r);
color[1] = colToByte(rgb.g);
color[2] = colToByte(rgb.b);
@@ -3056,6 +3316,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
#if SPLASH_CMYK
case splashModeCMYK8:
transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
+ backdrop = (1 - colToDbl(cmyk.k))
+ - 0.3 * colToDbl(cmyk.c)
+ - 0.59 * colToDbl(cmyk.m)
+ - 0.11 * colToDbl(cmyk.y);
+ if (backdrop < 0) {
+ backdrop = 0;
+ }
color[0] = colToByte(cmyk.c);
color[1] = colToByte(cmyk.m);
color[2] = colToByte(cmyk.y);
@@ -3067,10 +3334,15 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
delete tSplash;
}
}
+ if (transferFunc) {
+ transferFunc->transform(&backdrop, &backdrop2);
+ } else {
+ backdrop2 = backdrop;
+ }
softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
- memset(softMask->getDataPtr(), 0,
+ 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;
@@ -3198,12 +3470,19 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
Ref ref;
SplashOutFontFileID *id;
GfxFontLoc *fontLoc;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+ char blk[4096];
+ int n;
+#endif
SplashFontFile *fontFile;
SplashFont *fontObj;
FoFiTrueType *ff;
int *codeToGID;
Unicode u;
SplashCoord textMat[4];
+ SplashCoord oblique;
int cmap, i;
for (i = 0; i < nBuiltinFonts; ++i) {
@@ -3227,11 +3506,40 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
if (!(fontLoc = GfxFont::locateBase14Font(name))) {
return NULL;
}
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+ if (fontLoc->fontType == fontType1 ||
+ fontLoc->fontType == fontTrueType) {
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+ }
+#endif
if (fontLoc->fontType == fontType1) {
- fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(),
- gFalse, winAnsiEncoding);
+ fontFile = fontEngine->loadType1Font(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fontLoc->path->getCString(),
+ gFalse,
+#endif
+ winAnsiEncoding);
} else if (fontLoc->fontType == fontTrueType) {
- if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(),
+ fontLoc->fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
+ fontLoc->fontNum))) {
+#endif
delete fontLoc;
delete id;
return NULL;
@@ -3259,9 +3567,14 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
}
delete ff;
fontFile = fontEngine->loadTrueTypeFont(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fontLoc->path->getCString(),
+ gFalse,
+#endif
fontLoc->fontNum,
- gFalse, codeToGID, 256, NULL);
+ codeToGID, 256, NULL);
} else {
delete fontLoc;
delete id;
@@ -3274,10 +3587,12 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
}
// create the scaled font
+ oblique = (SplashCoord)
+ ((SplashOutFontFileID *)fontFile->getID())->getOblique();
textMat[0] = (SplashCoord)textMatA[0];
textMat[1] = (SplashCoord)textMatA[1];
- textMat[2] = (SplashCoord)textMatA[2];
- textMat[3] = (SplashCoord)textMatA[3];
+ textMat[2] = oblique * textMatA[0] + textMatA[2];
+ textMat[3] = oblique * textMatA[1] + textMatA[3];
fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());
return fontObj;
diff --git a/xpdf/SplashOutputDev.h b/xpdf/SplashOutputDev.h
index c48b786..ebbf8a1 100644
--- a/xpdf/SplashOutputDev.h
+++ b/xpdf/SplashOutputDev.h
@@ -110,7 +110,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -135,25 +135,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- Type 3 font operators
virtual void type3D0(GfxState *state, double wx, double wy);
@@ -194,6 +195,10 @@ public:
// for Windows BMP files).
void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; }
+ // Setting this to true disables the final composite (with the
+ // opaque paper color), resulting in transparent output.
+ void setNoComposite(GBool f) { noComposite = f; }
+
// Get the Splash object.
Splash *getSplash() { return splash; }
@@ -245,11 +250,18 @@ private:
Guchar *alphaLine);
static GBool maskedImageSrc(void *data, SplashColorPtr line,
Guchar *alphaLine);
+ void reduceImageResolution(Stream *str, double *mat,
+ int *width, int *height);
+ void clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax);
SplashColorMode colorMode;
int bitmapRowPad;
GBool bitmapTopDown;
GBool bitmapUpsideDown;
+ GBool noComposite;
GBool allowAntialias;
GBool vectorAntialias;
GBool reverseVideo; // reverse video mode
@@ -268,7 +280,6 @@ private:
t3FontCache[splashOutT3FontCacheSize];
int nT3Fonts; // number of valid entries in t3FontCache
T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack
- GBool haveT3Dx; // set after seeing a d0/d1 operator
SplashFont *font; // current font
GBool needFontUpdate; // set when the font needs to be updated
diff --git a/xpdf/Stream.cc b/xpdf/Stream.cc
index d91b941..7102966 100644
--- a/xpdf/Stream.cc
+++ b/xpdf/Stream.cc
@@ -16,7 +16,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#endif
#include <string.h>
@@ -98,11 +98,29 @@ char *Stream::getLine(char *buf, int size) {
return buf;
}
+Guint Stream::discardChars(Guint n) {
+ char buf[4096];
+ Guint count, i, j;
+
+ count = 0;
+ while (count < n) {
+ if ((i = n - count) > sizeof(buf)) {
+ i = (Guint)sizeof(buf);
+ }
+ j = (Guint)getBlock(buf, (int)i);
+ count += j;
+ if (j != i) {
+ break;
+ }
+ }
+ return count;
+}
+
GString *Stream::getPSFilter(int psLevel, const char *indent) {
return new GString();
}
-Stream *Stream::addFilters(Object *dict) {
+Stream *Stream::addFilters(Object *dict, int recursion) {
Object obj, obj2;
Object params, params2;
Stream *str;
@@ -120,7 +138,7 @@ Stream *Stream::addFilters(Object *dict) {
dict->dictLookup("DP", &params);
}
if (obj.isName()) {
- str = makeFilter(obj.getName(), str, &params);
+ str = makeFilter(obj.getName(), str, &params, recursion);
} else if (obj.isArray()) {
for (i = 0; i < obj.arrayGetLength(); ++i) {
obj.arrayGet(i, &obj2);
@@ -129,7 +147,7 @@ Stream *Stream::addFilters(Object *dict) {
else
params2.initNull();
if (obj2.isName()) {
- str = makeFilter(obj2.getName(), str, &params2);
+ str = makeFilter(obj2.getName(), str, &params2, recursion);
} else {
error(errSyntaxError, getPos(), "Bad filter name");
str = new EOFStream(str);
@@ -146,7 +164,8 @@ Stream *Stream::addFilters(Object *dict) {
return str;
}
-Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
+Stream *Stream::makeFilter(char *name, Stream *str, Object *params,
+ int recursion) {
int pred; // parameters
int colors;
int bits;
@@ -168,23 +187,23 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
bits = 8;
early = 1;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
- params->dictLookup("EarlyChange", &obj);
+ params->dictLookup("EarlyChange", &obj, recursion);
if (obj.isInt())
early = obj.getInt();
obj.free();
@@ -201,37 +220,37 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
endOfBlock = gTrue;
black = gFalse;
if (params->isDict()) {
- params->dictLookup("K", &obj);
+ params->dictLookup("K", &obj, recursion);
if (obj.isInt()) {
encoding = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfLine", &obj);
+ params->dictLookup("EndOfLine", &obj, recursion);
if (obj.isBool()) {
endOfLine = obj.getBool();
}
obj.free();
- params->dictLookup("EncodedByteAlign", &obj);
+ params->dictLookup("EncodedByteAlign", &obj, recursion);
if (obj.isBool()) {
byteAlign = obj.getBool();
}
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt()) {
columns = obj.getInt();
}
obj.free();
- params->dictLookup("Rows", &obj);
+ params->dictLookup("Rows", &obj, recursion);
if (obj.isInt()) {
rows = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfBlock", &obj);
+ params->dictLookup("EndOfBlock", &obj, recursion);
if (obj.isBool()) {
endOfBlock = obj.getBool();
}
obj.free();
- params->dictLookup("BlackIs1", &obj);
+ params->dictLookup("BlackIs1", &obj, recursion);
if (obj.isBool()) {
black = obj.getBool();
}
@@ -242,7 +261,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
} else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
colorXform = -1;
if (params->isDict()) {
- if (params->dictLookup("ColorTransform", &obj)->isInt()) {
+ if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) {
colorXform = obj.getInt();
}
obj.free();
@@ -254,19 +273,19 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
colors = 1;
bits = 8;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
@@ -274,7 +293,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
str = new FlateStream(str, pred, columns, colors, bits);
} else if (!strcmp(name, "JBIG2Decode")) {
if (params->isDict()) {
- params->dictLookup("JBIG2Globals", &globals);
+ params->dictLookup("JBIG2Globals", &globals, recursion);
}
str = new JBIG2Stream(str, &globals);
globals.free();
@@ -314,7 +333,7 @@ void FilterStream::close() {
str->close();
}
-void FilterStream::setPos(Guint pos, int dir) {
+void FilterStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on FilterStream");
}
@@ -365,6 +384,10 @@ void ImageStream::reset() {
str->reset();
}
+void ImageStream::close() {
+ str->close();
+}
+
GBool ImageStream::getPixel(Guchar *pix) {
int i;
@@ -405,6 +428,10 @@ Guchar *ImageStream::getLine() {
}
} else if (nBits == 8) {
// special case: imgLine == inputLine
+ } else if (nBits == 16) {
+ for (i = 0; i < nVals; ++i) {
+ imgLine[i] = (Guchar)inputLine[2*i];
+ }
} else {
bitMask = (1 << nBits) - 1;
buf = 0;
@@ -451,8 +478,8 @@ StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
return;
}
predLine = (Guchar *)gmalloc(rowBytes);
- memset(predLine, 0, rowBytes);
- predIdx = rowBytes;
+
+ reset();
ok = gTrue;
}
@@ -461,6 +488,11 @@ StreamPredictor::~StreamPredictor() {
gfree(predLine);
}
+void StreamPredictor::reset() {
+ memset(predLine, 0, rowBytes);
+ predIdx = rowBytes;
+}
+
int StreamPredictor::lookChar() {
if (predIdx >= rowBytes) {
if (!getNextLine()) {
@@ -573,19 +605,19 @@ GBool StreamPredictor::getNextLine() {
// apply TIFF (component) predictor
if (predictor == 2) {
- if (nBits == 1) {
- inBuf = predLine[pixBytes - 1];
- for (i = pixBytes; i < rowBytes; i += 8) {
- // 1-bit add is just xor
- inBuf = (inBuf << 8) | predLine[i];
- predLine[i] ^= inBuf >> nComps;
- }
- } else if (nBits == 8) {
+ if (nBits == 8) {
for (i = pixBytes; i < rowBytes; ++i) {
predLine[i] += predLine[i - nComps];
}
+ } else if (nBits == 16) {
+ for (i = pixBytes; i < rowBytes; i += 2) {
+ c = ((predLine[i] + predLine[i - 2*nComps]) << 8) +
+ predLine[i + 1] + predLine[i + 1 - 2*nComps];
+ predLine[i] = (Guchar)(c >> 8);
+ predLine[i+1] = (Guchar)(c & 0xff);
+ }
} else {
- memset(upLeftBuf, 0, nComps + 1);
+ memset(upLeftBuf, 0, nComps);
bitMask = (1 << nBits) - 1;
inBuf = outBuf = 0;
inBits = outBits = 0;
@@ -624,8 +656,8 @@ GBool StreamPredictor::getNextLine() {
// FileStream
//------------------------------------------------------------------------
-FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA):
+FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
BaseStream(dictA) {
f = fA;
start = startA;
@@ -641,22 +673,14 @@ FileStream::~FileStream() {
close();
}
-Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
return new FileStream(f, startA, limitedA, lengthA, dictA);
}
void FileStream::reset() {
-#if HAVE_FSEEKO
- savePos = (Guint)ftello(f);
- fseeko(f, start, SEEK_SET);
-#elif HAVE_FSEEK64
- savePos = (Guint)ftell64(f);
- fseek64(f, start, SEEK_SET);
-#else
- savePos = (Guint)ftell(f);
- fseek(f, start, SEEK_SET);
-#endif
+ savePos = gftell(f);
+ gfseek(f, start, SEEK_SET);
saved = gTrue;
bufPtr = bufEnd = buf;
bufPos = start;
@@ -664,13 +688,7 @@ void FileStream::reset() {
void FileStream::close() {
if (saved) {
-#if HAVE_FSEEKO
- fseeko(f, savePos, SEEK_SET);
-#elif HAVE_FSEEK64
- fseek64(f, savePos, SEEK_SET);
-#else
- fseek(f, savePos, SEEK_SET);
-#endif
+ gfseek(f, savePos, SEEK_SET);
saved = gFalse;
}
}
@@ -705,7 +723,7 @@ GBool FileStream::fillBuf() {
return gFalse;
}
if (limited && bufPos + fileStreamBufSize > start + length) {
- n = start + length - bufPos;
+ n = (int)(start + length - bufPos);
} else {
n = fileStreamBufSize;
}
@@ -717,41 +735,20 @@ GBool FileStream::fillBuf() {
return gTrue;
}
-void FileStream::setPos(Guint pos, int dir) {
- Guint size;
+void FileStream::setPos(GFileOffset pos, int dir) {
+ GFileOffset size;
if (dir >= 0) {
-#if HAVE_FSEEKO
- fseeko(f, pos, SEEK_SET);
-#elif HAVE_FSEEK64
- fseek64(f, pos, SEEK_SET);
-#else
- fseek(f, pos, SEEK_SET);
-#endif
+ gfseek(f, pos, SEEK_SET);
bufPos = pos;
} else {
-#if HAVE_FSEEKO
- fseeko(f, 0, SEEK_END);
- size = (Guint)ftello(f);
-#elif HAVE_FSEEK64
- fseek64(f, 0, SEEK_END);
- size = (Guint)ftell64(f);
-#else
- fseek(f, 0, SEEK_END);
- size = (Guint)ftell(f);
-#endif
- if (pos > size)
- pos = (Guint)size;
-#if HAVE_FSEEKO
- fseeko(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftello(f);
-#elif HAVE_FSEEK64
- fseek64(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftell64(f);
-#else
- fseek(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftell(f);
-#endif
+ gfseek(f, 0, SEEK_END);
+ size = gftell(f);
+ if (pos > size) {
+ pos = size;
+ }
+ gfseek(f, -pos, SEEK_END);
+ bufPos = gftell(f);
}
bufPtr = bufEnd = buf;
}
@@ -782,17 +779,24 @@ MemStream::~MemStream() {
}
}
-Stream *MemStream::makeSubStream(Guint startA, GBool limited,
- Guint lengthA, Object *dictA) {
+Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited,
+ GFileOffset lengthA, Object *dictA) {
MemStream *subStr;
- Guint newLength;
+ Guint newStart, newLength;
- if (!limited || startA + lengthA > start + length) {
- newLength = start + length - startA;
+ if (startA < start) {
+ newStart = start;
+ } else if (startA > start + length) {
+ newStart = start + (int)length;
+ } else {
+ newStart = (int)startA;
+ }
+ if (!limited || newStart + lengthA > start + length) {
+ newLength = start + length - newStart;
} else {
- newLength = lengthA;
+ newLength = (Guint)lengthA;
}
- subStr = new MemStream(buf, startA, newLength, dictA);
+ subStr = new MemStream(buf, newStart, newLength, dictA);
return subStr;
}
@@ -819,13 +823,13 @@ int MemStream::getBlock(char *blk, int size) {
return n;
}
-void MemStream::setPos(Guint pos, int dir) {
+void MemStream::setPos(GFileOffset pos, int dir) {
Guint i;
if (dir >= 0) {
- i = pos;
+ i = (Guint)pos;
} else {
- i = start + length - pos;
+ i = (Guint)(start + length - pos);
}
if (i < start) {
i = start;
@@ -846,7 +850,7 @@ void MemStream::moveStart(int delta) {
//------------------------------------------------------------------------
EmbedStream::EmbedStream(Stream *strA, Object *dictA,
- GBool limitedA, Guint lengthA):
+ GBool limitedA, GFileOffset lengthA):
BaseStream(dictA) {
str = strA;
limited = limitedA;
@@ -856,8 +860,8 @@ EmbedStream::EmbedStream(Stream *strA, Object *dictA,
EmbedStream::~EmbedStream() {
}
-Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
error(errInternal, -1, "Called makeSubStream() on EmbedStream");
return NULL;
}
@@ -887,11 +891,11 @@ int EmbedStream::getBlock(char *blk, int size) {
return str->getBlock(blk, size);
}
-void EmbedStream::setPos(Guint pos, int dir) {
+void EmbedStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on EmbedStream");
}
-Guint EmbedStream::getStart() {
+GFileOffset EmbedStream::getStart() {
error(errInternal, -1, "Called getStart() on EmbedStream");
return 0;
}
@@ -1173,6 +1177,9 @@ int LZWStream::getBlock(char *blk, int size) {
void LZWStream::reset() {
str->reset();
+ if (pred) {
+ pred->reset();
+ }
eof = gFalse;
inputBits = 0;
clearTable();
@@ -2068,14 +2075,16 @@ GBool CCITTFaxStream::isBinary(GBool last) {
//------------------------------------------------------------------------
// IDCT constants (20.12 fixed point format)
-#define dctCos1 4017 // cos(pi/16)
-#define dctSin1 799 // sin(pi/16)
-#define dctCos3 3406 // cos(3*pi/16)
-#define dctSin3 2276 // sin(3*pi/16)
-#define dctCos6 1567 // cos(6*pi/16)
-#define dctSin6 3784 // sin(6*pi/16)
-#define dctSqrt2 5793 // sqrt(2)
-#define dctSqrt1d2 2896 // sqrt(2) / 2
+#define dctSqrt2 5793 // sqrt(2)
+#define dctSqrt2Cos6 2217 // sqrt(2) * cos(6*pi/16)
+#define dctSqrt2Cos6PSin6 7568 // sqrt(2) * (cos(6*pi/16) + sin(6*pi/16))
+#define dctSqrt2Sin6MCos6 3135 // sqrt(2) * (sin(6*pi/16) - cos(6*pi/16))
+#define dctCos3 3406 // cos(3*pi/16)
+#define dctCos3PSin3 5681 // cos(3*pi/16) + sin(3*pi/16)
+#define dctSin3MCos3 -1130 // sin(3*pi/16) - cos(3*pi/16)
+#define dctCos1 4017 // cos(pi/16)
+#define dctCos1PSin1 4816 // cos(pi/16) + sin(pi/16)
+#define dctSin1MCos1 -3218 // sin(pi/16) - cos(pi/16)
// color conversion parameters (16.16 fixed point format)
#define dctCrToR 91881 // 1.4020
@@ -2083,10 +2092,47 @@ GBool CCITTFaxStream::isBinary(GBool last) {
#define dctCrToG -46802 // -0.71413636
#define dctCbToB 116130 // 1.772
-// clip [-256,511] --> [0,255]
-#define dctClipOffset 256
-static Guchar dctClip[768];
-static int dctClipInit = 0;
+// The dctClip function clips signed integers to the [0,255] range.
+// To handle valid DCT inputs, this must support an input range of at
+// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF
+// files) can result in arbitrary values, so we want to mask those
+// out. We round the input range size up to a power of 2 (so we can
+// use a bit mask), which gives us an input range of [-384,639]. The
+// end result is:
+// input output
+// ---------- ------
+// <-384 X invalid inputs -> output is "don't care"
+// -384..-257 0 invalid inputs, clipped
+// -256..-1 0 valid inputs, need to be clipped
+// 0..255 0..255
+// 256..511 255 valid inputs, need to be clipped
+// 512..639 255 invalid inputs, clipped
+// >=512 X invalid inputs -> output is "don't care"
+
+#define dctClipOffset 384
+#define dctClipMask 1023
+static Guchar dctClipData[1024];
+
+static inline void dctClipInit() {
+ static int initDone = 0;
+ int i;
+ if (!initDone) {
+ for (i = -384; i < 0; ++i) {
+ dctClipData[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClipData[dctClipOffset + i] = i;
+ }
+ for (i = 256; i < 639; ++i) {
+ dctClipData[dctClipOffset + i] = 255;
+ }
+ initDone = 1;
+ }
+}
+
+static inline int dctClip(int x) {
+ return dctClipData[(dctClipOffset + x) & dctClipMask];
+}
// zig zag decode map
static int dctZigZag[64] = {
@@ -2109,7 +2155,7 @@ static int dctZigZag[64] = {
DCTStream::DCTStream(Stream *strA, GBool colorXformA):
FilterStream(strA) {
- int i, j;
+ int i;
colorXform = colorXformA;
progressive = interleaved = gFalse;
@@ -2117,23 +2163,15 @@ DCTStream::DCTStream(Stream *strA, GBool colorXformA):
mcuWidth = mcuHeight = 0;
numComps = 0;
comp = 0;
- x = y = dy = 0;
+ x = y = 0;
for (i = 0; i < 4; ++i) {
- for (j = 0; j < 32; ++j) {
- rowBuf[i][j] = NULL;
- }
frameBuf[i] = NULL;
}
+ rowBuf = NULL;
+ memset(dcHuffTables, 0, sizeof(dcHuffTables));
+ memset(acHuffTables, 0, sizeof(acHuffTables));
- if (!dctClipInit) {
- for (i = -256; i < 0; ++i)
- dctClip[dctClipOffset + i] = 0;
- for (i = 0; i < 256; ++i)
- dctClip[dctClipOffset + i] = i;
- for (i = 256; i < 512; ++i)
- dctClip[dctClipOffset + i] = 255;
- dctClipInit = 1;
- }
+ dctClipInit();
}
DCTStream::~DCTStream() {
@@ -2142,7 +2180,7 @@ DCTStream::~DCTStream() {
}
void DCTStream::reset() {
- int i, j;
+ int i;
str->reset();
@@ -2157,6 +2195,8 @@ void DCTStream::reset() {
restartInterval = 0;
if (!readHeader()) {
+ // force an EOF condition
+ progressive = gTrue;
y = height;
return;
}
@@ -2229,17 +2269,11 @@ void DCTStream::reset() {
// allocate a buffer for one row of MCUs
bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
- for (i = 0; i < numComps; ++i) {
- for (j = 0; j < mcuHeight; ++j) {
- rowBuf[i][j] = (Guchar *)gmallocn(bufWidth, sizeof(Guchar));
- }
- }
+ rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth);
+ rowBufPtr = rowBufEnd = rowBuf;
// initialize counters
- comp = 0;
- x = 0;
- y = 0;
- dy = mcuHeight;
+ y = -mcuHeight;
restartMarker = 0xd0;
restart();
@@ -2247,26 +2281,24 @@ void DCTStream::reset() {
}
void DCTStream::close() {
- int i, j;
+ int i;
for (i = 0; i < 4; ++i) {
- for (j = 0; j < 32; ++j) {
- gfree(rowBuf[i][j]);
- rowBuf[i][j] = NULL;
- }
gfree(frameBuf[i]);
frameBuf[i] = NULL;
}
+ gfree(rowBuf);
+ rowBuf = NULL;
FilterStream::close();
}
int DCTStream::getChar() {
int c;
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
c = frameBuf[comp][y * bufWidth + x];
if (++comp == numComps) {
comp = 0;
@@ -2276,48 +2308,38 @@ int DCTStream::getChar() {
}
}
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ y += mcuHeight;
if (!readMCURow()) {
y = height;
return EOF;
}
- comp = 0;
- x = 0;
- dy = 0;
- }
- c = rowBuf[comp][dy][x];
- if (++comp == numComps) {
- comp = 0;
- if (++x == width) {
- x = 0;
- ++y;
- ++dy;
- if (y == height) {
- readTrailer();
- }
- }
}
+ c = *rowBufPtr++;
}
return c;
}
int DCTStream::lookChar() {
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
return frameBuf[comp][y * bufWidth + x];
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
if (!readMCURow()) {
y = height;
return EOF;
}
- comp = 0;
- x = 0;
- dy = 0;
}
- return rowBuf[comp][dy][x];
+ return *rowBufPtr;
}
}
@@ -2375,38 +2397,49 @@ GBool DCTStream::readMCURow() {
}
transformDataUnit(quantTables[compInfo[cc].quantTable],
data1, data2);
- if (hSub == 1 && vSub == 1) {
+ if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) {
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
- p1 = &rowBuf[cc][y2+y3][x1+x2];
- p1[0] = data2[i];
- p1[1] = data2[i+1];
- p1[2] = data2[i+2];
- p1[3] = data2[i+3];
- p1[4] = data2[i+4];
- p1[5] = data2[i+5];
- p1[6] = data2[i+6];
- p1[7] = data2[i+7];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p1[0] = data2[i];
+ p1[ numComps] = data2[i+1];
+ p1[2*numComps] = data2[i+2];
+ p1[3*numComps] = data2[i+3];
+ p1[4*numComps] = data2[i+4];
+ p1[5*numComps] = data2[i+5];
+ p1[6*numComps] = data2[i+6];
+ p1[7*numComps] = data2[i+7];
}
- } else if (hSub == 2 && vSub == 2) {
+ } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) {
for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
- p1 = &rowBuf[cc][y2+y3][x1+x2];
- p2 = &rowBuf[cc][y2+y3+1][x1+x2];
- p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
- p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
- p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
- p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
- p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
- p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
- p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
- p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p2 = p1 + width * numComps;
+ p1[0] = p1[numComps] =
+ p2[0] = p2[numComps] = data2[i];
+ p1[2*numComps] = p1[3*numComps] =
+ p2[2*numComps] = p2[3*numComps] = data2[i+1];
+ p1[4*numComps] = p1[5*numComps] =
+ p2[4*numComps] = p2[5*numComps] = data2[i+2];
+ p1[6*numComps] = p1[7*numComps] =
+ p2[6*numComps] = p2[7*numComps] = data2[i+3];
+ p1[8*numComps] = p1[9*numComps] =
+ p2[8*numComps] = p2[9*numComps] = data2[i+4];
+ p1[10*numComps] = p1[11*numComps] =
+ p2[10*numComps] = p2[11*numComps] = data2[i+5];
+ p1[12*numComps] = p1[13*numComps] =
+ p2[12*numComps] = p2[13*numComps] = data2[i+6];
+ p1[14*numComps] = p1[15*numComps] =
+ p2[14*numComps] = p2[15*numComps] = data2[i+7];
}
} else {
+ p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc];
i = 0;
for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
- for (y5 = 0; y5 < vSub; ++y5)
- for (x5 = 0; x5 < hSub; ++x5)
- rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) {
+ p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i];
+ }
+ }
++i;
}
}
@@ -2415,42 +2448,46 @@ GBool DCTStream::readMCURow() {
}
}
--restartCtr;
+ }
- // color space conversion
- if (colorXform) {
- // convert YCbCr to RGB
- if (numComps == 3) {
- for (y2 = 0; y2 < mcuHeight; ++y2) {
- for (x2 = 0; x2 < mcuWidth; ++x2) {
- pY = rowBuf[0][y2][x1+x2];
- pCb = rowBuf[1][y2][x1+x2] - 128;
- pCr = rowBuf[2][y2][x1+x2] - 128;
- pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];
- pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
- rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];
- pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];
- }
- }
- // convert YCbCrK to CMYK (K is passed through unchanged)
- } else if (numComps == 4) {
- for (y2 = 0; y2 < mcuHeight; ++y2) {
- for (x2 = 0; x2 < mcuWidth; ++x2) {
- pY = rowBuf[0][y2][x1+x2];
- pCb = rowBuf[1][y2][x1+x2] - 128;
- pCr = rowBuf[2][y2][x1+x2] - 128;
- pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
- pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
- rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
- pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
- }
- }
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = dctClip(pB);
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = 255 - dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = 255 - dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = 255 - dctClip(pB);
}
}
}
+
+ rowBufPtr = rowBuf;
+ if (y + mcuHeight <= height) {
+ rowBufEnd = rowBuf + numComps * width * mcuHeight;
+ } else {
+ rowBufEnd = rowBuf + numComps * width * (height - y);
+ }
+
return gTrue;
}
@@ -2609,7 +2646,7 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
return gTrue;
}
-// Read one data unit from a sequential JPEG stream.
+// Read one data unit from a progressive JPEG stream.
GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
DCTHuffTable *acHuffTable,
int *prevDC, int data[64]) {
@@ -2635,7 +2672,13 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
if ((bit = readBit()) == 9999) {
return gFalse;
}
- data[0] += bit << scanInfo.al;
+ if (bit) {
+ if (data[0] >= 0) {
+ data[0] += 1 << scanInfo.al;
+ } else {
+ data[0] -= 1 << scanInfo.al;
+ }
+ }
}
++i;
}
@@ -2652,7 +2695,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2678,7 +2725,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2701,7 +2752,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2723,7 +2778,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
j = dctZigZag[i++];
}
@@ -2837,12 +2896,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = dctClip[dctClipOffset + pR];
+ *p0++ = dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = dctClip[dctClipOffset + pG];
+ *p1++ = dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = dctClip[dctClipOffset + pB];
+ *p2++ = dctClip(pB);
}
}
// convert YCbCrK to CMYK (K is passed through unchanged)
@@ -2856,12 +2915,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = 255 - dctClip[dctClipOffset + pR];
+ *p0++ = 255 - dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = 255 - dctClip[dctClipOffset + pG];
+ *p1++ = 255 - dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = 255 - dctClip[dctClipOffset + pB];
+ *p2++ = 255 - dctClip(pB);
}
}
}
@@ -2880,71 +2939,76 @@ void DCTStream::decodeImage() {
// paper.
void DCTStream::transformDataUnit(Gushort *quantTable,
int dataIn[64], Guchar dataOut[64]) {
- int v0, v1, v2, v3, v4, v5, v6, v7, t;
+ int v0, v1, v2, v3, v4, v5, v6, v7, t0, t1, t2;
int *p;
+ Gushort *q;
int i;
- // dequant
- for (i = 0; i < 64; ++i) {
- dataIn[i] *= quantTable[i];
- }
-
- // inverse DCT on rows
+ // dequant; inverse DCT on rows
for (i = 0; i < 64; i += 8) {
p = dataIn + i;
+ q = quantTable + i;
// check for all-zero AC coefficients
if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
- t = (dctSqrt2 * p[0] + 512) >> 10;
- p[0] = t;
- p[1] = t;
- p[2] = t;
- p[3] = t;
- p[4] = t;
- p[5] = t;
- p[6] = t;
- p[7] = t;
+ t0 = p[0] * q[0];
+ p[0] = t0;
+ p[1] = t0;
+ p[2] = t0;
+ p[3] = t0;
+ p[4] = t0;
+ p[5] = t0;
+ p[6] = t0;
+ p[7] = t0;
continue;
}
// stage 4
- v0 = (dctSqrt2 * p[0] + 128) >> 8;
- v1 = (dctSqrt2 * p[4] + 128) >> 8;
- v2 = p[2];
- v3 = p[6];
- v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
- v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
- v5 = p[3] << 4;
- v6 = p[5] << 4;
+ v0 = p[0] * q[0];
+ v1 = p[4] * q[4];
+ v2 = p[2] * q[2];
+ v3 = p[6] * q[6];
+ t0 = p[1] * q[1];
+ t1 = p[7] * q[7];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = (dctSqrt2 * p[3] * q[3]) >> 12;
+ v6 = (dctSqrt2 * p[5] * q[5]) >> 12;
// stage 3
- t = (v0 - v1+ 1) >> 1;
- v0 = (v0 + v1 + 1) >> 1;
- v1 = t;
- t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
- v3 = t;
- t = (v4 - v6 + 1) >> 1;
- v4 = (v4 + v6 + 1) >> 1;
- v6 = t;
- t = (v7 + v5 + 1) >> 1;
- v5 = (v7 - v5 + 1) >> 1;
- v7 = t;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// stage 2
- t = (v0 - v3 + 1) >> 1;
- v0 = (v0 + v3 + 1) >> 1;
- v3 = t;
- t = (v1 - v2 + 1) >> 1;
- v1 = (v1 + v2 + 1) >> 1;
- v2 = t;
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
- v7 = t;
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
- v6 = t;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0] = v0 + v7;
@@ -2964,55 +3028,60 @@ void DCTStream::transformDataUnit(Gushort *quantTable,
// check for all-zero AC coefficients
if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
- t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
- p[0*8] = t;
- p[1*8] = t;
- p[2*8] = t;
- p[3*8] = t;
- p[4*8] = t;
- p[5*8] = t;
- p[6*8] = t;
- p[7*8] = t;
+ t0 = p[0*8];
+ p[1*8] = t0;
+ p[2*8] = t0;
+ p[3*8] = t0;
+ p[4*8] = t0;
+ p[5*8] = t0;
+ p[6*8] = t0;
+ p[7*8] = t0;
continue;
}
// stage 4
- v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
- v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+ v0 = p[0*8];
+ v1 = p[4*8];
v2 = p[2*8];
v3 = p[6*8];
- v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
- v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
- v5 = p[3*8];
- v6 = p[5*8];
+ v4 = p[1*8] - p[7*8];
+ v7 = p[1*8] + p[7*8];
+ v5 = (dctSqrt2 * p[3*8]) >> 12;
+ v6 = (dctSqrt2 * p[5*8]) >> 12;
// stage 3
- t = (v0 - v1 + 1) >> 1;
- v0 = (v0 + v1 + 1) >> 1;
- v1 = t;
- t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
- v3 = t;
- t = (v4 - v6 + 1) >> 1;
- v4 = (v4 + v6 + 1) >> 1;
- v6 = t;
- t = (v7 + v5 + 1) >> 1;
- v5 = (v7 - v5 + 1) >> 1;
- v7 = t;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// stage 2
- t = (v0 - v3 + 1) >> 1;
- v0 = (v0 + v3 + 1) >> 1;
- v3 = t;
- t = (v1 - v2 + 1) >> 1;
- v1 = (v1 + v2 + 1) >> 1;
- v2 = t;
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
- v7 = t;
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
- v6 = t;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0*8] = v0 + v7;
@@ -3027,7 +3096,7 @@ void DCTStream::transformDataUnit(Gushort *quantTable,
// convert to 8-bit integers
for (i = 0; i < 64; ++i) {
- dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
+ dataOut[i] = dctClip(128 + (dataIn[i] >> 3));
}
}
@@ -3103,7 +3172,6 @@ GBool DCTStream::readHeader() {
GBool doScan;
int n;
int c = 0;
- int i;
// read headers
doScan = gFalse;
@@ -3163,9 +3231,7 @@ GBool DCTStream::readHeader() {
// skip APPn / COM / etc.
if (c >= 0xe0) {
n = read16() - 2;
- for (i = 0; i < n; ++i) {
- str->getChar();
- }
+ str->discardChars(n);
} else {
error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c);
return gFalse;
@@ -3178,12 +3244,11 @@ GBool DCTStream::readHeader() {
}
GBool DCTStream::readBaselineSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -3218,12 +3283,11 @@ GBool DCTStream::readBaselineSOF() {
}
GBool DCTStream::readProgressiveSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -4194,6 +4258,9 @@ void FlateStream::reset() {
eof = gTrue;
str->reset();
+ if (pred) {
+ pred->reset();
+ }
// read header
//~ need to look at window size?
@@ -4274,10 +4341,10 @@ int FlateStream::getBlock(char *blk, int size) {
n = 0;
while (n < size) {
- if (endOfBlock && eof) {
- break;
- }
if (remain == 0) {
+ if (endOfBlock && eof) {
+ break;
+ }
readSome();
}
while (remain && n < size) {
@@ -4969,3 +5036,149 @@ GBool RunLengthEncoder::fillBuf() {
bufPtr = buf;
return gTrue;
}
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+LZWEncoder::LZWEncoder(Stream *strA):
+ FilterStream(strA)
+{
+ inBufLen = 0;
+ outBufLen = 0;
+}
+
+LZWEncoder::~LZWEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+void LZWEncoder::reset() {
+ int i;
+
+ str->reset();
+
+ // initialize code table
+ for (i = 0; i < 256; ++i) {
+ table[i].byte = i;
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+
+ // initialize input buffer
+ inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf));
+
+ // initialize output buffer with a clear-table code
+ outBuf = 256;
+ outBufLen = 9;
+ needEOD = gFalse;
+}
+
+int LZWEncoder::getChar() {
+ int ret;
+
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ ret = (outBuf >> (outBufLen - 8)) & 0xff;
+ outBufLen -= 8;
+ } else {
+ ret = (outBuf << (8 - outBufLen)) & 0xff;
+ outBufLen = 0;
+ }
+ return ret;
+}
+
+int LZWEncoder::lookChar() {
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ return (outBuf >> (outBufLen - 8)) & 0xff;
+ } else {
+ return (outBuf << (8 - outBufLen)) & 0xff;
+ }
+}
+
+// On input, outBufLen < 8.
+// This function generates, at most, 2 12-bit codes
+// --> outBufLen < 8 + 12 + 12 = 32
+void LZWEncoder::fillBuf() {
+ LZWEncoderNode *p0, *p1;
+ int seqLen, code, i;
+
+ if (needEOD) {
+ outBuf = (outBuf << codeLen) | 257;
+ outBufLen += codeLen;
+ needEOD = gFalse;
+ return;
+ }
+
+ // find longest matching sequence (if any)
+ p0 = table + inBuf[0];
+ seqLen = 1;
+ while (inBufLen > seqLen) {
+ for (p1 = p0->children; p1; p1 = p1->next) {
+ if (p1->byte == inBuf[seqLen]) {
+ break;
+ }
+ }
+ if (!p1) {
+ break;
+ }
+ p0 = p1;
+ ++seqLen;
+ }
+ code = (int)(p0 - table);
+
+ // generate an output code
+ outBuf = (outBuf << codeLen) | code;
+ outBufLen += codeLen;
+
+ // update the table
+ table[nextSeq].byte = seqLen < inBufLen ? inBuf[seqLen] : 0;
+ table[nextSeq].children = NULL;
+ if (table[code].children) {
+ table[nextSeq].next = table[code].children;
+ } else {
+ table[nextSeq].next = NULL;
+ }
+ table[code].children = table + nextSeq;
+ ++nextSeq;
+
+ // update the input buffer
+ memmove(inBuf, inBuf + seqLen, inBufLen - seqLen);
+ inBufLen -= seqLen;
+ inBufLen += str->getBlock((char *)inBuf + inBufLen,
+ sizeof(inBuf) - inBufLen);
+
+ // increment codeLen; generate clear-table code
+ if (nextSeq == (1 << codeLen)) {
+ ++codeLen;
+ if (codeLen == 13) {
+ outBuf = (outBuf << 12) | 256;
+ outBufLen += 12;
+ for (i = 0; i < 256; ++i) {
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+ }
+ }
+
+ // generate EOD next time
+ if (inBufLen == 0) {
+ needEOD = gTrue;
+ }
+}
diff --git a/xpdf/Stream.h b/xpdf/Stream.h
index 50710a6..c55d802 100644
--- a/xpdf/Stream.h
+++ b/xpdf/Stream.h
@@ -17,6 +17,7 @@
#include <stdio.h>
#include "gtypes.h"
+#include "gfile.h"
#include "Object.h"
class BaseStream;
@@ -97,13 +98,18 @@ public:
// Get next line from stream.
virtual char *getLine(char *buf, int size);
+ // Discard the next <n> bytes from stream. Returns the number of
+ // bytes discarded, which will be less than <n> only if EOF is
+ // reached.
+ virtual Guint discardChars(Guint n);
+
// Get current position in file.
- virtual int getPos() = 0;
+ virtual GFileOffset getPos() = 0;
// Go to a position in the stream. If <dir> is negative, the
// position is from the end of the file; otherwise the position is
// from the start of the file.
- virtual void setPos(Guint pos, int dir = 0) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
// Get PostScript command for the filter(s).
virtual GString *getPSFilter(int psLevel, const char *indent);
@@ -133,11 +139,11 @@ public:
// Add filters to this stream according to the parameters in <dict>.
// Returns the new stream.
- Stream *addFilters(Object *dict);
+ Stream *addFilters(Object *dict, int recursion = 0);
private:
- Stream *makeFilter(char *name, Stream *str, Object *params);
+ Stream *makeFilter(char *name, Stream *str, Object *params, int recursion);
int ref; // reference count
};
@@ -153,9 +159,9 @@ public:
BaseStream(Object *dictA);
virtual ~BaseStream();
- virtual Stream *makeSubStream(Guint start, GBool limited,
- Guint length, Object *dict) = 0;
- virtual void setPos(Guint pos, int dir = 0) = 0;
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset length, Object *dict) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
virtual GBool isBinary(GBool last = gTrue) { return last; }
virtual BaseStream *getBaseStream() { return this; }
virtual Stream *getUndecodedStream() { return this; }
@@ -163,7 +169,7 @@ public:
virtual GString *getFileName() { return NULL; }
// Get/set position of first byte of stream within the file.
- virtual Guint getStart() = 0;
+ virtual GFileOffset getStart() = 0;
virtual void moveStart(int delta) = 0;
private:
@@ -183,8 +189,8 @@ public:
FilterStream(Stream *strA);
virtual ~FilterStream();
virtual void close();
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); }
virtual Dict *getDict() { return str->getDict(); }
@@ -212,6 +218,9 @@ public:
// Reset the stream.
void reset();
+ // Close down the stream.
+ void close();
+
// Gets the next pixel from the stream. <pix> should be able to hold
// at least nComps elements. Returns false at end of file.
GBool getPixel(Guchar *pix);
@@ -252,6 +261,8 @@ public:
GBool isOk() { return ok; }
+ void reset();
+
int lookChar();
int getChar();
int getBlock(char *blk, int size);
@@ -282,11 +293,11 @@ private:
class FileStream: public BaseStream {
public:
- FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual ~FileStream();
- virtual Stream *makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strFile; }
virtual void reset();
virtual void close();
@@ -295,9 +306,9 @@ public:
virtual int lookChar()
{ return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return bufPos + (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -305,14 +316,14 @@ private:
GBool fillBuf();
FILE *f;
- Guint start;
+ GFileOffset start;
GBool limited;
- Guint length;
+ GFileOffset length;
char buf[fileStreamBufSize];
char *bufPtr;
char *bufEnd;
- Guint bufPos;
- int savePos;
+ GFileOffset bufPos;
+ GFileOffset savePos;
GBool saved;
};
@@ -325,8 +336,8 @@ public:
MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
virtual ~MemStream();
- virtual Stream *makeSubStream(Guint start, GBool limited,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strWeird; }
virtual void reset();
virtual void close();
@@ -335,9 +346,9 @@ public:
virtual int lookChar()
{ return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -363,25 +374,25 @@ private:
class EmbedStream: public BaseStream {
public:
- EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
+ EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA);
virtual ~EmbedStream();
- virtual Stream *makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return str->getKind(); }
virtual void reset() {}
virtual int getChar();
virtual int lookChar();
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart();
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart();
virtual void moveStart(int delta);
private:
Stream *str;
GBool limited;
- Guint length;
+ GFileOffset length;
};
//------------------------------------------------------------------------
@@ -623,9 +634,11 @@ private:
DCTHuffTable acHuffTables[4]; // AC Huffman tables
int numDCHuffTables; // number of DC Huffman tables
int numACHuffTables; // number of AC Huffman tables
- Guchar *rowBuf[4][32]; // buffer for one MCU (non-progressive mode)
+ Guchar *rowBuf;
+ Guchar *rowBufPtr; // current position within rowBuf
+ Guchar *rowBufEnd; // end of valid data in rowBuf
int *frameBuf[4]; // buffer for frame (progressive mode)
- int comp, x, y, dy; // current position within image/MCU
+ int comp, x, y; // current position within image/MCU
int restartCtr; // MCUs left until restart
int restartMarker; // next restart marker
int eobRun; // number of EOBs left in the current run
@@ -902,4 +915,42 @@ private:
GBool fillBuf();
};
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+struct LZWEncoderNode {
+ int byte;
+ LZWEncoderNode *next; // next sibling
+ LZWEncoderNode *children; // first child
+};
+
+class LZWEncoder: public FilterStream {
+public:
+
+ LZWEncoder(Stream *strA);
+ virtual ~LZWEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ LZWEncoderNode table[4096];
+ int nextSeq;
+ int codeLen;
+ Guchar inBuf[4096];
+ int inBufLen;
+ int outBuf;
+ int outBufLen;
+ GBool needEOD;
+
+ void fillBuf();
+};
+
#endif
diff --git a/xpdf/TextOutputDev.cc b/xpdf/TextOutputDev.cc
index 971a3fe..be1fad2 100644
--- a/xpdf/TextOutputDev.cc
+++ b/xpdf/TextOutputDev.cc
@@ -2,7 +2,7 @@
//
// TextOutputDev.cc
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2014 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include <stddef.h>
#include <math.h>
#include <ctype.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <fcntl.h> // for O_BINARY
#include <io.h> // for setmode
#endif
@@ -33,107 +33,342 @@
#include "Link.h"
#include "TextOutputDev.h"
-#ifdef MACOS
-// needed for setting type/creator of MacOS files
-#include "ICSupport.h"
-#endif
-
//------------------------------------------------------------------------
// parameters
//------------------------------------------------------------------------
-// Each bucket in a text pool includes baselines within a range of
-// this many points.
-#define textPoolStep 4
+// Size of bins used for horizontal and vertical profiles is
+// splitPrecisionMul * minFontSize.
+#define splitPrecisionMul 0.05
+
+// Minimum allowed split precision.
+#define minSplitPrecision 0.01
+
+// yMin and yMax (or xMin and xMax for rot=1,3) are adjusted by this
+// fraction of the text height, to allow for slightly overlapping
+// lines (or large ascent/descent values).
+#define ascentAdjustFactor 0
+#define descentAdjustFactor 0.35
+
+// Gaps larger than max{gap} - splitGapSlack * avgFontSize are
+// considered to be equivalent.
+#define splitGapSlack 0.2
+
+// The vertical gap threshold (minimum gap required to split
+// vertically) depends on the (approximate) number of lines in the
+// block:
+// threshold = (max + slope * nLines) * avgFontSize
+// with a min value of vertGapThresholdMin * avgFontSize.
+#define vertGapThresholdMin 0.8
+#define vertGapThresholdMax 3
+#define vertGapThresholdSlope -0.5
+
+// Vertical gap threshold for table mode.
+#define vertGapThresholdTableMin 0.2
+#define vertGapThresholdTableMax 0.5
+#define vertGapThresholdTableSlope -0.02
+
+// A large character has a font size larger than
+// largeCharThreshold * avgFontSize.
+#define largeCharThreshold 1.5
+
+// A block will be split vertically only if the resulting chunk
+// widths are greater than vertSplitChunkThreshold * avgFontSize.
+#define vertSplitChunkThreshold 2
-// Inter-character space width which will cause addChar to start a new
-// word.
-#define minWordBreakSpace 0.1
+// Max difference in primary,secondary coordinates (as a fraction of
+// the font size) allowed for duplicated text (fake boldface, drop
+// shadows) which is to be discarded.
+#define dupMaxPriDelta 0.1
+#define dupMaxSecDelta 0.2
-// Negative inter-character space width, i.e., overlap, which will
-// cause addChar to start a new word.
-#define minDupBreakOverlap 0.2
+// Inter-character spacing that varies by less than this multiple of
+// font size is assumed to be equivalent.
+#define uniformSpacing 0.07
-// Max distance between baselines of two lines within a block, as a
-// fraction of the font size.
-#define maxLineSpacingDelta 1.5
+// 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
-// Max difference in primary font sizes on two lines in the same
-// block. Delta1 is used when examining new lines above and below the
-// current block; delta2 is used when examining text that overlaps the
-// current block; delta3 is used when examining text to the left and
-// right of the current block.
-#define maxBlockFontSizeDelta1 0.05
-#define maxBlockFontSizeDelta2 0.6
-#define maxBlockFontSizeDelta3 0.2
+// Minimum paragraph indent from left margin, as a fraction of font
+// size.
+#define minParagraphIndent 0.5
-// Max difference in font sizes inside a word.
-#define maxWordFontSizeDelta 0.05
+// If the space between two lines is greater than
+// paragraphSpacingThreshold * avgLineSpacing, start a new paragraph.
+#define paragraphSpacingThreshold 1.2
-// Maximum distance between baselines of two words on the same line,
-// e.g., distance between subscript or superscript and the primary
-// baseline, as a fraction of the font size.
-#define maxIntraLineDelta 0.5
+// If font size changes by at least this much (measured in points)
+// between lines, start a new paragraph.
+#define paragraphFontSizeDelta 1
-// Minimum inter-word spacing, as a fraction of the font size. (Only
-// used for raw ordering.)
-#define minWordSpacing 0.15
+// Spaces at the start of a line in physical layout mode are this wide
+// (as a multiple of font size).
+#define physLayoutSpaceWidth 0.33
-// Maximum inter-word spacing, as a fraction of the font size.
-#define maxWordSpacing 1.5
+// 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
-// Maximum horizontal spacing which will allow a word to be pulled
-// into a block.
-#define minColSpacing1 0.3
+// Primary axis delta which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeLineDelta 0.5
-// Minimum spacing between columns, as a fraction of the font size.
-#define minColSpacing2 1.0
+// Secondary axis delta which will cause a word break in raw mode
+// (as a fraction of font size).
+#define rawModeWordSpacing 0.15
-// Maximum vertical spacing between blocks within a flow, as a
-// multiple of the font size.
-#define maxBlockSpacing 2.5
+// Secondary axis overlap which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeCharOverlap 0.2
-// Minimum spacing between characters within a word, as a fraction of
-// the font size.
-#define minCharSpacing -0.2
+// Max spacing (as a multiple of font size) allowed between the end of
+// a line and a clipped character to be included in that line.
+#define clippedTextMaxWordSpace 0.5
-// Maximum spacing between characters within a word, as a fraction of
-// the font size, when there is no obvious extra-wide character
-// spacing.
-#define maxCharSpacing 0.03
+// Max width of underlines (in points).
+#define maxUnderlineWidth 3
-// When extra-wide character spacing is detected, the inter-character
-// space threshold is set to the minimum inter-character space
-// multiplied by this constant.
-#define maxWideCharSpacingMul 1.3
+// Max horizontal distance between edge of word and start of underline
+// (as a fraction of font size).
+#define underlineSlack 0.2
-// Upper limit on spacing between characters in a word.
-#define maxWideCharSpacing 0.4
+// Max vertical distance between baseline of word and start of
+// underline (as a fraction of font size).
+#define underlineBaselineSlack 0.2
-// Max difference in primary,secondary coordinates (as a fraction of
-// the font size) allowed for duplicated text (fake boldface, drop
-// shadows) which is to be discarded.
-#define dupMaxPriDelta 0.1
-#define dupMaxSecDelta 0.2
+// Max distance between edge of text and edge of link border (as a
+// fraction of font size).
+#define hyperlinkSlack 0.2
-// Max width of underlines (in points).
-#define maxUnderlineWidth 3
+//------------------------------------------------------------------------
+// TextChar
+//------------------------------------------------------------------------
-// Min distance between baseline and underline (in points).
-//~ this should be font-size-dependent
-#define minUnderlineGap -2
+class TextChar {
+public:
-// Max distance between baseline and underline (in points).
-//~ this should be font-size-dependent
-#define maxUnderlineGap 4
+ TextChar(Unicode cA, int charPosA, int charLenA,
+ double xMinA, double yMinA, double xMaxA, double yMaxA,
+ int rotA, 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);
+
+ Unicode c;
+ int charPos;
+ int charLen;
+ double xMin, yMin, xMax, yMax;
+ Guchar rot;
+ char clipped;
+ char invisible;
+ TextFontInfo *font;
+ double fontSize;
+ double colorR,
+ colorG,
+ colorB;
+};
-// Max horizontal distance between edge of word and start of underline
-// (in points).
-//~ this should be font-size-dependent
-#define underlineSlack 1
+TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
+ double xMinA, double yMinA, double xMaxA, double yMaxA,
+ int rotA, GBool clippedA, GBool invisibleA,
+ TextFontInfo *fontA, double fontSizeA,
+ double colorRA, double colorGA, double colorBA) {
+ double t;
+
+ c = cA;
+ charPos = charPosA;
+ charLen = charLenA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ // this can happen with vertical writing mode, or with odd values
+ // for the char/word spacing parameters
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ rot = (Guchar)rotA;
+ clipped = (char)clippedA;
+ invisible = (char)invisibleA;
+ font = fontA;
+ fontSize = fontSizeA;
+ colorR = colorRA;
+ colorG = colorGA;
+ colorB = colorBA;
+}
+
+int TextChar::cmpX(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->xMin < ch2->xMin) {
+ return -1;
+ } else if (ch1->xMin > ch2->xMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int TextChar::cmpY(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->yMin < ch2->yMin) {
+ return -1;
+ } else if (ch1->yMin > ch2->yMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextBlock
+//------------------------------------------------------------------------
+
+enum TextBlockType {
+ blkVertSplit,
+ blkHorizSplit,
+ blkLeaf
+};
+
+enum TextBlockTag {
+ blkTagMulticolumn,
+ blkTagColumn,
+ blkTagLine
+};
+
+class TextBlock {
+public:
+
+ TextBlock(TextBlockType typeA, int rotA);
+ ~TextBlock();
+ void addChild(TextBlock *child);
+ void addChild(TextChar *child);
+ void prependChild(TextChar *child);
+ void updateBounds(int childIdx);
+
+ TextBlockType type;
+ TextBlockTag tag;
+ int rot;
+ double xMin, yMin, xMax, yMax;
+ GBool smallSplit; // true for blkVertSplit/blkHorizSplit
+ // where the gap size is small
+ GList *children; // for blkLeaf, children are TextWord;
+ // for others, children are TextBlock
+};
+
+TextBlock::TextBlock(TextBlockType typeA, int rotA) {
+ type = typeA;
+ tag = blkTagMulticolumn;
+ rot = rotA;
+ xMin = yMin = xMax = yMax = 0;
+ smallSplit = gFalse;
+ children = new GList();
+}
+
+TextBlock::~TextBlock() {
+ if (type == blkLeaf) {
+ delete children;
+ } else {
+ deleteGList(children, TextBlock);
+ }
+}
-// Max distance between edge of text and edge of link border
-#define hyperlinkSlack 2
+void TextBlock::addChild(TextBlock *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::addChild(TextChar *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::prependChild(TextChar *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->insert(0, child);
+}
+
+void TextBlock::updateBounds(int childIdx) {
+ TextBlock *child;
+
+ child = (TextBlock *)children->get(childIdx);
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+}
//------------------------------------------------------------------------
// TextUnderline
@@ -157,159 +392,202 @@ public:
class TextLink {
public:
- TextLink(int xMinA, int yMinA, int xMaxA, int yMaxA, Link *linkA)
- { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; link = linkA; }
- ~TextLink() {}
+ TextLink(double xMinA, double yMinA, double xMaxA, double yMaxA,
+ GString *uriA)
+ { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; uri = uriA; }
+ ~TextLink();
- int xMin, yMin, xMax, yMax;
- Link *link;
+ double xMin, yMin, xMax, yMax;
+ GString *uri;
};
+TextLink::~TextLink() {
+ if (uri) {
+ delete uri;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+TextOutputControl::TextOutputControl() {
+ mode = textOutReadingOrder;
+ fixedPitch = 0;
+ fixedLineSpacing = 0;
+ html = gFalse;
+ clipText = gFalse;
+}
+
+
//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
TextFontInfo::TextFontInfo(GfxState *state) {
+ GfxFont *gfxFont;
+
gfxFont = state->getFont();
-#if TEXTOUT_WORD_LIST
+ if (gfxFont) {
+ fontID = *gfxFont->getID();
+ ascent = gfxFont->getAscent();
+ descent = gfxFont->getDescent();
+ // "odd" ascent/descent values cause trouble more often than not
+ // (in theory these could be legitimate values for oddly designed
+ // fonts -- but they are more often due to buggy PDF generators)
+ // (values that are too small are a different issue -- those seem
+ // to be more commonly legitimate)
+ if (ascent > 1) {
+ ascent = 0.75;
+ }
+ if (descent < -0.5) {
+ descent = -0.25;
+ }
+ } else {
+ fontID.num = -1;
+ fontID.gen = -1;
+ ascent = 0.75;
+ descent = -0.25;
+ }
fontName = (gfxFont && gfxFont->getName()) ? gfxFont->getName()->copy()
: (GString *)NULL;
flags = gfxFont ? gfxFont->getFlags() : 0;
-#endif
+ mWidth = 0;
+ if (gfxFont && !gfxFont->isCIDFont()) {
+ char *name;
+ int code;
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] == 'm' && name[1] == '\0') {
+ mWidth = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+ break;
+ }
+ }
+ }
}
TextFontInfo::~TextFontInfo() {
-#if TEXTOUT_WORD_LIST
if (fontName) {
delete fontName;
}
-#endif
}
GBool TextFontInfo::matches(GfxState *state) {
- return state->getFont() == gfxFont;
+ Ref *id;
+
+ if (!state->getFont()) {
+ return gFalse;
+ }
+ id = state->getFont()->getID();
+ return id->num == fontID.num && id->gen == fontID.gen;
}
//------------------------------------------------------------------------
// TextWord
//------------------------------------------------------------------------
-TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSizeA) {
- GfxFont *gfxFont;
- double x, y, ascent, descent;
- int wMode;
+// 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, GBool spaceAfterA) {
+ TextChar *ch;
+ int i;
rot = rotA;
- font = fontA;
- fontSize = fontSizeA;
- state->transform(x0, y0, &x, &y);
- if ((gfxFont = font->gfxFont)) {
- ascent = gfxFont->getAscent() * fontSize;
- descent = gfxFont->getDescent() * fontSize;
- wMode = gfxFont->getWMode();
- } else {
- // this means that the PDF file draws text without a current font,
- // which should never happen
- ascent = 0.95 * fontSize;
- descent = -0.35 * fontSize;
- wMode = 0;
- }
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- yMin = y - fontSize;
- yMax = y;
- base = y;
- break;
- case 1:
- xMin = x;
- xMax = x + fontSize;
- base = x;
- break;
- case 2:
- yMin = y;
- yMax = y + fontSize;
- base = y;
- break;
- case 3:
- xMin = x - fontSize;
- xMax = x;
- base = x;
- break;
+ len = lenA;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ switch (rot) {
+ case 0:
+ default:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMax = ch->xMax;
+ break;
+ case 1:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMax = ch->yMax;
+ break;
+ case 2:
+ ch = (TextChar *)chars->get(start);
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMin = ch->xMin;
+ break;
+ case 3:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMin = ch->yMin;
+ break;
+ }
+ for (i = 0; i < len; ++i) {
+ ch = (TextChar *)chars->get(rot >= 2 ? start + len - 1 - i : start + i);
+ text[i] = ch->c;
+ charPos[i] = ch->charPos;
+ if (i == len - 1) {
+ charPos[len] = ch->charPos + ch->charLen;
}
- } else { // horizontal writing mode
switch (rot) {
case 0:
- yMin = y - ascent;
- yMax = y - descent;
- if (yMin == yMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- yMin = y;
- yMax = y + 1;
+ default:
+ edge[i] = ch->xMin;
+ if (i == len - 1) {
+ edge[len] = ch->xMax;
}
- base = y;
break;
case 1:
- xMin = x + descent;
- xMax = x + ascent;
- if (xMin == xMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- xMin = x;
- xMax = x + 1;
+ edge[i] = ch->yMin;
+ if (i == len - 1) {
+ edge[len] = ch->yMax;
}
- base = x;
break;
case 2:
- yMin = y + descent;
- yMax = y + ascent;
- if (yMin == yMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- yMin = y;
- yMax = y + 1;
+ edge[i] = ch->xMax;
+ if (i == len - 1) {
+ edge[len] = ch->xMin;
}
- base = y;
break;
case 3:
- xMin = x - ascent;
- xMax = x - descent;
- if (xMin == xMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- xMin = x;
- xMax = x + 1;
+ edge[i] = ch->yMax;
+ if (i == len - 1) {
+ edge[len] = ch->yMin;
}
- base = x;
break;
}
}
- text = NULL;
- edge = NULL;
- charPos = NULL;
- len = size = 0;
- spaceAfter = gFalse;
- next = NULL;
-
-#if TEXTOUT_WORD_LIST
- GfxRGB rgb;
-
- if ((state->getRender() & 3) == 1) {
- state->getStrokeRGB(&rgb);
- } else {
- state->getFillRGB(&rgb);
- }
- colorR = colToDbl(rgb.r);
- colorG = colToDbl(rgb.g);
- colorB = colToDbl(rgb.b);
-#endif
-
+ ch = (TextChar *)chars->get(start);
+ font = ch->font;
+ fontSize = ch->fontSize;
+ spaceAfter = spaceAfterA;
underlined = gFalse;
link = NULL;
+ colorR = ch->colorR;
+ colorG = ch->colorG;
+ colorB = ch->colorB;
+ invisible = ch->invisible;
+}
+
+TextWord::TextWord(TextWord *word) {
+ *this = *word;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ memcpy(text, word->text, len * sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ memcpy(edge, word->edge, (len + 1) * sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ memcpy(charPos, word->charPos, (len + 1) * sizeof(int));
}
TextWord::~TextWord() {
@@ -318,175 +596,65 @@ TextWord::~TextWord() {
gfree(charPos);
}
-void TextWord::addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- Unicode u) {
- int wMode;
-
- if (len == size) {
- size += 16;
- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
- edge = (double *)greallocn(edge, size + 1, sizeof(double));
- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
- }
- text[len] = u;
- charPos[len] = charPosA;
- charPos[len + 1] = charPosA + charLen;
- wMode = font->gfxFont ? font->gfxFont->getWMode() : 0;
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- if (len == 0) {
- xMin = x - fontSize;
- }
- edge[len] = x - fontSize;
- xMax = edge[len+1] = x;
- break;
- case 1:
- if (len == 0) {
- yMin = y - fontSize;
- }
- edge[len] = y - fontSize;
- yMax = edge[len+1] = y;
- break;
- case 2:
- if (len == 0) {
- xMax = x + fontSize;
- }
- edge[len] = x + fontSize;
- xMin = edge[len+1] = x;
- break;
- case 3:
- if (len == 0) {
- yMax = y + fontSize;
- }
- edge[len] = y + fontSize;
- yMin = edge[len+1] = y;
- break;
- }
- } else { // horizontal writing mode
- switch (rot) {
- case 0:
- if (len == 0) {
- xMin = x;
- }
- edge[len] = x;
- xMax = edge[len+1] = x + dx;
- break;
- case 1:
- if (len == 0) {
- yMin = y;
- }
- edge[len] = y;
- yMax = edge[len+1] = y + dy;
- break;
- case 2:
- if (len == 0) {
- xMax = x;
- }
- edge[len] = x;
- xMin = edge[len+1] = x + dx;
- break;
- case 3:
- if (len == 0) {
- yMax = y;
- }
- edge[len] = y;
- yMin = edge[len+1] = y + dy;
- break;
- }
+// This is used to append a clipped character to a word.
+void TextWord::appendChar(TextChar *ch) {
+ if (ch->xMin < xMin) {
+ xMin = ch->xMin;
}
- ++len;
-}
-
-void TextWord::merge(TextWord *word) {
- int i;
-
- if (word->xMin < xMin) {
- xMin = word->xMin;
- }
- if (word->yMin < yMin) {
- yMin = word->yMin;
- }
- if (word->xMax > xMax) {
- xMax = word->xMax;
- }
- if (word->yMax > yMax) {
- yMax = word->yMax;
+ if (ch->xMax > xMax) {
+ xMax = ch->xMax;
}
- if (len + word->len > size) {
- size = len + word->len;
- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
- edge = (double *)greallocn(edge, size + 1, sizeof(double));
- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
+ if (ch->yMin < yMin) {
+ yMin = ch->yMin;
}
- for (i = 0; i < word->len; ++i) {
- text[len + i] = word->text[i];
- edge[len + i] = word->edge[i];
- charPos[len + i] = word->charPos[i];
+ if (ch->yMax > yMax) {
+ yMax = ch->yMax;
}
- edge[len + word->len] = word->edge[word->len];
- charPos[len + word->len] = word->charPos[word->len];
- len += word->len;
-}
-
-inline int TextWord::primaryCmp(TextWord *word) {
- double cmp;
-
- cmp = 0; // make gcc happy
+ text = (Unicode *)greallocn(text, len + 1, sizeof(Unicode));
+ edge = (double *)greallocn(edge, len + 2, sizeof(double));
+ charPos = (int *)greallocn(charPos, len + 2, sizeof(int));
+ text[len] = ch->c;
+ charPos[len] = ch->charPos;
+ charPos[len+1] = ch->charPos + ch->charLen;
switch (rot) {
case 0:
- cmp = xMin - word->xMin;
+ default:
+ edge[len] = ch->xMin;
+ edge[len+1] = ch->xMax;
break;
case 1:
- cmp = yMin - word->yMin;
+ edge[len] = ch->yMin;
+ edge[len+1] = ch->yMax;
break;
case 2:
- cmp = word->xMax - xMax;
+ edge[len] = ch->xMax;
+ edge[len+1] = ch->xMin;
break;
case 3:
- cmp = word->yMax - yMax;
+ edge[len] = ch->yMax;
+ edge[len+1] = ch->yMin;
break;
}
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
-}
-
-double TextWord::primaryDelta(TextWord *word) {
- double delta;
-
- delta = 0; // make gcc happy
- switch (rot) {
- case 0:
- delta = word->xMin - xMax;
- break;
- case 1:
- delta = word->yMin - yMax;
- break;
- case 2:
- delta = xMin - word->xMax;
- break;
- case 3:
- delta = yMin - word->yMax;
- break;
- }
- return delta;
+ ++len;
}
int TextWord::cmpYX(const void *p1, const void *p2) {
- TextWord *word1 = *(TextWord **)p1;
- TextWord *word2 = *(TextWord **)p2;
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
double cmp;
- cmp = word1->yMin - word2->yMin;
- if (cmp == 0) {
+ if ((cmp = word1->yMin - word2->yMin) == 0) {
cmp = word1->xMin - word2->xMin;
}
return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}
-#if TEXTOUT_WORD_LIST
+int TextWord::cmpCharPos(const void *p1, const void *p2) {
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
+
+ return word1->charPos[0] - word2->charPos[0];
+}
GString *TextWord::getText() {
GString *s;
@@ -539,1285 +707,198 @@ void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA,
}
}
-#endif // TEXTOUT_WORD_LIST
-
-//------------------------------------------------------------------------
-// TextPool
-//------------------------------------------------------------------------
-
-TextPool::TextPool() {
- minBaseIdx = 0;
- maxBaseIdx = -1;
- pool = NULL;
- cursor = NULL;
- cursorBaseIdx = -1;
-}
-
-TextPool::~TextPool() {
- int baseIdx;
- TextWord *word, *word2;
-
- for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
- for (word = pool[baseIdx - minBaseIdx]; word; word = word2) {
- word2 = word->next;
- delete word;
- }
- }
- gfree(pool);
-}
-
-int TextPool::getBaseIdx(double base) {
- int baseIdx;
-
- baseIdx = (int)(base / textPoolStep);
- if (baseIdx < minBaseIdx) {
- return minBaseIdx;
- }
- if (baseIdx > maxBaseIdx) {
- return maxBaseIdx;
- }
- return baseIdx;
-}
-
-void TextPool::addWord(TextWord *word) {
- TextWord **newPool;
- int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx;
- TextWord *w0, *w1;
-
- // expand the array if needed
- wordBaseIdx = (int)(word->base / textPoolStep);
- if (minBaseIdx > maxBaseIdx) {
- minBaseIdx = wordBaseIdx - 128;
- maxBaseIdx = wordBaseIdx + 128;
- pool = (TextWord **)gmallocn(maxBaseIdx - minBaseIdx + 1,
- sizeof(TextWord *));
- for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
- pool[baseIdx - minBaseIdx] = NULL;
- }
- } else if (wordBaseIdx < minBaseIdx) {
- newMinBaseIdx = wordBaseIdx - 128;
- newPool = (TextWord **)gmallocn(maxBaseIdx - newMinBaseIdx + 1,
- sizeof(TextWord *));
- for (baseIdx = newMinBaseIdx; baseIdx < minBaseIdx; ++baseIdx) {
- newPool[baseIdx - newMinBaseIdx] = NULL;
- }
- memcpy(&newPool[minBaseIdx - newMinBaseIdx], pool,
- (maxBaseIdx - minBaseIdx + 1) * sizeof(TextWord *));
- gfree(pool);
- pool = newPool;
- minBaseIdx = newMinBaseIdx;
- } else if (wordBaseIdx > maxBaseIdx) {
- newMaxBaseIdx = wordBaseIdx + 128;
- pool = (TextWord **)greallocn(pool, newMaxBaseIdx - minBaseIdx + 1,
- sizeof(TextWord *));
- for (baseIdx = maxBaseIdx + 1; baseIdx <= newMaxBaseIdx; ++baseIdx) {
- pool[baseIdx - minBaseIdx] = NULL;
- }
- maxBaseIdx = newMaxBaseIdx;
- }
-
- // insert the new word
- if (cursor && wordBaseIdx == cursorBaseIdx &&
- word->primaryCmp(cursor) >= 0) {
- w0 = cursor;
- w1 = cursor->next;
- } else {
- w0 = NULL;
- w1 = pool[wordBaseIdx - minBaseIdx];
- }
- for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) ;
- word->next = w1;
- if (w0) {
- w0->next = word;
- } else {
- pool[wordBaseIdx - minBaseIdx] = word;
- }
- cursor = word;
- cursorBaseIdx = wordBaseIdx;
-}
-
-//------------------------------------------------------------------------
-// TextLine
-//------------------------------------------------------------------------
-
-TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) {
- blk = blkA;
- rot = rotA;
- xMin = yMin = 0;
- xMax = yMax = -1;
- base = baseA;
- words = lastWord = NULL;
- text = NULL;
- edge = NULL;
- col = NULL;
- len = 0;
- convertedLen = 0;
- hyphenated = gFalse;
- next = NULL;
-}
-
-TextLine::~TextLine() {
- TextWord *word;
-
- while (words) {
- word = words;
- words = words->next;
- delete word;
- }
- gfree(text);
- gfree(edge);
- gfree(col);
-}
-
-void TextLine::addWord(TextWord *word) {
- if (lastWord) {
- lastWord->next = word;
- } else {
- words = word;
- }
- lastWord = word;
-
- if (xMin > xMax) {
- xMin = word->xMin;
- xMax = word->xMax;
- yMin = word->yMin;
- yMax = word->yMax;
- } else {
- if (word->xMin < xMin) {
- xMin = word->xMin;
- }
- if (word->xMax > xMax) {
- xMax = word->xMax;
- }
- if (word->yMin < yMin) {
- yMin = word->yMin;
- }
- if (word->yMax > yMax) {
- yMax = word->yMax;
- }
- }
-}
-
-double TextLine::primaryDelta(TextLine *line) {
- double delta;
-
- delta = 0; // make gcc happy
- switch (rot) {
- case 0:
- delta = line->xMin - xMax;
- break;
- case 1:
- delta = line->yMin - yMax;
- break;
- case 2:
- delta = xMin - line->xMax;
- break;
- case 3:
- delta = yMin - line->yMax;
- break;
- }
- return delta;
-}
-
-int TextLine::primaryCmp(TextLine *line) {
- double cmp;
-
- cmp = 0; // make gcc happy
+double TextWord::getBaseline() {
switch (rot) {
case 0:
- cmp = xMin - line->xMin;
- break;
+ default:
+ return yMax + fontSize * font->descent;
case 1:
- cmp = yMin - line->yMin;
- break;
+ return xMin - fontSize * font->descent;
case 2:
- cmp = line->xMax - xMax;
- break;
+ return yMin - fontSize * font->descent;
case 3:
- cmp = line->yMax - yMax;
- break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
-}
-
-int TextLine::secondaryCmp(TextLine *line) {
- double cmp;
-
- cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base;
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
-}
-
-int TextLine::cmpYX(TextLine *line) {
- int cmp;
-
- if ((cmp = secondaryCmp(line))) {
- return cmp;
+ return xMax + fontSize * font->descent;
}
- return primaryCmp(line);
}
-int TextLine::cmpXY(const void *p1, const void *p2) {
- TextLine *line1 = *(TextLine **)p1;
- TextLine *line2 = *(TextLine **)p2;
- int cmp;
-
- if ((cmp = line1->primaryCmp(line2))) {
- return cmp;
- }
- return line1->secondaryCmp(line2);
+GString *TextWord::getLinkURI() {
+ return link ? link->uri : (GString *)NULL;
}
-void TextLine::coalesce(UnicodeMap *uMap) {
- TextWord *word0, *word1;
- double space, delta, minSpace;
- GBool isUnicode;
- char buf[8];
- int i, j;
-
- if (words->next) {
+//------------------------------------------------------------------------
+// TextLine
+//------------------------------------------------------------------------
- // compute the inter-word space threshold
- if (words->len > 1 || words->next->len > 1) {
- minSpace = 0;
- } else {
- minSpace = words->primaryDelta(words->next);
- for (word0 = words->next, word1 = word0->next;
- word1 && minSpace > 0;
- word0 = word1, word1 = word0->next) {
- if (word1->len > 1) {
- minSpace = 0;
- }
- delta = word0->primaryDelta(word1);
- if (delta < minSpace) {
- minSpace = delta;
- }
- }
- }
- if (minSpace <= 0) {
- space = maxCharSpacing * words->fontSize;
- } else {
- space = maxWideCharSpacingMul * minSpace;
- if (space > maxWideCharSpacing * words->fontSize) {
- space = maxWideCharSpacing * words->fontSize;
- }
- }
+TextLine::TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA) {
+ TextWord *word;
+ int i, j, k;
- // merge words
- word0 = words;
- word1 = words->next;
- while (word1) {
- if (word0->primaryDelta(word1) >= space) {
- word0->spaceAfter = gTrue;
- word0 = word1;
- word1 = word1->next;
- } else if (word0->font == word1->font &&
- word0->underlined == word1->underlined &&
- fabs(word0->fontSize - word1->fontSize) <
- maxWordFontSizeDelta * words->fontSize &&
- word1->charPos[0] == word0->charPos[word0->len]) {
- word0->merge(word1);
- word0->next = word1->next;
- delete word1;
- word1 = word0->next;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
- }
+ words = wordsA;
+ rot = 0;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ fontSize = fontSizeA;
+ px = 0;
+ pw = 0;
- // build the line text
- isUnicode = uMap ? uMap->isUnicode() : gFalse;
+ // build the text
len = 0;
- for (word1 = words; word1; word1 = word1->next) {
- len += word1->len;
- if (word1->spaceAfter) {
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ len += word->len;
+ if (word->spaceAfter) {
++len;
}
}
text = (Unicode *)gmallocn(len, sizeof(Unicode));
edge = (double *)gmallocn(len + 1, sizeof(double));
- i = 0;
- for (word1 = words; word1; word1 = word1->next) {
- for (j = 0; j < word1->len; ++j) {
- text[i] = word1->text[j];
- edge[i] = word1->edge[j];
- ++i;
+ j = 0;
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ if (i == 0) {
+ rot = word->rot;
}
- edge[i] = word1->edge[word1->len];
- if (word1->spaceAfter) {
- text[i] = (Unicode)0x0020;
- ++i;
+ for (k = 0; k < word->len; ++k) {
+ text[j] = word->text[k];
+ edge[j] = word->edge[k];
+ ++j;
}
- }
-
- // compute convertedLen and set up the col array
- col = (int *)gmallocn(len + 1, sizeof(int));
- convertedLen = 0;
- for (i = 0; i < len; ++i) {
- col[i] = convertedLen;
- if (isUnicode) {
- ++convertedLen;
- } else if (uMap) {
- convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf));
+ edge[j] = word->edge[word->len];
+ if (word->spaceAfter) {
+ text[j] = (Unicode)0x0020;
+ ++j;
+ edge[j] = edge[j - 1];
}
}
- col[len] = convertedLen;
-
- // check for hyphen at end of line
- //~ need to check for other chars used as hyphens
+ //~ need to check for other Unicode chars used as hyphens
hyphenated = text[len - 1] == (Unicode)'-';
}
-//------------------------------------------------------------------------
-// TextLineFrag
-//------------------------------------------------------------------------
-
-class TextLineFrag {
-public:
-
- TextLine *line; // the line object
- int start, len; // offset and length of this fragment
- // (in Unicode chars)
- double xMin, xMax; // bounding box coordinates
- double yMin, yMax;
- double base; // baseline virtual coordinate
- int col; // first column
-
- void init(TextLine *lineA, int startA, int lenA);
- void computeCoords(GBool oneRot);
-
- static int cmpYXPrimaryRot(const void *p1, const void *p2);
- static int cmpYXLineRot(const void *p1, const void *p2);
- static int cmpXYLineRot(const void *p1, const void *p2);
- static int cmpXYColumnPrimaryRot(const void *p1, const void *p2);
- static int cmpXYColumnLineRot(const void *p1, const void *p2);
-};
-
-void TextLineFrag::init(TextLine *lineA, int startA, int lenA) {
- line = lineA;
- start = startA;
- len = lenA;
- col = line->col[start];
-}
-
-void TextLineFrag::computeCoords(GBool oneRot) {
- TextBlock *blk;
- double d0, d1, d2, d3, d4;
-
- if (oneRot) {
-
- switch (line->rot) {
- case 0:
- xMin = line->edge[start];
- xMax = line->edge[start + len];
- yMin = line->yMin;
- yMax = line->yMax;
- break;
- case 1:
- xMin = line->xMin;
- xMax = line->xMax;
- yMin = line->edge[start];
- yMax = line->edge[start + len];
- break;
- case 2:
- xMin = line->edge[start + len];
- xMax = line->edge[start];
- yMin = line->yMin;
- yMax = line->yMax;
- break;
- case 3:
- xMin = line->xMin;
- xMax = line->xMax;
- yMin = line->edge[start + len];
- yMax = line->edge[start];
- break;
- }
- base = line->base;
-
- } else {
-
- if (line->rot == 0 && line->blk->page->primaryRot == 0) {
-
- xMin = line->edge[start];
- xMax = line->edge[start + len];
- yMin = line->yMin;
- yMax = line->yMax;
- base = line->base;
-
- } else {
-
- blk = line->blk;
- d0 = line->edge[start];
- d1 = line->edge[start + len];
- d2 = d3 = d4 = 0; // make gcc happy
-
- switch (line->rot) {
- case 0:
- d2 = line->yMin;
- d3 = line->yMax;
- d4 = line->base;
- d0 = (d0 - blk->xMin) / (blk->xMax - blk->xMin);
- d1 = (d1 - blk->xMin) / (blk->xMax - blk->xMin);
- d2 = (d2 - blk->yMin) / (blk->yMax - blk->yMin);
- d3 = (d3 - blk->yMin) / (blk->yMax - blk->yMin);
- d4 = (d4 - blk->yMin) / (blk->yMax - blk->yMin);
- break;
- case 1:
- d2 = line->xMax;
- d3 = line->xMin;
- d4 = line->base;
- d0 = (d0 - blk->yMin) / (blk->yMax - blk->yMin);
- d1 = (d1 - blk->yMin) / (blk->yMax - blk->yMin);
- d2 = (blk->xMax - d2) / (blk->xMax - blk->xMin);
- d3 = (blk->xMax - d3) / (blk->xMax - blk->xMin);
- d4 = (blk->xMax - d4) / (blk->xMax - blk->xMin);
- break;
- case 2:
- d2 = line->yMax;
- d3 = line->yMin;
- d4 = line->base;
- d0 = (blk->xMax - d0) / (blk->xMax - blk->xMin);
- d1 = (blk->xMax - d1) / (blk->xMax - blk->xMin);
- d2 = (blk->yMax - d2) / (blk->yMax - blk->yMin);
- d3 = (blk->yMax - d3) / (blk->yMax - blk->yMin);
- d4 = (blk->yMax - d4) / (blk->yMax - blk->yMin);
- break;
- case 3:
- d2 = line->xMin;
- d3 = line->xMax;
- d4 = line->base;
- d0 = (blk->yMax - d0) / (blk->yMax - blk->yMin);
- d1 = (blk->yMax - d1) / (blk->yMax - blk->yMin);
- d2 = (d2 - blk->xMin) / (blk->xMax - blk->xMin);
- d3 = (d3 - blk->xMin) / (blk->xMax - blk->xMin);
- d4 = (d4 - blk->xMin) / (blk->xMax - blk->xMin);
- break;
- }
-
- switch (line->blk->page->primaryRot) {
- case 0:
- xMin = blk->xMin + d0 * (blk->xMax - blk->xMin);
- xMax = blk->xMin + d1 * (blk->xMax - blk->xMin);
- yMin = blk->yMin + d2 * (blk->yMax - blk->yMin);
- yMax = blk->yMin + d3 * (blk->yMax - blk->yMin);
- base = blk->yMin + d4 * (blk->yMax - blk->yMin);
- break;
- case 1:
- xMin = blk->xMax - d3 * (blk->xMax - blk->xMin);
- xMax = blk->xMax - d2 * (blk->xMax - blk->xMin);
- yMin = blk->yMin + d0 * (blk->yMax - blk->yMin);
- yMax = blk->yMin + d1 * (blk->yMax - blk->yMin);
- base = blk->xMax - d4 * (blk->xMax - blk->xMin);
- break;
- case 2:
- xMin = blk->xMax - d1 * (blk->xMax - blk->xMin);
- xMax = blk->xMax - d0 * (blk->xMax - blk->xMin);
- yMin = blk->yMax - d3 * (blk->yMax - blk->yMin);
- yMax = blk->yMax - d2 * (blk->yMax - blk->yMin);
- base = blk->yMax - d4 * (blk->yMax - blk->yMin);
- break;
- case 3:
- xMin = blk->xMin + d2 * (blk->xMax - blk->xMin);
- xMax = blk->xMin + d3 * (blk->xMax - blk->xMin);
- yMin = blk->yMax - d1 * (blk->yMax - blk->yMin);
- yMax = blk->yMax - d0 * (blk->yMax - blk->yMin);
- base = blk->xMin + d4 * (blk->xMax - blk->xMin);
- break;
- }
-
- }
- }
-}
-
-int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
-
- cmp = 0; // make gcc happy
- switch (frag1->line->blk->page->primaryRot) {
- case 0:
- if (fabs(cmp = frag1->yMin - frag2->yMin) < 0.01) {
- cmp = frag1->xMin - frag2->xMin;
- }
- break;
- case 1:
- if (fabs(cmp = frag2->xMax - frag1->xMax) < 0.01) {
- cmp = frag1->yMin - frag2->yMin;
- }
- break;
- case 2:
- if (fabs(cmp = frag2->yMin - frag1->yMin) < 0.01) {
- cmp = frag2->xMax - frag1->xMax;
- }
- break;
- case 3:
- if (fabs(cmp = frag1->xMax - frag2->xMax) < 0.01) {
- cmp = frag2->yMax - frag1->yMax;
- }
- break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
-}
-
-int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
-
- cmp = 0; // make gcc happy
- switch (frag1->line->rot) {
- case 0:
- if ((cmp = frag1->yMin - frag2->yMin) == 0) {
- cmp = frag1->xMin - frag2->xMin;
- }
- break;
- case 1:
- if ((cmp = frag2->xMax - frag1->xMax) == 0) {
- cmp = frag1->yMin - frag2->yMin;
- }
- break;
- case 2:
- if ((cmp = frag2->yMin - frag1->yMin) == 0) {
- cmp = frag2->xMax - frag1->xMax;
- }
- break;
- case 3:
- if ((cmp = frag1->xMax - frag2->xMax) == 0) {
- cmp = frag2->yMax - frag1->yMax;
- }
- break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+TextLine::~TextLine() {
+ deleteGList(words, TextWord);
+ gfree(text);
+ gfree(edge);
}
-int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
+double TextLine::getBaseline() {
+ TextWord *word0;
- cmp = 0; // make gcc happy
- switch (frag1->line->rot) {
+ word0 = (TextWord *)words->get(0);
+ switch (rot) {
case 0:
- if ((cmp = frag1->xMin - frag2->xMin) == 0) {
- cmp = frag1->yMin - frag2->yMin;
- }
- break;
+ default:
+ return yMax + fontSize * word0->font->descent;
case 1:
- if ((cmp = frag1->yMin - frag2->yMin) == 0) {
- cmp = frag2->xMax - frag1->xMax;
- }
- break;
+ return xMin - fontSize * word0->font->descent;
case 2:
- if ((cmp = frag2->xMax - frag1->xMax) == 0) {
- cmp = frag2->yMin - frag1->yMin;
- }
- break;
+ return yMin - fontSize * word0->font->descent;
case 3:
- if ((cmp = frag2->yMax - frag1->yMax) == 0) {
- cmp = frag1->xMax - frag2->xMax;
- }
- break;
+ return xMax + fontSize * word0->font->descent;
}
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
-}
-
-int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
-
- // if columns overlap, compare y values
- if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -
- frag2->line->col[frag2->start]) &&
- frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start])) {
- cmp = 0; // make gcc happy
- switch (frag1->line->blk->page->primaryRot) {
- case 0: cmp = frag1->yMin - frag2->yMin; break;
- case 1: cmp = frag2->xMax - frag1->xMax; break;
- case 2: cmp = frag2->yMin - frag1->yMin; break;
- case 3: cmp = frag1->xMax - frag2->xMax; break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
- }
-
- // otherwise, compare starting column
- return frag1->col - frag2->col;
-}
-
-int TextLineFrag::cmpXYColumnLineRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
-
- // if columns overlap, compare y values
- if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -
- frag2->line->col[frag2->start]) &&
- frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start])) {
- cmp = 0; // make gcc happy
- switch (frag1->line->rot) {
- case 0: cmp = frag1->yMin - frag2->yMin; break;
- case 1: cmp = frag2->xMax - frag1->xMax; break;
- case 2: cmp = frag2->yMin - frag1->yMin; break;
- case 3: cmp = frag1->xMax - frag2->xMax; break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
- }
-
- // otherwise, compare starting column
- return frag1->col - frag2->col;
}
//------------------------------------------------------------------------
-// TextBlock
+// TextParagraph
//------------------------------------------------------------------------
-TextBlock::TextBlock(TextPage *pageA, int rotA) {
- page = pageA;
- rot = rotA;
- xMin = yMin = 0;
- xMax = yMax = -1;
- priMin = 0;
- priMax = page->pageWidth;
- pool = new TextPool();
- lines = NULL;
- curLine = NULL;
- next = NULL;
- stackNext = NULL;
-}
-
-TextBlock::~TextBlock() {
+TextParagraph::TextParagraph(GList *linesA) {
TextLine *line;
+ int i;
- delete pool;
- while (lines) {
- line = lines;
- lines = lines->next;
- delete line;
- }
-}
-
-void TextBlock::addWord(TextWord *word) {
- pool->addWord(word);
- if (xMin > xMax) {
- xMin = word->xMin;
- xMax = word->xMax;
- yMin = word->yMin;
- yMax = word->yMax;
- } else {
- if (word->xMin < xMin) {
- xMin = word->xMin;
- }
- if (word->xMax > xMax) {
- xMax = word->xMax;
- }
- if (word->yMin < yMin) {
- yMin = word->yMin;
- }
- if (word->yMax > yMax) {
- yMax = word->yMax;
- }
- }
-}
-
-void TextBlock::coalesce(UnicodeMap *uMap, double fixedPitch) {
- TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord;
- TextLine *line, *line0, *line1;
- int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx;
- int baseIdx, bestWordBaseIdx, idx0, idx1;
- double minBase, maxBase;
- double fontSize, wordSpacing, delta, priDelta, secDelta;
- TextLine **lineArray;
- GBool found, overlap;
- int col1, col2;
- int i, j, k;
-
- // discard duplicated text (fake boldface, drop shadows)
- for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) {
- word0 = pool->getPool(idx0);
- while (word0) {
- priDelta = dupMaxPriDelta * word0->fontSize;
- secDelta = dupMaxSecDelta * word0->fontSize;
- maxBaseIdx = pool->getBaseIdx(word0->base + secDelta);
- found = gFalse;
- word1 = word2 = NULL; // make gcc happy
- for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) {
- if (idx1 == idx0) {
- word1 = word0;
- word2 = word0->next;
- } else {
- word1 = NULL;
- word2 = pool->getPool(idx1);
- }
- for (; word2; word1 = word2, word2 = word2->next) {
- if (word2->len == word0->len &&
- !memcmp(word2->text, word0->text,
- word0->len * sizeof(Unicode))) {
- switch (rot) {
- case 0:
- case 2:
- found = fabs(word0->xMin - word2->xMin) < priDelta &&
- fabs(word0->xMax - word2->xMax) < priDelta &&
- fabs(word0->yMin - word2->yMin) < secDelta &&
- fabs(word0->yMax - word2->yMax) < secDelta;
- break;
- case 1:
- case 3:
- found = fabs(word0->xMin - word2->xMin) < secDelta &&
- fabs(word0->xMax - word2->xMax) < secDelta &&
- fabs(word0->yMin - word2->yMin) < priDelta &&
- fabs(word0->yMax - word2->yMax) < priDelta;
- break;
- }
- }
- if (found) {
- break;
- }
- }
- if (found) {
- break;
- }
- }
- if (found) {
- if (word1) {
- word1->next = word2->next;
- } else {
- pool->setPool(idx1, word2->next);
- }
- delete word2;
- } else {
- word0 = word0->next;
- }
- }
- }
-
- // build the lines
- curLine = NULL;
- poolMinBaseIdx = pool->minBaseIdx;
- charCount = 0;
- nLines = 0;
- while (1) {
-
- // find the first non-empty line in the pool
- for (;
- poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx);
- ++poolMinBaseIdx) ;
- if (poolMinBaseIdx > pool->maxBaseIdx) {
- break;
- }
-
- // look for the left-most word in the first four lines of the
- // pool -- this avoids starting with a superscript word
- startBaseIdx = poolMinBaseIdx;
- for (baseIdx = poolMinBaseIdx + 1;
- baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
- ++baseIdx) {
- if (!pool->getPool(baseIdx)) {
- continue;
- }
- if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
- < 0) {
- startBaseIdx = baseIdx;
- }
- }
-
- // create a new line
- word0 = pool->getPool(startBaseIdx);
- pool->setPool(startBaseIdx, word0->next);
- word0->next = NULL;
- line = new TextLine(this, word0->rot, word0->base);
- line->addWord(word0);
- lastWord = word0;
-
- // compute the search range
- fontSize = word0->fontSize;
- minBase = word0->base - maxIntraLineDelta * fontSize;
- maxBase = word0->base + maxIntraLineDelta * fontSize;
- minBaseIdx = pool->getBaseIdx(minBase);
- maxBaseIdx = pool->getBaseIdx(maxBase);
- wordSpacing = fixedPitch ? fixedPitch : maxWordSpacing * fontSize;
-
- // find the rest of the words in this line
- while (1) {
-
- // find the left-most word whose baseline is in the range for
- // this line
- bestWordBaseIdx = 0;
- bestWord0 = bestWord1 = NULL;
- overlap = gFalse;
- for (baseIdx = minBaseIdx;
- !overlap && baseIdx <= maxBaseIdx;
- ++baseIdx) {
- for (word0 = NULL, word1 = pool->getPool(baseIdx);
- word1;
- word0 = word1, word1 = word1->next) {
- if (word1->base >= minBase &&
- word1->base <= maxBase) {
- delta = lastWord->primaryDelta(word1);
- if (delta < minCharSpacing * fontSize) {
- overlap = gTrue;
- break;
- } else {
- if (delta < wordSpacing &&
- (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) {
- bestWordBaseIdx = baseIdx;
- bestWord0 = word0;
- bestWord1 = word1;
- }
- break;
- }
- }
- }
- }
- if (overlap || !bestWord1) {
- break;
- }
-
- // remove it from the pool, and add it to the line
- if (bestWord0) {
- bestWord0->next = bestWord1->next;
- } else {
- pool->setPool(bestWordBaseIdx, bestWord1->next);
- }
- bestWord1->next = NULL;
- line->addWord(bestWord1);
- lastWord = bestWord1;
- }
-
- // add the line
- if (curLine && line->cmpYX(curLine) > 0) {
- line0 = curLine;
- line1 = curLine->next;
- } else {
- line0 = NULL;
- line1 = lines;
- }
- for (;
- line1 && line->cmpYX(line1) > 0;
- line0 = line1, line1 = line1->next) ;
- if (line0) {
- line0->next = line;
- } else {
- lines = line;
- }
- line->next = line1;
- curLine = line;
- line->coalesce(uMap);
- charCount += line->len;
- ++nLines;
- }
-
- // sort lines into xy order for column assignment
- lineArray = (TextLine **)gmallocn(nLines, sizeof(TextLine *));
- for (line = lines, i = 0; line; line = line->next, ++i) {
- lineArray[i] = line;
- }
- qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY);
-
- // column assignment
- nColumns = 0;
- if (fixedPitch) {
- for (i = 0; i < nLines; ++i) {
- line0 = lineArray[i];
- col1 = 0; // make gcc happy
- switch (rot) {
- case 0:
- col1 = (int)((line0->xMin - xMin) / fixedPitch + 0.5);
- break;
- case 1:
- col1 = (int)((line0->yMin - yMin) / fixedPitch + 0.5);
- break;
- case 2:
- col1 = (int)((xMax - line0->xMax) / fixedPitch + 0.5);
- break;
- case 3:
- col1 = (int)((yMax - line0->yMax) / fixedPitch + 0.5);
- break;
- }
- for (k = 0; k <= line0->len; ++k) {
- line0->col[k] += col1;
- }
- if (line0->col[line0->len] > nColumns) {
- nColumns = line0->col[line0->len];
- }
- }
- } else {
- for (i = 0; i < nLines; ++i) {
- line0 = lineArray[i];
- col1 = 0;
- for (j = 0; j < i; ++j) {
- line1 = lineArray[j];
- if (line1->primaryDelta(line0) >= 0) {
- col2 = line1->col[line1->len] + 1;
- } else {
- k = 0; // make gcc happy
- switch (rot) {
- case 0:
- for (k = 0;
- k < line1->len &&
- line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
- ++k) ;
- break;
- case 1:
- for (k = 0;
- k < line1->len &&
- line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
- ++k) ;
- break;
- case 2:
- for (k = 0;
- k < line1->len &&
- line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
- ++k) ;
- break;
- case 3:
- for (k = 0;
- k < line1->len &&
- line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
- ++k) ;
- break;
- }
- col2 = line1->col[k];
- }
- if (col2 > col1) {
- col1 = col2;
- }
- }
- for (k = 0; k <= line0->len; ++k) {
- line0->col[k] += col1;
- }
- if (line0->col[line0->len] > nColumns) {
- nColumns = line0->col[line0->len];
- }
- }
- }
- gfree(lineArray);
-}
-
-void TextBlock::updatePriMinMax(TextBlock *blk) {
- double newPriMin, newPriMax;
- GBool gotPriMin, gotPriMax;
-
- gotPriMin = gotPriMax = gFalse;
- newPriMin = newPriMax = 0; // make gcc happy
- switch (page->primaryRot) {
- case 0:
- case 2:
- if (blk->yMin < yMax && blk->yMax > yMin) {
- if (blk->xMin < xMin) {
- newPriMin = blk->xMax;
- gotPriMin = gTrue;
- }
- if (blk->xMax > xMax) {
- newPriMax = blk->xMin;
- gotPriMax = gTrue;
- }
- }
- break;
- case 1:
- case 3:
- if (blk->xMin < xMax && blk->xMax > xMin) {
- if (blk->yMin < yMin) {
- newPriMin = blk->yMax;
- gotPriMin = gTrue;
- }
- if (blk->yMax > yMax) {
- newPriMax = blk->yMin;
- gotPriMax = gTrue;
- }
- }
- break;
- }
- if (gotPriMin) {
- if (newPriMin > xMin) {
- newPriMin = xMin;
- }
- if (newPriMin > priMin) {
- priMin = newPriMin;
- }
- }
- if (gotPriMax) {
- if (newPriMax < xMax) {
- newPriMax = xMax;
- }
- if (newPriMax < priMax) {
- priMax = newPriMax;
- }
- }
-}
-
-int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) {
- TextBlock *blk1 = *(TextBlock **)p1;
- TextBlock *blk2 = *(TextBlock **)p2;
- double cmp;
-
- cmp = 0; // make gcc happy
- switch (blk1->page->primaryRot) {
- case 0:
- if ((cmp = blk1->xMin - blk2->xMin) == 0) {
- cmp = blk1->yMin - blk2->yMin;
+ lines = linesA;
+ xMin = yMin = xMax = yMax = 0;
+ for (i = 0; i < lines->getLength(); ++i) {
+ line = (TextLine *)lines->get(i);
+ if (i == 0 || line->xMin < xMin) {
+ xMin = line->xMin;
}
- break;
- case 1:
- if ((cmp = blk1->yMin - blk2->yMin) == 0) {
- cmp = blk2->xMax - blk1->xMax;
+ if (i == 0 || line->yMin < yMin) {
+ yMin = line->yMin;
}
- break;
- case 2:
- if ((cmp = blk2->xMax - blk1->xMax) == 0) {
- cmp = blk2->yMin - blk1->yMin;
+ if (i == 0 || line->xMax > xMax) {
+ xMax = line->xMax;
}
- break;
- case 3:
- if ((cmp = blk2->yMax - blk1->yMax) == 0) {
- cmp = blk1->xMax - blk2->xMax;
+ if (i == 0 || line->yMax > yMax) {
+ yMax = line->yMax;
}
- break;
}
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}
-int TextBlock::cmpYXPrimaryRot(const void *p1, const void *p2) {
- TextBlock *blk1 = *(TextBlock **)p1;
- TextBlock *blk2 = *(TextBlock **)p2;
- double cmp;
-
- cmp = 0; // make gcc happy
- switch (blk1->page->primaryRot) {
- case 0:
- if ((cmp = blk1->yMin - blk2->yMin) == 0) {
- cmp = blk1->xMin - blk2->xMin;
- }
- break;
- case 1:
- if ((cmp = blk2->xMax - blk1->xMax) == 0) {
- cmp = blk1->yMin - blk2->yMin;
- }
- break;
- case 2:
- if ((cmp = blk2->yMin - blk1->yMin) == 0) {
- cmp = blk2->xMax - blk1->xMax;
- }
- break;
- case 3:
- if ((cmp = blk1->xMax - blk2->xMax) == 0) {
- cmp = blk2->yMax - blk1->yMax;
- }
- break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+TextParagraph::~TextParagraph() {
+ deleteGList(lines, TextLine);
}
-int TextBlock::primaryCmp(TextBlock *blk) {
- double cmp;
+//------------------------------------------------------------------------
+// TextColumn
+//------------------------------------------------------------------------
- cmp = 0; // make gcc happy
- switch (rot) {
- case 0:
- cmp = xMin - blk->xMin;
- break;
- case 1:
- cmp = yMin - blk->yMin;
- break;
- case 2:
- cmp = blk->xMax - xMax;
- break;
- case 3:
- cmp = blk->yMax - yMax;
- break;
- }
- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+TextColumn::TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA) {
+ paragraphs = paragraphsA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ px = py = 0;
+ pw = ph = 0;
}
-double TextBlock::secondaryDelta(TextBlock *blk) {
- double delta;
-
- delta = 0; // make gcc happy
- switch (rot) {
- case 0:
- delta = blk->yMin - yMax;
- break;
- case 1:
- delta = xMin - blk->xMax;
- break;
- case 2:
- delta = yMin - blk->yMax;
- break;
- case 3:
- delta = blk->xMin - xMax;
- break;
- }
- return delta;
+TextColumn::~TextColumn() {
+ deleteGList(paragraphs, TextParagraph);
}
-GBool TextBlock::isBelow(TextBlock *blk) {
- GBool below;
+int TextColumn::cmpX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
- below = gFalse; // make gcc happy
- switch (page->primaryRot) {
- case 0:
- below = xMin >= blk->priMin && xMax <= blk->priMax &&
- yMin > blk->yMin;
- break;
- case 1:
- below = yMin >= blk->priMin && yMax <= blk->priMax &&
- xMax < blk->xMax;
- break;
- case 2:
- below = xMin >= blk->priMin && xMax <= blk->priMax &&
- yMax < blk->yMax;
- break;
- case 3:
- below = yMin >= blk->priMin && yMax <= blk->priMax &&
- xMin > blk->xMin;
- break;
+ if (col1->xMin < col2->xMin) {
+ return -1;
+ } else if (col1->xMin > col2->xMin) {
+ return 1;
+ } else {
+ return 0;
}
-
- return below;
}
-//------------------------------------------------------------------------
-// TextFlow
-//------------------------------------------------------------------------
-
-TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) {
- page = pageA;
- xMin = blk->xMin;
- xMax = blk->xMax;
- yMin = blk->yMin;
- yMax = blk->yMax;
- priMin = blk->priMin;
- priMax = blk->priMax;
- blocks = lastBlk = blk;
- next = NULL;
-}
+int TextColumn::cmpY(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
-TextFlow::~TextFlow() {
- TextBlock *blk;
-
- while (blocks) {
- blk = blocks;
- blocks = blocks->next;
- delete blk;
- }
-}
-
-void TextFlow::addBlock(TextBlock *blk) {
- if (lastBlk) {
- lastBlk->next = blk;
+ if (col1->yMin < col2->yMin) {
+ return -1;
+ } else if (col1->yMin > col2->yMin) {
+ return 1;
} else {
- blocks = blk;
- }
- lastBlk = blk;
- if (blk->xMin < xMin) {
- xMin = blk->xMin;
- }
- if (blk->xMax > xMax) {
- xMax = blk->xMax;
- }
- if (blk->yMin < yMin) {
- yMin = blk->yMin;
- }
- if (blk->yMax > yMax) {
- yMax = blk->yMax;
+ return 0;
}
}
-GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {
- GBool fits;
+int TextColumn::cmpPX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
- // lower blocks must use smaller fonts
- if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) {
- return gFalse;
- }
-
- fits = gFalse; // make gcc happy
- switch (page->primaryRot) {
- case 0:
- fits = blk->xMin >= priMin && blk->xMax <= priMax;
- break;
- case 1:
- fits = blk->yMin >= priMin && blk->yMax <= priMax;
- break;
- case 2:
- fits = blk->xMin >= priMin && blk->xMax <= priMax;
- break;
- case 3:
- fits = blk->yMin >= priMin && blk->yMax <= priMax;
- break;
+ if (col1->px < col2->px) {
+ return -1;
+ } else if (col1->px > col2->px) {
+ return 1;
+ } else {
+ return 0;
}
- return fits;
}
-#if TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextWordList
//------------------------------------------------------------------------
-TextWordList::TextWordList(TextPage *text, GBool physLayout) {
- TextFlow *flow;
- TextBlock *blk;
- TextLine *line;
- TextWord *word;
- TextWord **wordArray;
- int nWords, i;
-
- words = new GList();
-
- if (text->rawOrder) {
- for (word = text->rawWords; word; word = word->next) {
- words->append(word);
- }
-
- } else if (physLayout) {
- // this is inefficient, but it's also the least useful of these
- // three cases
- nWords = 0;
- for (flow = text->flows; flow; flow = flow->next) {
- for (blk = flow->blocks; blk; blk = blk->next) {
- for (line = blk->lines; line; line = line->next) {
- for (word = line->words; word; word = word->next) {
- ++nWords;
- }
- }
- }
- }
- wordArray = (TextWord **)gmallocn(nWords, sizeof(TextWord *));
- i = 0;
- for (flow = text->flows; flow; flow = flow->next) {
- for (blk = flow->blocks; blk; blk = blk->next) {
- for (line = blk->lines; line; line = line->next) {
- for (word = line->words; word; word = word->next) {
- wordArray[i++] = word;
- }
- }
- }
- }
- qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX);
- for (i = 0; i < nWords; ++i) {
- words->append(wordArray[i]);
- }
- gfree(wordArray);
-
- } else {
- for (flow = text->flows; flow; flow = flow->next) {
- for (blk = flow->blocks; blk; blk = blk->next) {
- for (line = blk->lines; line; line = line->next) {
- for (word = line->words; word; word = word->next) {
- words->append(word);
- }
- }
- }
- }
- }
+TextWordList::TextWordList(GList *wordsA) {
+ words = wordsA;
}
TextWordList::~TextWordList() {
- delete words;
+ deleteGList(words, TextWord);
}
int TextWordList::getLength() {
@@ -1831,54 +912,47 @@ TextWord *TextWordList::get(int idx) {
return (TextWord *)words->get(idx);
}
-#endif // TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------
-TextPage::TextPage(GBool rawOrderA) {
- int rot;
-
- rawOrder = rawOrderA;
- curWord = NULL;
+TextPage::TextPage(TextOutputControl *controlA) {
+ control = *controlA;
+ pageWidth = pageHeight = 0;
charPos = 0;
curFont = NULL;
curFontSize = 0;
- nest = 0;
+ curRot = 0;
nTinyChars = 0;
- lastCharOverlap = gFalse;
actualText = NULL;
actualTextLen = 0;
+ actualTextX0 = 0;
+ actualTextY0 = 0;
+ actualTextX1 = 0;
+ actualTextY1 = 0;
actualTextNBytes = 0;
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- pools[rot] = new TextPool();
- }
- }
- flows = NULL;
- blocks = NULL;
- rawWords = NULL;
- rawLastWord = NULL;
+
+ chars = new GList();
fonts = new GList();
- lastFindXMin = lastFindYMin = 0;
- haveLastFind = gFalse;
+
underlines = new GList();
links = new GList();
+
+ findCols = NULL;
+ findLR = gTrue;
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
}
TextPage::~TextPage() {
- int rot;
-
clear();
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- delete pools[rot];
- }
- }
- delete fonts;
+ deleteGList(chars, TextChar);
+ deleteGList(fonts, TextFontInfo);
deleteGList(underlines, TextUnderline);
deleteGList(links, TextLink);
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ }
}
void TextPage::startPage(GfxState *state) {
@@ -1891,64 +965,33 @@ void TextPage::startPage(GfxState *state) {
}
}
-void TextPage::endPage() {
- if (curWord) {
- endWord();
- }
-}
-
void TextPage::clear() {
- int rot;
- TextFlow *flow;
- TextWord *word;
-
- if (curWord) {
- delete curWord;
- curWord = NULL;
- }
- gfree(actualText);
- if (rawOrder) {
- while (rawWords) {
- word = rawWords;
- rawWords = rawWords->next;
- delete word;
- }
- } else {
- for (rot = 0; rot < 4; ++rot) {
- delete pools[rot];
- }
- while (flows) {
- flow = flows;
- flows = flows->next;
- delete flow;
- }
- gfree(blocks);
- }
- deleteGList(fonts, TextFontInfo);
- deleteGList(underlines, TextUnderline);
- deleteGList(links, TextLink);
-
- curWord = NULL;
+ pageWidth = pageHeight = 0;
charPos = 0;
curFont = NULL;
curFontSize = 0;
- nest = 0;
+ curRot = 0;
nTinyChars = 0;
+ gfree(actualText);
actualText = NULL;
actualTextLen = 0;
actualTextNBytes = 0;
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- pools[rot] = new TextPool();
- }
- }
- flows = NULL;
- blocks = NULL;
- rawWords = NULL;
- rawLastWord = NULL;
+ deleteGList(chars, TextChar);
+ chars = new GList();
+ deleteGList(fonts, TextFontInfo);
fonts = new GList();
+ deleteGList(underlines, TextUnderline);
underlines = new GList();
+ deleteGList(links, TextLink);
links = new GList();
+
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ findCols = NULL;
+ }
+ findLR = gTrue;
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
}
void TextPage::updateFont(GfxState *state) {
@@ -1957,6 +1000,7 @@ void TextPage::updateFont(GfxState *state) {
char *name;
int code, mCode, letterCode, anyCode;
double w;
+ double m[4], m2[4];
int i;
// get the font info object
@@ -2017,55 +1061,36 @@ void TextPage::updateFont(GfxState *state) {
curFontSize *= fabs(fm[3] / fm[0]);
}
}
-}
-
-void TextPage::beginWord(GfxState *state, double x0, double y0) {
- double *fontm;
- double m[4], m2[4];
- int rot;
-
- // This check is needed because Type 3 characters can contain
- // text-drawing operations (when TextPage is being used via
- // {X,Win}SplashOutputDev rather than TextOutputDev).
- if (curWord) {
- ++nest;
- return;
- }
// compute the rotation
state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
- if (state->getFont()->getType() == fontType3) {
- fontm = state->getFont()->getFontMatrix();
- m2[0] = fontm[0] * m[0] + fontm[1] * m[2];
- m2[1] = fontm[0] * m[1] + fontm[1] * m[3];
- m2[2] = fontm[2] * m[0] + fontm[3] * m[2];
- m2[3] = fontm[2] * m[1] + fontm[3] * m[3];
+ if (gfxFont && gfxFont->getType() == fontType3) {
+ fm = gfxFont->getFontMatrix();
+ m2[0] = fm[0] * m[0] + fm[1] * m[2];
+ m2[1] = fm[0] * m[1] + fm[1] * m[3];
+ m2[2] = fm[2] * m[0] + fm[3] * m[2];
+ m2[3] = fm[2] * m[1] + fm[3] * m[3];
m[0] = m2[0];
m[1] = m2[1];
m[2] = m2[2];
m[3] = m2[3];
}
if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) {
- rot = (m[0] > 0 || m[3] < 0) ? 0 : 2;
+ curRot = (m[0] > 0 || m[3] < 0) ? 0 : 2;
} else {
- rot = (m[2] > 0) ? 1 : 3;
+ curRot = (m[2] > 0) ? 1 : 3;
}
-
- // for vertical writing mode, the lines are effectively rotated 90
- // degrees
- if (state->getFont()->getWMode()) {
- rot = (rot + 1) & 3;
- }
-
- curWord = new TextWord(state, rot, x0, y0, curFont, curFontSize);
}
void TextPage::addChar(GfxState *state, double x, double y,
double dx, double dy,
CharCode c, int nBytes, Unicode *u, int uLen) {
- double x1, y1, w1, h1, dx2, dy2, base, sp, delta;
- GBool overlap;
- int i;
+ double x1, y1, x2, y2, w1, h1, dx2, dy2, ascent, descent, sp;
+ double xMin, yMin, xMax, yMax;
+ double clipXMin, clipYMin, clipXMax, clipYMax;
+ GfxRGB rgb;
+ GBool clipped, rtl;
+ int i, j;
// if we're in an ActualText span, save the position info (the
// ActualText chars will be added by TextPage::endActualText()).
@@ -2109,90 +1134,93 @@ void TextPage::addChar(GfxState *state, double x, double y,
}
}
- // break words at space character
+ // skip space characters
if (uLen == 1 && u[0] == (Unicode)0x20) {
charPos += nBytes;
- endWord();
return;
}
- // start a new word if:
- // (1) this character doesn't fall in the right place relative to
- // the end of the previous word (this places upper and lower
- // constraints on the position deltas along both the primary
- // and secondary axes), or
- // (2) this character overlaps the previous one (duplicated text), or
- // (3) the previous character was an overlap (we want each duplicated
- // character to be in a word by itself at this stage),
- // (4) the font or font size has changed
- if (curWord && curWord->len > 0) {
- base = sp = delta = 0; // make gcc happy
- switch (curWord->rot) {
- case 0:
- base = y1;
- sp = x1 - curWord->xMax;
- delta = x1 - curWord->edge[curWord->len - 1];
- break;
- case 1:
- base = x1;
- sp = y1 - curWord->yMax;
- delta = y1 - curWord->edge[curWord->len - 1];
- break;
- case 2:
- base = y1;
- sp = curWord->xMin - x1;
- delta = curWord->edge[curWord->len - 1] - x1;
- break;
- case 3:
- base = x1;
- sp = curWord->yMin - y1;
- delta = curWord->edge[curWord->len - 1] - y1;
- break;
+ // check for clipping
+ clipped = gFalse;
+ if (control.clipText) {
+ state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
+ if (x1 + 0.1 * w1 < clipXMin || x1 + 0.9 * w1 > clipXMax ||
+ y1 + 0.1 * h1 < clipYMin || y1 + 0.9 * h1 > clipYMax) {
+ clipped = gTrue;
}
- overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize &&
- fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize;
- if (overlap || lastCharOverlap ||
- sp < -minDupBreakOverlap * curWord->fontSize ||
- sp > minWordBreakSpace * curWord->fontSize ||
- fabs(base - curWord->base) > 0.5 ||
- curFont != curWord->font ||
- curFontSize != curWord->fontSize) {
- endWord();
- }
- lastCharOverlap = overlap;
- } else {
- lastCharOverlap = gFalse;
}
- if (uLen != 0) {
- // start a new word if needed
- if (!curWord) {
- beginWord(state, x, y);
- }
+ // add the characters
+ if (uLen > 0) {
- // page rotation and/or transform matrices can cause text to be
- // drawn in reverse order -- in this case, swap the begin/end
- // coordinates and break text into individual chars
- if ((curWord->rot == 0 && w1 < 0) ||
- (curWord->rot == 1 && h1 < 0) ||
- (curWord->rot == 2 && w1 > 0) ||
- (curWord->rot == 3 && h1 > 0)) {
- endWord();
- beginWord(state, x + dx, y + dy);
- x1 += w1;
- y1 += h1;
- w1 = -w1;
- h1 = -h1;
+ // handle right-to-left ligatures: if there are multiple Unicode
+ // characters, and they're all right-to-left, insert them in
+ // right-to-left order
+ if (uLen > 1) {
+ rtl = gTrue;
+ for (i = 0; i < uLen; ++i) {
+ if (!unicodeTypeR(u[i])) {
+ rtl = gFalse;
+ break;
+ }
+ }
+ } else {
+ rtl = gFalse;
}
- // add the characters to the current word
w1 /= uLen;
h1 /= uLen;
+ ascent = curFont->ascent * curFontSize;
+ descent = curFont->descent * curFontSize;
for (i = 0; i < uLen; ++i) {
- curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1,
- charPos, nBytes, u[i]);
+ x2 = x1 + i * w1;
+ y2 = y1 + i * h1;
+ switch (curRot) {
+ case 0:
+ default:
+ xMin = x2;
+ xMax = x2 + w1;
+ yMin = y2 - ascent;
+ yMax = y2 - descent;
+ break;
+ case 1:
+ xMin = x2 + descent;
+ xMax = x2 + ascent;
+ yMin = y2;
+ yMax = y2 + h1;
+ break;
+ case 2:
+ xMin = x2 + w1;
+ xMax = x2;
+ yMin = y2 + descent;
+ yMax = y2 + ascent;
+ break;
+ case 3:
+ xMin = x2 - ascent;
+ xMax = x2 - descent;
+ yMin = y2 + h1;
+ yMax = y2;
+ break;
+ }
+ if ((state->getRender() & 3) == 1) {
+ state->getStrokeRGB(&rgb);
+ } else {
+ state->getFillRGB(&rgb);
+ }
+ if (rtl) {
+ j = uLen - 1 - i;
+ } else {
+ j = i;
+ }
+ chars->append(new TextChar(u[j], charPos, nBytes, xMin, yMin, xMax, yMax,
+ curRot, clipped,
+ state->getRender() == 3,
+ curFont, curFontSize,
+ colToDbl(rgb.r), colToDbl(rgb.g),
+ colToDbl(rgb.b)));
}
}
+
charPos += nBytes;
}
@@ -2229,901 +1257,2381 @@ void TextPage::endActualText(GfxState *state) {
actualTextNBytes = gFalse;
}
-void TextPage::endWord() {
- // This check is needed because Type 3 characters can contain
- // text-drawing operations (when TextPage is being used via
- // {X,Win}SplashOutputDev rather than TextOutputDev).
- if (nest > 0) {
- --nest;
+void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
+ underlines->append(new TextUnderline(x0, y0, x1, y1));
+}
+
+void TextPage::addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link) {
+ GString *uri;
+
+ if (link && link->getAction() && link->getAction()->getKind() == actionURI) {
+ uri = ((LinkURI *)link->getAction())->getURI()->copy();
+ links->append(new TextLink(xMin, yMin, xMax, yMax, uri));
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: output
+//------------------------------------------------------------------------
+
+void TextPage::write(void *outputStream, TextOutputFunc outputFunc) {
+ UnicodeMap *uMap;
+ char space[8], eol[16], eop[8];
+ int spaceLen, eolLen, eopLen;
+ GBool pageBreaks;
+
+ // get the output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
return;
}
+ spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
+ eolLen = 0; // make gcc happy
+ switch (globalParams->getTextEOL()) {
+ case eolUnix:
+ eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
+ break;
+ case eolDOS:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
+ break;
+ case eolMac:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ break;
+ }
+ eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
+ pageBreaks = globalParams->getTextPageBreaks();
- if (curWord) {
- addWord(curWord);
- curWord = NULL;
+ switch (control.mode) {
+ case textOutReadingOrder:
+ writeReadingOrder(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutPhysLayout:
+ case textOutTableLayout:
+ writePhysLayout(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutLinePrinter:
+ writeLinePrinter(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutRawOrder:
+ writeRaw(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ }
+
+ // end of page
+ if (pageBreaks) {
+ (*outputFunc)(outputStream, eop, eopLen);
}
+
+ uMap->decRefCnt();
}
-void TextPage::addWord(TextWord *word) {
- // throw away zero-length words -- they don't have valid xMin/xMax
- // values, and they're useless anyway
- if (word->len == 0) {
- delete word;
+void TextPage::writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ GList *columns;
+ GBool primaryLR;
+ GString *s;
+ int colIdx, parIdx, lineIdx, rot, n;
+
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
return;
}
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
- if (rawOrder) {
- if (rawLastWord) {
- rawLastWord->next = word;
- } else {
- rawWords = word;
+ 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);
+ n = line->len;
+ if (line->hyphenated && lineIdx + 1 < par->lines->getLength()) {
+ --n;
+ }
+ s = new GString();
+ encodeFragment(line->text, n, uMap, primaryLR, s);
+ if (lineIdx + 1 < par->lines->getLength() && !line->hyphenated) {
+ s->append(space, spaceLen);
+ }
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
}
- rawLastWord = word;
- } else {
- pools[word->rot]->addWord(word);
+ (*outputFunc)(outputStream, eol, eolLen);
}
-}
-void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
- underlines->append(new TextUnderline(x0, y0, x1, y1));
+ deleteGList(columns, TextColumn);
}
-void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, Link *link) {
- links->append(new TextLink(xMin, yMin, xMax, yMax, link));
+GList *TextPage::makeColumns() {
+ TextBlock *tree;
+ GList *columns;
+
+ tree = splitChars(chars);
+ if (!tree) {
+ // no text
+ return new GList();
+ }
+ columns = buildColumns(tree);
+ delete tree;
+ if (control.html) {
+ generateUnderlinesAndLinks(columns);
+ }
+ return columns;
}
-void TextPage::coalesce(GBool physLayout, double fixedPitch, GBool doHTML) {
- UnicodeMap *uMap;
- TextPool *pool;
- TextWord *word0, *word1, *word2;
+// This handles both physical layout and table layout modes.
+void TextPage::writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
TextLine *line;
- TextBlock *blkList, *blkStack, *blk, *lastBlk, *blk0, *blk1;
- TextBlock **blkArray;
- TextFlow *flow, *lastFlow;
- TextUnderline *underline;
- TextLink *link;
- int rot, poolMinBaseIdx, baseIdx, startBaseIdx, endBaseIdx;
- double minBase, maxBase, newMinBase, newMaxBase;
- double fontSize, colSpace1, colSpace2, lineSpace, intraLineSpace, blkSpace;
- GBool found;
- int count[4];
- int lrCount;
- int firstBlkIdx, nBlocksLeft;
- int col1, col2;
- int i, j, n;
-
- if (rawOrder) {
- primaryRot = 0;
- primaryLR = gTrue;
+ GList *columns;
+ GBool primaryLR;
+ int ph, colIdx, parIdx, lineIdx, rot, y, i;
+
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
return;
}
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
- uMap = globalParams->getTextEncoding();
- blkList = NULL;
- lastBlk = NULL;
- nBlocks = 0;
- primaryRot = 0;
-
-#if 0 // for debugging
- printf("*** initial words ***\n");
- for (rot = 0; rot < 4; ++rot) {
- pool = pools[rot];
- for (baseIdx = pool->minBaseIdx; baseIdx <= pool->maxBaseIdx; ++baseIdx) {
- for (word0 = pool->getPool(baseIdx); word0; word0 = word0->next) {
- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f rot=%d link=%p '",
- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
- word0->base, word0->fontSize, rot*90, word0->link);
- for (i = 0; i < word0->len; ++i) {
- fputc(word0->text[i] & 0xff, stdout);
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
}
- printf("'\n");
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
+ }
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
}
}
}
- printf("\n");
-#endif
-#if 0 //~ for debugging
- for (i = 0; i < underlines->getLength(); ++i) {
- underline = (TextUnderline *)underlines->get(i);
- printf("underline: x=%g..%g y=%g..%g horiz=%d\n",
- underline->x0, underline->x1, underline->y0, underline->y1,
- underline->horiz);
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ (*outputFunc)(outputStream, out[i]->getCString(), out[i]->getLength());
+ delete out[i];
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
}
-#endif
- if (doHTML) {
+ gfree(out);
+ gfree(outLen);
- //----- handle underlining
- for (i = 0; i < underlines->getLength(); ++i) {
- underline = (TextUnderline *)underlines->get(i);
- if (underline->horiz) {
- // rot = 0
- if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {
- startBaseIdx = pools[0]->getBaseIdx(underline->y0 + minUnderlineGap);
- endBaseIdx = pools[0]->getBaseIdx(underline->y0 + maxUnderlineGap);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {
- //~ need to check the y value against the word baseline
- if (underline->x0 < word0->xMin + underlineSlack &&
- word0->xMax - underlineSlack < underline->x1) {
- word0->underlined = gTrue;
- }
- }
+ deleteGList(columns, TextColumn);
+}
+
+void TextPage::writeLinePrinter(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GList *line;
+ GString *s;
+ char buf[8];
+ double pitch, lineSpacing, delta;
+ double yMin0, yShift, xMin0, xShift;
+ double y, x;
+ int rot, n, i, j, k;
+
+ rot = rotateChars(chars);
+ chars->sort(&TextChar::cmpX);
+ removeDuplicates(chars, 0);
+ chars->sort(&TextChar::cmpY);
+
+ // get character pitch
+ if (control.fixedPitch > 0) {
+ pitch = control.fixedPitch;
+ } else {
+ // compute (approximate) character pitch
+ pitch = pageWidth;
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ for (j = i + 1; j < chars->getLength(); ++j) {
+ ch2 = (TextChar *)chars->get(j);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) <
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin) &&
+ 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) {
+ pitch = delta;
}
}
+ }
+ }
+ }
- // rot = 2
- if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {
- startBaseIdx = pools[2]->getBaseIdx(underline->y0 - maxUnderlineGap);
- endBaseIdx = pools[2]->getBaseIdx(underline->y0 - minUnderlineGap);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {
- if (underline->x0 < word0->xMin + underlineSlack &&
- word0->xMax - underlineSlack < underline->x1) {
- word0->underlined = gTrue;
- }
- }
- }
+ // get line spacing
+ if (control.fixedLineSpacing > 0) {
+ lineSpacing = control.fixedLineSpacing;
+ } else {
+ // compute (approximate) line spacing
+ lineSpacing = pageHeight;
+ i = 0;
+ while (i < chars->getLength()) {
+ ch = (TextChar *)chars->get(i);
+ // look for the first char that does not (substantially)
+ // vertically overlap this one
+ delta = 0;
+ for (++i; delta == 0 && i < chars->getLength(); ++i) {
+ ch2 = (TextChar *)chars->get(i);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) >
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin)) {
+ delta = ch2->yMin - ch->yMin;
}
+ }
+ if (delta > 0 && delta < lineSpacing) {
+ lineSpacing = delta;
+ }
+ }
+ }
+
+ // shift the grid to avoid problems with floating point accuracy --
+ // for fixed line spacing, this avoids problems with
+ // dropping/inserting blank lines
+ if (chars->getLength()) {
+ yMin0 = ((TextChar *)chars->get(0))->yMin;
+ yShift = yMin0 - (int)(yMin0 / lineSpacing + 0.5) * lineSpacing
+ - 0.5 * lineSpacing;
+ } else {
+ yShift = 0;
+ }
+
+ // for each line...
+ i = 0;
+ j = chars->getLength() - 1;
+ for (y = yShift; y < pageHeight; y += lineSpacing) {
+
+ // get the characters in this line
+ line = new GList;
+ while (i < chars->getLength() &&
+ ((TextChar *)chars->get(i))->yMin < y + lineSpacing) {
+ line->append(chars->get(i++));
+ }
+ line->sort(&TextChar::cmpX);
+
+ // shift the grid to avoid problems with floating point accuracy
+ // -- for fixed char spacing, this avoids problems with
+ // dropping/inserting spaces
+ if (line->getLength()) {
+ xMin0 = ((TextChar *)line->get(0))->xMin;
+ xShift = xMin0 - (int)(xMin0 / pitch + 0.5) * pitch - 0.5 * pitch;
+ } else {
+ xShift = 0;
+ }
+
+ // write the line
+ s = new GString();
+ x = xShift;
+ k = 0;
+ while (k < line->getLength()) {
+ ch = (TextChar *)line->get(k);
+ if (ch->xMin < x + pitch) {
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+ ++k;
} else {
- // rot = 1
- if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {
- startBaseIdx = pools[1]->getBaseIdx(underline->x0 - maxUnderlineGap);
- endBaseIdx = pools[1]->getBaseIdx(underline->x0 - minUnderlineGap);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {
- if (underline->y0 < word0->yMin + underlineSlack &&
- word0->yMax - underlineSlack < underline->y1) {
- word0->underlined = gTrue;
- }
- }
+ s->append(space, spaceLen);
+ n = spaceLen;
+ }
+ x += (uMap->isUnicode() ? 1 : n) * pitch;
+ }
+ s->append(eol, eolLen);
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ delete line;
+ }
+
+ unrotateChars(chars, rot);
+}
+
+void TextPage::writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GString *s;
+ char buf[8];
+ int n, i;
+
+ s = new GString();
+
+ for (i = 0; i < chars->getLength(); ++i) {
+
+ // process one char
+ ch = (TextChar *)chars->get(i);
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+
+ // check for space or eol
+ if (i+1 < chars->getLength()) {
+ ch2 = (TextChar *)chars->get(i+1);
+ if (ch2->rot != ch->rot) {
+ s->append(eol, eolLen);
+ } else {
+ switch (ch->rot) {
+ case 0:
+ default:
+ if (fabs(ch2->yMin - ch->yMin) > rawModeLineDelta * ch->fontSize ||
+ ch2->xMin - ch->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch2->xMin - ch->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 1:
+ if (fabs(ch->xMax - ch2->xMax) > rawModeLineDelta * ch->fontSize ||
+ ch2->yMin - ch->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch2->yMin - ch->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 2:
+ if (fabs(ch->yMax - ch2->yMax) > rawModeLineDelta * ch->fontSize ||
+ ch->xMin - ch2->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->xMin - ch2->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 3:
+ if (fabs(ch2->xMin - ch->xMin) > rawModeLineDelta * ch->fontSize ||
+ ch->yMin - ch2->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->yMin - ch2->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
}
+ break;
}
+ }
+ } else {
+ s->append(eol, eolLen);
+ }
- // rot = 3
- if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {
- startBaseIdx = pools[3]->getBaseIdx(underline->x0 + minUnderlineGap);
- endBaseIdx = pools[3]->getBaseIdx(underline->x0 + maxUnderlineGap);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {
- if (underline->y0 < word0->yMin + underlineSlack &&
- word0->yMax - underlineSlack < underline->y1) {
- word0->underlined = gTrue;
- }
- }
+ if (s->getLength() > 1000) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ s->clear();
+ }
+ }
+
+ if (s->getLength() > 0) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ }
+ delete s;
+}
+
+void TextPage::encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s) {
+ char lre[8], rle[8], popdf[8], buf[8];
+ int lreLen, rleLen, popdfLen, n;
+ int i, j, k;
+
+ if (uMap->isUnicode()) {
+
+ lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
+ rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
+ popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));
+
+ if (primaryLR) {
+
+ i = 0;
+ while (i < len) {
+ // output a left-to-right section
+ for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ;
+ for (k = i; k < j; ++k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ i = j;
+ // output a right-to-left section
+ for (j = i;
+ j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
+ ++j) ;
+ if (j > i) {
+ s->append(rle, rleLen);
+ for (k = j - 1; k >= i; --k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ s->append(popdf, popdfLen);
+ i = j;
+ }
+ }
+
+ } else {
+
+ // Note: This code treats numeric characters (European and
+ // Arabic/Indic) as left-to-right, which isn't strictly correct
+ // (incurs extra LRE/POPDF pairs), but does produce correct
+ // visual formatting.
+ s->append(rle, rleLen);
+ i = len - 1;
+ while (i >= 0) {
+ // output a right-to-left section
+ for (j = i;
+ j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
+ --j) ;
+ for (k = i; k > j; --k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ i = j;
+ // output a left-to-right section
+ for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ;
+ if (j < i) {
+ s->append(lre, lreLen);
+ for (k = j + 1; k <= i; ++k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
}
+ s->append(popdf, popdfLen);
+ i = j;
}
}
+ s->append(popdf, popdfLen);
+ }
+
+ } else {
+ for (i = 0; i < len; ++i) {
+ n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: layout analysis
+//------------------------------------------------------------------------
+
+// Determine primary (most common) rotation value. Rotate all chars
+// to that primary rotation.
+int TextPage::rotateChars(GList *charsA) {
+ TextChar *ch;
+ int nChars[4];
+ double xMin, yMin, xMax, yMax, t;
+ int rot, i;
+
+ // determine primary rotation
+ nChars[0] = nChars[1] = nChars[2] = nChars[3] = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ ++nChars[ch->rot];
+ }
+ rot = 0;
+ for (i = 1; i < 4; ++i) {
+ if (nChars[i] > nChars[rot]) {
+ rot = i;
+ }
+ }
+
+ // rotate
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 2) & 3;
}
+ break;
+ case 3:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ }
+
+ return rot;
+}
+
+// Rotate the TextUnderlines and TextLinks to match the transform
+// performed by rotateChars().
+void TextPage::rotateUnderlinesAndLinks(int rot) {
+ TextUnderline *underline;
+ TextLink *link;
+ double xMin, yMin, xMax, yMax;
+ int i;
- //----- handle links
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = underline->y0;
+ xMax = underline->y1;
+ yMin = pageWidth - underline->x1;
+ yMax = pageWidth - underline->x0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
for (i = 0; i < links->getLength(); ++i) {
link = (TextLink *)links->get(i);
+ xMin = link->yMin;
+ xMax = link->yMax;
+ yMin = pageWidth - link->xMax;
+ yMax = pageWidth - link->xMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 2:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageWidth - underline->x1;
+ xMax = pageWidth - underline->x0;
+ yMin = pageHeight - underline->y1;
+ yMax = pageHeight - underline->y0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageWidth - link->xMax;
+ xMax = pageWidth - link->xMin;
+ yMin = pageHeight - link->yMax;
+ yMax = pageHeight - link->yMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 3:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageHeight - underline->y1;
+ xMax = pageHeight - underline->y0;
+ yMin = underline->x0;
+ yMax = underline->x1;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageHeight - link->yMax;
+ xMax = pageHeight - link->yMin;
+ yMin = link->xMin;
+ yMax = link->xMax;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ }
+}
- // rot = 0
- if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {
- startBaseIdx = pools[0]->getBaseIdx(link->yMin);
- endBaseIdx = pools[0]->getBaseIdx(link->yMax);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {
- if (link->xMin < word0->xMin + hyperlinkSlack &&
- word0->xMax - hyperlinkSlack < link->xMax &&
- link->yMin < word0->yMin + hyperlinkSlack &&
- word0->yMax - hyperlinkSlack < link->yMax) {
- word0->link = link->link;
- }
+// Undo the coordinate transform performed by rotateChars().
+void TextPage::unrotateChars(GList *charsA, int rot) {
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax, t;
+ int i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 2) & 3;
+ }
+ break;
+ case 3:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ break;
+ }
+}
+
+// Undo the coordinate transform performed by rotateChars().
+void TextPage::unrotateColumns(GList *columns, int rot) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ double xMin, yMin, xMax, yMax, t;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 1) & 3;
+ 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;
+ word->rot = (word->rot + 1) & 3;
}
}
}
-
- // rot = 2
- if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {
- startBaseIdx = pools[2]->getBaseIdx(link->yMin);
- endBaseIdx = pools[2]->getBaseIdx(link->yMax);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {
- if (link->xMin < word0->xMin + hyperlinkSlack &&
- word0->xMax - hyperlinkSlack < link->xMax &&
- link->yMin < word0->yMin + hyperlinkSlack &&
- word0->yMax - hyperlinkSlack < link->yMax) {
- word0->link = link->link;
+ }
+ break;
+ case 2:
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 2) & 3;
+ 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;
+ word->rot = (word->rot + 2) & 3;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageWidth - word->edge[i];
}
}
}
}
-
- // rot = 1
- if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {
- startBaseIdx = pools[1]->getBaseIdx(link->xMin);
- endBaseIdx = pools[1]->getBaseIdx(link->xMax);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {
- if (link->yMin < word0->yMin + hyperlinkSlack &&
- word0->yMax - hyperlinkSlack < link->yMax &&
- link->xMin < word0->xMin + hyperlinkSlack &&
- word0->xMax - hyperlinkSlack < link->xMax) {
- word0->link = link->link;
+ }
+ break;
+ case 3:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 3) & 3;
+ 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;
+ word->rot = (word->rot + 3) & 3;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageHeight - word->edge[i];
}
}
}
}
+ }
+ break;
+ }
+}
- // rot = 3
- if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {
- startBaseIdx = pools[3]->getBaseIdx(link->xMin);
- endBaseIdx = pools[3]->getBaseIdx(link->xMax);
- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
- for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {
- if (link->yMin < word0->yMin + hyperlinkSlack &&
- word0->yMax - hyperlinkSlack < link->yMax &&
- link->xMin < word0->xMin + hyperlinkSlack &&
- word0->xMax - hyperlinkSlack < link->xMax) {
- word0->link = link->link;
- }
- }
- }
+void TextPage::unrotateWords(GList *words, int rot) {
+ TextWord *word;
+ double xMin, yMin, xMax, yMax;
+ int i, j;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 1) & 3;
+ }
+ break;
+ case 2:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 2) & 3;
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageWidth - word->edge[j];
}
}
+ break;
+ case 3:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 3) & 3;
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageHeight - word->edge[j];
+ }
+ }
+ break;
}
+}
- //----- assemble the blocks
+// Determine the primary text direction (LR vs RL). Returns true for
+// LR, false for RL.
+GBool TextPage::checkPrimaryLR(GList *charsA) {
+ TextChar *ch;
+ int i, lrCount;
- //~ add an outer loop for writing mode (vertical text)
+ lrCount = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (unicodeTypeL(ch->c)) {
+ ++lrCount;
+ } else if (unicodeTypeR(ch->c)) {
+ --lrCount;
+ }
+ }
+ return lrCount >= 0;
+}
- // build blocks for each rotation value
- for (rot = 0; rot < 4; ++rot) {
- pool = pools[rot];
- poolMinBaseIdx = pool->minBaseIdx;
- count[rot] = 0;
-
- // add blocks until no more words are left
- while (1) {
-
- // find the first non-empty line in the pool
- for (;
- poolMinBaseIdx <= pool->maxBaseIdx &&
- !pool->getPool(poolMinBaseIdx);
- ++poolMinBaseIdx) ;
- if (poolMinBaseIdx > pool->maxBaseIdx) {
- break;
- }
+// Remove duplicate characters. The list of chars has been sorted --
+// by x for rot=0,2; by y for rot=1,3.
+void TextPage::removeDuplicates(GList *charsA, int rot) {
+ TextChar *ch, *ch2;
+ double xDelta, yDelta;
+ int i, j;
- // look for the left-most word in the first four lines of the
- // pool -- this avoids starting with a superscript word
- startBaseIdx = poolMinBaseIdx;
- for (baseIdx = poolMinBaseIdx + 1;
- baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
- ++baseIdx) {
- if (!pool->getPool(baseIdx)) {
- continue;
+ if (rot & 1) {
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxSecDelta * ch->fontSize;
+ yDelta = dupMaxPriDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->yMin - ch->yMin >= yDelta) {
+ break;
}
- if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
- < 0) {
- startBaseIdx = baseIdx;
+ if (ch2->c == ch->c &&
+ fabs(ch2->xMin - ch->xMin) < xDelta &&
+ fabs(ch2->xMax - ch->xMax) < xDelta &&
+ fabs(ch2->yMax - ch->yMax) < yDelta) {
+ charsA->del(j);
+ } else {
+ ++j;
}
}
-
- // create a new block
- word0 = pool->getPool(startBaseIdx);
- pool->setPool(startBaseIdx, word0->next);
- word0->next = NULL;
- blk = new TextBlock(this, rot);
- blk->addWord(word0);
-
- fontSize = word0->fontSize;
- minBase = maxBase = word0->base;
- colSpace1 = minColSpacing1 * fontSize;
- colSpace2 = minColSpacing2 * fontSize;
- lineSpace = maxLineSpacingDelta * fontSize;
- intraLineSpace = maxIntraLineDelta * fontSize;
-
- // add words to the block
- do {
- found = gFalse;
-
- // look for words on the line above the current top edge of
- // the block
- newMinBase = minBase;
- for (baseIdx = pool->getBaseIdx(minBase);
- baseIdx >= pool->getBaseIdx(minBase - lineSpace);
- --baseIdx) {
- word0 = NULL;
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base < minBase &&
- word1->base >= minBase - lineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
- : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta1 * fontSize) {
- word2 = word1;
- if (word0) {
- word0->next = word1->next;
- } else {
- pool->setPool(baseIdx, word1->next);
- }
- word1 = word1->next;
- word2->next = NULL;
- blk->addWord(word2);
- found = gTrue;
- newMinBase = word2->base;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
- }
- minBase = newMinBase;
-
- // look for words on the line below the current bottom edge of
- // the block
- newMaxBase = maxBase;
- for (baseIdx = pool->getBaseIdx(maxBase);
- baseIdx <= pool->getBaseIdx(maxBase + lineSpace);
- ++baseIdx) {
- word0 = NULL;
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base > maxBase &&
- word1->base <= maxBase + lineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
- : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta1 * fontSize) {
- word2 = word1;
- if (word0) {
- word0->next = word1->next;
- } else {
- pool->setPool(baseIdx, word1->next);
- }
- word1 = word1->next;
- word2->next = NULL;
- blk->addWord(word2);
- found = gTrue;
- newMaxBase = word2->base;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
+ }
+ } else {
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxPriDelta * ch->fontSize;
+ yDelta = dupMaxSecDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->xMin - ch->xMin >= xDelta) {
+ break;
}
- maxBase = newMaxBase;
-
- // look for words that are on lines already in the block, and
- // that overlap the block horizontally
- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
- ++baseIdx) {
- word0 = NULL;
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base >= minBase - intraLineSpace &&
- word1->base <= maxBase + intraLineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMin < blk->xMax + colSpace1 &&
- word1->xMax > blk->xMin - colSpace1)
- : (word1->yMin < blk->yMax + colSpace1 &&
- word1->yMax > blk->yMin - colSpace1)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta2 * fontSize) {
- word2 = word1;
- if (word0) {
- word0->next = word1->next;
- } else {
- pool->setPool(baseIdx, word1->next);
- }
- word1 = word1->next;
- word2->next = NULL;
- blk->addWord(word2);
- found = gTrue;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
+ if (ch2->c == ch->c &&
+ fabs(ch2->xMax - ch->xMax) < xDelta &&
+ fabs(ch2->yMin - ch->yMin) < yDelta &&
+ fabs(ch2->yMax - ch->yMax) < yDelta) {
+ charsA->del(j);
+ } else {
+ ++j;
}
+ }
+ }
+ }
+}
- // only check for outlying words (the next two chunks of code)
- // if we didn't find anything else
- if (found) {
- continue;
- }
+// 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) {
+ TextBlock *tree[4];
+ TextBlock *blk;
+ GList *chars2, *clippedChars;
+ TextChar *ch;
+ int rot, i;
- // scan down the left side of the block, looking for words
- // that are near (but not overlapping) the block; if there are
- // three or fewer, add them to the block
- n = 0;
- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
- ++baseIdx) {
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base >= minBase - intraLineSpace &&
- word1->base <= maxBase + intraLineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMax <= blk->xMin &&
- word1->xMax > blk->xMin - colSpace2)
- : (word1->yMax <= blk->yMin &&
- word1->yMax > blk->yMin - colSpace2)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta3 * fontSize) {
- ++n;
- break;
- }
- word1 = word1->next;
- }
- }
- if (n > 0 && n <= 3) {
- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
- ++baseIdx) {
- word0 = NULL;
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base >= minBase - intraLineSpace &&
- word1->base <= maxBase + intraLineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMax <= blk->xMin &&
- word1->xMax > blk->xMin - colSpace2)
- : (word1->yMax <= blk->yMin &&
- word1->yMax > blk->yMin - colSpace2)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta3 * fontSize) {
- word2 = word1;
- if (word0) {
- word0->next = word1->next;
- } else {
- pool->setPool(baseIdx, word1->next);
- }
- word1 = word1->next;
- word2->next = NULL;
- blk->addWord(word2);
- if (word2->base < minBase) {
- minBase = word2->base;
- } else if (word2->base > maxBase) {
- maxBase = word2->base;
- }
- found = gTrue;
- break;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
+ // split: build a tree of TextBlocks for each rotation
+ clippedChars = new GList();
+ for (rot = 0; rot < 4; ++rot) {
+ chars2 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->rot == rot) {
+ chars2->append(ch);
+ }
+ }
+ tree[rot] = NULL;
+ if (chars2->getLength() > 0) {
+ chars2->sort((rot & 1) ? &TextChar::cmpY : &TextChar::cmpX);
+ removeDuplicates(chars2, rot);
+ if (control.clipText) {
+ i = 0;
+ while (i < chars2->getLength()) {
+ ch = (TextChar *)chars2->get(i);
+ if (ch->clipped) {
+ ch = (TextChar *)chars2->del(i);
+ clippedChars->append(ch);
+ } else {
+ ++i;
}
}
+ }
+ if (chars2->getLength() > 0) {
+ tree[rot] = split(chars2, rot);
+ }
+ }
+ delete chars2;
+ }
- // scan down the right side of the block, looking for words
- // that are near (but not overlapping) the block; if there are
- // three or fewer, add them to the block
- n = 0;
- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
- ++baseIdx) {
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base >= minBase - intraLineSpace &&
- word1->base <= maxBase + intraLineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMin >= blk->xMax &&
- word1->xMin < blk->xMax + colSpace2)
- : (word1->yMin >= blk->yMax &&
- word1->yMin < blk->yMax + colSpace2)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta3 * fontSize) {
- ++n;
- break;
- }
- word1 = word1->next;
- }
+ // if the page contains no (unclipped) text, just leave an empty
+ // column list
+ if (!tree[0]) {
+ delete clippedChars;
+ return NULL;
+ }
+
+ // if the main tree is not a multicolumn node, insert one so that
+ // rotated text has somewhere to go
+ if (tree[0]->tag != blkTagMulticolumn) {
+ blk = new TextBlock(blkHorizSplit, 0);
+ blk->addChild(tree[0]);
+ blk->tag = blkTagMulticolumn;
+ tree[0] = blk;
+ }
+
+ // merge non-primary-rotation text into the primary-rotation tree
+ for (rot = 1; rot < 4; ++rot) {
+ if (tree[rot]) {
+ insertIntoTree(tree[rot], tree[0]);
+ tree[rot] = NULL;
+ }
+ }
+
+ if (clippedChars->getLength()) {
+ insertClippedChars(clippedChars, tree[0]);
+ }
+ delete clippedChars;
+
+#if 0 //~debug
+ dumpTree(tree[0]);
+#endif
+
+ return tree[0];
+}
+
+// Generate a tree of TextBlocks, marked as columns, lines, and words.
+TextBlock *TextPage::split(GList *charsA, int rot) {
+ TextBlock *blk;
+ GList *chars2, *chars3;
+ int *horizProfile, *vertProfile;
+ double xMin, yMin, xMax, yMax;
+ int xMinI, yMinI, xMaxI, yMaxI;
+ int xMinI2, yMinI2, xMaxI2, yMaxI2;
+ TextChar *ch;
+ double minFontSize, avgFontSize, splitPrecision;
+ double nLines, vertGapThreshold, ascentAdjust, descentAdjust, minChunk;
+ int horizGapSize, vertGapSize;
+ double horizGapSize2, vertGapSize2;
+ int minHorizChunkWidth, minVertChunkWidth, nHorizGaps, nVertGaps;
+ double largeCharSize;
+ int nLargeChars;
+ GBool doHorizSplit, doVertSplit, smallSplit;
+ int i, x, y, prev, start;
+
+ //----- compute bbox, min font size, average font size, and
+ // split precision for this block
+
+ xMin = yMin = xMax = yMax = 0; // make gcc happy
+ minFontSize = avgFontSize = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (i == 0 || ch->xMin < xMin) {
+ xMin = ch->xMin;
+ }
+ if (i == 0 || ch->yMin < yMin) {
+ yMin = ch->yMin;
+ }
+ if (i == 0 || ch->xMax > xMax) {
+ xMax = ch->xMax;
+ }
+ if (i == 0 || ch->yMax > yMax) {
+ yMax = ch->yMax;
+ }
+ avgFontSize += ch->fontSize;
+ if (i == 0 || ch->fontSize < minFontSize) {
+ minFontSize = ch->fontSize;
+ }
+ }
+ avgFontSize /= charsA->getLength();
+ splitPrecision = splitPrecisionMul * minFontSize;
+ if (splitPrecision < minSplitPrecision) {
+ splitPrecision = minSplitPrecision;
+ }
+
+ //----- compute the horizontal and vertical profiles
+
+ // add some slack to the array bounds to avoid floating point
+ // precision problems
+ xMinI = (int)floor(xMin / splitPrecision) - 1;
+ yMinI = (int)floor(yMin / splitPrecision) - 1;
+ xMaxI = (int)floor(xMax / splitPrecision) + 1;
+ yMaxI = (int)floor(yMax / splitPrecision) + 1;
+ horizProfile = (int *)gmallocn(yMaxI - yMinI + 1, sizeof(int));
+ vertProfile = (int *)gmallocn(xMaxI - xMinI + 1, sizeof(int));
+ memset(horizProfile, 0, (yMaxI - yMinI + 1) * sizeof(int));
+ memset(vertProfile, 0, (xMaxI - xMinI + 1) * sizeof(int));
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // yMinI2 and yMaxI2 are adjusted to allow for slightly overlapping lines
+ switch (rot) {
+ case 0:
+ default:
+ xMinI2 = (int)floor(ch->xMin / splitPrecision);
+ xMaxI2 = (int)floor(ch->xMax / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + ascentAdjust) / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - descentAdjust) / splitPrecision);
+ break;
+ case 1:
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + descentAdjust) / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - ascentAdjust) / splitPrecision);
+ yMinI2 = (int)floor(ch->yMin / splitPrecision);
+ yMaxI2 = (int)floor(ch->yMax / splitPrecision);
+ break;
+ case 2:
+ xMinI2 = (int)floor(ch->xMin / splitPrecision);
+ xMaxI2 = (int)floor(ch->xMax / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + descentAdjust) / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - ascentAdjust) / splitPrecision);
+ break;
+ case 3:
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + ascentAdjust) / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - descentAdjust) / splitPrecision);
+ yMinI2 = (int)floor(ch->yMin / splitPrecision);
+ yMaxI2 = (int)floor(ch->yMax / splitPrecision);
+ break;
+ }
+ for (y = yMinI2; y <= yMaxI2; ++y) {
+ ++horizProfile[y - yMinI];
+ }
+ for (x = xMinI2; x <= xMaxI2; ++x) {
+ ++vertProfile[x - xMinI];
+ }
+ }
+
+ //----- find the largest gaps in the horizontal and vertical profiles
+
+ horizGapSize = 0;
+ for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize) {
+ horizGapSize = y - start;
+ }
+ }
+ }
+ vertGapSize = 0;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize) {
+ vertGapSize = x - start;
+ }
+ }
+ }
+ horizGapSize2 = horizGapSize - splitGapSlack * avgFontSize / splitPrecision;
+ if (horizGapSize2 < 0.99) {
+ horizGapSize2 = 0.99;
+ }
+ vertGapSize2 = vertGapSize - splitGapSlack * avgFontSize / splitPrecision;
+ if (vertGapSize2 < 0.99) {
+ vertGapSize2 = 0.99;
+ }
+
+ //----- count horiz/vert gaps equivalent to largest gaps
+
+ minHorizChunkWidth = yMaxI - yMinI;
+ nHorizGaps = 0;
+ for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ;
+ prev = start - 1;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize2) {
+ ++nHorizGaps;
+ if (start - prev < minHorizChunkWidth) {
+ minHorizChunkWidth = start - prev;
}
- if (n > 0 && n <= 3) {
- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
- ++baseIdx) {
- word0 = NULL;
- word1 = pool->getPool(baseIdx);
- while (word1) {
- if (word1->base >= minBase - intraLineSpace &&
- word1->base <= maxBase + intraLineSpace &&
- ((rot == 0 || rot == 2)
- ? (word1->xMin >= blk->xMax &&
- word1->xMin < blk->xMax + colSpace2)
- : (word1->yMin >= blk->yMax &&
- word1->yMin < blk->yMax + colSpace2)) &&
- fabs(word1->fontSize - fontSize) <
- maxBlockFontSizeDelta3 * fontSize) {
- word2 = word1;
- if (word0) {
- word0->next = word1->next;
- } else {
- pool->setPool(baseIdx, word1->next);
- }
- word1 = word1->next;
- word2->next = NULL;
- blk->addWord(word2);
- if (word2->base < minBase) {
- minBase = word2->base;
- } else if (word2->base > maxBase) {
- maxBase = word2->base;
- }
- found = gTrue;
- break;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
- }
+ prev = y;
+ }
+ }
+ }
+ minVertChunkWidth = xMaxI - xMinI;
+ nVertGaps = 0;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ prev = start - 1;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize2) {
+ ++nVertGaps;
+ if (start - prev < minVertChunkWidth) {
+ minVertChunkWidth = start - prev;
}
+ prev = x;
+ }
+ }
+ }
- } while (found);
+ //----- compute splitting parameters
- //~ need to compute the primary writing mode (horiz/vert) in
- //~ addition to primary rotation
+ // approximation of number of lines in block
+ if (fabs(avgFontSize) < 0.001) {
+ nLines = 1;
+ } else if (rot & 1) {
+ nLines = (xMax - xMin) / avgFontSize;
+ } else {
+ nLines = (yMax - yMin) / avgFontSize;
+ }
+
+ // compute the minimum allowed vertical gap size
+ // (this is a horizontal gap threshold for rot=1,3
+ if (control.mode == textOutTableLayout) {
+ vertGapThreshold = vertGapThresholdTableMax
+ + vertGapThresholdTableSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdTableMin) {
+ vertGapThreshold = vertGapThresholdTableMin;
+ }
+ } else {
+ vertGapThreshold = vertGapThresholdMax + vertGapThresholdSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdMin) {
+ vertGapThreshold = vertGapThresholdMin;
+ }
+ }
+ vertGapThreshold = vertGapThreshold * avgFontSize / splitPrecision;
+
+ // compute the minimum allowed chunk width
+ if (control.mode == textOutTableLayout) {
+ minChunk = 0;
+ } else {
+ minChunk = vertSplitChunkThreshold * avgFontSize / splitPrecision;
+ }
+
+ // look for large chars
+ // -- this kludge (multiply by 256, convert to int, divide by 256.0)
+ // prevents floating point stability issues on x86 with gcc, where
+ // 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;
+ }
+ }
+
+ // figure out which type of split to do
+ doHorizSplit = doVertSplit = gFalse;
+ smallSplit = gFalse;
+ if (rot & 1) {
+ if (nHorizGaps > 0 &&
+ (horizGapSize > vertGapSize || control.mode == textOutTableLayout) &&
+ horizGapSize > vertGapThreshold &&
+ minHorizChunkWidth > minChunk) {
+ doHorizSplit = gTrue;
+ } else if (nVertGaps > 0) {
+ doVertSplit = gTrue;
+ } else if (nLargeChars == 0 && nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ } else {
+ if (nVertGaps > 0 &&
+ (vertGapSize > horizGapSize || control.mode == textOutTableLayout) &&
+ vertGapSize > vertGapThreshold &&
+ minVertChunkWidth > minChunk) {
+ doVertSplit = gTrue;
+ } else if (nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ } else if (nLargeChars == 0 && nVertGaps > 0) {
+ doVertSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ }
- // coalesce the block, and add it to the list
- blk->coalesce(uMap, fixedPitch);
- if (lastBlk) {
- lastBlk->next = blk;
+ //----- split the block
+
+ //~ this could use "other content" (vector graphics, rotated text) --
+ //~ presence of other content in a gap means we should definitely split
+
+ // split vertically
+ if (doVertSplit) {
+ blk = new TextBlock(blkVertSplit, rot);
+ blk->smallSplit = smallSplit;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ prev = start - 1;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize2) {
+ chars2 = getChars(charsA, (prev + 0.5) * splitPrecision, yMin - 1,
+ (start + 1.5) * splitPrecision, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ prev = x;
+ }
+ }
+ }
+ chars2 = getChars(charsA, (prev + 0.5) * splitPrecision, yMin - 1,
+ xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split horizontally
+ } else if (doHorizSplit) {
+ blk = new TextBlock(blkHorizSplit, rot);
+ blk->smallSplit = smallSplit;
+ for (start = yMinI;
+ start < yMaxI && !horizProfile[start - yMinI];
+ ++start) ;
+ prev = start - 1;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize2) {
+ chars2 = getChars(charsA, xMin - 1, (prev + 0.5) * splitPrecision,
+ xMax + 1, (start + 1.5) * splitPrecision);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ prev = y;
+ }
+ }
+ }
+ chars2 = getChars(charsA, xMin - 1, (prev + 0.5) * splitPrecision,
+ xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split into larger and smaller chars
+ } else if (nLargeChars > 0) {
+ chars2 = new GList();
+ chars3 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->fontSize > largeCharSize) {
+ chars2->append(ch);
} else {
- blkList = blk;
+ chars3->append(ch);
}
- lastBlk = blk;
- count[rot] += blk->charCount;
- ++nBlocks;
}
+ blk = split(chars3, rot);
+ insertLargeChars(chars2, blk);
+ delete chars2;
+ delete chars3;
- if (count[rot] > count[primaryRot]) {
- primaryRot = rot;
+ // create a leaf node
+ } else {
+ blk = new TextBlock(blkLeaf, rot);
+ for (i = 0; i < charsA->getLength(); ++i) {
+ blk->addChild((TextChar *)charsA->get(i));
}
}
-#if 0 // for debugging
- printf("*** rotation ***\n");
- for (rot = 0; rot < 4; ++rot) {
- printf(" %d: %6d\n", rot, count[rot]);
+ gfree(horizProfile);
+ gfree(vertProfile);
+
+ tagBlock(blk);
+
+ return blk;
+}
+
+// Return the subset of chars inside a rectangle.
+GList *TextPage::getChars(GList *charsA, double xMin, double yMin,
+ double xMax, double yMax) {
+ GList *ret;
+ TextChar *ch;
+ double x, y;
+ int i;
+
+ ret = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // because of {ascent,descent}AdjustFactor, the y coords (or x
+ // coords for rot 1,3) for the gaps will be a little bit tight --
+ // so we use the center of the character here
+ x = 0.5 * (ch->xMin + ch->xMax);
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (x > xMin && x < xMax && y > yMin && y < yMax) {
+ ret->append(ch);
+ }
}
- printf(" primary rot = %d\n", primaryRot);
- printf("\n");
-#endif
+ return ret;
+}
-#if 0 // for debugging
- printf("*** blocks ***\n");
- for (blk = blkList; blk; blk = blk->next) {
- printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n",
- blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax);
- for (line = blk->lines; line; line = line->next) {
- printf(" line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n",
- line->xMin, line->xMax, line->yMin, line->yMax, line->base);
- for (word0 = line->words; word0; word0 = word0->next) {
- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
- word0->base, word0->fontSize, word0->spaceAfter);
- for (i = 0; i < word0->len; ++i) {
- fputc(word0->text[i] & 0xff, stdout);
+// Decide whether this block is a line, column, or multiple columns:
+// - all leaf nodes are lines
+// - horiz split nodes whose children are lines or columns are columns
+// - other horiz split nodes are multiple columns
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - other vert split nodes are multiple columns
+// (for rot=1,3: the horiz and vert splits are swapped)
+// In table layout mode:
+// - all leaf nodes are lines
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - everything else is multiple columns
+void TextPage::tagBlock(TextBlock *blk) {
+ TextBlock *child;
+ int i;
+
+ if (control.mode == textOutTableLayout) {
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+ } else if (blk->type == ((blk->rot & 1) ? blkHorizSplit : blkVertSplit) &&
+ blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
}
- printf("'\n");
}
+ } else {
+ blk->tag = blkTagMulticolumn;
}
+ return;
}
- printf("\n");
-#endif
- // determine the primary direction
- lrCount = 0;
- for (blk = blkList; blk; blk = blk->next) {
- for (line = blk->lines; line; line = line->next) {
- for (word0 = line->words; word0; word0 = word0->next) {
- for (i = 0; i < word0->len; ++i) {
- if (unicodeTypeL(word0->text[i])) {
- ++lrCount;
- } else if (unicodeTypeR(word0->text[i])) {
- --lrCount;
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+
+ } else {
+ if (blk->type == ((blk->rot & 1) ? blkVertSplit : blkHorizSplit)) {
+ blk->tag = blkTagColumn;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagColumn && child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
+ }
+ }
+ } else {
+ if (blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
}
}
+ } else {
+ blk->tag = blkTagMulticolumn;
}
}
}
- primaryLR = lrCount >= 0;
+}
-#if 0 // for debugging
- printf("*** direction ***\n");
- printf("lrCount = %d\n", lrCount);
- printf("primaryLR = %d\n", primaryLR);
-#endif
+// Insert a list of large characters into a tree.
+void TextPage::insertLargeChars(GList *largeChars, TextBlock *blk) {
+ TextChar *ch, *ch2;
+ GBool singleLine;
+ double xLimit, yLimit, minOverlap;
+ int i;
- //----- column assignment
+ //~ this currently works only for characters in the primary rotation
+
+ // check to see if the large chars are a single line, in the
+ // upper-left corner of blk (this is just a rough estimate)
+ xLimit = blk->xMin + 0.5 * (blk->xMin + blk->xMax);
+ yLimit = blk->yMin + 0.5 * (blk->yMin + blk->yMax);
+ singleLine = gTrue;
+ // note: largeChars are already sorted by x
+ for (i = 0; i < largeChars->getLength(); ++i) {
+ ch2 = (TextChar *)largeChars->get(i);
+ if (ch2->xMax > xLimit || ch2->yMax > yLimit) {
+ singleLine = gFalse;
+ break;
+ }
+ if (i > 0) {
+ ch = (TextChar *)largeChars->get(i-1);
+ minOverlap = 0.5 * (ch->fontSize < ch2->fontSize ? ch->fontSize
+ : ch2->fontSize);
+ if (ch->yMax - ch2->yMin < minOverlap ||
+ ch2->yMax - ch->yMin < minOverlap) {
+ singleLine = gFalse;
+ break;
+ }
+ }
+ }
- if (physLayout && fixedPitch) {
+ if (singleLine) {
+ // if the large chars are a single line, prepend them to the first
+ // leaf node in blk
+ insertLargeCharsInFirstLeaf(largeChars, blk);
+ } else {
+ // if the large chars are not a single line, prepend each one to
+ // the appropriate leaf node -- this handles cases like bullets
+ // drawn in a large font, on the left edge of a column
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ insertLargeCharInLeaf(ch, blk);
+ }
+ }
+}
- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
- blocks[i] = blk;
- col1 = 0; // make gcc happy
- switch (primaryRot) {
- case 0:
- col1 = (int)(blk->xMin / fixedPitch + 0.5);
- break;
- case 1:
- col1 = (int)(blk->yMin / fixedPitch + 0.5);
- break;
- case 2:
- col1 = (int)((pageWidth - blk->xMax) / fixedPitch + 0.5);
- break;
- case 3:
- col1 = (int)((pageHeight - blk->yMax) / fixedPitch + 0.5);
+// Find the first leaf (in depth-first order) in blk, and prepend a
+// list of large chars.
+void TextPage::insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk) {
+ TextChar *ch;
+ int i;
+
+ if (blk->type == blkLeaf) {
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ blk->prependChild(ch);
+ }
+ } else {
+ insertLargeCharsInFirstLeaf(largeChars, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Find the leaf in <blk> where large char <ch> belongs, and prepend
+// it.
+void TextPage::insertLargeCharInLeaf(TextChar *ch, TextBlock *blk) {
+ TextBlock *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ //~ this currently just looks down the left edge of blk
+ //~ -- it could be extended to do more
+
+ // estimate the baseline of ch
+ y = ch->yMin + 0.75 * (ch->yMax - ch->yMin);
+
+ if (blk->type == blkLeaf) {
+ blk->prependChild(ch);
+ } else if (blk->type == blkHorizSplit) {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (y < child->yMax || i == blk->children->getLength() - 1) {
+ insertLargeCharInLeaf(ch, child);
+ blk->updateBounds(i);
break;
}
- blk->col = col1;
- for (line = blk->lines; line; line = line->next) {
- for (j = 0; j <= line->len; ++j) {
- line->col[j] += col1;
+ }
+ } else {
+ insertLargeCharInLeaf(ch, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Merge blk (rot != 0) into primaryTree (rot == 0).
+void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) {
+ TextBlock *child;
+
+ // we insert a whole column at a time - so call insertIntoTree
+ // recursively until we get to a column (or line)
+
+ if (blk->tag == blkTagMulticolumn) {
+ while (blk->children->getLength()) {
+ child = (TextBlock *)blk->children->del(0);
+ insertIntoTree(child, primaryTree);
+ }
+ delete blk;
+ } else {
+ insertColumnIntoTree(blk, primaryTree);
+ }
+}
+
+// Insert a column (as an atomic subtree) into tree.
+// Requirement: tree is not a leaf node.
+void TextPage::insertColumnIntoTree(TextBlock *column, TextBlock *tree) {
+ TextBlock *child;
+ int i;
+
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (child->tag == blkTagMulticolumn &&
+ column->xMin >= child->xMin &&
+ column->yMin >= child->yMin &&
+ column->xMax <= child->xMax &&
+ column->yMax <= child->yMax) {
+ insertColumnIntoTree(column, child);
+ tree->tag = blkTagMulticolumn;
+ return;
+ }
+ }
+
+ if (tree->type == blkVertSplit) {
+ if (tree->rot == 1 || tree->rot == 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMax > 0.5 * (child->xMin + child->xMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMin < 0.5 * (child->xMin + child->xMax)) {
+ break;
}
}
}
+ } else if (tree->type == blkHorizSplit) {
+ if (tree->rot >= 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMax > 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMin < 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ }
+ } else {
+ // this should never happen
+ return;
+ }
+ tree->children->insert(i, column);
+ tree->tag = blkTagMulticolumn;
+}
+// Insert clipped characters back into the TextBlock tree.
+void TextPage::insertClippedChars(GList *clippedChars, TextBlock *tree) {
+ TextChar *ch, *ch2;
+ TextBlock *leaf;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ clippedChars->sort(TextChar::cmpX);
+ while (clippedChars->getLength()) {
+ ch = (TextChar *)clippedChars->del(0);
+ if (ch->rot != 0) {
+ continue;
+ }
+ if (!(leaf = findClippedCharLeaf(ch, tree))) {
+ continue;
+ }
+ leaf->addChild(ch);
+ i = 0;
+ while (i < clippedChars->getLength()) {
+ ch2 = (TextChar *)clippedChars->get(i);
+ if (ch2->xMin > ch->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ break;
+ }
+ y = 0.5 * (ch2->yMin + ch2->yMax);
+ if (y > leaf->yMin && y < leaf->yMax) {
+ ch2 = (TextChar *)clippedChars->del(i);
+ leaf->addChild(ch2);
+ ch = ch2;
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+// Find the leaf in <tree> to which clipped char <ch> can be appended.
+// Returns NULL if there is no appropriate append point.
+TextBlock *TextPage::findClippedCharLeaf(TextChar *ch, TextBlock *tree) {
+ TextBlock *ret, *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (tree->type == blkLeaf) {
+ if (tree->rot == 0) {
+ if (y > tree->yMin && y < tree->yMax &&
+ ch->xMin <= tree->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ return tree;
+ }
+ }
} else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if ((ret = findClippedCharLeaf(ch, child))) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+// Convert the tree of TextBlocks into a list of TextColumns.
+GList *TextPage::buildColumns(TextBlock *tree) {
+ GList *columns;
+
+ columns = new GList();
+ buildColumns2(tree, columns);
+ return columns;
+}
+
+void TextPage::buildColumns2(TextBlock *blk, GList *columns) {
+ TextColumn *col;
+ int i;
- // sort blocks into xy order for column assignment
- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
- blocks[i] = blk;
+ switch (blk->tag) {
+ case blkTagLine:
+ case blkTagColumn:
+ col = buildColumn(blk);
+ columns->append(col);
+ break;
+ case blkTagMulticolumn:
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildColumns2((TextBlock *)blk->children->get(i), columns);
}
- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);
+ break;
+ }
+}
- // column assignment
- for (i = 0; i < nBlocks; ++i) {
- blk0 = blocks[i];
- col1 = 0;
- for (j = 0; j < i; ++j) {
- blk1 = blocks[j];
- col2 = 0; // make gcc happy
- switch (primaryRot) {
- case 0:
- if (blk0->xMin > blk1->xMax) {
- col2 = blk1->col + blk1->nColumns + 3;
- } else if (blk1->xMax == blk1->xMin) {
- col2 = blk1->col;
- } else {
- col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) /
- (blk1->xMax - blk1->xMin)) *
- blk1->nColumns);
+TextColumn *TextPage::buildColumn(TextBlock *blk) {
+ GList *lines, *parLines;
+ GList *paragraphs;
+ TextLine *line0, *line1;
+ double spaceThresh, indent0, indent1, fontSize0, fontSize1;
+ int i;
+
+ lines = new GList();
+ buildLines(blk, lines);
+
+ spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
+
+ //~ could look for bulleted lists here: look for the case where
+ //~ all out-dented lines start with the same char
+
+ // build the paragraphs
+ paragraphs = new GList();
+ i = 0;
+ while (i < lines->getLength()) {
+
+ // get the first line of the paragraph
+ parLines = new GList();
+ line0 = (TextLine *)lines->get(i);
+ parLines->append(line0);
+ ++i;
+
+ if (i < lines->getLength()) {
+ line1 = (TextLine *)lines->get(i);
+ indent0 = getLineIndent(line0, blk);
+ indent1 = getLineIndent(line1, blk);
+ fontSize0 = line0->fontSize;
+ fontSize1 = line1->fontSize;
+
+ // inverted indent
+ if (indent1 - indent0 > minParagraphIndent * fontSize0 &&
+ fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent0 - indent1 > minParagraphIndent * fontSize0) {
+ break;
}
- break;
- case 1:
- if (blk0->yMin > blk1->yMax) {
- col2 = blk1->col + blk1->nColumns + 3;
- } else if (blk1->yMax == blk1->yMin) {
- col2 = blk1->col;
- } else {
- col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) /
- (blk1->yMax - blk1->yMin)) *
- blk1->nColumns);
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
}
- break;
- case 2:
- if (blk0->xMax < blk1->xMin) {
- col2 = blk1->col + blk1->nColumns + 3;
- } else if (blk1->xMin == blk1->xMax) {
- col2 = blk1->col;
- } else {
- col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) /
- (blk1->xMin - blk1->xMax)) *
- blk1->nColumns);
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
}
- break;
- case 3:
- if (blk0->yMax < blk1->yMin) {
- col2 = blk1->col + blk1->nColumns + 3;
- } else if (blk1->yMin == blk1->yMax) {
- col2 = blk1->col;
- } else {
- col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) /
- (blk1->yMin - blk1->yMax)) *
- blk1->nColumns);
+ parLines->append(line1);
+ }
+
+ // drop cap
+ } else if (fontSize0 > largeCharThreshold * fontSize1 &&
+ indent1 - indent0 > minParagraphIndent * fontSize1 &&
+ getLineSpacing(line0, line1) < 0) {
+ parLines->append(line1);
+ fontSize0 = fontSize1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ if (indent1 - indent0 <= minParagraphIndent * fontSize0) {
+ break;
}
- break;
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
- if (col2 > col1) {
- col1 = col2;
+ for (; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
- }
- blk0->col = col1;
- for (line = blk0->lines; line; line = line->next) {
- for (j = 0; j <= line->len; ++j) {
- line->col[j] += col1;
+
+ // regular indent or no indent
+ } else if (fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
}
}
+ paragraphs->append(new TextParagraph(parLines));
}
-#if 0 // for debugging
- printf("*** blocks, after column assignment ***\n");
- for (blk = blkList; blk; blk = blk->next) {
- printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f col=%d nCols=%d\n",
- blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col,
- blk->nColumns);
- for (line = blk->lines; line; line = line->next) {
- printf(" line: col[0]=%d\n", line->col[0]);
- for (word0 = line->words; word0; word0 = word0->next) {
- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
- word0->base, word0->fontSize, word0->spaceAfter);
- for (i = 0; i < word0->len; ++i) {
- fputc(word0->text[i] & 0xff, stdout);
- }
- printf("'\n");
- }
+ delete lines;
+
+ return new TextColumn(paragraphs, blk->xMin, blk->yMin,
+ blk->xMax, blk->yMax);
+}
+
+double TextPage::getLineIndent(TextLine *line, TextBlock *blk) {
+ double indent;
+
+ switch (line->rot) {
+ case 0:
+ default: indent = line->xMin - blk->xMin; break;
+ case 1: indent = line->yMin - blk->yMin; break;
+ case 2: indent = blk->xMax - line->xMax; break;
+ case 3: indent = blk->yMax - line->yMax; break;
+ }
+ return indent;
+}
+
+// Compute average line spacing in column.
+double TextPage::getAverageLineSpacing(GList *lines) {
+ double avg, sp;
+ int n, i;
+
+ avg = 0;
+ n = 0;
+ for (i = 1; i < lines->getLength(); ++i) {
+ sp = getLineSpacing((TextLine *)lines->get(i - 1),
+ (TextLine *)lines->get(i));
+ if (sp > 0) {
+ avg += sp;
+ ++n;
}
}
- printf("\n");
-#endif
+ if (n > 0) {
+ avg /= n;
+ }
+ return avg;
+}
+
+// Compute the space between two lines.
+double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) {
+ double sp;
+
+ switch (line0->rot) {
+ case 0:
+ default: sp = line1->yMin - line0->yMax; break;
+ case 1: sp = line0->xMin - line1->xMax; break;
+ case 2: sp = line0->yMin - line1->yMin; break;
+ case 3: sp = line1->xMin - line1->xMax; break;
+ }
+ return sp;
+}
+
+void TextPage::buildLines(TextBlock *blk, GList *lines) {
+ TextLine *line;
+ int i;
+
+ switch (blk->tag) {
+ case blkTagLine:
+ line = buildLine(blk);
+ if (blk->rot == 1 || blk->rot == 2) {
+ lines->insert(0, line);
+ } else {
+ lines->append(line);
+ }
+ break;
+ case blkTagColumn:
+ case blkTagMulticolumn: // multicolumn should never happen here
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildLines((TextBlock *)blk->children->get(i), lines);
+ }
+ break;
+ }
+}
+
+TextLine *TextPage::buildLine(TextBlock *blk) {
+ GList *charsA;
+ GList *words;
+ TextChar *ch, *ch2;
+ TextWord *word;
+ double wordSp, lineFontSize, sp;
+ GBool spaceAfter, spaceAfter2;
+ int i, j;
- //----- reading order sort
+ charsA = new GList();
+ getLineChars(blk, charsA);
- // sort blocks into yx order (in preparation for reading order sort)
- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);
+ wordSp = computeWordSpacingThreshold(charsA, blk->rot);
- // compute space on left and right sides of each block
- for (i = 0; i < nBlocks; ++i) {
- blk0 = blocks[i];
- for (j = 0; j < nBlocks; ++j) {
- blk1 = blocks[j];
- if (blk1 != blk0) {
- blk0->updatePriMinMax(blk1);
+ words = new GList();
+ lineFontSize = 0;
+ spaceAfter = gFalse;
+ i = 0;
+ while (i < charsA->getLength()) {
+ sp = wordSp - 1;
+ 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);
+ if (sp > wordSp ||
+ ch->font != ch2->font ||
+ fabs(ch->fontSize - ch2->fontSize) > 0.01 ||
+ (control.mode == textOutRawOrder &&
+ ch2->charPos != ch->charPos + ch->charLen)) {
+ break;
}
+ sp = wordSp - 1;
+ }
+ spaceAfter2 = spaceAfter;
+ spaceAfter = sp > wordSp;
+ word = new TextWord(charsA, i, j - i, blk->rot,
+ (blk->rot >= 2) ? spaceAfter2 : spaceAfter);
+ i = j;
+ if (blk->rot >= 2) {
+ words->insert(0, word);
+ } else {
+ words->append(word);
+ }
+ if (i == 0 || word->fontSize > lineFontSize) {
+ lineFontSize = word->fontSize;
}
}
-#if 0 // for debugging
- printf("*** blocks, after yx sort ***\n");
- for (i = 0; i < nBlocks; ++i) {
- blk = blocks[i];
- printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f space=%.2f..%.2f\n",
- blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
- blk->priMin, blk->priMax);
- for (line = blk->lines; line; line = line->next) {
- printf(" line:\n");
- for (word0 = line->words; word0; word0 = word0->next) {
- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
- word0->base, word0->fontSize, word0->spaceAfter);
- for (j = 0; j < word0->len; ++j) {
- fputc(word0->text[j] & 0xff, stdout);
- }
- printf("'\n");
+ delete charsA;
+
+ return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax,
+ lineFontSize);
+}
+
+void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
+ int i;
+
+ if (blk->type == blkLeaf) {
+ charsA->append(blk->children);
+ } else {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ getLineChars((TextBlock *)blk->children->get(i), charsA);
+ }
+ }
+}
+
+// Compute the inter-word spacing threshold for a line of chars.
+// Spaces greater than this threshold will be considered inter-word
+// spaces.
+double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
+ TextChar *ch, *ch2;
+ double avgFontSize, minSp, maxSp, sp;
+ int i;
+
+ avgFontSize = 0;
+ minSp = maxSp = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ avgFontSize += ch->fontSize;
+ if (i < charsA->getLength() - 1) {
+ ch2 = (TextChar *)charsA->get(i+1);
+ sp = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ if (i == 0 || sp < minSp) {
+ minSp = sp;
+ }
+ if (sp > maxSp) {
+ maxSp = sp;
}
}
}
- printf("\n");
-#endif
+ avgFontSize /= charsA->getLength();
+ if (minSp < 0) {
+ minSp = 0;
+ }
- // build the flows
- //~ this needs to be adjusted for writing mode (vertical text)
- //~ this also needs to account for right-to-left column ordering
- blkArray = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
- memcpy(blkArray, blocks, nBlocks * sizeof(TextBlock *));
- flows = lastFlow = NULL;
- firstBlkIdx = 0;
- nBlocksLeft = nBlocks;
- while (nBlocksLeft > 0) {
-
- // find the upper-left-most block
- for (; !blkArray[firstBlkIdx]; ++firstBlkIdx) ;
- i = firstBlkIdx;
- blk = blkArray[i];
- for (j = firstBlkIdx + 1; j < nBlocks; ++j) {
- blk1 = blkArray[j];
- if (blk1) {
- if (blk && blk->secondaryDelta(blk1) > 0) {
- break;
+ // if spacing is completely uniform, assume it's a single word
+ // (technically it could be either "ABC" or "A B C", but it's
+ // essentially impossible to tell)
+ if (maxSp - minSp < uniformSpacing * avgFontSize) {
+ return maxSp + 1;
+
+ // if there is some variation in spacing, but it's small, assume
+ // there are some inter-word spaces
+ } else if (maxSp - minSp < wordSpacing * avgFontSize) {
+ return 0.5 * (minSp + maxSp);
+
+ // otherwise, assume a reasonable threshold for inter-word spacing
+ // (we can't use something like 0.5*(minSp+maxSp) here because there
+ // can be outliers at the high end)
+ } else {
+ return minSp + wordSpacing * avgFontSize;
+ }
+}
+
+int TextPage::assignPhysLayoutPositions(GList *columns) {
+ assignLinePhysPositions(columns);
+ return assignColumnPhysPositions(columns);
+}
+
+// Assign a physical x coordinate for each TextLine (relative to the
+// containing TextColumn). This also computes TextColumn width and
+// height.
+void TextPage::assignLinePhysPositions(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ UnicodeMap *uMap;
+ int colIdx, parIdx, lineIdx;
+
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return;
+ }
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ col->pw = col->ph = 0;
+ 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);
+ computeLinePhysWidth(line, uMap);
+ if (control.fixedPitch > 0) {
+ line->px = (int)((line->xMin - col->xMin) / control.fixedPitch);
+ } else if (fabs(line->fontSize) < 0.001) {
+ line->px = 0;
+ } else {
+ line->px = (int)((line->xMin - col->xMin) /
+ (physLayoutSpaceWidth * line->fontSize));
}
- if (blk1->primaryCmp(blk) < 0) {
- i = j;
- blk = blk1;
+ if (line->px + line->pw > col->pw) {
+ col->pw = line->px + line->pw;
}
}
+ col->ph += par->lines->getLength();
}
- blkArray[i] = NULL;
- --nBlocksLeft;
- blk->next = NULL;
+ col->ph += col->paragraphs->getLength() - 1;
+ }
- // create a new flow, starting with the upper-left-most block
- flow = new TextFlow(this, blk);
- if (lastFlow) {
- lastFlow->next = flow;
+ uMap->decRefCnt();
+}
+
+void TextPage::computeLinePhysWidth(TextLine *line, UnicodeMap *uMap) {
+ char buf[8];
+ int n, i;
+
+ if (uMap->isUnicode()) {
+ line->pw = line->len;
+ } else {
+ line->pw = 0;
+ for (i = 0; i < line->len; ++i) {
+ n = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
+ line->pw += n;
+ }
+ }
+}
+
+// Assign physical x and y coordinates for each TextColumn. Returns
+// the text height (max physical y + 1).
+int TextPage::assignColumnPhysPositions(GList *columns) {
+ TextColumn *col, *col2;
+ double slack, xOverlap, yOverlap;
+ int ph, i, j;
+
+ if (control.mode == textOutTableLayout) {
+ slack = tableCellOverlapSlack;
+ } else {
+ slack = 0;
+ }
+
+ // assign x positions
+ columns->sort(&TextColumn::cmpX);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ if (control.fixedPitch) {
+ col->px = (int)(col->xMin / control.fixedPitch);
} else {
- flows = flow;
- }
- lastFlow = flow;
- fontSize = blk->lines->words->fontSize;
-
- // push the upper-left-most block on the stack
- blk->stackNext = NULL;
- blkStack = blk;
-
- // find the other blocks in this flow
- while (blkStack) {
-
- // find the upper-left-most block under (but within
- // maxBlockSpacing of) the top block on the stack
- blkSpace = maxBlockSpacing * blkStack->lines->words->fontSize;
- blk = NULL;
- i = -1;
- for (j = firstBlkIdx; j < nBlocks; ++j) {
- blk1 = blkArray[j];
- if (blk1) {
- if (blkStack->secondaryDelta(blk1) > blkSpace) {
- break;
- }
- if (blk && blk->secondaryDelta(blk1) > 0) {
- break;
+ col->px = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ xOverlap = col2->xMax - col->xMin;
+ if (xOverlap < slack * (col2->xMax - col2->xMin)) {
+ if (col2->px + col2->pw + 2 > col->px) {
+ col->px = col2->px + col2->pw + 2;
}
- if (blk1->isBelow(blkStack) &&
- (!blk || blk1->primaryCmp(blk) < 0)) {
- i = j;
- blk = blk1;
+ } else {
+ yOverlap = (col->yMax < col2->yMax ? col->yMax : col2->yMax) -
+ (col->yMin > col2->yMin ? col->yMin : col2->yMin);
+ if (yOverlap > 0 && xOverlap < yOverlap) {
+ if (col2->px + col2->pw > col->px) {
+ col->px = col2->px + col2->pw;
+ }
+ } else {
+ if (col2->px > col->px) {
+ col->px = col2->px;
+ }
}
}
}
+ }
+ }
- // if a suitable block was found, add it to the flow and push it
- // onto the stack
- if (blk && flow->blockFits(blk, blkStack)) {
- blkArray[i] = NULL;
- --nBlocksLeft;
- blk->next = NULL;
- flow->addBlock(blk);
- fontSize = blk->lines->words->fontSize;
- blk->stackNext = blkStack;
- blkStack = blk;
-
- // otherwise (if there is no block under the top block or the
- // block is not suitable), pop the stack
+ // assign y positions
+ ph = 0;
+ columns->sort(&TextColumn::cmpY);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ col->py = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ yOverlap = col2->yMax - col->yMin;
+ if (yOverlap < slack * (col2->yMax - col2->yMin)) {
+ if (col2->py + col2->ph + 1 > col->py) {
+ col->py = col2->py + col2->ph + 1;
+ }
} else {
- blkStack = blkStack->stackNext;
+ xOverlap = (col->xMax < col2->xMax ? col->xMax : col2->xMax) -
+ (col->xMin > col2->xMin ? col->xMin : col2->xMin);
+ if (xOverlap > 0 && yOverlap < xOverlap) {
+ if (col2->py + col2->ph > col->py) {
+ col->py = col2->py + col2->ph;
+ }
+ } else {
+ if (col2->py > col->py) {
+ col->py = col2->py;
+ }
+ }
}
}
+ if (col->py + col->ph > ph) {
+ ph = col->py + col->ph;
+ }
}
- gfree(blkArray);
-
-#if 0 // for debugging
- printf("*** flows ***\n");
- for (flow = flows; flow; flow = flow->next) {
- printf("flow: x=%.2f..%.2f y=%.2f..%.2f pri:%.2f..%.2f\n",
- flow->xMin, flow->xMax, flow->yMin, flow->yMax,
- flow->priMin, flow->priMax);
- for (blk = flow->blocks; blk; blk = blk->next) {
- printf(" block: rot=%d x=%.2f..%.2f y=%.2f..%.2f pri=%.2f..%.2f\n",
- blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
- blk->priMin, blk->priMax);
- for (line = blk->lines; line; line = line->next) {
- printf(" line:\n");
- for (word0 = line->words; word0; word0 = word0->next) {
- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
- word0->base, word0->fontSize, word0->spaceAfter);
- for (i = 0; i < word0->len; ++i) {
- fputc(word0->text[i] & 0xff, stdout);
+
+ return ph;
+}
+
+void TextPage::generateUnderlinesAndLinks(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ TextUnderline *underline;
+ TextLink *link;
+ double base, uSlack, ubSlack, hSlack;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ 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);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ base = word->getBaseline();
+ uSlack = underlineSlack * word->fontSize;
+ ubSlack = underlineBaselineSlack * word->fontSize;
+ hSlack = hyperlinkSlack * word->fontSize;
+
+ //----- handle underlining
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ if (underline->horiz) {
+ if (word->rot == 0 || word->rot == 2) {
+ if (fabs(underline->y0 - base) < ubSlack &&
+ underline->x0 < word->xMin + uSlack &&
+ word->xMax - uSlack < underline->x1) {
+ word->underlined = gTrue;
+ }
+ }
+ } else {
+ if (word->rot == 1 || word->rot == 3) {
+ if (fabs(underline->x0 - base) < ubSlack &&
+ underline->y0 < word->yMin + uSlack &&
+ word->yMax - uSlack < underline->y1) {
+ word->underlined = gTrue;
+ }
+ }
+ }
+ }
+
+ //----- handle links
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ if (link->xMin < word->xMin + hSlack &&
+ word->xMax - hSlack < link->xMax &&
+ link->yMin < word->yMin + hSlack &&
+ word->yMax - hSlack < link->yMax) {
+ word->link = link;
+ }
}
- printf("'\n");
}
}
}
}
- printf("\n");
-#endif
-
- if (uMap) {
- uMap->decRefCnt();
- }
}
+//------------------------------------------------------------------------
+// TextPage: access
+//------------------------------------------------------------------------
+
GBool TextPage::findText(Unicode *s, int len,
GBool startAtTop, GBool stopAtBottom,
GBool startAtLast, GBool stopAtLast,
@@ -3131,20 +3639,31 @@ GBool TextPage::findText(Unicode *s, int len,
GBool wholeWord,
double *xMin, double *yMin,
double *xMax, double *yMax) {
- TextBlock *blk;
+ TextBlock *tree;
+ TextColumn *column;
+ TextParagraph *par;
TextLine *line;
Unicode *s2, *txt;
Unicode *p;
- int txtSize, m, i, j, k;
double xStart, yStart, xStop, yStop;
double xMin0, yMin0, xMax0, yMax0;
double xMin1, yMin1, xMax1, yMax1;
GBool found;
+ int txtSize, m, rot, colIdx, parIdx, lineIdx, i, j, k;
- //~ needs to handle right-to-left text
+ //~ need to handle right-to-left text
- if (rawOrder) {
- return gFalse;
+ if (!findCols) {
+ rot = rotateChars(chars);
+ if ((tree = splitChars(chars))) {
+ findCols = buildColumns(tree);
+ delete tree;
+ } else {
+ // no text
+ findCols = new GList();
+ }
+ unrotateChars(chars, rot);
+ unrotateColumns(findCols, rot);
}
// convert the search string to uppercase
@@ -3180,139 +3699,152 @@ GBool TextPage::findText(Unicode *s, int len,
xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
- for (i = backward ? nBlocks - 1 : 0;
- backward ? i >= 0 : i < nBlocks;
- i += backward ? -1 : 1) {
- blk = blocks[i];
+ for (colIdx = backward ? findCols->getLength() - 1 : 0;
+ backward ? colIdx >= 0 : colIdx < findCols->getLength();
+ colIdx += backward ? -1 : 1) {
+ column = (TextColumn *)findCols->get(colIdx);
- // check: is the block above the top limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the blocks won't be sorted in the useful order)
- if (!startAtTop && primaryRot == 0 &&
- (backward ? blk->yMin > yStart : blk->yMax < yStart)) {
+ // check: is the column above the top limit?
+ if (!startAtTop && (backward ? column->yMin > yStart
+ : column->yMax < yStart)) {
continue;
}
- // check: is the block below the bottom limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the blocks won't be sorted in the useful order)
- if (!stopAtBottom && primaryRot == 0 &&
- (backward ? blk->yMax < yStop : blk->yMin > yStop)) {
- break;
+ // check: is the column below the bottom limit?
+ if (!stopAtBottom && (backward ? column->yMax < yStop
+ : column->yMin > yStop)) {
+ continue;
}
- for (line = blk->lines; line; line = line->next) {
+ for (parIdx = backward ? column->paragraphs->getLength() - 1 : 0;
+ backward ? parIdx >= 0 : parIdx < column->paragraphs->getLength();
+ parIdx += backward ? -1 : 1) {
+ par = (TextParagraph *)column->paragraphs->get(parIdx);
- // check: is the line above the top limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the lines won't be sorted in the useful order)
- if (!startAtTop && primaryRot == 0 &&
- (backward ? line->yMin > yStart : line->yMin < yStart)) {
+ // check: is the paragraph above the top limit?
+ if (!startAtTop && (backward ? par->yMin > yStart
+ : par->yMax < yStart)) {
continue;
}
- // check: is the line below the bottom limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the lines won't be sorted in the useful order)
- if (!stopAtBottom && primaryRot == 0 &&
- (backward ? line->yMin < yStop : line->yMin > yStop)) {
+ // check: is the paragraph below the bottom limit?
+ if (!stopAtBottom && (backward ? par->yMax < yStop
+ : par->yMin > yStop)) {
continue;
}
- // convert the line to uppercase
- m = line->len;
- if (!caseSensitive) {
- if (m > txtSize) {
- txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
- txtSize = m;
+ for (lineIdx = backward ? par->lines->getLength() - 1 : 0;
+ backward ? lineIdx >= 0 : lineIdx < par->lines->getLength();
+ lineIdx += backward ? -1 : 1) {
+ line = (TextLine *)par->lines->get(lineIdx);
+
+ // check: is the line above the top limit?
+ if (!startAtTop && (backward ? line->yMin > yStart
+ : line->yMax < yStart)) {
+ continue;
}
- for (k = 0; k < m; ++k) {
- txt[k] = unicodeToUpper(line->text[k]);
+
+ // check: is the line below the bottom limit?
+ if (!stopAtBottom && (backward ? line->yMax < yStop
+ : line->yMin > yStop)) {
+ continue;
}
- } else {
- txt = line->text;
- }
- // search each position in this line
- j = backward ? m - len : 0;
- p = txt + j;
- while (backward ? j >= 0 : j <= m - len) {
- if (!wholeWord ||
- ((j == 0 || !unicodeTypeAlphaNum(txt[j - 1])) &&
- (j + len == m || !unicodeTypeAlphaNum(txt[j + len])))) {
-
- // compare the strings
- for (k = 0; k < len; ++k) {
- if (p[k] != s2[k]) {
- break;
- }
+ // convert the line to uppercase
+ m = line->len;
+ if (!caseSensitive) {
+ if (m > txtSize) {
+ txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
+ txtSize = m;
}
+ for (k = 0; k < m; ++k) {
+ txt[k] = unicodeToUpper(line->text[k]);
+ }
+ } else {
+ txt = line->text;
+ }
- // found it
- if (k == len) {
- switch (line->rot) {
- case 0:
- xMin1 = line->edge[j];
- xMax1 = line->edge[j + len];
- yMin1 = line->yMin;
- yMax1 = line->yMax;
- break;
- case 1:
- xMin1 = line->xMin;
- xMax1 = line->xMax;
- yMin1 = line->edge[j];
- yMax1 = line->edge[j + len];
- break;
- case 2:
- xMin1 = line->edge[j + len];
- xMax1 = line->edge[j];
- yMin1 = line->yMin;
- yMax1 = line->yMax;
- break;
- case 3:
- xMin1 = line->xMin;
- xMax1 = line->xMax;
- yMin1 = line->edge[j + len];
- yMax1 = line->edge[j];
- break;
+ // search each position in this line
+ j = backward ? m - len : 0;
+ p = txt + j;
+ while (backward ? j >= 0 : j <= m - len) {
+ if (!wholeWord ||
+ ((j == 0 || !unicodeTypeWord(txt[j - 1])) &&
+ (j + len == m || !unicodeTypeWord(txt[j + len])))) {
+
+ // compare the strings
+ for (k = 0; k < len; ++k) {
+ if (p[k] != s2[k]) {
+ break;
+ }
}
- if (backward) {
- if ((startAtTop ||
- yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
- (stopAtBottom ||
- yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
- if (!found ||
- yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
- xMin0 = xMin1;
- xMax0 = xMax1;
- yMin0 = yMin1;
- yMax0 = yMax1;
- found = gTrue;
- }
+
+ // found it
+ if (k == len) {
+ switch (line->rot) {
+ case 0:
+ xMin1 = line->edge[j];
+ xMax1 = line->edge[j + len];
+ yMin1 = line->yMin;
+ yMax1 = line->yMax;
+ break;
+ case 1:
+ xMin1 = line->xMin;
+ xMax1 = line->xMax;
+ yMin1 = line->edge[j];
+ yMax1 = line->edge[j + len];
+ break;
+ case 2:
+ xMin1 = line->edge[j + len];
+ xMax1 = line->edge[j];
+ yMin1 = line->yMin;
+ yMax1 = line->yMax;
+ break;
+ case 3:
+ xMin1 = line->xMin;
+ xMax1 = line->xMax;
+ yMin1 = line->edge[j + len];
+ yMax1 = line->edge[j];
+ break;
}
- } else {
- if ((startAtTop ||
- yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
- (stopAtBottom ||
- yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
- if (!found ||
- yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
- xMin0 = xMin1;
- xMax0 = xMax1;
- yMin0 = yMin1;
- yMax0 = yMax1;
- found = gTrue;
+ if (backward) {
+ if ((startAtTop ||
+ yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
+ (stopAtBottom ||
+ yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
+ if (!found ||
+ yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
+ xMin0 = xMin1;
+ xMax0 = xMax1;
+ yMin0 = yMin1;
+ yMax0 = yMax1;
+ found = gTrue;
+ }
+ }
+ } else {
+ if ((startAtTop ||
+ yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
+ (stopAtBottom ||
+ yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
+ if (!found ||
+ yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
+ xMin0 = xMin1;
+ xMax0 = xMax1;
+ yMin0 = yMin1;
+ yMax0 = yMax1;
+ found = gTrue;
+ }
}
}
}
}
- }
- if (backward) {
- --j;
- --p;
- } else {
- ++j;
- ++p;
+ if (backward) {
+ --j;
+ --p;
+ } else {
+ ++j;
+ ++p;
+ }
}
}
}
@@ -3339,32 +3871,27 @@ GBool TextPage::findText(Unicode *s, int len,
GString *TextPage::getText(double xMin, double yMin,
double xMax, double yMax) {
- GString *s;
UnicodeMap *uMap;
- GBool isUnicode;
- TextBlock *blk;
- TextLine *line;
- TextLineFrag *frags;
- int nFrags, fragsSize;
- TextLineFrag *frag;
char space[8], eol[16];
int spaceLen, eolLen;
- int lastRot;
- double x, y, delta;
- int col, idx0, idx1, i, j;
- GBool multiLine, oneRot;
-
- s = new GString();
-
- if (rawOrder) {
- return s;
- }
+ GList *chars2;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextChar *ch;
+ GBool primaryLR;
+ TextBlock *tree;
+ GList *columns;
+ GString *ret;
+ double xx, yy;
+ int rot, colIdx, parIdx, lineIdx, ph, y, i;
// get the output encoding
if (!(uMap = globalParams->getTextEncoding())) {
- return s;
+ return NULL;
}
- isUnicode = uMap->isUnicode();
spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
eolLen = 0; // make gcc happy
switch (globalParams->getTextEOL()) {
@@ -3380,655 +3907,276 @@ GString *TextPage::getText(double xMin, double yMin,
break;
}
- //~ writing mode (horiz/vert)
-
- // collect the line fragments that are in the rectangle
- fragsSize = 256;
- frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));
- nFrags = 0;
- lastRot = -1;
- oneRot = gTrue;
- for (i = 0; i < nBlocks; ++i) {
- blk = blocks[i];
- if (xMin < blk->xMax && blk->xMin < xMax &&
- yMin < blk->yMax && blk->yMin < yMax) {
- for (line = blk->lines; line; line = line->next) {
- if (xMin < line->xMax && line->xMin < xMax &&
- yMin < line->yMax && line->yMin < yMax) {
- idx0 = idx1 = -1;
- switch (line->rot) {
- case 0:
- y = 0.5 * (line->yMin + line->yMax);
- if (yMin < y && y < yMax) {
- j = 0;
- while (j < line->len) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
- idx0 = j;
- break;
- }
- ++j;
- }
- j = line->len - 1;
- while (j >= 0) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
- idx1 = j;
- break;
- }
- --j;
- }
- }
- break;
- case 1:
- x = 0.5 * (line->xMin + line->xMax);
- if (xMin < x && x < xMax) {
- j = 0;
- while (j < line->len) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
- idx0 = j;
- break;
- }
- ++j;
- }
- j = line->len - 1;
- while (j >= 0) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
- idx1 = j;
- break;
- }
- --j;
- }
- }
- break;
- case 2:
- y = 0.5 * (line->yMin + line->yMax);
- if (yMin < y && y < yMax) {
- j = 0;
- while (j < line->len) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
- idx0 = j;
- break;
- }
- ++j;
- }
- j = line->len - 1;
- while (j >= 0) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
- idx1 = j;
- break;
- }
- --j;
- }
- }
- break;
- case 3:
- x = 0.5 * (line->xMin + line->xMax);
- if (xMin < x && x < xMax) {
- j = 0;
- while (j < line->len) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
- idx0 = j;
- break;
- }
- ++j;
- }
- j = line->len - 1;
- while (j >= 0) {
- if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
- idx1 = j;
- break;
- }
- --j;
- }
- }
- break;
- }
- if (idx0 >= 0 && idx1 >= 0) {
- if (nFrags == fragsSize) {
- fragsSize *= 2;
- frags = (TextLineFrag *)
- greallocn(frags, fragsSize, sizeof(TextLineFrag));
- }
- frags[nFrags].init(line, idx0, idx1 - idx0 + 1);
- ++nFrags;
- if (lastRot >= 0 && line->rot != lastRot) {
- oneRot = gFalse;
- }
- lastRot = line->rot;
- }
- }
- }
+ // get all chars in the rectangle
+ // (i.e., all chars whose center lies inside the rectangle)
+ chars2 = new GList();
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ xx = 0.5 * (ch->xMin + ch->xMax);
+ yy = 0.5 * (ch->yMin + ch->yMax);
+ if (xx > xMin && xx < xMax && yy > yMin && yy < yMax) {
+ chars2->append(ch);
}
}
+#if 0 //~debug
+ dumpChars(chars2);
+#endif
- // sort the fragments and generate the string
- if (nFrags > 0) {
-
- for (i = 0; i < nFrags; ++i) {
- frags[i].computeCoords(oneRot);
- }
- assignColumns(frags, nFrags, oneRot);
-
- // if all lines in the region have the same rotation, use it;
- // otherwise, use the page's primary rotation
- if (oneRot) {
- qsort(frags, nFrags, sizeof(TextLineFrag),
- &TextLineFrag::cmpYXLineRot);
- } else {
- qsort(frags, nFrags, sizeof(TextLineFrag),
- &TextLineFrag::cmpYXPrimaryRot);
- }
- i = 0;
- while (i < nFrags) {
- delta = maxIntraLineDelta * frags[i].line->words->fontSize;
- for (j = i+1;
- j < nFrags && fabs(frags[j].base - frags[i].base) < delta;
- ++j) ;
- qsort(frags + i, j - i, sizeof(TextLineFrag),
- oneRot ? &TextLineFrag::cmpXYColumnLineRot
- : &TextLineFrag::cmpXYColumnPrimaryRot);
- i = j;
- }
-
- col = 0;
- multiLine = gFalse;
- for (i = 0; i < nFrags; ++i) {
- frag = &frags[i];
-
- // insert a return
- if (frag->col < col ||
- (i > 0 && fabs(frag->base - frags[i-1].base) >
- maxIntraLineDelta * frags[i-1].line->words->fontSize)) {
- s->append(eol, eolLen);
- col = 0;
- multiLine = gTrue;
+ rot = rotateChars(chars2);
+ primaryLR = checkPrimaryLR(chars2);
+ tree = splitChars(chars2);
+ if (!tree) {
+ unrotateChars(chars2, rot);
+ delete chars2;
+ return new GString();
+ }
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ columns = buildColumns(tree);
+ delete tree;
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
+ unrotateChars(chars2, rot);
+ delete chars2;
+
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
+ }
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
}
-
- // column alignment
- for (; col < frag->col; ++col) {
- s->append(space, spaceLen);
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
}
-
- // get the fragment text
- col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
}
+ }
- if (multiLine) {
- s->append(eol, eolLen);
+ ret = new GString();
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ ret->append(out[i]);
+ delete out[i];
+ }
+ if (ph > 1) {
+ ret->append(eol, eolLen);
}
}
- gfree(frags);
+ gfree(out);
+ gfree(outLen);
+ deleteGList(columns, TextColumn);
uMap->decRefCnt();
- return s;
+ return ret;
}
GBool TextPage::findCharRange(int pos, int length,
double *xMin, double *yMin,
double *xMax, double *yMax) {
- TextBlock *blk;
- TextLine *line;
- TextWord *word;
- double xMin0, xMax0, yMin0, yMax0;
- double xMin1, xMax1, yMin1, yMax1;
+ TextChar *ch;
+ double xMin2, yMin2, xMax2, yMax2;
GBool first;
- int i, j0, j1;
-
- if (rawOrder) {
- return gFalse;
- }
+ int i;
//~ this doesn't correctly handle ranges split across multiple lines
//~ (the highlighted region is the bounding box of all the parts of
//~ the range)
+
+ xMin2 = yMin2 = xMax2 = yMax2 = 0;
first = gTrue;
- xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
- xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
- for (i = 0; i < nBlocks; ++i) {
- blk = blocks[i];
- for (line = blk->lines; line; line = line->next) {
- for (word = line->words; word; word = word->next) {
- if (pos < word->charPos[word->len] &&
- pos + length > word->charPos[0]) {
- for (j0 = 0;
- j0 < word->len && pos >= word->charPos[j0 + 1];
- ++j0) ;
- for (j1 = word->len - 1;
- j1 > j0 && pos + length <= word->charPos[j1];
- --j1) ;
- switch (line->rot) {
- case 0:
- xMin1 = word->edge[j0];
- xMax1 = word->edge[j1 + 1];
- yMin1 = word->yMin;
- yMax1 = word->yMax;
- break;
- case 1:
- xMin1 = word->xMin;
- xMax1 = word->xMax;
- yMin1 = word->edge[j0];
- yMax1 = word->edge[j1 + 1];
- break;
- case 2:
- xMin1 = word->edge[j1 + 1];
- xMax1 = word->edge[j0];
- yMin1 = word->yMin;
- yMax1 = word->yMax;
- break;
- case 3:
- xMin1 = word->xMin;
- xMax1 = word->xMax;
- yMin1 = word->edge[j1 + 1];
- yMax1 = word->edge[j0];
- break;
- }
- if (first || xMin1 < xMin0) {
- xMin0 = xMin1;
- }
- if (first || xMax1 > xMax0) {
- xMax0 = xMax1;
- }
- if (first || yMin1 < yMin0) {
- yMin0 = yMin1;
- }
- if (first || yMax1 > yMax0) {
- yMax0 = yMax1;
- }
- first = gFalse;
- }
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ if (ch->charPos >= pos && ch->charPos < pos + length) {
+ if (first || ch->xMin < xMin2) {
+ xMin2 = ch->xMin;
+ }
+ if (first || ch->yMin < yMin2) {
+ yMin2 = ch->yMin;
+ }
+ if (first || ch->xMax > xMax2) {
+ xMax2 = ch->xMax;
}
+ if (first || ch->yMax > yMax2) {
+ yMax2 = ch->yMax;
+ }
+ first = gFalse;
}
}
- if (!first) {
- *xMin = xMin0;
- *xMax = xMax0;
- *yMin = yMin0;
- *yMax = yMax0;
- return gTrue;
+ if (first) {
+ return gFalse;
}
- return gFalse;
+ *xMin = xMin2;
+ *yMin = yMin2;
+ *xMax = xMax2;
+ *yMax = yMax2;
+ return gTrue;
}
-void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
- GBool physLayout) {
- UnicodeMap *uMap;
- TextFlow *flow;
- TextBlock *blk;
+TextWordList *TextPage::makeWordList() {
+ TextBlock *tree;
+ GList *columns;
+ TextColumn *col;
+ TextParagraph *par;
TextLine *line;
- TextLineFrag *frags;
TextWord *word;
- int nFrags, fragsSize;
- TextLineFrag *frag;
- char space[8], eol[16], eop[8];
- int spaceLen, eolLen, eopLen;
- GBool pageBreaks;
- GString *s;
- double delta;
- int col, i, j, d, n;
+ GList *words;
+ int rot, colIdx, parIdx, lineIdx, wordIdx;
- // get the output encoding
- if (!(uMap = globalParams->getTextEncoding())) {
- return;
+ rot = rotateChars(chars);
+ tree = splitChars(chars);
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
+ return new TextWordList(new GList());
}
- spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
- eolLen = 0; // make gcc happy
- switch (globalParams->getTextEOL()) {
- case eolUnix:
- eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
- break;
- case eolDOS:
- eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
- eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
- break;
- case eolMac:
- eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
- break;
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
}
- eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
- pageBreaks = globalParams->getTextPageBreaks();
- //~ writing mode (horiz/vert)
-
- // output the page in raw (content stream) order
- if (rawOrder) {
-
- for (word = rawWords; word; word = word->next) {
- s = new GString();
- dumpFragment(word->text, word->len, uMap, s);
- (*outputFunc)(outputStream, s->getCString(), s->getLength());
- delete s;
- if (word->next &&
- fabs(word->next->base - word->base) <
- maxIntraLineDelta * word->fontSize &&
- word->next->xMin >
- word->xMax - minDupBreakOverlap * word->fontSize) {
- if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {
- (*outputFunc)(outputStream, space, spaceLen);
+ words = new GList();
+ 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);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ words->append(word->copy());
}
- } else {
- (*outputFunc)(outputStream, eol, eolLen);
}
}
+ }
- // output the page, maintaining the original physical layout
- } else if (physLayout) {
-
- // collect the line fragments for the page and sort them
- fragsSize = 256;
- frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));
- nFrags = 0;
- for (i = 0; i < nBlocks; ++i) {
- blk = blocks[i];
- for (line = blk->lines; line; line = line->next) {
- if (nFrags == fragsSize) {
- fragsSize *= 2;
- frags = (TextLineFrag *)greallocn(frags,
- fragsSize, sizeof(TextLineFrag));
- }
- frags[nFrags].init(line, 0, line->len);
- frags[nFrags].computeCoords(gTrue);
- ++nFrags;
- }
- }
- qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot);
- i = 0;
- while (i < nFrags) {
- delta = maxIntraLineDelta * frags[i].line->words->fontSize;
- for (j = i+1;
- j < nFrags && fabs(frags[j].base - frags[i].base) < delta;
- ++j) ;
- qsort(frags + i, j - i, sizeof(TextLineFrag),
- &TextLineFrag::cmpXYColumnPrimaryRot);
- i = j;
- }
-
-#if 0 // for debugging
- printf("*** line fragments ***\n");
- for (i = 0; i < nFrags; ++i) {
- frag = &frags[i];
- printf("frag: x=%.2f..%.2f y=%.2f..%.2f base=%.2f '",
- frag->xMin, frag->xMax, frag->yMin, frag->yMax, frag->base);
- for (n = 0; n < frag->len; ++n) {
- fputc(frag->line->text[frag->start + n] & 0xff, stdout);
- }
- printf("'\n");
- }
- printf("\n");
-#endif
+ switch (control.mode) {
+ case textOutReadingOrder:
+ // already in reading order
+ break;
+ case textOutPhysLayout:
+ case textOutTableLayout:
+ case textOutLinePrinter:
+ words->sort(&TextWord::cmpYX);
+ break;
+ case textOutRawOrder:
+ words->sort(&TextWord::cmpCharPos);
+ break;
+ }
- // generate output
- col = 0;
- for (i = 0; i < nFrags; ++i) {
- frag = &frags[i];
+ // this has to be done after sorting with cmpYX
+ unrotateColumns(columns, rot);
+ unrotateWords(words, rot);
- // column alignment
- for (; col < frag->col; ++col) {
- (*outputFunc)(outputStream, space, spaceLen);
- }
+ deleteGList(columns, TextColumn);
- // print the line
- s = new GString();
- col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
- (*outputFunc)(outputStream, s->getCString(), s->getLength());
- delete s;
-
- // print one or more returns if necessary
- if (i == nFrags - 1 ||
- frags[i+1].col < col ||
- fabs(frags[i+1].base - frag->base) >
- maxIntraLineDelta * frag->line->words->fontSize) {
- if (i < nFrags - 1) {
- d = (int)((frags[i+1].base - frag->base) /
- frag->line->words->fontSize);
- if (d < 1) {
- d = 1;
- } else if (d > 5) {
- d = 5;
- }
- } else {
- d = 1;
- }
- for (; d > 0; --d) {
- (*outputFunc)(outputStream, eol, eolLen);
- }
- col = 0;
- }
- }
+ return new TextWordList(words);
+}
- gfree(frags);
+//------------------------------------------------------------------------
+// TextPage: debug
+//------------------------------------------------------------------------
- // output the page, "undoing" the layout
- } else {
- for (flow = flows; flow; flow = flow->next) {
- for (blk = flow->blocks; blk; blk = blk->next) {
- for (line = blk->lines; line; line = line->next) {
- n = line->len;
- if (line->hyphenated && (line->next || blk->next)) {
- --n;
- }
- s = new GString();
- dumpFragment(line->text, n, uMap, s);
- (*outputFunc)(outputStream, s->getCString(), s->getLength());
- delete s;
- if (!line->hyphenated) {
- if (line->next) {
- (*outputFunc)(outputStream, space, spaceLen);
- } else if (blk->next) {
- //~ this is a bit of a kludge - we should really do a more
- //~ intelligent determination of paragraphs
- if (blk->next->lines->words->fontSize ==
- blk->lines->words->fontSize) {
- (*outputFunc)(outputStream, space, spaceLen);
- } else {
- (*outputFunc)(outputStream, eol, eolLen);
- }
- }
- }
- }
- }
- (*outputFunc)(outputStream, eol, eolLen);
- (*outputFunc)(outputStream, eol, eolLen);
- }
- }
+#if 0 //~debug
- // end of page
- if (pageBreaks) {
- (*outputFunc)(outputStream, eop, eopLen);
- }
+void TextPage::dumpChars(GList *charsA) {
+ TextChar *ch;
+ int i;
- uMap->decRefCnt();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ printf("char: U+%04x '%c' xMin=%g yMin=%g xMax=%g yMax=%g fontSize=%g rot=%d\n",
+ ch->c, ch->c & 0xff, ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->fontSize, ch->rot);
+ }
}
-void TextPage::assignColumns(TextLineFrag *frags, int nFrags, GBool oneRot) {
- TextLineFrag *frag0, *frag1;
- int rot, col1, col2, i, j, k;
-
- // all text in the region has the same rotation -- recompute the
- // column numbers based only on the text in the region
- if (oneRot) {
- qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpXYLineRot);
- rot = frags[0].line->rot;
- for (i = 0; i < nFrags; ++i) {
- frag0 = &frags[i];
- col1 = 0;
- for (j = 0; j < i; ++j) {
- frag1 = &frags[j];
- col2 = 0; // make gcc happy
- switch (rot) {
- case 0:
- if (frag0->xMin >= frag1->xMax) {
- col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start]) + 1;
- } else {
- for (k = frag1->start;
- k < frag1->start + frag1->len &&
- frag0->xMin >= 0.5 * (frag1->line->edge[k] +
- frag1->line->edge[k+1]);
- ++k) ;
- col2 = frag1->col +
- frag1->line->col[k] - frag1->line->col[frag1->start];
- }
- break;
- case 1:
- if (frag0->yMin >= frag1->yMax) {
- col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start]) + 1;
- } else {
- for (k = frag1->start;
- k < frag1->start + frag1->len &&
- frag0->yMin >= 0.5 * (frag1->line->edge[k] +
- frag1->line->edge[k+1]);
- ++k) ;
- col2 = frag1->col +
- frag1->line->col[k] - frag1->line->col[frag1->start];
- }
- break;
- case 2:
- if (frag0->xMax <= frag1->xMin) {
- col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start]) + 1;
- } else {
- for (k = frag1->start;
- k < frag1->start + frag1->len &&
- frag0->xMax <= 0.5 * (frag1->line->edge[k] +
- frag1->line->edge[k+1]);
- ++k) ;
- col2 = frag1->col +
- frag1->line->col[k] - frag1->line->col[frag1->start];
- }
- break;
- case 3:
- if (frag0->yMax <= frag1->yMin) {
- col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
- frag1->line->col[frag1->start]) + 1;
- } else {
- for (k = frag1->start;
- k < frag1->start + frag1->len &&
- frag0->yMax <= 0.5 * (frag1->line->edge[k] +
- frag1->line->edge[k+1]);
- ++k) ;
- col2 = frag1->col +
- frag1->line->col[k] - frag1->line->col[frag1->start];
- }
- break;
- }
- if (col2 > col1) {
- col1 = col2;
- }
- }
- frag0->col = col1;
- }
+void TextPage::dumpTree(TextBlock *tree, int indent) {
+ TextChar *ch;
+ int i;
- // the region includes text at different rotations -- use the
- // globally assigned column numbers, offset by the minimum column
- // number (i.e., shift everything over to column 0)
- } else {
- col1 = frags[0].col;
- for (i = 1; i < nFrags; ++i) {
- if (frags[i].col < col1) {
- col1 = frags[i].col;
- }
+ printf("%*sblock: type=%s tag=%s small=%d rot=%d xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ indent, "",
+ tree->type == blkLeaf ? "leaf" :
+ tree->type == blkHorizSplit ? "horiz" : "vert",
+ tree->tag == blkTagMulticolumn ? "multicolumn" :
+ tree->tag == blkTagColumn ? "column" : "line",
+ tree->smallSplit,
+ tree->rot, tree->xMin, tree->yMin, tree->xMax, tree->yMax);
+ if (tree->type == blkLeaf) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ ch = (TextChar *)tree->children->get(i);
+ printf("%*schar: '%c' xMin=%g yMin=%g xMax=%g yMax=%g font=%d.%d\n",
+ indent + 2, "", ch->c & 0xff,
+ ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->font->fontID.num, ch->font->fontID.gen);
}
- for (i = 0; i < nFrags; ++i) {
- frags[i].col -= col1;
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ dumpTree((TextBlock *)tree->children->get(i), indent + 2);
}
}
}
-int TextPage::dumpFragment(Unicode *text, int len, UnicodeMap *uMap,
- GString *s) {
- char lre[8], rle[8], popdf[8], buf[8];
- int lreLen, rleLen, popdfLen, n;
- int nCols, i, j, k;
-
- nCols = 0;
-
- if (uMap->isUnicode()) {
-
- lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
- rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
- popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));
-
- if (primaryLR) {
-
- i = 0;
- while (i < len) {
- // output a left-to-right section
- for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ;
- for (k = i; k < j; ++k) {
- n = uMap->mapUnicode(text[k], buf, sizeof(buf));
- s->append(buf, n);
- ++nCols;
- }
- i = j;
- // output a right-to-left section
- for (j = i;
- j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
- ++j) ;
- if (j > i) {
- s->append(rle, rleLen);
- for (k = j - 1; k >= i; --k) {
- n = uMap->mapUnicode(text[k], buf, sizeof(buf));
- s->append(buf, n);
- ++nCols;
- }
- s->append(popdf, popdfLen);
- i = j;
- }
- }
-
- } else {
-
- // Note: This code treats numeric characters (European and
- // Arabic/Indic) as left-to-right, which isn't strictly correct
- // (incurs extra LRE/POPDF pairs), but does produce correct
- // visual formatting.
- s->append(rle, rleLen);
- i = len - 1;
- while (i >= 0) {
- // output a right-to-left section
- for (j = i;
- j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
- --j) ;
- for (k = i; k > j; --k) {
- n = uMap->mapUnicode(text[k], buf, sizeof(buf));
- s->append(buf, n);
- ++nCols;
- }
- i = j;
- // output a left-to-right section
- for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ;
- if (j < i) {
- s->append(lre, lreLen);
- for (k = j + 1; k <= i; ++k) {
- n = uMap->mapUnicode(text[k], buf, sizeof(buf));
- s->append(buf, n);
- ++nCols;
- }
- s->append(popdf, popdfLen);
- i = j;
+void TextPage::dumpColumns(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ int colIdx, parIdx, lineIdx, i;
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ printf("column: xMin=%g yMin=%g xMax=%g yMax=%g px=%d py=%d pw=%d ph=%d\n",
+ col->xMin, col->yMin, col->xMax, col->yMax,
+ col->px, col->py, col->pw, col->ph);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ printf(" paragraph:\n");
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ printf(" line: xMin=%g yMin=%g xMax=%g yMax=%g px=%d pw=%d rot=%d\n",
+ line->xMin, line->yMin, line->xMax, line->yMax,
+ line->px, line->pw, line->rot);
+ printf(" ");
+ for (i = 0; i < line->len; ++i) {
+ printf("%c", line->text[i] & 0xff);
}
+ printf("\n");
}
- s->append(popdf, popdfLen);
-
- }
-
- } else {
- for (i = 0; i < len; ++i) {
- n = uMap->mapUnicode(text[i], buf, sizeof(buf));
- s->append(buf, n);
- nCols += n;
}
}
-
- return nCols;
}
-#if TEXTOUT_WORD_LIST
-TextWordList *TextPage::makeWordList(GBool physLayout) {
- return new TextWordList(this, physLayout);
-}
-#endif
+#endif //~debug
//------------------------------------------------------------------------
// TextOutputDev
@@ -4038,14 +4186,10 @@ static void outputToFile(void *stream, const char *text, int len) {
fwrite(text, 1, len, (FILE *)stream);
}
-TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
- double fixedPitchA, GBool rawOrderA,
+TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append) {
text = NULL;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
+ control = *controlA;
ok = gTrue;
// open file
@@ -4070,28 +4214,21 @@ TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
}
// set up text object
- text = new TextPage(rawOrderA);
+ text = new TextPage(&control);
}
TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA) {
+ TextOutputControl *controlA) {
outputFunc = func;
outputStream = stream;
needClose = gFalse;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
- text = new TextPage(rawOrderA);
+ control = *controlA;
+ text = new TextPage(&control);
ok = gTrue;
}
TextOutputDev::~TextOutputDev() {
if (needClose) {
-#ifdef MACOS
- ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
-#endif
fclose((FILE *)outputStream);
}
if (text) {
@@ -4104,10 +4241,8 @@ void TextOutputDev::startPage(int pageNum, GfxState *state) {
}
void TextOutputDev::endPage() {
- text->endPage();
- text->coalesce(physLayout, fixedPitch, doHTML);
if (outputStream) {
- text->dump(outputStream, outputFunc, physLayout);
+ text->write(outputStream, outputFunc);
}
}
@@ -4129,7 +4264,7 @@ void TextOutputDev::drawChar(GfxState *state, double x, double y,
double dx, double dy,
double originX, double originY,
CharCode c, int nBytes, Unicode *u, int uLen) {
- text->addChar(state, x - originX, y - originY, dx, dy, c, nBytes, u, uLen);
+ text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
}
void TextOutputDev::incCharCount(int nChars) {
@@ -4149,7 +4284,7 @@ void TextOutputDev::stroke(GfxState *state) {
GfxSubpath *subpath;
double x[2], y[2];
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4176,7 +4311,7 @@ void TextOutputDev::fill(GfxState *state) {
double rx0, ry0, rx1, ry1, t;
int i;
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4238,7 +4373,7 @@ void TextOutputDev::fill(GfxState *state) {
}
void TextOutputDev::eoFill(GfxState *state) {
- if (!doHTML) {
+ if (!control.html) {
return;
}
fill(state);
@@ -4248,7 +4383,7 @@ void TextOutputDev::processLink(Link *link) {
double x1, y1, x2, y2;
int xMin, yMin, xMax, yMax, x, y;
- if (!doHTML) {
+ if (!control.html) {
return;
}
link->getRect(&x1, &y1, &x2, &y2);
@@ -4315,16 +4450,14 @@ GBool TextOutputDev::findCharRange(int pos, int length,
return text->findCharRange(pos, length, xMin, yMin, xMax, yMax);
}
-#if TEXTOUT_WORD_LIST
TextWordList *TextOutputDev::makeWordList() {
- return text->makeWordList(physLayout);
+ return text->makeWordList();
}
-#endif
TextPage *TextOutputDev::takeText() {
TextPage *ret;
ret = text;
- text = new TextPage(rawOrder);
+ text = new TextPage(&control);
return ret;
}
diff --git a/xpdf/TextOutputDev.h b/xpdf/TextOutputDev.h
index e3bb26c..4399029 100644
--- a/xpdf/TextOutputDev.h
+++ b/xpdf/TextOutputDev.h
@@ -2,7 +2,7 @@
//
// TextOutputDev.h
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2012 Glyph & Cog, LLC
//
//========================================================================
@@ -20,20 +20,12 @@
#include "GfxFont.h"
#include "OutputDev.h"
-class GString;
class GList;
-class GfxFont;
-class GfxState;
class UnicodeMap;
-class Link;
-class TextWord;
-class TextPool;
-class TextLine;
-class TextLineFrag;
class TextBlock;
-class TextFlow;
-class TextWordList;
+class TextChar;
+class TextLink;
class TextPage;
//------------------------------------------------------------------------
@@ -41,6 +33,37 @@ class TextPage;
typedef void (*TextOutputFunc)(void *stream, const char *text, int len);
//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+enum TextOutputMode {
+ textOutReadingOrder, // format into reading order
+ textOutPhysLayout, // maintain original physical layout
+ textOutTableLayout, // similar to PhysLayout, but optimized
+ // for tables
+ textOutLinePrinter, // strict fixed-pitch/height layout
+ textOutRawOrder // keep text in content stream order
+};
+
+class TextOutputControl {
+public:
+
+ TextOutputControl();
+ ~TextOutputControl() {}
+
+ TextOutputMode mode; // formatting mode
+ double fixedPitch; // if this is non-zero, assume fixed-pitch
+ // characters with this width
+ // (only relevant for PhysLayout, Table,
+ // and LinePrinter modes)
+ double fixedLineSpacing; // fixed line spacing (only relevant for
+ // LinePrinter mode)
+ GBool html; // enable extra processing for HTML
+ GBool clipText; // separate clipped text and add it back
+ // in after forming columns
+};
+
+//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
@@ -52,7 +75,6 @@ public:
GBool matches(GfxState *state);
-#if TEXTOUT_WORD_LIST
// Get the font name (which may be NULL).
GString *getFontName() { return fontName; }
@@ -62,18 +84,21 @@ public:
GBool isSymbolic() { return flags & fontSymbolic; }
GBool isItalic() { return flags & fontItalic; }
GBool isBold() { return flags & fontBold; }
-#endif
+
+ // Get the width of the 'm' character, if available.
+ double getMWidth() { return mWidth; }
private:
- GfxFont *gfxFont;
-#if TEXTOUT_WORD_LIST
+ Ref fontID;
GString *fontName;
int flags;
-#endif
+ double mWidth;
+ double ascent, descent;
- friend class TextWord;
+ friend class TextLine;
friend class TextPage;
+ friend class TextWord;
};
//------------------------------------------------------------------------
@@ -83,44 +108,21 @@ private:
class TextWord {
public:
- // Constructor.
- TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSize);
-
- // Destructor.
+ TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool spaceAfterA);
~TextWord();
-
- // Add a character to the word.
- void addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- Unicode u);
-
- // Merge <word> onto the end of <this>.
- void merge(TextWord *word);
-
- // Compares <this> to <word>, returning -1 (<), 0 (=), or +1 (>),
- // based on a primary-axis comparison, e.g., x ordering if rot=0.
- int primaryCmp(TextWord *word);
-
- // Return the distance along the primary axis between <this> and
- // <word>.
- double primaryDelta(TextWord *word);
-
- static int cmpYX(const void *p1, const void *p2);
+ TextWord *copy() { return new TextWord(this); }
// Get the TextFontInfo object associated with this word.
TextFontInfo *getFontInfo() { return font; }
- // Get the next TextWord on the linked list.
- TextWord *getNext() { return next; }
-
-#if TEXTOUT_WORD_LIST
int getLength() { return len; }
Unicode getChar(int idx) { return text[idx]; }
GString *getText();
GString *getFontName() { return font->fontName; }
void getColor(double *r, double *g, double *b)
{ *r = colorR; *g = colorG; *b = colorB; }
+ GBool isInvisible() { return invisible; }
void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
{ *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
void getCharBBox(int charIdx, double *xMinA, double *yMinA,
@@ -130,76 +132,43 @@ public:
int getCharPos() { return charPos[0]; }
int getCharLen() { return charPos[len] - charPos[0]; }
GBool getSpaceAfter() { return spaceAfter; }
-#endif
-
+ double getBaseline();
GBool isUnderlined() { return underlined; }
- Link *getLink() { return link; }
+ GString *getLinkURI();
private:
+ TextWord(TextWord *word);
+ void appendChar(TextChar *ch);
+ 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
- double base; // baseline x or y coordinate
Unicode *text; // the text
- double *edge; // "near" edge x or y coord of each char
- // (plus one extra entry for the last char)
int *charPos; // character position (within content stream)
// of each char (plus one extra entry for
// the last char)
- int len; // length of text/edge/charPos arrays
- int size; // size of text/edge/charPos arrays
+ double *edge; // "near" edge x or y coord of each char
+ // (plus one extra entry for the last char)
+ int len; // number of characters
TextFontInfo *font; // font information
double fontSize; // font size
GBool spaceAfter; // set if there is a space between this
// word and the next word on the line
- TextWord *next; // next word in line
-#if TEXTOUT_WORD_LIST
+ GBool underlined;
+ TextLink *link;
+
double colorR, // word color
colorG,
colorB;
-#endif
-
- GBool underlined;
- Link *link;
-
- friend class TextPool;
- friend class TextLine;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
- friend class TextPage;
-};
-
-//------------------------------------------------------------------------
-// TextPool
-//------------------------------------------------------------------------
-
-class TextPool {
-public:
-
- TextPool();
- ~TextPool();
-
- TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; }
- void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; }
-
- int getBaseIdx(double base);
-
- void addWord(TextWord *word);
-
-private:
-
- int minBaseIdx; // min baseline bucket index
- int maxBaseIdx; // max baseline bucket index
- TextWord **pool; // array of linked lists, one for each
- // baseline value (multiple of 4 pts)
- TextWord *cursor; // pointer to last-accessed word
- int cursorBaseIdx; // baseline bucket index of last-accessed word
+ GBool invisible; // set for invisible text (render mode 3)
friend class TextBlock;
+ friend class TextLine;
friend class TextPage;
};
@@ -210,168 +179,92 @@ private:
class TextLine {
public:
- TextLine(TextBlock *blkA, int rotA, double baseA);
+ TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA);
~TextLine();
- void addWord(TextWord *word);
-
- // Return the distance along the primary axis between <this> and
- // <line>.
- double primaryDelta(TextLine *line);
-
- // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
- // based on a primary-axis comparison, e.g., x ordering if rot=0.
- int primaryCmp(TextLine *line);
-
- // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
- // based on a secondary-axis comparison of the baselines, e.g., y
- // ordering if rot=0.
- int secondaryCmp(TextLine *line);
-
- int cmpYX(TextLine *line);
-
- static int cmpXY(const void *p1, const void *p2);
-
- void coalesce(UnicodeMap *uMap);
-
- // Get the head of the linked list of TextWords.
- TextWord *getWords() { return words; }
-
- // Get the next TextLine on the linked list.
- TextLine *getNext() { return next; }
-
- // Returns true if the last char of the line is a hyphen.
- GBool isHyphenated() { return hyphenated; }
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getBaseline();
+ int getRotation() { return rot; }
+ GList *getWords() { return words; }
private:
- TextBlock *blk; // parent block
- int rot; // text rotation
+ GList *words; // [TextWord]
+ int rot; // rotation, multiple of 90 degrees
+ // (0, 1, 2, or 3)
double xMin, xMax; // bounding box x coordinates
double yMin, yMax; // bounding box y coordinates
- double base; // baseline x or y coordinate
- TextWord *words; // words in this line
- TextWord *lastWord; // last word in this line
+ double fontSize; // main (max) font size for this line
Unicode *text; // Unicode text of the line, including
// spaces between words
double *edge; // "near" edge x or y coord of each char
// (plus one extra entry for the last char)
- int *col; // starting column number of each Unicode char
int len; // number of Unicode chars
- int convertedLen; // total number of converted characters
GBool hyphenated; // set if last char is a hyphen
- TextLine *next; // next line in block
+ int px; // x offset (in characters, relative to
+ // containing column) in physical layout mode
+ int pw; // line width (in characters) in physical
+ // layout mode
- friend class TextLineFrag;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
friend class TextPage;
+ friend class TextParagraph;
};
//------------------------------------------------------------------------
-// TextBlock
+// TextParagraph
//------------------------------------------------------------------------
-class TextBlock {
+class TextParagraph {
public:
- TextBlock(TextPage *pageA, int rotA);
- ~TextBlock();
-
- void addWord(TextWord *word);
-
- void coalesce(UnicodeMap *uMap, double fixedPitch);
-
- // Update this block's priMin and priMax values, looking at <blk>.
- void updatePriMinMax(TextBlock *blk);
-
- static int cmpXYPrimaryRot(const void *p1, const void *p2);
-
- static int cmpYXPrimaryRot(const void *p1, const void *p2);
-
- int primaryCmp(TextBlock *blk);
+ TextParagraph(GList *linesA);
+ ~TextParagraph();
- double secondaryDelta(TextBlock *blk);
-
- // Returns true if <this> is below <blk>, relative to the page's
- // primary rotation.
- GBool isBelow(TextBlock *blk);
-
- // Get the head of the linked list of TextLines.
- TextLine *getLines() { return lines; }
-
- // Get the next TextBlock on the linked list.
- TextBlock *getNext() { return next; }
+ // Get the list of TextLine objects.
+ GList *getLines() { return lines; }
private:
- TextPage *page; // the parent page
- int rot; // text rotation
+ GList *lines; // [TextLine]
double xMin, xMax; // bounding box x coordinates
double yMin, yMax; // bounding box y coordinates
- double priMin, priMax; // whitespace bounding box along primary axis
-
- TextPool *pool; // pool of words (used only until lines
- // are built)
- TextLine *lines; // linked list of lines
- TextLine *curLine; // most recently added line
- int nLines; // number of lines
- int charCount; // number of characters in the block
- int col; // starting column
- int nColumns; // number of columns in the block
- TextBlock *next;
- TextBlock *stackNext;
-
- friend class TextLine;
- friend class TextLineFrag;
- friend class TextFlow;
- friend class TextWordList;
friend class TextPage;
};
//------------------------------------------------------------------------
-// TextFlow
+// TextColumn
//------------------------------------------------------------------------
-class TextFlow {
+class TextColumn {
public:
- TextFlow(TextPage *pageA, TextBlock *blk);
- ~TextFlow();
-
- // Add a block to the end of this flow.
- void addBlock(TextBlock *blk);
-
- // Returns true if <blk> fits below <prevBlk> in the flow, i.e., (1)
- // it uses a font no larger than the last block added to the flow,
- // and (2) it fits within the flow's [priMin, priMax] along the
- // primary axis.
- GBool blockFits(TextBlock *blk, TextBlock *prevBlk);
+ TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA);
+ ~TextColumn();
- // Get the head of the linked list of TextBlocks.
- TextBlock *getBlocks() { return blocks; }
-
- // Get the next TextFlow on the linked list.
- TextFlow *getNext() { return next; }
+ // Get the list of TextParagraph objects.
+ GList *getParagraphs() { return paragraphs; }
private:
- TextPage *page; // the parent page
+ static int cmpX(const void *p1, const void *p2);
+ static int cmpY(const void *p1, const void *p2);
+ static int cmpPX(const void *p1, const void *p2);
+
+ GList *paragraphs; // [TextParagraph]
double xMin, xMax; // bounding box x coordinates
double yMin, yMax; // bounding box y coordinates
- double priMin, priMax; // whitespace bounding box along primary axis
- TextBlock *blocks; // blocks in flow
- TextBlock *lastBlk; // last block in this flow
- TextFlow *next;
+ int px, py; // x, y position (in characters) in physical
+ // layout mode
+ int pw, ph; // column width, height (in characters) in
+ // physical layout mode
- friend class TextWordList;
friend class TextPage;
};
-#if TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextWordList
//------------------------------------------------------------------------
@@ -379,11 +272,7 @@ private:
class TextWordList {
public:
- // Build a flat word list, in content stream order (if
- // text->rawOrder is true), physical layout order (if <physLayout>
- // is true and text->rawOrder is false), or reading order (if both
- // flags are false).
- TextWordList(TextPage *text, GBool physLayout);
+ TextWordList(GList *wordsA);
~TextWordList();
@@ -398,8 +287,6 @@ private:
GList *words; // [TextWord]
};
-#endif // TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------
@@ -407,52 +294,11 @@ private:
class TextPage {
public:
- // Constructor.
- TextPage(GBool rawOrderA);
-
- // Destructor.
+ TextPage(TextOutputControl *controlA);
~TextPage();
- // Start a new page.
- void startPage(GfxState *state);
-
- // End the current page.
- void endPage();
-
- // Update the current font.
- void updateFont(GfxState *state);
-
- // Begin a new word.
- void beginWord(GfxState *state, double x0, double y0);
-
- // Add a character to the current word.
- void addChar(GfxState *state, double x, double y,
- double dx, double dy,
- CharCode c, int nBytes, Unicode *u, int uLen);
-
- // Add <nChars> invisible characters.
- void incCharCount(int nChars);
-
- // Begin/end an "ActualText" span, where the char indexes are
- // supplied by a marked content operator rather than the text
- // drawing operators.
- void beginActualText(GfxState *state, Unicode *u, int uLen);
- void endActualText(GfxState *state);
-
- // End the current word, sorting it into the list of words.
- void endWord();
-
- // Add a word, sorting it into the list of words.
- void addWord(TextWord *word);
-
- // Add a (potential) underline.
- void addUnderline(double x0, double y0, double x1, double y1);
-
- // Add a hyperlink.
- void addLink(int xMin, int yMin, int xMax, int yMax, Link *link);
-
- // Coalesce strings that look like parts of the same line.
- void coalesce(GBool physLayout, double fixedPitch, GBool doHTML);
+ // Write contents of page to a stream.
+ void write(void *outputStream, TextOutputFunc outputFunc);
// Find a string. If <startAtTop> is true, starts looking at the
// top of the page; else if <startAtLast> is true, starts looking
@@ -480,39 +326,106 @@ public:
double *xMin, double *yMin,
double *xMax, double *yMax);
- // Dump contents of page to a file.
- void dump(void *outputStream, TextOutputFunc outputFunc,
- GBool physLayout);
+ // Create and return a list of TextColumn objects.
+ GList *makeColumns();
- // Get the head of the linked list of TextFlows.
- TextFlow *getFlows() { return flows; }
+ // Get the list of all TextFontInfo objects used on this page.
+ GList *getFonts() { return fonts; }
-#if TEXTOUT_WORD_LIST
- // Build a flat word list, in content stream order (if
- // this->rawOrder is true), physical layout order (if <physLayout>
- // is true and this->rawOrder is false), or reading order (if both
- // flags are false).
- TextWordList *makeWordList(GBool physLayout);
-#endif
+ // Build a flat word list, in the specified ordering.
+ TextWordList *makeWordList();
private:
+ void startPage(GfxState *state);
void clear();
- void assignColumns(TextLineFrag *frags, int nFrags, int rot);
- int dumpFragment(Unicode *text, int len, UnicodeMap *uMap, GString *s);
+ void updateFont(GfxState *state);
+ void addChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode c, int nBytes, Unicode *u, int uLen);
+ void incCharCount(int nChars);
+ void beginActualText(GfxState *state, Unicode *u, int uLen);
+ void endActualText(GfxState *state);
+ void addUnderline(double x0, double y0, double x1, double y1);
+ void addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link);
+
+ // output
+ void writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeLinePrinter(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s);
+
+ // analysis
+ int rotateChars(GList *charsA);
+ void rotateUnderlinesAndLinks(int rot);
+ void unrotateChars(GList *charsA, int rot);
+ void unrotateColumns(GList *columns, int rot);
+ void unrotateWords(GList *words, int rot);
+ GBool checkPrimaryLR(GList *charsA);
+ void removeDuplicates(GList *charsA, int rot);
+ TextBlock *splitChars(GList *charsA);
+ TextBlock *split(GList *charsA, int rot);
+ GList *getChars(GList *charsA, double xMin, double yMin,
+ double xMax, double yMax);
+ void tagBlock(TextBlock *blk);
+ void insertLargeChars(GList *largeChars, TextBlock *blk);
+ void insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk);
+ void insertLargeCharInLeaf(TextChar *ch, TextBlock *blk);
+ void insertIntoTree(TextBlock *subtree, TextBlock *primaryTree);
+ void insertColumnIntoTree(TextBlock *column, TextBlock *tree);
+ void insertClippedChars(GList *clippedChars, TextBlock *tree);
+ TextBlock *findClippedCharLeaf(TextChar *ch, TextBlock *tree);
+ GList *buildColumns(TextBlock *tree);
+ void buildColumns2(TextBlock *blk, GList *columns);
+ TextColumn *buildColumn(TextBlock *tree);
+ double getLineIndent(TextLine *line, TextBlock *blk);
+ double getAverageLineSpacing(GList *lines);
+ double getLineSpacing(TextLine *line0, TextLine *line1);
+ void buildLines(TextBlock *blk, GList *lines);
+ TextLine *buildLine(TextBlock *blk);
+ void getLineChars(TextBlock *blk, GList *charsA);
+ double computeWordSpacingThreshold(GList *charsA, int rot);
+ int assignPhysLayoutPositions(GList *columns);
+ void assignLinePhysPositions(GList *columns);
+ void computeLinePhysWidth(TextLine *line, UnicodeMap *uMap);
+ int assignColumnPhysPositions(GList *columns);
+ void generateUnderlinesAndLinks(GList *columns);
+
+ // debug
+#if 0 //~debug
+ void dumpChars(GList *charsA);
+ void dumpTree(TextBlock *tree, int indent = 0);
+ void dumpColumns(GList *columns);
+#endif
- GBool rawOrder; // keep text in content stream order
+ TextOutputControl control; // formatting parameters
double pageWidth, pageHeight; // width and height of current page
- TextWord *curWord; // currently active string
int charPos; // next character position (within content
// stream)
TextFontInfo *curFont; // current font
double curFontSize; // current font size
- int nest; // current nesting level (for Type 3 fonts)
+ int curRot; // current rotation
int nTinyChars; // number of "tiny" chars seen so far
- GBool lastCharOverlap; // set if the last added char overlapped the
- // previous char
Unicode *actualText; // current "ActualText" span
int actualTextLen;
double actualTextX0,
@@ -521,32 +434,22 @@ private:
actualTextY1;
int actualTextNBytes;
- TextPool *pools[4]; // a "pool" of TextWords for each rotation
- TextFlow *flows; // linked list of flows
- TextBlock **blocks; // array of blocks, in yx order
- int nBlocks; // number of blocks
- int primaryRot; // primary rotation
- GBool primaryLR; // primary direction (true means L-to-R,
- // false means R-to-L)
- TextWord *rawWords; // list of words, in raw order (only if
- // rawOrder is set)
- TextWord *rawLastWord; // last word on rawWords list
-
+ GList *chars; // [TextChar]
GList *fonts; // all font info objects used on this
// page [TextFontInfo]
+ GList *underlines; // [TextUnderline]
+ GList *links; // [TextLink]
+
+ GList *findCols; // text used by the findText function
+ // [TextColumn]
+ GBool findLR; // primary text direction, used by the
+ // findText function
double lastFindXMin, // coordinates of the last "find" result
lastFindYMin;
GBool haveLastFind;
- GList *underlines; // [TextUnderline]
- GList *links; // [TextLink]
-
- friend class TextLine;
- friend class TextLineFrag;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
+ friend class TextOutputDev;
};
//------------------------------------------------------------------------
@@ -561,8 +464,7 @@ public:
// <physLayoutA> is true, the original physical layout of the text
// is maintained. If <rawOrder> is true, the text is kept in
// content stream order.
- TextOutputDev(char *fileName, GBool physLayoutA,
- double fixedPitchA, GBool rawOrderA,
+ TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append);
// Create a TextOutputDev which will write to a generic stream. If
@@ -570,8 +472,7 @@ public:
// is maintained. If <rawOrder> is true, the text is kept in
// content stream order.
TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA);
+ TextOutputControl *controlA);
// Destructor.
virtual ~TextOutputDev();
@@ -660,20 +561,18 @@ public:
double *xMin, double *yMin,
double *xMax, double *yMax);
-#if TEXTOUT_WORD_LIST
// Build a flat word list, in content stream order (if
// this->rawOrder is true), physical layout order (if
// this->physLayout is true and this->rawOrder is false), or reading
// order (if both flags are false).
TextWordList *makeWordList();
-#endif
// Returns the TextPage object for the last rasterized page,
// transferring ownership to the caller.
TextPage *takeText();
// Turn extra processing for HTML conversion on or off.
- void enableHTMLExtras(GBool doHTMLA) { doHTML = doHTMLA; }
+ void enableHTMLExtras(GBool html) { control.html = html; }
private:
@@ -682,13 +581,7 @@ private:
GBool needClose; // need to close the output file?
// (only if outputStream is a FILE*)
TextPage *text; // text for the current page
- GBool physLayout; // maintain original physical layout when
- // dumping text
- double fixedPitch; // if physLayout is true and this is non-zero,
- // assume fixed-pitch characters with this
- // width
- GBool rawOrder; // keep text in content stream order
- GBool doHTML; // extra processing for HTML conversion
+ TextOutputControl control; // formatting parameters
GBool ok; // set up ok?
};
diff --git a/xpdf/TextString.cc b/xpdf/TextString.cc
new file mode 100644
index 0000000..5bc9de2
--- /dev/null
+++ b/xpdf/TextString.cc
@@ -0,0 +1,164 @@
+//========================================================================
+//
+// TextString.cc
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "PDFDocEncoding.h"
+#include "TextString.h"
+
+//------------------------------------------------------------------------
+
+TextString::TextString() {
+ u = NULL;
+ len = size = 0;
+}
+
+TextString::TextString(GString *s) {
+ u = NULL;
+ len = size = 0;
+ append(s);
+}
+
+TextString::TextString(TextString *s) {
+ len = size = s->len;
+ if (len) {
+ u = (Unicode *)gmallocn(size, sizeof(Unicode));
+ memcpy(u, s->u, len * sizeof(Unicode));
+ } else {
+ u = NULL;
+ }
+}
+
+TextString::~TextString() {
+ gfree(u);
+}
+
+TextString *TextString::append(Unicode c) {
+ expand(1);
+ u[len] = c;
+ ++len;
+ return this;
+}
+
+TextString *TextString::append(GString *s) {
+ int n, i;
+
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ n = (s->getLength() - 2) / 2;
+ expand(n);
+ for (i = 0; i < n; ++i) {
+ u[len + i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+ (s->getChar(3 + 2*i) & 0xff);
+ }
+ len += n;
+ } else {
+ n = s->getLength();
+ expand(n);
+ for (i = 0; i < n; ++i) {
+ u[len + i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ len += n;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, Unicode c) {
+ if (idx >= 0 && idx <= len) {
+ expand(1);
+ if (idx < len) {
+ memmove(u + idx + 1, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ u[idx] = c;
+ ++len;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, GString *s) {
+ int n, i;
+
+ if (idx >= 0 && idx <= len) {
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ n = (s->getLength() - 2) / 2;
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ for (i = 0; i < n; ++i) {
+ u[idx + i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+ (s->getChar(3 + 2*i) & 0xff);
+ }
+ len += n;
+ } else {
+ n = s->getLength();
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ for (i = 0; i < n; ++i) {
+ u[idx + i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ len += n;
+ }
+ }
+ return this;
+}
+
+void TextString::expand(int delta) {
+ int newLen;
+
+ newLen = len + delta;
+ if (delta > INT_MAX - len) {
+ // trigger an out-of-memory error
+ size = -1;
+ } else if (newLen <= size) {
+ return;
+ } else if (size > 0 && size <= INT_MAX / 2 && size*2 >= newLen) {
+ size *= 2;
+ } else {
+ size = newLen;
+ }
+ u = (Unicode *)greallocn(u, size, sizeof(Unicode));
+}
+
+GString *TextString::toPDFTextString() {
+ GString *s;
+ GBool useUnicode;
+ int i;
+
+ useUnicode = gFalse;
+ for (i = 0; i < len; ++i) {
+ if (u[i] >= 0x80) {
+ useUnicode = gTrue;
+ break;
+ }
+ }
+ s = new GString();
+ if (useUnicode) {
+ s->append((char)0xfe);
+ s->append((char)0xff);
+ for (i = 0; i < len; ++i) {
+ s->append((char)(u[i] >> 8));
+ s->append((char)u[i]);
+ }
+ } else {
+ for (i = 0; i < len; ++i) {
+ s->append((char)u[i]);
+ }
+ }
+ return s;
+}
diff --git a/xpdf/TextString.h b/xpdf/TextString.h
new file mode 100644
index 0000000..fac15e9
--- /dev/null
+++ b/xpdf/TextString.h
@@ -0,0 +1,66 @@
+//========================================================================
+//
+// TextString.h
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+// Represents a PDF "text string", which can either be a UTF-16BE
+// string (with a leading byte order marker), or an 8-bit string in
+// PDFDocEncoding.
+//
+//========================================================================
+
+#ifndef TEXTSTRING_H
+#define TEXTSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+class TextString {
+public:
+
+ // Create an empty TextString.
+ TextString();
+
+ // Create a TextString from a PDF text string.
+ TextString(GString *s);
+
+ // Copy a TextString.
+ TextString(TextString *s);
+
+ ~TextString();
+
+ // Append a Unicode character or PDF text string to this TextString.
+ TextString *append(Unicode c);
+ TextString *append(GString *s);
+
+ // Insert a Unicode character or PDF text string in this TextString.
+ TextString *insert(int idx, Unicode c);
+ TextString *insert(int idx, GString *s);
+
+ // Get the Unicode characters in the TextString.
+ int getLength() { return len; }
+ Unicode *getUnicode() { return u; }
+
+ // Create a PDF text string from a TextString.
+ GString *toPDFTextString();
+
+private:
+
+ void expand(int delta);
+
+ Unicode *u; // NB: not null-terminated
+ int len;
+ int size;
+};
+
+#endif
diff --git a/xpdf/UnicodeTypeTable.cc b/xpdf/UnicodeTypeTable.cc
index edd2970..34e68dc 100644
--- a/xpdf/UnicodeTypeTable.cc
+++ b/xpdf/UnicodeTypeTable.cc
@@ -2,7 +2,7 @@
//
// UnicodeTypeTable.cc
//
-// Copyright 2004 Glyph & Cog, LLC
+// Copyright 2004-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -20,21 +20,21 @@ struct UnicodeCaseTableVector {
};
static UnicodeMapTableEntry typeTable[256] = {
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N####NNNNLNNNNN####NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N....NNNNLNNNNN..##NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
- { "RRRR#########RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN####################RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
+ { "RRRR.........RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN#################.##RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
{ "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL##LLLLLLLNNNNN", 'X' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL##NNNNNNNNNNNNNN", 'X' },
- { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN#NLLLLL", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL..LLLLLLLNNNNN", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL..NNNNNNNNNNNNNN", 'X' },
+ { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN.NLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN#####LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN.....LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ NULL, 'L' },
@@ -43,7 +43,7 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL#LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'L' },
@@ -52,9 +52,9 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },
- { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN#####NNNNNNNNNNNNNNN#NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L##########NNNL############NNN###################################NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL#LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNN##NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN.....NNNNNNNNNNNNNNN.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L########..NNNL##########..NNN...................................NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL.LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNN..NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
@@ -271,11 +271,11 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ NULL, 'L' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR#RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR.RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
{ NULL, 'R' },
{ "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N#NN#NNNNNNNNN#NN##NNNNN##NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#####NNN##NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N.NN.NNNNNNNNN.NN..NNNNN..NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
+ { "NNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL.....NNN..NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
};
static UnicodeCaseTableVector caseTable00 = {{
@@ -935,13 +935,23 @@ GBool unicodeTypeR(Unicode c) {
}
GBool unicodeTypeNum(Unicode c) {
- return getType(c) == '#';
+ char t;
+
+ t = getType(c);
+ return t == '#' || t == '.';
}
GBool unicodeTypeAlphaNum(Unicode c) {
char t;
t = getType(c);
+ return t == 'L' || t == 'R' || t == '#' || t == '.';
+}
+
+GBool unicodeTypeWord(Unicode c) {
+ char t;
+
+ t = getType(c);
return t == 'L' || t == 'R' || t == '#';
}
diff --git a/xpdf/UnicodeTypeTable.h b/xpdf/UnicodeTypeTable.h
index 879363d..3230d92 100644
--- a/xpdf/UnicodeTypeTable.h
+++ b/xpdf/UnicodeTypeTable.h
@@ -2,7 +2,7 @@
//
// UnicodeTypeTable.h
//
-// Copyright 2003 Glyph & Cog, LLC
+// Copyright 2003-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -19,6 +19,8 @@ extern GBool unicodeTypeNum(Unicode c);
extern GBool unicodeTypeAlphaNum(Unicode c);
+extern GBool unicodeTypeWord(Unicode c);
+
extern Unicode unicodeToUpper(Unicode c);
#endif
diff --git a/xpdf/XFAForm.cc b/xpdf/XFAForm.cc
new file mode 100644
index 0000000..fd420a9
--- /dev/null
+++ b/xpdf/XFAForm.cc
@@ -0,0 +1,1458 @@
+//========================================================================
+//
+// XFAForm.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.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 "XFAForm.h"
+
+#ifdef _WIN32
+# 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 }
+};
+
+//------------------------------------------------------------------------
+// XFAForm
+//------------------------------------------------------------------------
+
+XFAForm *XFAForm::load(PDFDoc *docA, Object *acroFormObj, Object *xfaObj) {
+ XFAForm *xfaForm;
+ ZxDoc *xmlA;
+ ZxElement *tmpl;
+ Object catDict, resourceDictA, obj1;
+ GString *data;
+ GBool fullXFAA;
+ GString *name;
+ char buf[4096];
+ int n, i;
+
+ 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, xmlA, &resourceDictA, fullXFAA);
+
+ resourceDictA.free();
+
+ if (xfaForm->xml->getRoot()) {
+ if ((tmpl = xfaForm->xml->getRoot()->findFirstChildElement("template"))) {
+ name = new GString("form");
+ xfaForm->curPageNum = 1;
+ xfaForm->curXOffset = xfaForm->curYOffset = 0;
+ xfaForm->scanFields(tmpl, name, name);
+ delete name;
+ }
+ }
+
+ return xfaForm;
+}
+
+XFAForm::XFAForm(PDFDoc *docA, ZxDoc *xmlA, Object *resourceDictA,
+ GBool fullXFAA): Form(docA) {
+ xml = xmlA;
+ fields = new GList();
+ resourceDictA->copy(&resourceDict);
+ fullXFA = fullXFAA;
+}
+
+XFAForm::~XFAForm() {
+ delete xml;
+ deleteGList(fields, XFAFormField);
+ resourceDict.free();
+}
+
+void XFAForm::scanFields(ZxElement *elem, GString *name, GString *dataName) {
+ ZxAttr *attr;
+ ZxNode *child;
+ ZxElement *bindElem;
+ GHash *names1, *names2;
+ GString *childName, *fullName, *fullDataName;
+ int i;
+
+ //~ need to handle subform
+
+ //~ need to handle exclGroup
+ //~ - fields in an exclGroup may/must(?) not have names
+ //~ - each field has an items element with the the value when that
+ //~ field is selected
+
+ if (elem->isElement("field")) {
+ fields->append(new XFAFormField(this, elem, name->copy(),
+ dataName->copy(), curPageNum,
+ curXOffset, curYOffset));
+ } else if (elem->isElement("breakBefore")) {
+ if ((attr = elem->findAttr("targetType")) &&
+ !attr->getValue()->cmp("pageArea") &&
+ (attr = elem->findAttr("startNew")) &&
+ !attr->getValue()->cmp("1")) {
+ ++curPageNum;
+ }
+ } else if (elem->isElement("break")) {
+ if ((attr = elem->findAttr("before")) &&
+ !attr->getValue()->cmp("pageArea") &&
+ (attr = elem->findAttr("startNew")) &&
+ !attr->getValue()->cmp("1")) {
+ ++curPageNum;
+ }
+ } else if (elem->isElement("contentArea")) {
+ curXOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0);
+ curYOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0);
+ } else {
+ names1 = new GHash();
+ for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
+ if (child->isElement() &&
+ (attr = ((ZxElement *)child)->findAttr("name"))) {
+ childName = attr->getValue();
+ names1->replace(childName, names1->lookupInt(childName) + 1);
+ }
+ }
+ names2 = new GHash();
+ for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
+ if (child->isElement()) {
+ if (!((bindElem = child->findFirstChildElement("bind")) &&
+ (attr = bindElem->findAttr("match")) &&
+ !attr->getValue()->cmp("none")) &&
+ (attr = ((ZxElement *)child)->findAttr("name"))) {
+ childName = attr->getValue();
+ if (names1->lookupInt(childName) > 1) {
+ i = names2->lookupInt(childName);
+ fullName = GString::format("{0:t}.{1:t}[{2:d}]",
+ name, childName, i);
+ fullDataName = GString::format("{0:t}.{1:t}[{2:d}]",
+ dataName, childName, i);
+ names2->replace(childName, i + 1);
+ } else {
+ fullName = GString::format("{0:t}.{1:t}", name, childName);
+ fullDataName = GString::format("{0:t}.{1:t}", dataName, childName);
+ }
+ } else {
+ fullName = name->copy();
+ fullDataName = dataName->copy();
+ }
+ scanFields((ZxElement *)child, fullName, fullDataName);
+ delete fullName;
+ delete fullDataName;
+ }
+ }
+ delete names1;
+ delete names2;
+ }
+}
+
+void XFAForm::draw(int pageNum, Gfx *gfx, GBool printing) {
+ GfxFontDict *fontDict;
+ Object obj1;
+ int i;
+
+ // build the font dictionary
+ if (resourceDict.isDict() &&
+ resourceDict.dictLookup("Font", &obj1)->isDict()) {
+ fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+ } else {
+ fontDict = NULL;
+ }
+ 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 *dataNameA, int pageNumA,
+ double xOffsetA, double yOffsetA) {
+ xfaForm = xfaFormA;
+ xml = xmlA;
+ name = nameA;
+ dataName = dataNameA;
+ pageNum = pageNumA;
+ xOffset = xOffsetA;
+ yOffset = yOffsetA;
+}
+
+XFAFormField::~XFAFormField() {
+ delete name;
+ delete dataName;
+}
+
+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("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");
+ } else if (node->isElement("barcode")) {
+ s = getFieldValue("text");
+ }
+ //~ 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::draw(int pageNumA, Gfx *gfx, GBool printing,
+ GfxFontDict *fontDict) {
+ Page *page;
+ PDFRectangle *pageRect;
+ ZxElement *uiElem;
+ ZxNode *node;
+ ZxAttr *attr;
+ GString *appearBuf;
+ MemStream *appearStream;
+ Object appearDict, appearance, obj1, obj2;
+ double mat[6];
+ double x, y, w, h, x2, y2, w2, h2, x3, y3, w3, h3;
+ double anchorX, anchorY;
+ int pageRot, rot, rot3;
+
+ if (pageNumA != pageNum) {
+ return;
+ }
+
+ 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;
+ }
+ }
+ x = getMeasurement(xml->findAttr("x"), 0) + xOffset;
+ y = getMeasurement(xml->findAttr("y"), 0) + yOffset;
+ w = getMeasurement(xml->findAttr("w"), 0);
+ h = getMeasurement(xml->findAttr("h"), 0);
+ if ((attr = xml->findAttr("rotate"))) {
+ rot = atoi(attr->getValue()->getCString());
+ if ((rot %= 360) < 0) {
+ rot += 360;
+ }
+ } else {
+ rot = 0;
+ }
+
+ // 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 = x - anchorX * w;
+ y2 = y - anchorY * h;
+ w2 = w;
+ h2 = h;
+ break;
+ case 90:
+ x2 = x - anchorY * h;
+ y2 = y - (1 - anchorX) * w;
+ w2 = h;
+ h2 = w;
+ break;
+ case 180:
+ x2 = x - (1 - anchorX) * w;
+ y2 = y - (1 - anchorY) * h;
+ w2 = w;
+ h2 = h;
+ break;
+ case 270:
+ x2 = x - (1 - anchorY) * h;
+ y2 = y - anchorX * w;
+ w2 = h;
+ h2 = w;
+ break;
+ }
+
+ // convert annot rect to PDF coords (LL corner, width, height),
+ // taking page rotation into account
+ switch (pageRot) {
+ case 0:
+ default:
+ x3 = pageRect->x1 + x2;
+ y3 = pageRect->y2 - (y2 + h2);
+ w3 = w2;
+ h3 = h2;
+ break;
+ case 90:
+ x3 = pageRect->x1 + y2;
+ y3 = pageRect->y1 + x2;
+ w3 = h2;
+ h3 = w2;
+ break;
+ case 180:
+ x3 = pageRect->x2 - (x2 + w2);
+ y3 = pageRect->y1 + y2;
+ w3 = w2;
+ h3 = h2;
+ break;
+ case 270:
+ x3 = pageRect->x2 - (y2 + h2);
+ y3 = pageRect->y1 + (x2 + w2);
+ w3 = h2;
+ h3 = w2;
+ break;
+ }
+ rot3 = (rot + pageRot) % 360;
+
+ // 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] = h; mat[5] = 0;
+ break;
+ case 180:
+ mat[0] = -1; mat[1] = 0;
+ mat[2] = 0; mat[3] = -1;
+ mat[4] = w; mat[5] = h;
+ break;
+ case 270:
+ mat[0] = 0; mat[1] = -1;
+ mat[2] = 1; mat[3] = 0;
+ mat[4] = 0; mat[5] = w;
+ break;
+ }
+
+ // get the appearance stream data
+ appearBuf = new GString();
+#if 0 //~ for debugging
+ appearBuf->appendf("q 1 1 0 rg 0 0 {0:.4f} {1:.4f} re f Q\n", w, h);
+#endif
+ if ((uiElem = xml->findFirstChildElement("ui"))) {
+ for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement("textEdit")) {
+ drawTextEdit(fontDict, w, h, rot3, appearBuf);
+ break;
+ } else if (node->isElement("barcode")) {
+ drawBarCode(fontDict, w, h, rot3, appearBuf);
+ break;
+ }
+ //~ other field types go here
+ }
+ }
+
+ // 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(w));
+ obj1.arrayAdd(obj2.initReal(h));
+ 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);
+ if (xfaForm->resourceDict.isDict()) {
+ appearDict.dictAdd(copyString("Resources"),
+ xfaForm->resourceDict.copy(&obj1));
+ }
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.initStream(appearStream);
+ gfx->drawAnnot(&appearance, NULL, x3, y3, x3 + w3, y3 + h3);
+ appearance.free();
+ delete appearBuf;
+}
+
+void XFAFormField::drawTextEdit(GfxFontDict *fontDict,
+ double w, double h, int rot,
+ GString *appearBuf) {
+ ZxElement *valueElem, *textElem, *uiElem, *textEditElem, *combElem;
+ ZxElement *fontElem, *paraElem;
+ ZxAttr *attr;
+ GString *value, *fontName;
+ double fontSize;
+ int maxChars, combCells;
+ GBool multiLine, bold, italic;
+ XFAHorizAlign hAlign;
+ XFAVertAlign vAlign;
+
+ if (!(value = getFieldValue("text"))) {
+ return;
+ }
+
+ 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 = xml->findFirstChildElement("ui")) &&
+ (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(value, multiLine, combCells,
+ fontName, bold, italic, fontSize,
+ hAlign, vAlign, 0, 0, w, h, gFalse, fontDict, appearBuf);
+ delete fontName;
+}
+
+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, fontSize;
+ double yText, wText, yBarcode, hBarcode, wNarrow, xx;
+ GBool doText;
+ int dataLength;
+ GBool bold, italic;
+ char *p;
+ int i, j, c;
+
+ //--- get field value
+ if (!(value = getFieldValue("text"))) {
+ return;
+ }
+
+ //--- get field attributes
+ barcodeType = NULL;
+ wideNarrowRatio = 3;
+ dataLength = 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, 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("dataLength"))) {
+ dataLength = atoi(attr->getValue()->getCString());
+ }
+ if ((attr = barcodeElem->findAttr("textLocation"))) {
+ textLocation = attr->getValue();
+ }
+ }
+ if (!barcodeType) {
+ error(errSyntaxError, -1, "Missing 'type' attribute in XFA barcode field");
+ return;
+ }
+ if (!dataLength) {
+ error(errSyntaxError, -1,
+ "Missing 'dataLength' attribute in XFA barcode field");
+ 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");
+ }
+
+ //--- 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
+ //~ this may depend on barcode type
+ value2 = value->copy();
+ 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")) {
+ 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 {
+ error(errSyntaxError, -1,
+ "Unimplemented barcode type in XFA barcode field");
+ }
+ //~ 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);
+ }
+ delete fontName;
+ delete value2;
+}
+
+Object *XFAFormField::getResources(Object *res) {
+ return xfaForm->resourceDict.copy(res);
+}
+
+double XFAFormField::getMeasurement(ZxAttr *attr, double defaultVal) {
+ GString *s;
+ double val, mul;
+ GBool neg;
+ int i;
+
+ if (!attr) {
+ return defaultVal;
+ }
+ s = attr->getValue();
+ i = 0;
+ 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, *elem;
+ char *p;
+
+ // 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();
+ }
+ }
+
+ // check the <datasets> packet
+ if (!xfaForm->xml->getRoot() ||
+ !(datasets =
+ xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) ||
+ !(data = datasets->findFirstChildElement("xfa:data"))) {
+ return NULL;
+ }
+ p = name->getCString();
+ if (!strncmp(p, "form.", 5)) {
+ p += 5;
+ } else {
+ return NULL;
+ }
+ elem = findFieldData(data, p);
+ if (elem &&
+ elem->getFirstChild() &&
+ elem->getFirstChild()->isCharData() &&
+ ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
+ return ((ZxCharData *)elem->getFirstChild())->getData();
+ }
+
+ return NULL;
+}
+
+ZxElement *XFAFormField::findFieldData(ZxElement *elem, char *partName) {
+ ZxNode *node;
+ GString *nodeName;
+ 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] == '.') {
+ return findFieldData((ZxElement *)node, partName + n + 1);
+ }
+ }
+ }
+ }
+ 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) {
+ GfxFont *font;
+ GString *s;
+ double xx, yy, tw, charWidth, lineHeight;
+ double rectX, rectY, rectW, rectH;
+ int line, i, j, k, c, rectI;
+
+ //~ deal with Unicode text (is it UTF-8?)
+
+ // find the font
+ if (!(font = findFont(fontDict, fontName, bold, italic))) {
+ 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");
+ return;
+ }
+
+ // setup
+ rectW = rectH = 0;
+ rectI = appearBuf->getLength();
+ appearBuf->append("BT\n");
+ appearBuf->appendf("/{0:t} {1:.2f} Tf\n", font->getTag(), fontSize);
+
+ // multi-line text
+ if (multiLine) {
+
+ // figure out how many lines will fit
+ lineHeight = 1.2 * fontSize;
+
+ // write a series of lines of text
+ line = 0;
+ i = 0;
+ while (i < text->getLength()) {
+
+ getNextLine(text, 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 = y + h - fontSize * font->getAscent() - line * lineHeight;
+
+ // draw the line
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy);
+ appearBuf->append('(');
+ for (; i < j; ++i) {
+ c = text->getChar(i) & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append(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 - text->getLength())) * tw;
+ break;
+ case xfaHAlignRight:
+ xx = x + w - text->getLength() * tw;
+ break;
+ }
+ rectW = text->getLength() * tw;
+ switch (vAlign) {
+ case xfaVAlignTop:
+ default:
+ yy = y + h - fontSize * font->getAscent();
+ break;
+ case xfaVAlignMiddle:
+ yy = y + 0.5 * (h - fontSize * (font->getAscent() +
+ font->getDescent()));
+ break;
+ case xfaVAlignBottom:
+ yy = y - fontSize * font->getDescent();
+ break;
+ }
+ rectY = yy + fontSize * font->getDescent();
+ rectH = fontSize * (font->getAscent() - font->getDescent());
+
+ // write the text string
+ for (i = 0; i < text->getLength(); ++i) {
+ c = text->getChar(i) & 0xff;
+ if (!font->isCIDFont()) {
+ charWidth = fontSize * ((Gfx8BitFont *)font)->getWidth(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(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("{0:.4f} 0 Td\n", w);
+ } else {
+ appearBuf->append(c);
+ }
+ appearBuf->append(") Tj\n");
+ }
+
+ // regular (non-comb) formatting
+ } else {
+
+ // compute string width
+ if (!font->isCIDFont()) {
+ tw = 0;
+ for (i = 0; i < text->getLength(); ++i) {
+ tw += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ tw = text->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 - fontSize * font->getAscent();
+ break;
+ case xfaVAlignMiddle:
+ yy = y + 0.5 * (h - fontSize * (font->getAscent() +
+ font->getDescent()));
+ break;
+ case xfaVAlignBottom:
+ yy = y - fontSize * font->getDescent();
+ break;
+ }
+ rectY = yy + fontSize * font->getDescent();
+ rectH = fontSize * (font->getAscent() - font->getDescent());
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx, yy);
+
+ // write the text string
+ appearBuf->append('(');
+ for (i = 0; i < text->getLength(); ++i) {
+ c = text->getChar(i) & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append(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;
+ 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;
+ }
+}
+
+// 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;
+}
+
+// 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(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;
+}
diff --git a/xpdf/XFAForm.h b/xpdf/XFAForm.h
new file mode 100644
index 0000000..95bf14c
--- /dev/null
+++ b/xpdf/XFAForm.h
@@ -0,0 +1,126 @@
+//========================================================================
+//
+// 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 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, 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, ZxDoc *xmlA, Object *resourceDictA, GBool fullXFAA);
+ void scanFields(ZxElement *elem, GString *name, GString *dataName);
+
+ ZxDoc *xml;
+ GList *fields; // [XFAFormField]
+ Object resourceDict;
+ GBool fullXFA; // true for "Full XFA", false for
+ // "XFA Foreground"
+ 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 *dataNameA, int pageNumA,
+ double xOffsetA, double yOffsetA);
+
+ virtual ~XFAFormField();
+
+ virtual const char *getType();
+ virtual Unicode *getName(int *length);
+ virtual Unicode *getValue(int *length);
+
+ virtual Object *getResources(Object *res);
+
+private:
+
+ Unicode *utf8ToUnicode(GString *s, int *length);
+ void draw(int pageNumA, Gfx *gfx, GBool printing, GfxFontDict *fontDict);
+ void drawTextEdit(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);
+ GString *getFieldValue(const char *valueChildType);
+ ZxElement *findFieldData(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);
+ void getNextLine(GString *text, int start,
+ GfxFont *font, double fontSize, double wMax,
+ int *end, double *width, int *next);
+
+ XFAForm *xfaForm;
+ ZxElement *xml;
+ GString *name;
+ GString *dataName;
+ int pageNum;
+ double xOffset, yOffset;
+
+ friend class XFAForm;
+};
+
+#endif
diff --git a/xpdf/XPDFCore.cc b/xpdf/XPDFCore.cc
index b98bc37..9a3725f 100644
--- a/xpdf/XPDFCore.cc
+++ b/xpdf/XPDFCore.cc
@@ -493,7 +493,7 @@ void XPDFCore::doAction(LinkAction *action) {
}
s = ((LinkGoToR *)action)->getFileName()->getCString();
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = new GString(s);
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -531,7 +531,7 @@ void XPDFCore::doAction(LinkAction *action) {
if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
!strcmp(s + fileName->getLength() - 4, ".PDF")) {
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = fileName->copy();
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -640,7 +640,8 @@ void XPDFCore::doAction(LinkAction *action) {
if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
if (obj1.dictLookup("F", &obj2)) {
if ((fileName = LinkAction::getFileSpecName(&obj2))) {
- if (!isAbsolutePath(fileName->getCString())) {
+ if (!isAbsolutePath(fileName->getCString()) &&
+ doc->getFileName()) {
fileName2 = appendToPath(
grabPath(doc->getFileName()->getCString()),
fileName->getCString());
@@ -658,6 +659,13 @@ void XPDFCore::doAction(LinkAction *action) {
movieAnnot.free();
break;
+ // unsupported action types
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ error(errSyntaxError, -1, "Unsupported link action type");
+ break;
+
// unknown action type
case actionUnknown:
error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
@@ -1124,6 +1132,9 @@ void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
case actionMovie:
s = "[movie]";
break;
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
case actionUnknown:
s = "[unknown link]";
break;
@@ -1356,6 +1367,8 @@ void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
XFillRectangle(display, drawAreaWin, drawAreaGC,
xDest, yDest, width, height);
}
+
+ XFlush(display);
}
void XPDFCore::updateScrollbars() {
diff --git a/xpdf/XPDFViewer.cc b/xpdf/XPDFViewer.cc
index 2de349d..4292a0e 100644
--- a/xpdf/XPDFViewer.cc
+++ b/xpdf/XPDFViewer.cc
@@ -165,6 +165,7 @@ XPDFViewerCmd XPDFViewer::cmdTab[] = {
{ "about", 0, gFalse, gFalse, &XPDFViewer::cmdAbout },
{ "closeOutline", 0, gFalse, gFalse, &XPDFViewer::cmdCloseOutline },
{ "closeWindow", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindow },
+ { "closeWindowOrQuit", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindowOrQuit },
{ "continuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdContinuousMode },
{ "endPan", 0, gTrue, gTrue, &XPDFViewer::cmdEndPan },
{ "endSelection", 0, gTrue, gTrue, &XPDFViewer::cmdEndSelection },
@@ -384,7 +385,9 @@ void XPDFViewer::open(GString *fileName, int pageA, GString *destName) {
int pg;
double z;
- if (!core->getDoc() || fileName->cmp(core->getDoc()->getFileName())) {
+ if (!core->getDoc() ||
+ !core->getDoc()->getFileName() ||
+ fileName->cmp(core->getDoc()->getFileName())) {
if (!loadFile(fileName, NULL, NULL)) {
return;
}
@@ -445,7 +448,7 @@ GBool XPDFViewer::loadFile(GString *fileName, GString *ownerPassword,
void XPDFViewer::reloadFile() {
int pg;
- if (!core->getDoc()) {
+ if (!core->getDoc() || !core->getDoc()->getFileName()) {
return;
}
pg = core->getPageNum();
@@ -808,6 +811,11 @@ void XPDFViewer::cmdCloseWindow(GString *args[], int nArgs,
app->close(this, gFalse);
}
+void XPDFViewer::cmdCloseWindowOrQuit(GString *args[], int nArgs,
+ XEvent *event) {
+ app->close(this, gTrue);
+}
+
void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs,
XEvent *event) {
Widget btn;
@@ -1803,7 +1811,7 @@ void XPDFViewer::initToolbar(Widget parent) {
menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n);
for (i = 0; i < nZoomMenuItems; ++i) {
n = 0;
- s = XmStringCreateLocalized(zoomMenuInfo[i].label);
+ s = XmStringCreateLocalized((char *)zoomMenuInfo[i].label);
XtSetArg(args[n], XmNlabelString, s); ++n;
XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n;
sprintf(buf, "zoom%d", i);
@@ -3422,17 +3430,18 @@ void XPDFViewer::setupPrintDialog() {
doc = core->getDoc();
psFileName = globalParams->getPSFile();
if (!psFileName || psFileName->getChar(0) == '|') {
- pdfFileName = doc->getFileName();
- p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
- if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
- psFileName2 = new GString(pdfFileName->getCString(),
- pdfFileName->getLength() - 4);
- } else {
- psFileName2 = pdfFileName->copy();
+ if ((pdfFileName = doc->getFileName())) {
+ p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
+ if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
+ psFileName2 = new GString(pdfFileName->getCString(),
+ pdfFileName->getLength() - 4);
+ } else {
+ psFileName2 = pdfFileName->copy();
+ }
+ psFileName2->append(".ps");
+ XmTextFieldSetString(printFileText, psFileName2->getCString());
+ delete psFileName2;
}
- psFileName2->append(".ps");
- XmTextFieldSetString(printFileText, psFileName2->getCString());
- delete psFileName2;
}
if (psFileName && psFileName->getChar(0) == '|') {
XmToggleButtonSetState(printWithCmdBtn, True, False);
diff --git a/xpdf/XPDFViewer.h b/xpdf/XPDFViewer.h
index 8a345e8..8b03e7f 100644
--- a/xpdf/XPDFViewer.h
+++ b/xpdf/XPDFViewer.h
@@ -103,6 +103,7 @@ private:
void cmdAbout(GString *args[], int nArgs, XEvent *event);
void cmdCloseOutline(GString *args[], int nArgs, XEvent *event);
void cmdCloseWindow(GString *args[], int nArgs, XEvent *event);
+ void cmdCloseWindowOrQuit(GString *args[], int nArgs, XEvent *event);
void cmdContinuousMode(GString *args[], int nArgs, XEvent *event);
void cmdEndPan(GString *args[], int nArgs, XEvent *event);
void cmdEndSelection(GString *args[], int nArgs, XEvent *event);
diff --git a/xpdf/XRef.cc b/xpdf/XRef.cc
index 83322c3..71540a9 100644
--- a/xpdf/XRef.cc
+++ b/xpdf/XRef.cc
@@ -18,6 +18,7 @@
#include <ctype.h>
#include <limits.h>
#include "gmem.h"
+#include "gfile.h"
#include "Object.h"
#include "Stream.h"
#include "Lexer.h"
@@ -43,6 +44,84 @@
#define defPermFlags 0xfffc
//------------------------------------------------------------------------
+// XRefPosSet
+//------------------------------------------------------------------------
+
+class XRefPosSet {
+public:
+
+ XRefPosSet();
+ ~XRefPosSet();
+ void add(GFileOffset pos);
+ GBool check(GFileOffset pos);
+
+private:
+
+ int find(GFileOffset pos);
+
+ GFileOffset *tab;
+ int size;
+ int len;
+};
+
+XRefPosSet::XRefPosSet() {
+ size = 16;
+ len = 0;
+ tab = (GFileOffset *)gmallocn(size, sizeof(GFileOffset));
+}
+
+XRefPosSet::~XRefPosSet() {
+ gfree(tab);
+}
+
+void XRefPosSet::add(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ if (i < len && tab[i] == pos) {
+ return;
+ }
+ if (len == size) {
+ if (size > INT_MAX / 2) {
+ gMemError("Integer overflow in XRefPosSet::add()");
+ }
+ size *= 2;
+ tab = (GFileOffset *)greallocn(tab, size, sizeof(GFileOffset));
+ }
+ if (i < len) {
+ memmove(&tab[i + 1], &tab[i], (len - i) * sizeof(GFileOffset));
+ }
+ tab[i] = pos;
+ ++len;
+}
+
+GBool XRefPosSet::check(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ return i < len && tab[i] == pos;
+}
+
+int XRefPosSet::find(GFileOffset pos) {
+ int a, b, m;
+
+ a = - 1;
+ b = len;
+ // invariant: tab[a] < pos < tab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (tab[m] < pos) {
+ a = m;
+ } else if (tab[m] > pos) {
+ b = m;
+ } else {
+ return m;
+ }
+ }
+ return b;
+}
+
+//------------------------------------------------------------------------
// ObjectStream
//------------------------------------------------------------------------
@@ -134,7 +213,7 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
obj2.free();
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
objNums[i] = obj1.getInt();
offsets[i] = obj2.getInt();
@@ -144,7 +223,7 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
(i > 0 && offsets[i] < offsets[i-1])) {
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
}
while (str->getChar() != EOF) ;
@@ -153,8 +232,8 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
// skip to the first object - this shouldn't be necessary because
// the First key is supposed to be equal to offsets[0], but just in
// case...
- for (i = first; i < offsets[0]; ++i) {
- objStr.getStream()->getChar();
+ if (i < offsets[0]) {
+ objStr.getStream()->discardChars(offsets[0] - i);
}
// parse the objects
@@ -175,6 +254,8 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
gfree(offsets);
ok = gTrue;
+ err2:
+ objStr.streamClose();
err1:
objStr.free();
}
@@ -203,8 +284,10 @@ Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
//------------------------------------------------------------------------
XRef::XRef(BaseStream *strA, GBool repair) {
- Guint pos;
+ GFileOffset pos;
Object obj;
+ XRefPosSet *posSet;
+ int i;
ok = gTrue;
errCode = errNone;
@@ -213,12 +296,18 @@ XRef::XRef(BaseStream *strA, GBool repair) {
entries = NULL;
streamEnds = NULL;
streamEndsLen = 0;
- objStr = NULL;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ objStrs[i] = NULL;
+ }
encrypted = gFalse;
permFlags = defPermFlags;
ownerPasswordOk = gFalse;
+ for (i = 0; i < xrefCacheSize; ++i) {
+ cache[i].num = -1;
+ }
+
str = strA;
start = str->getStart();
@@ -241,7 +330,9 @@ XRef::XRef(BaseStream *strA, GBool repair) {
}
// read the xref table
- while (readXRef(&pos)) ;
+ posSet = new XRefPosSet();
+ while (readXRef(&pos, posSet)) ;
+ delete posSet;
if (!ok) {
errCode = errDamaged;
return;
@@ -268,30 +359,34 @@ XRef::XRef(BaseStream *strA, GBool repair) {
}
XRef::~XRef() {
+ int i;
+
+ for (i = 0; i < xrefCacheSize; ++i) {
+ if (cache[i].num >= 0) {
+ cache[i].obj.free();
+ }
+ }
gfree(entries);
trailerDict.free();
if (streamEnds) {
gfree(streamEnds);
}
- if (objStr) {
- delete objStr;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ if (objStrs[i]) {
+ delete objStrs[i];
+ }
}
}
// Read the 'startxref' position.
-Guint XRef::getStartXref() {
+GFileOffset XRef::getStartXref() {
char buf[xrefSearchSize+1];
char *p;
- int c, n, i;
+ int n, i;
// read last xrefSearchSize bytes
str->setPos(xrefSearchSize, -1);
- for (n = 0; n < xrefSearchSize; ++n) {
- if ((c = str->getChar()) == EOF) {
- break;
- }
- buf[n] = c;
- }
+ n = str->getBlock(buf, xrefSearchSize);
buf[n] = '\0';
// find startxref
@@ -304,86 +399,123 @@ Guint XRef::getStartXref() {
return 0;
}
for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ;
- lastXRefPos = strToUnsigned(p);
+ lastXRefPos = strToFileOffset(p);
return lastXRefPos;
}
// Read one xref table section. Also reads the associated trailer
// dictionary, and returns the prev pointer (if any).
-GBool XRef::readXRef(Guint *pos) {
+GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) {
Parser *parser;
Object obj;
GBool more;
+ char buf[100];
+ int n, i;
- // start up a parser, parse one token
- obj.initNull();
- parser = new Parser(NULL,
- new Lexer(NULL,
- str->makeSubStream(start + *pos, gFalse, 0, &obj)),
- gTrue);
- parser->getObj(&obj, gTrue);
+ // the xref data should either be "xref ..." (for an xref table) or
+ // "nn gg obj << ... >> stream ..." (for an xref stream); possibly
+ // preceded by whitespace
+ str->setPos(start + *pos);
+ n = str->getBlock(buf, 100);
+ for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
// parse an old-style xref table
- if (obj.isCmd("xref")) {
- obj.free();
- more = readXRefTable(parser, pos);
+ if (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 (obj.isInt()) {
+ } else if (i < n && buf[i] >= '0' && buf[i] <= '9') {
+ 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;
+ }
obj.free();
if (!parser->getObj(&obj, gTrue)->isInt()) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj)->isStream()) {
- goto err1;
+ goto err2;
}
more = readXRefStream(obj.getStream(), pos);
obj.free();
+ delete parser;
} else {
goto err1;
}
- delete parser;
return more;
- err1:
+ err2:
obj.free();
delete parser;
+ err1:
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
+GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
XRefEntry entry;
- GBool more;
+ Parser *parser;
Object obj, obj2;
- Guint pos2;
- int first, n, newSize, i;
+ char buf[6];
+ GFileOffset off, pos2;
+ GBool more;
+ int first, n, newSize, gen, i, c;
+
+ if (posSet->check(*pos)) {
+ error(errSyntaxWarning, -1, "Infinite loop in xref table");
+ return gFalse;
+ }
+ posSet->add(*pos);
+
+ str->setPos(start + *pos + offset);
while (1) {
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("trailer")) {
- obj.free();
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 't') {
+ if (str->getBlock(buf, 6) != 6 || memcmp(buf, "railer", 6)) {
+ goto err1;
+ }
break;
}
- if (!obj.isInt()) {
+ if (c < '0' || c > '9') {
goto err1;
}
- first = obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ first = 0;
+ do {
+ first = (first * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- n = obj.getInt();
- obj.free();
- if (first < 0 || n < 0 || first + n < 0) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ n = 0;
+ do {
+ n = (n * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (first < 0 || n < 0 || first > INT_MAX - n) {
goto err1;
}
if (first + n > size) {
@@ -395,32 +527,51 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
}
for (i = first; i < first + n; ++i) {
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ off = 0;
+ do {
+ off = (off * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.offset = (Guint)obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ entry.offset = off;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ gen = 0;
+ do {
+ gen = (gen * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.gen = obj.getInt();
- obj.free();
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("n")) {
+ entry.gen = gen;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 'n') {
entry.type = xrefEntryUncompressed;
- } else if (obj.isCmd("f")) {
+ } else if (c == 'f') {
entry.type = xrefEntryFree;
} else {
goto err1;
}
- obj.free();
- if (entries[i].offset == 0xffffffff) {
+ c = str->getChar();
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (entries[i].offset == (GFileOffset)-1) {
entries[i] = entry;
// PDF files of patents from the IBM Intellectual Property
// Network have a bug: the xref table claims to start at 1
@@ -430,7 +581,7 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
entries[1].type == xrefEntryFree) {
i = first = 0;
entries[0] = entries[1];
- entries[1].offset = 0xffffffff;
+ entries[1].offset = (GFileOffset)-1;
}
if (i > last) {
last = i;
@@ -440,32 +591,29 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
// read the trailer dictionary
- if (!parser->getObj(&obj)->isDict()) {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(str->getPos(), gFalse, 0, &obj)),
+ gTrue);
+ parser->getObj(&obj);
+ delete parser;
+ if (!obj.isDict()) {
+ obj.free();
goto err1;
}
// get the 'Prev' pointer
+ //~ this can be a 64-bit int (?)
obj.getDict()->lookupNF("Prev", &obj2);
if (obj2.isInt()) {
- pos2 = (Guint)obj2.getInt();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getInt();
+ more = gTrue;
} else if (obj2.isRef()) {
// certain buggy PDF generators generate "/Prev NNN 0 R" instead
// of "/Prev NNN"
- pos2 = (Guint)obj2.getRefNum();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getRefNum();
+ more = gTrue;
} else {
more = gFalse;
}
@@ -477,9 +625,10 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
// check for an 'XRefStm' key
+ //~ this can be a 64-bit int (?)
if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
- pos2 = (Guint)obj2.getInt();
- readXRef(&pos2);
+ pos2 = (GFileOffset)(Guint)obj2.getInt();
+ readXRef(&pos2, posSet);
if (!ok) {
obj2.free();
goto err1;
@@ -491,12 +640,11 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
return more;
err1:
- obj.free();
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
+GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) {
Dict *dict;
int w[3];
GBool more;
@@ -516,7 +664,7 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
if (newSize > size) {
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -533,11 +681,13 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
w[i] = obj2.getInt();
obj2.free();
- if (w[i] < 0 || w[i] > 4) {
- goto err1;
- }
}
obj.free();
+ if (w[0] < 0 || w[0] > 4 ||
+ w[1] < 0 || w[1] > (int)sizeof(GFileOffset) ||
+ w[2] < 0 || w[2] > 4) {
+ goto err0;
+ }
xrefStr->reset();
dict->lookupNF("Index", &idx);
@@ -569,9 +719,10 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
idx.free();
+ //~ this can be a 64-bit int (?)
dict->lookupNF("Prev", &obj);
if (obj.isInt()) {
- *pos = (Guint)obj.getInt();
+ *pos = (GFileOffset)(Guint)obj.getInt();
more = gTrue;
} else {
more = gFalse;
@@ -591,7 +742,7 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
- Guint offset;
+ GFileOffset offset;
int type, gen, c, newSize, i, j;
if (first + n < 0) {
@@ -606,7 +757,7 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -634,7 +785,7 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
}
gen = (gen << 8) + c;
}
- if (entries[i].offset == 0xffffffff) {
+ if (entries[i].offset == (GFileOffset)-1) {
switch (type) {
case 0:
entries[i].offset = offset;
@@ -668,7 +819,7 @@ GBool XRef::constructXRef() {
Parser *parser;
Object newTrailerDict, obj;
char buf[256];
- Guint pos;
+ GFileOffset pos;
int num, gen;
int newSize;
int streamEndsSize;
@@ -748,7 +899,7 @@ GBool XRef::constructXRef() {
entries = (XRefEntry *)
greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -771,15 +922,16 @@ GBool XRef::constructXRef() {
} else if (!strncmp(p, "endstream", 9)) {
if (streamEndsLen == streamEndsSize) {
streamEndsSize += 64;
- streamEnds = (Guint *)greallocn(streamEnds,
- streamEndsSize, sizeof(Guint));
+ streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
+ sizeof(GFileOffset));
}
streamEnds[streamEndsLen++] = pos;
}
}
- if (gotRoot)
+ if (gotRoot) {
return gTrue;
+ }
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
return gFalse;
@@ -824,13 +976,31 @@ GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
XRefEntry *e;
Parser *parser;
+ ObjectStream *objStr;
Object obj1, obj2, obj3;
+ XRefCacheEntry tmp;
+ int i, j;
// check for bogus ref - this can happen in corrupted PDF files
if (num < 0 || num >= size) {
goto err;
}
+ // check the cache
+ if (cache[0].num == num && cache[0].gen == gen) {
+ return cache[0].obj.copy(obj);
+ }
+ for (i = 1; i < xrefCacheSize; ++i) {
+ if (cache[i].num == num && cache[i].gen == gen) {
+ tmp = cache[i];
+ for (j = i; j > 0; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = tmp;
+ return cache[0].obj.copy(obj);
+ }
+ }
+
e = &entries[num];
switch (e->type) {
@@ -869,21 +1039,13 @@ Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
goto err;
}
#endif
- if (e->offset >= (Guint)size ||
+ if (e->offset >= (GFileOffset)size ||
entries[e->offset].type != xrefEntryUncompressed) {
error(errSyntaxError, -1, "Invalid object stream");
goto err;
}
- if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
- if (objStr) {
- delete objStr;
- }
- objStr = new ObjectStream(this, e->offset);
- if (!objStr->isOk()) {
- delete objStr;
- objStr = NULL;
- goto err;
- }
+ if (!(objStr = getObjectStream((int)e->offset))) {
+ goto err;
}
objStr->getObject(e->gen, num, obj);
break;
@@ -892,12 +1054,61 @@ Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
goto err;
}
+ // put the new object in the cache, throwing away the oldest object
+ // currently in the cache
+ if (cache[xrefCacheSize - 1].num >= 0) {
+ cache[xrefCacheSize - 1].obj.free();
+ }
+ for (i = xrefCacheSize - 1; i > 0; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0].num = num;
+ cache[0].gen = gen;
+ obj->copy(&cache[0].obj);
+
return obj;
err:
return obj->initNull();
}
+ObjectStream *XRef::getObjectStream(int objStrNum) {
+ ObjectStream *objStr;
+ int i, j;
+
+ // check the MRU entry in the cache
+ if (objStrs[0] && objStrs[0]->getObjStrNum() == objStrNum) {
+ return objStrs[0];
+ }
+
+ // check the rest of the cache
+ for (i = 1; i < objStrCacheSize; ++i) {
+ if (objStrs[i] && objStrs[i]->getObjStrNum() == objStrNum) {
+ objStr = objStrs[i];
+ for (j = i; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+ }
+ }
+
+ // load a new ObjectStream
+ objStr = new ObjectStream(this, objStrNum);
+ if (!objStr->isOk()) {
+ delete objStr;
+ return NULL;
+ }
+ if (objStrs[objStrCacheSize - 1]) {
+ delete objStrs[objStrCacheSize - 1];
+ }
+ for (j = objStrCacheSize - 1; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+}
+
Object *XRef::getDocInfo(Object *obj) {
return trailerDict.dictLookup("Info", obj);
}
@@ -907,7 +1118,7 @@ Object *XRef::getDocInfoNF(Object *obj) {
return trailerDict.dictLookupNF("Info", obj);
}
-GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
+GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) {
int a, b, m;
if (streamEndsLen == 0 ||
@@ -930,14 +1141,14 @@ GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
return gTrue;
}
-Guint XRef::strToUnsigned(char *s) {
- Guint x, d;
+GFileOffset XRef::strToFileOffset(char *s) {
+ GFileOffset x, d;
char *p;
x = 0;
for (p = s; *p && isdigit(*p & 0xff); ++p) {
d = *p - '0';
- if (x > (UINT_MAX - d) / 10) {
+ if (x > (GFILEOFFSET_MAX - d) / 10) {
break;
}
x = 10 * x + d;
diff --git a/xpdf/XRef.h b/xpdf/XRef.h
index f2c4ac3..0b74dcb 100644
--- a/xpdf/XRef.h
+++ b/xpdf/XRef.h
@@ -16,12 +16,14 @@
#endif
#include "gtypes.h"
+#include "gfile.h"
#include "Object.h"
class Dict;
class Stream;
class Parser;
class ObjectStream;
+class XRefPosSet;
//------------------------------------------------------------------------
// XRef
@@ -34,11 +36,21 @@ enum XRefEntryType {
};
struct XRefEntry {
- Guint offset;
+ GFileOffset offset;
int gen;
XRefEntryType type;
};
+struct XRefCacheEntry {
+ int num;
+ int gen;
+ Object obj;
+};
+
+#define xrefCacheSize 16
+
+#define objStrCacheSize 4
+
class XRef {
public:
@@ -83,7 +95,7 @@ public:
int getNumObjects() { return last + 1; }
// Return the offset of the last xref table.
- Guint getLastXRefPos() { return lastXRefPos; }
+ GFileOffset getLastXRefPos() { return lastXRefPos; }
// Return the catalog object reference.
int getRootNum() { return rootNum; }
@@ -91,7 +103,7 @@ public:
// Get end position for a stream in a damaged file.
// Returns false if unknown or file is not damaged.
- GBool getStreamEnd(Guint streamStart, Guint *streamEnd);
+ GBool getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd);
// Direct access.
int getSize() { return size; }
@@ -101,7 +113,7 @@ public:
private:
BaseStream *str; // input stream
- Guint start; // offset in file (to allow for garbage
+ GFileOffset start; // offset in file (to allow for garbage
// at beginning of file)
XRefEntry *entries; // xref entries
int size; // size of <entries> array
@@ -110,11 +122,12 @@ private:
GBool ok; // true if xref table is valid
int errCode; // error code (if <ok> is false)
Object trailerDict; // trailer dictionary
- Guint lastXRefPos; // offset of last xref table
- Guint *streamEnds; // 'endstream' positions - only used in
+ GFileOffset lastXRefPos; // offset of last xref table
+ GFileOffset *streamEnds; // 'endstream' positions - only used in
// damaged files
int streamEndsLen; // number of valid entries in streamEnds
- ObjectStream *objStr; // cached object stream
+ ObjectStream * // cached object streams
+ objStrs[objStrCacheSize];
GBool encrypted; // true if file is encrypted
int permFlags; // permission bits
GBool ownerPasswordOk; // true if owner password is correct
@@ -122,14 +135,17 @@ private:
int keyLength; // length of key, in bytes
int encVersion; // encryption version
CryptAlgorithm encAlgorithm; // encryption algorithm
+ XRefCacheEntry // cache of recently accessed objects
+ cache[xrefCacheSize];
- Guint getStartXref();
- GBool readXRef(Guint *pos);
- GBool readXRefTable(Parser *parser, Guint *pos);
+ GFileOffset getStartXref();
+ GBool readXRef(GFileOffset *pos, XRefPosSet *posSet);
+ GBool readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet);
GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
- GBool readXRefStream(Stream *xrefStr, Guint *pos);
+ GBool readXRefStream(Stream *xrefStr, GFileOffset *pos);
GBool constructXRef();
- Guint strToUnsigned(char *s);
+ ObjectStream *getObjectStream(int objStrNum);
+ GFileOffset strToFileOffset(char *s);
};
#endif
diff --git a/xpdf/XpdfPluginAPI.cc b/xpdf/XpdfPluginAPI.cc
index 4c51537..d513bec 100644
--- a/xpdf/XpdfPluginAPI.cc
+++ b/xpdf/XpdfPluginAPI.cc
@@ -14,7 +14,7 @@
#include "GlobalParams.h"
#include "Object.h"
#include "PDFDoc.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "WinPDFCore.h"
#else
#include "XPDFCore.h"
diff --git a/xpdf/Zoox.cc b/xpdf/Zoox.cc
new file mode 100644
index 0000000..9524267
--- /dev/null
+++ b/xpdf/Zoox.cc
@@ -0,0 +1,839 @@
+//========================================================================
+//
+// Zoox.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "Zoox.h"
+
+//~ all of this code assumes the encoding is UTF-8 or ASCII or something
+//~ similar (ISO-8859-*)
+
+//------------------------------------------------------------------------
+
+static char nameStartChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+static char nameChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+//------------------------------------------------------------------------
+
+ZxNode::ZxNode() {
+ next = NULL;
+ parent = NULL;
+ firstChild = lastChild = NULL;
+}
+
+ZxNode::~ZxNode() {
+ ZxNode *child;
+
+ while (firstChild) {
+ child = firstChild;
+ firstChild = firstChild->next;
+ delete child;
+ }
+}
+
+ZxElement *ZxNode::findFirstElement(const char *type) {
+ ZxNode *child;
+ ZxElement *result;
+
+ if (isElement(type)) {
+ return (ZxElement *)this;
+ }
+ for (child = firstChild; child; child = child->next) {
+ if ((result = child->findFirstElement(type))) {
+ return result;
+ }
+ }
+ return NULL;
+}
+
+ZxElement *ZxNode::findFirstChildElement(const char *type) {
+ ZxNode *child;
+
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ return (ZxElement *)child;
+ }
+ }
+ return NULL;
+}
+
+GList *ZxNode::findAllElements(const char *type) {
+ GList *results;
+
+ results = new GList();
+ findAllElements(type, results);
+ return results;
+}
+
+void ZxNode::findAllElements(const char *type, GList *results) {
+ ZxNode *child;
+
+ if (isElement(type)) {
+ results->append(this);
+ }
+ for (child = firstChild; child; child = child->next) {
+ child->findAllElements(type, results);
+ }
+}
+
+GList *ZxNode::findAllChildElements(const char *type) {
+ GList *results;
+ ZxNode *child;
+
+ results = new GList();
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ results->append(child);
+ }
+ }
+ return results;
+}
+
+void ZxNode::addChild(ZxNode *child) {
+ if (lastChild) {
+ lastChild->next = child;
+ lastChild = child;
+ } else {
+ firstChild = lastChild = child;
+ }
+ child->parent = this;
+ child->next = NULL;
+}
+
+//------------------------------------------------------------------------
+
+ZxDoc::ZxDoc() {
+ xmlDecl = NULL;
+ docTypeDecl = NULL;
+ root = NULL;
+}
+
+ZxDoc *ZxDoc::loadMem(const char *data, Guint dataLen) {
+ ZxDoc *doc;
+
+ doc = new ZxDoc();
+ if (!doc->parse(data, dataLen)) {
+ delete doc;
+ return NULL;
+ }
+ return doc;
+}
+
+ZxDoc *ZxDoc::loadFile(const char *fileName) {
+ ZxDoc *doc;
+ FILE *f;
+ char *data;
+ Guint dataLen;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ fseek(f, 0, SEEK_END);
+ dataLen = (Guint)ftell(f);
+ if (!dataLen) {
+ fclose(f);
+ return NULL;
+ }
+ fseek(f, 0, SEEK_SET);
+ data = (char *)gmalloc(dataLen);
+ if (fread(data, 1, dataLen, f) != dataLen) {
+ fclose(f);
+ gfree(data);
+ return NULL;
+ }
+ fclose(f);
+ doc = loadMem(data, dataLen);
+ gfree(data);
+ return doc;
+}
+
+ZxDoc::~ZxDoc() {
+}
+
+void ZxDoc::addChild(ZxNode *node) {
+ if (node->isXMLDecl() && !xmlDecl) {
+ xmlDecl = (ZxXMLDecl *)node;
+ } else if (node->isDocTypeDecl() && !docTypeDecl) {
+ docTypeDecl = (ZxDocTypeDecl *)node;
+ } else if (node->isElement() && !root) {
+ root = (ZxElement *)node;
+ }
+ ZxNode::addChild(node);
+}
+
+bool ZxDoc::parse(const char *data, Guint dataLen) {
+ parsePtr = data;
+ parseEnd = data + dataLen;
+
+ parseSpace();
+ parseXMLDecl(this);
+ parseMisc(this);
+ parseDocTypeDecl(this);
+ parseMisc(this);
+ if (match("<")) {
+ parseElement(this);
+ }
+ parseMisc(this);
+ return root != NULL;
+}
+
+void ZxDoc::parseXMLDecl(ZxNode *par) {
+ GString *version, *encoding, *s;
+ bool standalone;
+
+ if (!match("<?xml")) {
+ return;
+ }
+ parsePtr += 5;
+ parseSpace();
+
+ // version
+ version = NULL;
+ if (match("version")) {
+ parsePtr += 7;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ version = parseQuotedString();
+ }
+ }
+ if (!version) {
+ version = new GString("1.0");
+ }
+ parseSpace();
+
+ // encoding
+ encoding = NULL;
+ if (match("encoding")) {
+ parsePtr += 8;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ encoding = parseQuotedString();
+ }
+ }
+ parseSpace();
+
+ // standalone
+ standalone = false;
+ if (match("standalone")) {
+ parsePtr += 10;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ s = parseQuotedString();
+ standalone = !s->cmp("yes");
+ delete s;
+ }
+ }
+ parseSpace();
+
+ if (match("?>")) {
+ parsePtr += 2;
+ }
+
+ par->addChild(new ZxXMLDecl(version, encoding, standalone));
+}
+
+//~ this just skips everything after the name
+void ZxDoc::parseDocTypeDecl(ZxNode *par) {
+ GString *name;
+ int state;
+ char c, quote;
+
+ if (!match("<!DOCTYPE")) {
+ return;
+ }
+ parsePtr += 9;
+ parseSpace();
+
+ name = parseName();
+ parseSpace();
+
+ state = 0;
+ quote = '\0';
+ while (parsePtr < parseEnd && state < 4) {
+ c = *parsePtr++;
+ switch (state) {
+ case 0: // not in square brackets; not in quotes
+ if (c == '>') {
+ state = 4;
+ } else if (c == '"' || c == '\'') {
+ state = 1;
+ } else if (c == '[') {
+ state = 2;
+ }
+ break;
+ case 1: // not in square brackets; in quotes
+ if (c == quote) {
+ state = 0;
+ }
+ break;
+ case 2: // in square brackets; not in quotes
+ if (c == ']') {
+ state = 0;
+ } else if (c == '"' || c == '\'') {
+ state = 3;
+ }
+ break;
+ case 3: // in square brackets; in quotes
+ if (c == quote) {
+ state = 2;
+ }
+ break;
+ }
+ }
+
+ par->addChild(new ZxDocTypeDecl(name));
+}
+
+// assumes match("<")
+void ZxDoc::parseElement(ZxNode *par) {
+ GString *type;
+ ZxElement *elem;
+ ZxAttr *attr;
+
+ ++parsePtr;
+ type = parseName();
+ elem = new ZxElement(type);
+ parseSpace();
+ while ((attr = parseAttr())) {
+ elem->addAttr(attr);
+ parseSpace();
+ }
+ if (match("/>")) {
+ parsePtr += 2;
+ } else if (match(">")) {
+ ++parsePtr;
+ parseContent(elem);
+ }
+ par->addChild(elem);
+}
+
+ZxAttr *ZxDoc::parseAttr() {
+ GString *name, *value;
+ const char *start;
+ char quote, c;
+ int x, n;
+
+ name = parseName();
+ parseSpace();
+ if (!match("=")) {
+ delete name;
+ return NULL;
+ }
+ ++parsePtr;
+ parseSpace();
+ if (!(parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\''))) {
+ delete name;
+ return NULL;
+ }
+ quote = *parsePtr++;
+ value = new GString();
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ value->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ value->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ value->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ value->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ value->append('"');
+ } else {
+ value->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ value->append(start, (int)(parsePtr - start));
+ }
+ }
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ return new ZxAttr(name, value);
+}
+
+// this consumes the end tag
+void ZxDoc::parseContent(ZxElement *par) {
+ GString *endType;
+
+ endType = (new GString("</"))->append(par->getType());
+
+ while (parsePtr < parseEnd) {
+ if (match(endType->getCString())) {
+ parsePtr += endType->getLength();
+ parseSpace();
+ if (match(">")) {
+ ++parsePtr;
+ }
+ break;
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (match("<![CDATA[")) {
+ parseCDSect(par);
+ } else if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<")) {
+ parseElement(par);
+ } else {
+ parseCharData(par);
+ }
+ }
+
+ delete endType;
+}
+
+void ZxDoc::parseCharData(ZxElement *par) {
+ GString *data;
+ const char *start;
+ char c;
+ int x, n;
+
+ data = new GString();
+ while (parsePtr < parseEnd && *parsePtr != '<') {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ data->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ data->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ data->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ data->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ data->append('"');
+ } else {
+ data->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ data->append(start, (int)(parsePtr - start));
+ }
+ }
+ par->addChild(new ZxCharData(data, true));
+}
+
+void ZxDoc::appendUTF8(GString *s, int c) {
+ if (c <= 0x7f) {
+ s->append((char)c);
+ } else if (c <= 0x7ff) {
+ s->append((char)(0xc0 + (c >> 6)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0xffff) {
+ s->append((char)(0xe0 + (c >> 12)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x1fffff) {
+ s->append((char)(0xf0 + (c >> 18)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x3ffffff) {
+ s->append((char)(0xf8 + (c >> 24)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x7fffffff) {
+ s->append((char)(0xfc + (c >> 30)));
+ s->append((char)(0x80 + ((c >> 24) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ }
+}
+
+// assumes match("<![CDATA[")
+void ZxDoc::parseCDSect(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 9;
+ start = parsePtr;
+ while (parsePtr < parseEnd - 3) {
+ if (!strncmp(parsePtr, "]]>", 3)) {
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+}
+
+void ZxDoc::parseMisc(ZxNode *par) {
+ while (1) {
+ if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ } else {
+ break;
+ }
+ }
+}
+
+// assumes match("<!--")
+void ZxDoc::parseComment(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 4;
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 3) {
+ if (!strncmp(parsePtr, "-->", 3)) {
+ par->addChild(new ZxComment(new GString(start, (int)(parsePtr - start))));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+}
+
+// assumes match("<?")
+void ZxDoc::parsePI(ZxNode *par) {
+ GString *target;
+ const char *start;
+
+ parsePtr += 2;
+ target = parseName();
+ parseSpace();
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 2) {
+ if (!strncmp(parsePtr, "?>", 2)) {
+ par->addChild(new ZxPI(target, new GString(start,
+ (int)(parsePtr - start))));
+ parsePtr += 2;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxPI(target, new GString(start, (int)(parsePtr - start))));
+}
+
+//~ this accepts all chars >= 0x80
+//~ this doesn't check for properly-formed UTF-8
+GString *ZxDoc::parseName() {
+ GString *name;
+
+ name = new GString();
+ if (parsePtr < parseEnd && nameStartChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ while (parsePtr < parseEnd && nameChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ }
+ }
+ return name;
+}
+
+GString *ZxDoc::parseQuotedString() {
+ GString *s;
+ const char *start;
+ char quote;
+
+ if (parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\'')) {
+ quote = *parsePtr++;
+ start = parsePtr;
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ ++parsePtr;
+ }
+ s = new GString(start, (int)(parsePtr - start));
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ } else {
+ s = new GString();
+ }
+ return s;
+}
+
+void ZxDoc::parseSpace() {
+ while (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ }
+}
+
+bool ZxDoc::match(const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ return parseEnd - parsePtr >= n && !strncmp(parsePtr, s, n);
+}
+
+//------------------------------------------------------------------------
+
+ZxXMLDecl::ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA) {
+ version = versionA;
+ encoding = encodingA;
+ standalone = standaloneA;
+}
+
+ZxXMLDecl::~ZxXMLDecl() {
+ delete version;
+ if (encoding) {
+ delete encoding;
+ }
+}
+
+//------------------------------------------------------------------------
+
+ZxDocTypeDecl::ZxDocTypeDecl(GString *nameA) {
+ name = nameA;
+}
+
+ZxDocTypeDecl::~ZxDocTypeDecl() {
+ delete name;
+}
+
+//------------------------------------------------------------------------
+
+ZxComment::ZxComment(GString *textA) {
+ text = textA;
+}
+
+ZxComment::~ZxComment() {
+ delete text;
+}
+
+//------------------------------------------------------------------------
+
+ZxPI::ZxPI(GString *targetA, GString *textA) {
+ target = targetA;
+ text = textA;
+}
+
+ZxPI::~ZxPI() {
+ delete target;
+ delete text;
+}
+
+//------------------------------------------------------------------------
+
+ZxElement::ZxElement(GString *typeA) {
+ type = typeA;
+ attrs = new GHash();
+ firstAttr = lastAttr = NULL;
+}
+
+ZxElement::~ZxElement() {
+ delete type;
+ deleteGHash(attrs, ZxAttr);
+}
+
+bool ZxElement::isElement(const char *typeA) {
+ return !type->cmp(typeA);
+}
+
+ZxAttr *ZxElement::findAttr(const char *attrName) {
+ return (ZxAttr *)attrs->lookup(attrName);
+}
+
+void ZxElement::addAttr(ZxAttr *attr) {
+ attrs->add(attr->getName(), attr);
+ if (lastAttr) {
+ lastAttr->next = attr;
+ lastAttr= attr;
+ } else {
+ firstAttr = lastAttr = attr;
+ }
+ attr->parent = this;
+ attr->next = NULL;
+}
+
+//------------------------------------------------------------------------
+
+ZxAttr::ZxAttr(GString *nameA, GString *valueA) {
+ name = nameA;
+ value = valueA;
+ parent = NULL;
+ next = NULL;
+}
+
+ZxAttr::~ZxAttr() {
+ delete name;
+ delete value;
+}
+
+//------------------------------------------------------------------------
+
+ZxCharData::ZxCharData(GString *dataA, bool parsedA) {
+ data = dataA;
+ parsed = parsedA;
+}
+
+ZxCharData::~ZxCharData() {
+ delete data;
+}
diff --git a/xpdf/Zoox.h b/xpdf/Zoox.h
new file mode 100644
index 0000000..091f72e
--- /dev/null
+++ b/xpdf/Zoox.h
@@ -0,0 +1,241 @@
+//========================================================================
+//
+// Zoox.h
+//
+//========================================================================
+
+#ifndef ZOOX_H
+#define ZOOX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+class GList;
+class GHash;
+
+class ZxAttr;
+class ZxDocTypeDecl;
+class ZxElement;
+class ZxXMLDecl;
+
+//------------------------------------------------------------------------
+
+class ZxNode {
+public:
+
+ ZxNode();
+ virtual ~ZxNode();
+
+ virtual bool isDoc() { return false; }
+ virtual bool isXMLDecl() { return false; }
+ virtual bool isDocTypeDecl() { return false; }
+ virtual bool isComment() { return false; }
+ virtual bool isPI() { return false; }
+ virtual bool isElement() { return false; }
+ virtual bool isElement(const char *type) { return false; }
+ virtual bool isCharData() { return false; }
+ virtual ZxNode *getFirstChild() { return firstChild; }
+ virtual ZxNode *getNextChild() { return next; }
+ ZxElement *findFirstElement(const char *type);
+ ZxElement *findFirstChildElement(const char *type);
+ GList *findAllElements(const char *type);
+ GList *findAllChildElements(const char *type);
+ virtual void addChild(ZxNode *child);
+
+protected:
+
+ void findAllElements(const char *type, GList *results);
+
+ ZxNode *next;
+ ZxNode *parent;
+ ZxNode *firstChild,
+ *lastChild;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDoc: public ZxNode {
+public:
+
+ ZxDoc();
+
+ // Parse from memory. Returns NULL on error.
+ static ZxDoc *loadMem(const char *data, Guint dataLen);
+
+ // Parse from disk. Returns NULL on error.
+ static ZxDoc *loadFile(const char *fileName);
+
+ virtual ~ZxDoc();
+
+ virtual bool isDoc() { return true; }
+ ZxXMLDecl *getXMLDecl() { return xmlDecl; }
+ ZxDocTypeDecl *getDocTypeDecl() { return docTypeDecl; }
+ ZxElement *getRoot() { return root; }
+ virtual void addChild(ZxNode *node);
+
+private:
+
+ bool parse(const char *data, Guint dataLen);
+ void parseXMLDecl(ZxNode *par);
+ void parseDocTypeDecl(ZxNode *par);
+ void parseElement(ZxNode *par);
+ ZxAttr *parseAttr();
+ void parseContent(ZxElement *par);
+ void parseCharData(ZxElement *par);
+ void appendUTF8(GString *s, int c);
+ void parseCDSect(ZxNode *par);
+ void parseMisc(ZxNode *par);
+ void parseComment(ZxNode *par);
+ void parsePI(ZxNode *par);
+ GString *parseName();
+ GString *parseQuotedString();
+ void parseSpace();
+ bool match(const char *s);
+
+ ZxXMLDecl *xmlDecl; // may be NULL
+ ZxDocTypeDecl *docTypeDecl; // may be NULL
+ ZxElement *root; // may be NULL
+
+ const char *parsePtr;
+ const char *parseEnd;
+};
+
+//------------------------------------------------------------------------
+
+class ZxXMLDecl: public ZxNode {
+public:
+
+ ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA);
+ virtual ~ZxXMLDecl();
+
+ virtual bool isXMLDecl() { return true; }
+ GString *getVersion() { return version; }
+ GString *getEncoding() { return encoding; }
+ bool getStandalone() { return standalone; }
+
+private:
+
+ GString *version;
+ GString *encoding; // may be NULL
+ bool standalone;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDocTypeDecl: public ZxNode {
+public:
+
+ ZxDocTypeDecl(GString *nameA);
+ virtual ~ZxDocTypeDecl();
+
+ virtual bool isDocTypeDecl() { return true; }
+ GString *getName() { return name; }
+
+private:
+
+ GString *name;
+};
+
+//------------------------------------------------------------------------
+
+class ZxComment: public ZxNode {
+public:
+
+ ZxComment(GString *textA);
+ virtual ~ZxComment();
+
+ virtual bool isComment() { return true; }
+ GString *getText() { return text; }
+
+private:
+
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxPI: public ZxNode {
+public:
+
+ ZxPI(GString *targetA, GString *textA);
+ virtual ~ZxPI();
+
+ virtual bool isPI() { return true; }
+ GString *getTarget() { return target; }
+ GString *getText() { return text; }
+
+private:
+
+ GString *target;
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxElement: public ZxNode {
+public:
+
+ ZxElement(GString *typeA);
+ virtual ~ZxElement();
+
+ virtual bool isElement() { return true; }
+ virtual bool isElement(const char *typeA);
+ GString *getType() { return type; }
+ ZxAttr *findAttr(const char *attrName);
+ ZxAttr *getFirstAttr() { return firstAttr; }
+ void addAttr(ZxAttr *attr);
+
+private:
+
+ GString *type;
+ GHash *attrs; // [ZxAttr]
+ ZxAttr *firstAttr, *lastAttr;
+};
+
+//------------------------------------------------------------------------
+
+class ZxAttr {
+public:
+
+ ZxAttr(GString *nameA, GString *valueA);
+ ~ZxAttr();
+
+ GString *getName() { return name; }
+ GString *getValue() { return value; }
+ ZxAttr *getNextAttr() { return next; }
+
+private:
+
+ GString *name;
+ GString *value;
+ ZxElement *parent;
+ ZxAttr *next;
+
+ friend class ZxElement;
+};
+
+//------------------------------------------------------------------------
+
+class ZxCharData: public ZxNode {
+public:
+
+ ZxCharData(GString *dataA, bool parsedA);
+ virtual ~ZxCharData();
+
+ virtual bool isCharData() { return true; }
+ GString *getData() { return data; }
+ bool isParsed() { return parsed; }
+
+private:
+
+ GString *data; // in UTF-8 format
+ bool parsed;
+};
+
+#endif
diff --git a/xpdf/config.h b/xpdf/config.h
index 998d345..c2b8779 100644
--- a/xpdf/config.h
+++ b/xpdf/config.h
@@ -2,7 +2,7 @@
//
// config.h
//
-// Copyright 1996-2011 Glyph & Cog, LLC
+// Copyright 1996-2014 Glyph & Cog, LLC
//
//========================================================================
@@ -14,13 +14,13 @@
//------------------------------------------------------------------------
// xpdf version
-#define xpdfVersion "3.03"
-#define xpdfVersionNum 3.03
+#define xpdfVersion "3.04"
+#define xpdfVersionNum 3.04
#define xpdfMajorVersion 3
-#define xpdfMinorVersion 3
+#define xpdfMinorVersion 4
#define xpdfUpdateVersion 0
#define xpdfMajorVersionStr "3"
-#define xpdfMinorVersionStr "3"
+#define xpdfMinorVersionStr "4"
#define xpdfUpdateVersionStr "0"
// supported PDF version
@@ -28,11 +28,11 @@
#define supportedPDFVersionNum 1.7
// copyright notice
-#define xpdfCopyright "Copyright 1996-2011 Glyph & Cog, LLC"
+#define xpdfCopyright "Copyright 1996-2014 Glyph & Cog, LLC"
// Windows resource file stuff
-#define winxpdfVersion "WinXpdf 3.03"
-#define xpdfCopyrightAmp "Copyright 1996-2011 Glyph && Cog, LLC"
+#define winxpdfVersion "WinXpdf 3.04"
+#define xpdfCopyrightAmp "Copyright 1996-2014 Glyph && Cog, LLC"
//------------------------------------------------------------------------
// paper size
@@ -52,7 +52,7 @@
//------------------------------------------------------------------------
// user config file name, relative to the user's home directory
-#if defined(VMS) || defined(WIN32)
+#if defined(VMS) || defined(_WIN32)
#define xpdfUserConfigFile "xpdfrc"
#else
#define xpdfUserConfigFile ".xpdfrc"
@@ -83,7 +83,7 @@
#define pclose _pclose
#endif
-#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)
+#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(_WIN32) || defined(__DJGPP__) || defined(MACOS)
#define POPEN_READ_MODE "rb"
#else
#define POPEN_READ_MODE "r"
diff --git a/xpdf/pdfdetach.cc b/xpdf/pdfdetach.cc
index 4be3a48..6d29ff3 100644
--- a/xpdf/pdfdetach.cc
+++ b/xpdf/pdfdetach.cc
@@ -147,7 +147,7 @@ int main(int argc, char *argv[]) {
} else if (saveAll) {
for (i = 0; i < nFiles; ++i) {
if (savePath[0]) {
- n = strlen(savePath);
+ n = (int)strlen(savePath);
if (n > (int)sizeof(path) - 2) {
n = sizeof(path) - 2;
}
diff --git a/xpdf/pdffonts.cc b/xpdf/pdffonts.cc
index 80403a1..e1a0b43 100644
--- a/xpdf/pdffonts.cc
+++ b/xpdf/pdffonts.cc
@@ -22,6 +22,7 @@
#include "Dict.h"
#include "GfxFont.h"
#include "Annot.h"
+#include "Form.h"
#include "PDFDoc.h"
#include "config.h"
@@ -41,11 +42,14 @@ static const char *fontTypeNames[] = {
"CID TrueType (OT)"
};
+static void scanFonts(Object *obj, PDFDoc *doc);
static void scanFonts(Dict *resDict, PDFDoc *doc);
static void scanFont(GfxFont *font, PDFDoc *doc);
static int firstPage = 1;
static int lastPage = 0;
+static GBool showFontLoc = gFalse;
+static GBool showFontLocPS = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static char cfgFileName[256] = "";
@@ -57,6 +61,10 @@ static ArgDesc argDesc[] = {
"first page to examine"},
{"-l", argInt, &lastPage, 0,
"last page to examine"},
+ {"-loc", argFlag, &showFontLoc, 0,
+ "print extended info on font location"},
+ {"-locPS", argFlag, &showFontLocPS, 0,
+ "print extended info on font location for PostScript conversion"},
{"-opw", argString, ownerPassword, sizeof(ownerPassword),
"owner password (for encrypted files)"},
{"-upw", argString, userPassword, sizeof(userPassword),
@@ -80,6 +88,10 @@ static Ref *fonts;
static int fontsLen;
static int fontsSize;
+static Ref *seenObjs;
+static int seenObjsLen;
+static int seenObjsSize;
+
int main(int argc, char *argv[]) {
PDFDoc *doc;
GString *fileName;
@@ -88,8 +100,9 @@ int main(int argc, char *argv[]) {
Page *page;
Dict *resDict;
Annots *annots;
+ Form *form;
Object obj1, obj2;
- int pg, i;
+ int pg, i, j;
int exitCode;
exitCode = 99;
@@ -108,6 +121,7 @@ int main(int argc, char *argv[]) {
// read config file
globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
// open PDF file
if (ownerPassword[0] != '\001') {
@@ -141,10 +155,17 @@ int main(int argc, char *argv[]) {
}
// scan the fonts
- printf("name type emb sub uni object ID\n");
- printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ if (showFontLoc || showFontLocPS) {
+ printf("name type emb sub uni object ID location\n");
+ printf("------------------------------------ ----------------- --- --- --- --------- --------\n");
+ } else {
+ printf("name type emb sub uni object ID\n");
+ printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ }
fonts = NULL;
fontsLen = fontsSize = 0;
+ seenObjs = NULL;
+ seenObjsLen = seenObjsSize = 0;
for (pg = firstPage; pg <= lastPage; ++pg) {
page = doc->getCatalog()->getPage(pg);
if ((resDict = page->getResourceDict())) {
@@ -154,21 +175,35 @@ int main(int argc, char *argv[]) {
obj1.free();
for (i = 0; i < annots->getNumAnnots(); ++i) {
if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
- obj1.streamGetDict()->lookup("Resources", &obj2);
- if (obj2.isDict()) {
- scanFonts(obj2.getDict(), doc);
- }
+ obj1.streamGetDict()->lookupNF("Resources", &obj2);
+ scanFonts(&obj2, doc);
obj2.free();
}
obj1.free();
}
delete annots;
}
+ if ((form = doc->getCatalog()->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGetNF(j, &obj2);
+ scanFonts(&obj2, doc);
+ obj2.free();
+ }
+ } else if (obj1.isDict()) {
+ scanFonts(obj1.getDict(), doc);
+ }
+ obj1.free();
+ }
+ }
exitCode = 0;
// clean up
gfree(fonts);
+ gfree(seenObjs);
err1:
delete doc;
delete globalParams;
@@ -181,8 +216,37 @@ int main(int argc, char *argv[]) {
return exitCode;
}
+static void scanFonts(Object *obj, PDFDoc *doc) {
+ Object obj2;
+ int i;
+
+ if (obj->isRef()) {
+ for (i = 0; i < seenObjsLen; ++i) {
+ if (obj->getRefNum() == seenObjs[i].num &&
+ obj->getRefGen() == seenObjs[i].gen) {
+ return;
+ }
+ }
+ if (seenObjsLen == seenObjsSize) {
+ if (seenObjsSize <= INT_MAX - 32) {
+ seenObjsSize += 32;
+ } else {
+ // let greallocn throw an exception
+ seenObjsSize = -1;
+ }
+ seenObjs = (Ref *)greallocn(seenObjs, seenObjsSize, sizeof(Ref));
+ }
+ seenObjs[seenObjsLen++] = obj->getRef();
+ }
+ if (obj->fetch(doc->getXRef(), &obj2)->isDict()) {
+ scanFonts(obj2.getDict(), doc);
+ }
+ obj2.free();
+}
+
static void scanFonts(Dict *resDict, PDFDoc *doc) {
- Object obj1, obj2, xObjDict, xObj, resObj;
+ Object obj1, obj2, xObjDict, xObj;
+ Object patternDict, pattern, gsDict, gs, smask, smaskGroup, resObj;
Ref r;
GfxFontDict *gfxFontDict;
GfxFont *font;
@@ -211,23 +275,58 @@ static void scanFonts(Dict *resDict, PDFDoc *doc) {
}
obj1.free();
- // recursively scan any resource dictionaries in objects in this
+ // recursively scan any resource dictionaries in XObjects in this
// resource dictionary
resDict->lookup("XObject", &xObjDict);
if (xObjDict.isDict()) {
for (i = 0; i < xObjDict.dictGetLength(); ++i) {
xObjDict.dictGetVal(i, &xObj);
if (xObj.isStream()) {
- xObj.streamGetDict()->lookup("Resources", &resObj);
- if (resObj.isDict()) {
- scanFonts(resObj.getDict(), doc);
- }
+ xObj.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
resObj.free();
}
xObj.free();
}
}
xObjDict.free();
+
+ // recursively scan any resource dictionaries in Patterns in this
+ // resource dictionary
+ resDict->lookup("Pattern", &patternDict);
+ if (patternDict.isDict()) {
+ for (i = 0; i < patternDict.dictGetLength(); ++i) {
+ patternDict.dictGetVal(i, &pattern);
+ if (pattern.isStream()) {
+ pattern.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ pattern.free();
+ }
+ }
+ patternDict.free();
+
+ // recursively scan any resource dictionaries in ExtGStates in this
+ // resource dictionary
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+ }
+ gsDict.free();
}
static void scanFont(GfxFont *font, PDFDoc *doc) {
@@ -235,6 +334,7 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
Object fontObj, toUnicodeObj;
GString *name;
GBool emb, subset, hasToUnicode;
+ GfxFontLoc *loc;
int i;
fontRef = *font->getID();
@@ -284,10 +384,38 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
subset ? "yes" : "no",
hasToUnicode ? "yes" : "no");
if (fontRef.gen >= 100000) {
- printf(" [none]\n");
+ printf(" [none]");
} else {
- printf(" %6d %2d\n", fontRef.num, fontRef.gen);
+ printf(" %6d %2d", fontRef.num, fontRef.gen);
+ }
+ if (showFontLoc || showFontLocPS) {
+ if (font->getType() == fontType3) {
+ printf(" embedded");
+ } else {
+ loc = font->locateFont(doc->getXRef(), showFontLocPS);
+ if (loc) {
+ if (loc->locType == gfxFontLocEmbedded) {
+ printf(" embedded");
+ } else if (loc->locType == gfxFontLocExternal) {
+ if (loc->path) {
+ printf(" external: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ } else if (loc->locType == gfxFontLocResident) {
+ if (loc->path) {
+ printf(" resident: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ }
+ } else {
+ printf(" unknown");
+ }
+ delete loc;
+ }
}
+ printf("\n");
// add this font to the list
if (fontsLen == fontsSize) {
diff --git a/xpdf/pdfinfo.cc b/xpdf/pdfinfo.cc
index ad482ac..9c0493d 100644
--- a/xpdf/pdfinfo.cc
+++ b/xpdf/pdfinfo.cc
@@ -2,7 +2,7 @@
//
// pdfinfo.cc
//
-// Copyright 1998-2003 Glyph & Cog, LLC
+// Copyright 1998-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -16,6 +16,7 @@
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
+#include "gfile.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
@@ -27,7 +28,7 @@
#include "PDFDoc.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Error.h"
#include "config.h"
@@ -239,6 +240,7 @@ int main(int argc, char *argv[]) {
wISO /= sqrt(2.0);
}
}
+ printf(" (rotated %d degrees)", doc->getPageRotate(pg));
printf("\n");
}
@@ -275,16 +277,8 @@ int main(int argc, char *argv[]) {
f = fopen(fileName->getCString(), "rb");
#endif
if (f) {
-#if HAVE_FSEEKO
- fseeko(f, 0, SEEK_END);
- printf("File size: %u bytes\n", (Guint)ftello(f));
-#elif HAVE_FSEEK64
- fseek64(f, 0, SEEK_END);
- printf("File size: %u bytes\n", (Guint)ftell64(f));
-#else
- fseek(f, 0, SEEK_END);
- printf("File size: %d bytes\n", (int)ftell(f));
-#endif
+ gfseek(f, 0, SEEK_END);
+ printf("File size: %u bytes\n", (Guint)gftell(f));
fclose(f);
}
@@ -322,36 +316,21 @@ int main(int argc, char *argv[]) {
static void printInfoString(Dict *infoDict, const char *key, const char *text,
UnicodeMap *uMap) {
Object obj;
- GString *s1;
- GBool isUnicode;
- Unicode u;
+ TextString *s;
+ Unicode *u;
char buf[8];
int i, n;
if (infoDict->lookup(key, &obj)->isString()) {
fputs(text, stdout);
- s1 = obj.getString();
- if ((s1->getChar(0) & 0xff) == 0xfe &&
- (s1->getChar(1) & 0xff) == 0xff) {
- isUnicode = gTrue;
- i = 2;
- } else {
- isUnicode = gFalse;
- i = 0;
- }
- while (i < obj.getString()->getLength()) {
- if (isUnicode) {
- u = ((s1->getChar(i) & 0xff) << 8) |
- (s1->getChar(i+1) & 0xff);
- i += 2;
- } else {
- u = pdfDocEncoding[s1->getChar(i) & 0xff];
- ++i;
- }
- n = uMap->mapUnicode(u, buf, sizeof(buf));
+ s = new TextString(obj.getString());
+ u = s->getUnicode();
+ for (i = 0; i < s->getLength(); ++i) {
+ n = uMap->mapUnicode(u[i], buf, sizeof(buf));
fwrite(buf, 1, n, stdout);
}
fputc('\n', stdout);
+ delete s;
}
obj.free();
}
diff --git a/xpdf/pdftohtml.cc b/xpdf/pdftohtml.cc
new file mode 100644
index 0000000..f1fe691
--- /dev/null
+++ b/xpdf/pdftohtml.cc
@@ -0,0 +1,246 @@
+//========================================================================
+//
+// pdftohtml.cc
+//
+// Copyright 2005 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "parseargs.h"
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "HTMLGen.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "config.h"
+
+//------------------------------------------------------------------------
+
+static GBool createIndex(char *htmlDir);
+
+//------------------------------------------------------------------------
+
+static int firstPage = 1;
+static int lastPage = 0;
+static int resolution = 150;
+static GBool skipInvisible = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to convert"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to convert"},
+ {"-r", argInt, &resolution, 0,
+ "resolution, in DPI (default is 150)"},
+ {"-skipinvisible", argFlag, &skipInvisible, 0,
+ "do not draw invisible text"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+//------------------------------------------------------------------------
+
+static int writeToFile(void *file, const char *data, int size) {
+ return (int)fwrite(data, 1, size, (FILE *)file);
+}
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ GString *fileName;
+ char *htmlDir;
+ GString *ownerPW, *userPW;
+ HTMLGen *htmlGen;
+ GString *htmlFileName, *pngFileName, *pngURL;
+ FILE *htmlFile, *pngFile;
+ int pg, err, exitCode;
+ GBool ok;
+
+ exitCode = 99;
+
+ // parse args
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftohtml version %s\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftohtml", "<PDF-file> <html-dir>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = new GString(argv[1]);
+ htmlDir = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+ globalParams->setupBaseFonts(NULL);
+ globalParams->setTextEncoding("UTF-8");
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // check for copy permission
+ if (!doc->okToCopy()) {
+ error(errNotAllowed, -1,
+ "Copying of text from this document is not allowed.");
+ exitCode = 3;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // create HTML directory
+ if (!createDir(htmlDir, 0755)) {
+ error(errIO, -1, "Couldn't create HTML output directory '{0:s}'",
+ htmlDir);
+ exitCode = 2;
+ goto err1;
+ }
+
+ // set up the HTMLGen object
+ htmlGen = new HTMLGen(resolution);
+ if (!htmlGen->isOk()) {
+ exitCode = 99;
+ goto err1;
+ }
+ htmlGen->setDrawInvisibleText(!skipInvisible);
+ 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"))) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ if (!(pngFile = fopen(pngFileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open PNG file '{0:t}'", pngFileName);
+ fclose(htmlFile);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ pngURL = GString::format("page{0:d}.png", pg);
+ err = htmlGen->convertPage(pg, pngURL->getCString(),
+ &writeToFile, htmlFile,
+ &writeToFile, pngFile);
+ delete pngURL;
+ fclose(htmlFile);
+ fclose(pngFile);
+ delete htmlFileName;
+ delete pngFileName;
+ if (err != errNone) {
+ error(errIO, -1, "Error converting page {0:d}", pg);
+ exitCode = 2;
+ goto err2;
+ }
+ }
+
+ // create the master index
+ if (!createIndex(htmlDir)) {
+ exitCode = 2;
+ goto err2;
+ }
+
+ exitCode = 0;
+
+ // clean up
+ err2:
+ delete htmlGen;
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static GBool createIndex(char *htmlDir) {
+ GString *htmlFileName;
+ FILE *html;
+ int pg;
+
+ htmlFileName = GString::format("{0:s}/index.html", htmlDir);
+ html = fopen(htmlFileName->getCString(), "w");
+ delete htmlFileName;
+ if (!html) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ return gFalse;
+ }
+
+ fprintf(html, "<html>\n");
+ fprintf(html, "<body>\n");
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ fprintf(html, "<a href=\"page%d.html\">page %d</a><br>\n", pg, pg);
+ }
+ fprintf(html, "</body>\n");
+ fprintf(html, "</html>\n");
+
+ fclose(html);
+
+ return gTrue;
+}
diff --git a/xpdf/pdftopng.cc b/xpdf/pdftopng.cc
new file mode 100644
index 0000000..bb91eba
--- /dev/null
+++ b/xpdf/pdftopng.cc
@@ -0,0 +1,289 @@
+//========================================================================
+//
+// pdftopng.cc
+//
+// Copyright 2009 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <png.h>
+#include "parseargs.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "SplashBitmap.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static int resolution = 150;
+static GBool mono = gFalse;
+static GBool gray = gFalse;
+static char enableFreeTypeStr[16] = "";
+static char antialiasStr[16] = "";
+static char vectorAntialiasStr[16] = "";
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to print"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to print"},
+ {"-r", argInt, &resolution, 0,
+ "resolution, in DPI (default is 150)"},
+ {"-mono", argFlag, &mono, 0,
+ "generate a monochrome PBM file"},
+ {"-gray", argFlag, &gray, 0,
+ "generate a grayscale PGM file"},
+#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
+ {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
+ "enable FreeType font rasterizer: yes, no"},
+#endif
+ {"-aa", argString, antialiasStr, sizeof(antialiasStr),
+ "enable font anti-aliasing: yes, no"},
+ {"-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr),
+ "enable vector anti-aliasing: yes, no"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType,
+ SplashBitmap *bitmap);
+static void writePNGData(png_structp png, SplashBitmap *bitmap);
+static void finishPNG(png_structp *png, png_infop *pngInfo);
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ GString *fileName;
+ char *pngRoot;
+ GString *pngFile;
+ GString *ownerPW, *userPW;
+ SplashColor paperColor;
+ SplashOutputDev *splashOut;
+ GBool ok;
+ int exitCode;
+ int pg;
+ png_structp png;
+ png_infop pngInfo;
+ FILE *f;
+
+ exitCode = 99;
+
+ // parse args
+ ok = parseArgs(argDesc, &argc, argv);
+ if (mono && gray) {
+ ok = gFalse;
+ }
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftopng version %s\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftopng", "<PDF-file> <PNG-root>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = new GString(argv[1]);
+ pngRoot = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
+ if (enableFreeTypeStr[0]) {
+ if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
+ fprintf(stderr, "Bad '-freetype' value on command line\n");
+ }
+ }
+ if (antialiasStr[0]) {
+ if (!globalParams->setAntialias(antialiasStr)) {
+ fprintf(stderr, "Bad '-aa' value on command line\n");
+ }
+ }
+ if (vectorAntialiasStr[0]) {
+ if (!globalParams->setVectorAntialias(vectorAntialiasStr)) {
+ fprintf(stderr, "Bad '-aaVector' value on command line\n");
+ }
+ }
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0]) {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0]) {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1)
+ firstPage = 1;
+ if (lastPage < 1 || lastPage > doc->getNumPages())
+ lastPage = doc->getNumPages();
+
+
+ // write PNG files
+ if (mono) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono1, 1, gFalse, paperColor);
+ } else if (gray) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor);
+ } else {
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+ }
+ splashOut->startDoc(doc->getXRef());
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ doc->displayPage(splashOut, pg, resolution, resolution, 0,
+ gFalse, gTrue, gFalse);
+ if (mono) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 1, PNG_COLOR_TYPE_GRAY, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else if (gray) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, PNG_COLOR_TYPE_GRAY, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else { // RGB
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, PNG_COLOR_TYPE_RGB, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ }
+ }
+ delete splashOut;
+
+ exitCode = 0;
+
+ // clean up
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType,
+ SplashBitmap *bitmap) {
+ if (!(*png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(*pngInfo = png_create_info_struct(*png))) {
+ exit(2);
+ }
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_init_io(*png, f);
+ png_set_IHDR(*png, *pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ bitDepth, colorType, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(*png, *pngInfo);
+}
+
+static void writePNGData(png_structp png, SplashBitmap *bitmap) {
+ Guchar *p;
+ int y;
+
+ if (setjmp(png_jmpbuf(png))) {
+ exit(2);
+ }
+ p = bitmap->getDataPtr();
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+}
+
+
+
+static void finishPNG(png_structp *png, png_infop *pngInfo) {
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_write_end(*png, *pngInfo);
+ png_destroy_write_struct(png, pngInfo);
+}
diff --git a/xpdf/pdftoppm.cc b/xpdf/pdftoppm.cc
index cfc7236..17c0bbf 100644
--- a/xpdf/pdftoppm.cc
+++ b/xpdf/pdftoppm.cc
@@ -8,6 +8,14 @@
#include <aconf.h>
#include <stdio.h>
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#endif
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "gmem.h"
#include "GString.h"
@@ -24,7 +32,6 @@ static int lastPage = 0;
static int resolution = 150;
static GBool mono = gFalse;
static GBool gray = gFalse;
-static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -46,10 +53,6 @@ static ArgDesc argDesc[] = {
"generate a monochrome PBM file"},
{"-gray", argFlag, &gray, 0,
"generate a grayscale PGM file"},
-#if HAVE_T1LIB_H
- {"-t1lib", argString, enableT1libStr, sizeof(enableT1libStr),
- "enable t1lib font rasterizer: yes, no"},
-#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -91,6 +94,21 @@ int main(int argc, char *argv[]) {
int exitCode;
int pg;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -112,11 +130,6 @@ int main(int argc, char *argv[]) {
// read config file
globalParams = new GlobalParams(cfgFileName);
globalParams->setupBaseFonts(NULL);
- if (enableT1libStr[0]) {
- if (!globalParams->setEnableT1lib(enableT1libStr)) {
- fprintf(stderr, "Bad '-t1lib' value on command line\n");
- }
- }
if (enableFreeTypeStr[0]) {
if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
fprintf(stderr, "Bad '-freetype' value on command line\n");
@@ -182,6 +195,9 @@ int main(int argc, char *argv[]) {
doc->displayPage(splashOut, pg, resolution, resolution, 0,
gFalse, gTrue, gFalse);
if (!strcmp(ppmRoot, "-")) {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
splashOut->getBitmap()->writePNMFile(stdout);
} else {
ppmFile = GString::format("{0:s}-{1:06d}.{2:s}",
diff --git a/xpdf/pdftops.cc b/xpdf/pdftops.cc
index eae441f..a6f478d 100644
--- a/xpdf/pdftops.cc
+++ b/xpdf/pdftops.cc
@@ -11,6 +11,10 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -147,6 +151,21 @@ int main(int argc, char *argv[]) {
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -216,6 +235,9 @@ int main(int argc, char *argv[]) {
if (noCrop) {
globalParams->setPSCrop(gFalse);
}
+ if (pageCrop) {
+ globalParams->setPSUseCropBoxAsPage(gTrue);
+ }
if (expand) {
globalParams->setPSExpandSmaller(gTrue);
}
@@ -318,15 +340,18 @@ int main(int argc, char *argv[]) {
firstPage, lastPage, mode);
if (psOut->isOk()) {
doc->displayPages(psOut, firstPage, lastPage, 72, 72,
- 0, !pageCrop, globalParams->getPSCrop(), gTrue);
+ 0, !globalParams->getPSUseCropBoxAsPage(),
+ globalParams->getPSCrop(), gTrue);
} else {
delete psOut;
exitCode = 2;
goto err2;
}
- delete psOut;
-
exitCode = 0;
+ if (!psOut->checkIO()) {
+ exitCode = 2;
+ }
+ delete psOut;
// clean up
err2:
diff --git a/xpdf/pdftotext.cc b/xpdf/pdftotext.cc
index 5296ac4..758413e 100644
--- a/xpdf/pdftotext.cc
+++ b/xpdf/pdftotext.cc
@@ -2,7 +2,7 @@
//
// pdftotext.cc
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -11,6 +11,10 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -26,21 +30,19 @@
#include "TextOutputDev.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
+#include "TextString.h"
#include "Error.h"
#include "config.h"
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const char *text2,
- UnicodeMap *uMap);
-static void printInfoDate(FILE *f, Dict *infoDict, const char *key,
- const char *fmt);
-
static int firstPage = 1;
static int lastPage = 0;
static GBool physLayout = gFalse;
-static double fixedPitch = 0;
+static GBool tableLayout = gFalse;
+static GBool linePrinter = gFalse;
static GBool rawOrder = gFalse;
-static GBool htmlMeta = gFalse;
+static double fixedPitch = 0;
+static double fixedLineSpacing = 0;
+static GBool clipText = gFalse;
static char textEncName[128] = "";
static char textEOL[16] = "";
static GBool noPageBreaks = gFalse;
@@ -58,12 +60,18 @@ static ArgDesc argDesc[] = {
"last page to convert"},
{"-layout", argFlag, &physLayout, 0,
"maintain original physical layout"},
- {"-fixed", argFP, &fixedPitch, 0,
- "assume fixed-pitch (or tabular) text"},
+ {"-table", argFlag, &tableLayout, 0,
+ "similar to -layout, but optimized for tables"},
+ {"-lineprinter", argFlag, &linePrinter, 0,
+ "use strict fixed-pitch/height layout"},
{"-raw", argFlag, &rawOrder, 0,
"keep strings in content stream order"},
- {"-htmlmeta", argFlag, &htmlMeta, 0,
- "generate a simple HTML file, including the meta information"},
+ {"-fixed", argFP, &fixedPitch, 0,
+ "assume fixed-pitch (or tabular) text"},
+ {"-linespacing", argFP, &fixedLineSpacing, 0,
+ "fixed line spacing for LinePrinter mode"},
+ {"-clip", argFlag, &clipText, 0,
+ "separate clipped text"},
{"-enc", argString, textEncName, sizeof(textEncName),
"output text encoding name"},
{"-eol", argString, textEOL, sizeof(textEOL),
@@ -96,14 +104,29 @@ int main(int argc, char *argv[]) {
GString *fileName;
GString *textFileName;
GString *ownerPW, *userPW;
+ TextOutputControl textOutControl;
TextOutputDev *textOut;
- FILE *f;
UnicodeMap *uMap;
Object info;
GBool ok;
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -117,9 +140,6 @@ int main(int argc, char *argv[]) {
goto err0;
}
fileName = new GString(argv[1]);
- if (fixedPitch) {
- physLayout = gTrue;
- }
// read config file
globalParams = new GlobalParams(cfgFileName);
@@ -187,7 +207,7 @@ int main(int argc, char *argv[]) {
} else {
textFileName = fileName->copy();
}
- textFileName->append(htmlMeta ? ".html" : ".txt");
+ textFileName->append(".txt");
}
// get page range
@@ -198,50 +218,25 @@ int main(int argc, char *argv[]) {
lastPage = doc->getNumPages();
}
- // write HTML header
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "wb"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- exitCode = 2;
- goto err3;
- }
- }
- fputs("<html>\n", f);
- fputs("<head>\n", f);
- doc->getDocInfo(&info);
- if (info.isDict()) {
- printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n",
- uMap);
- printInfoString(f, info.getDict(), "Subject",
- "<meta name=\"Subject\" content=\"", "\">\n", uMap);
- printInfoString(f, info.getDict(), "Keywords",
- "<meta name=\"Keywords\" content=\"", "\">\n", uMap);
- printInfoString(f, info.getDict(), "Author",
- "<meta name=\"Author\" content=\"", "\">\n", uMap);
- printInfoString(f, info.getDict(), "Creator",
- "<meta name=\"Creator\" content=\"", "\">\n", uMap);
- printInfoString(f, info.getDict(), "Producer",
- "<meta name=\"Producer\" content=\"", "\">\n", uMap);
- printInfoDate(f, info.getDict(), "CreationDate",
- "<meta name=\"CreationDate\" content=\"%s\">\n");
- printInfoDate(f, info.getDict(), "LastModifiedDate",
- "<meta name=\"ModDate\" content=\"%s\">\n");
- }
- info.free();
- fputs("</head>\n", f);
- fputs("<body>\n", f);
- fputs("<pre>\n", f);
- if (f != stdout) {
- fclose(f);
- }
- }
-
// write text file
- textOut = new TextOutputDev(textFileName->getCString(),
- physLayout, fixedPitch, rawOrder, htmlMeta);
+ if (tableLayout) {
+ textOutControl.mode = textOutTableLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (physLayout) {
+ textOutControl.mode = textOutPhysLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (linePrinter) {
+ textOutControl.mode = textOutLinePrinter;
+ textOutControl.fixedPitch = fixedPitch;
+ textOutControl.fixedLineSpacing = fixedLineSpacing;
+ } else if (rawOrder) {
+ textOutControl.mode = textOutRawOrder;
+ } else {
+ textOutControl.mode = textOutReadingOrder;
+ }
+ textOutControl.clipText = clipText;
+ textOut = new TextOutputDev(textFileName->getCString(), &textOutControl,
+ gFalse);
if (textOut->isOk()) {
doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
gFalse, gTrue, gFalse);
@@ -252,25 +247,6 @@ int main(int argc, char *argv[]) {
}
delete textOut;
- // write end of HTML file
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "ab"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- exitCode = 2;
- goto err3;
- }
- }
- fputs("</pre>\n", f);
- fputs("</body>\n", f);
- fputs("</html>\n", f);
- if (f != stdout) {
- fclose(f);
- }
- }
-
exitCode = 0;
// clean up
@@ -289,56 +265,3 @@ int main(int argc, char *argv[]) {
return exitCode;
}
-
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const char *text2,
- UnicodeMap *uMap) {
- Object obj;
- GString *s1;
- GBool isUnicode;
- Unicode u;
- char buf[8];
- int i, n;
-
- if (infoDict->lookup(key, &obj)->isString()) {
- fputs(text1, f);
- s1 = obj.getString();
- if ((s1->getChar(0) & 0xff) == 0xfe &&
- (s1->getChar(1) & 0xff) == 0xff) {
- isUnicode = gTrue;
- i = 2;
- } else {
- isUnicode = gFalse;
- i = 0;
- }
- while (i < obj.getString()->getLength()) {
- if (isUnicode) {
- u = ((s1->getChar(i) & 0xff) << 8) |
- (s1->getChar(i+1) & 0xff);
- i += 2;
- } else {
- u = s1->getChar(i) & 0xff;
- ++i;
- }
- n = uMap->mapUnicode(u, buf, sizeof(buf));
- fwrite(buf, 1, n, f);
- }
- fputs(text2, f);
- }
- obj.free();
-}
-
-static void printInfoDate(FILE *f, Dict *infoDict, const char *key,
- const char *fmt) {
- Object obj;
- char *s;
-
- if (infoDict->lookup(key, &obj)->isString()) {
- s = obj.getString()->getCString();
- if (s[0] == 'D' && s[1] == ':') {
- s += 2;
- }
- fprintf(f, fmt, s);
- }
- obj.free();
-}
diff --git a/xpdf/vms_make.com b/xpdf/vms_make.com
deleted file mode 100644
index f4fb74a..0000000
--- a/xpdf/vms_make.com
+++ /dev/null
@@ -1,129 +0,0 @@
-$!========================================================================
-$!
-$! Xpdf compile script for VMS.
-$!
-$! Written by Patrick Moreau, Martin P.J. Zinser.
-$!
-$! Copyright 1996-2003 Glyph & Cog, LLC
-$!
-$!========================================================================
-$!
-$ i = 0
-$ j = 0
-$ APPS = "XPDF,PDFTOPS,PDFTOTEXT,PDFINFO,PDFTOPBM,PDFIMAGES,PDFFONTS"
-$ if f$search("COMMON.OLB").eqs."" then lib/create common.olb
-$!
-$ COMMON_OBJS = "Annot.obj,Array.obj,BuiltinFont.obj," + -
- "BuiltinFontTables.obj,Catalog.obj,CharCodeToUnicode.obj," + -
- "CMap.obj,Decrypt.obj,Dict.obj,Error.obj," + -
- "FontEncodingTables.obj,FontFile.obj," + -
- "Function.obj,Gfx.obj,GfxFont.obj,GfxState.obj,"+ -
- "GlobalParams.obj,JArithmeticDecoder.obj,JBIG2Stream.obj,"+ -
- "Lexer.obj,Link.obj,NameToCharCode.obj,Object.obj,"+ -
- "Outline.obj,OutputDev.obj,Page.obj,Parser.obj,PDFdoc.obj," + -
- "PDFDocEncoding.obj,PSTokenizer.obj,Stream.obj," + -
- "UnicodeMap.obj,UnicodeTypeTable.obj,XRef.obj"
-$ COMMON_LIBS = "[]common.olb/lib,[-.goo]libgoo.olb/lib"
-$!
-$ XPDF_OBJS = "xpdf.obj,FTFont.obj,PSOutputDev.obj," + -
- "SFont.obj,T1Font.obj,TextOutputDev.obj,TTFont.obj," + -
- "XOutputDev.obj,XPDFApp.obj,XPDFCore.obj,XPDFTree.obj," + -
- "XPDFViewer.obj,XPixmapOutputDev.obj"
-$ XPDF_LIBS = ""
-$!
-$ PDFTOPS_OBJS = "pdftops.obj,PSOutputDev.obj"
-$ PDFTOPS_LIBS = ""
-$!
-$ PDFTOTEXT_OBJS = "pdftotext.obj,TextOutputDev.obj"
-$ PDFTOTEXT_LIBS = ""
-$!
-$ PDFINFO_OBJS = "pdfinfo.obj"
-$ PDFINFO_LIBS = ""
-$!
-$ PDFTOPBM_OBJS = "pdftopbm.obj,FTFont.obj,PBMOutputDev.obj,SFont.obj," + -
- "T1Font.obj,TextOutputDev.obj,TTFont.obj,XOutputDev.obj"
-$ PDFTOPBM_LIBS = ""
-$!
-$ PDFIMAGES_OBJS = "pdfimages.obj,ImageOutputDev.obj"
-$ PDFIMAGES_LIBS = ""
-$!
-$ PDFFONTS_OBJS = "pdffonts.obj"
-$ PDFFONTS_LIBS = ""
-$!
-$COMPILE_CXX_LOOP:
-$ file = f$element(i, ",",COMMON_OBJS)
-$ if file .eqs. "," then goto BUILD_APPS
-$ i = i + 1
-$ name = f$parse(file,,,"NAME")
-$ call make 'file "CXXCOMP ''name'.cc" -
- 'name'.cc
-$ call make common.olb "lib/replace common.olb ''name'.obj" -
- 'name'.obj
-$ goto COMPILE_CXX_LOOP
-$!
-$BUILD_APPS:
-$ curr_app = f$element(j,",",APPS)
-$ if curr_app .eqs. "," then exit
-$ j = j + 1
-$ i = 0
-$COMPILE_APP:
-$ file = f$element(i,",",'curr_app'_OBJS)
-$ if file .eqs. "," then goto LINK_APP
-$ i = i + 1
-$ name = f$parse(file,,,"NAME")
-$ call make 'file "CXXCOMP ''name'.cc" -
- 'name'.cc
-$ goto COMPILE_APP
-$LINK_APP:
-$ if 'curr_app'_LIBS .nes. ""
-$ then
-$ LIBS = 'curr_app'_LIBS + "," + COMMON_LIBS
-$ else
-$ LIBS = COMMON_LIBS
-$ endif
-$ OBJS = 'curr_app'_OBJS
-$ write sys$output "Linking ''curr_app'..."
-$ xpdf_link/exe='curr_app'.exe 'OBJS','libs',[-]xpdf.opt/opt
-$!
-$ goto BUILD_APPS
-$ exit
-$!
-$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES
-$ V = 'F$Verify(0)
-$! P1 = What we are trying to make
-$! P2 = Command to make it
-$! P3 - P8 What it depends on
-$
-$ If F$Search(P1) .Eqs. "" Then Goto Makeit
-$ Time = F$CvTime(F$File(P1,"RDT"))
-$arg=3
-$Loop:
-$ Argument = P'arg
-$ If Argument .Eqs. "" Then Goto Exit
-$ El=0
-$Loop2:
-$ File = F$Element(El," ",Argument)
-$ If File .Eqs. " " Then Goto Endl
-$ AFile = ""
-$Loop3:
-$ OFile = AFile
-$ AFile = F$Search(File)
-$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
-$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
-$ Goto Loop3
-$NextEL:
-$ El = El + 1
-$ Goto Loop2
-$EndL:
-$ arg=arg+1
-$ If arg .Le. 8 Then Goto Loop
-$ Goto Exit
-$
-$Makeit:
-$ VV=F$VERIFY(0)
-$ write sys$output P2
-$ 'P2
-$ VV='F$Verify(VV)
-$Exit:
-$ If V Then Set Verify
-$ENDSUBROUTINE
diff --git a/xpdf/xpdf.cc b/xpdf/xpdf.cc
index e5d91ca..1041265 100644
--- a/xpdf/xpdf.cc
+++ b/xpdf/xpdf.cc
@@ -22,7 +22,6 @@
//------------------------------------------------------------------------
static GBool contView = gFalse;
-static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -66,10 +65,6 @@ static ArgDesc argDesc[] = {
"initial zoom level (percent, 'page', 'width')"},
{"-cont", argFlag, &contView, 0,
"start in continuous view mode" },
-#if HAVE_T1LIB_H
- {"-t1lib", argString, enableT1libStr, sizeof(enableT1libStr),
- "enable t1lib font rasterizer: yes, no"},
-#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -185,11 +180,6 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Bad '-eol' value on command line\n");
}
}
- if (enableT1libStr[0]) {
- if (!globalParams->setEnableT1lib(enableT1libStr)) {
- fprintf(stderr, "Bad '-t1lib' value on command line\n");
- }
- }
if (enableFreeTypeStr[0]) {
if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
fprintf(stderr, "Bad '-freetype' value on command line\n");