summaryrefslogtreecommitdiff
path: root/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc')
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
new file mode 100644
index 00000000000..d7229a2fe86
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
@@ -0,0 +1,1443 @@
+//========================================================================
+//
+// Annot.cc
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "Catalog.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "Lexer.h"
+#include "PDFDoc.h"
+#include "OptionalContent.h"
+#include "AcroForm.h"
+#include "BuiltinFontTables.h"
+#include "FontEncodingTables.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
+
+// 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 *colorA, int nColorCompsA) {
+ type = typeA;
+ width = widthA;
+ dash = dashA;
+ dashLength = dashLengthA;
+ color[0] = colorA[0];
+ color[1] = colorA[1];
+ color[2] = colorA[2];
+ color[3] = colorA[3];
+ nColorComps = nColorCompsA;
+}
+
+AnnotBorderStyle::~AnnotBorderStyle() {
+ if (dash) {
+ gfree(dash);
+ }
+}
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
+ Object apObj, asObj, obj1, obj2, obj3;
+ AnnotBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ int borderDashLength;
+ double borderColor[4];
+ int nBorderColorComps;
+ double t;
+ int i;
+
+ ok = gTrue;
+ doc = docA;
+ xref = doc->getXRef();
+ ref = *refA;
+ type = NULL;
+ appearanceState = NULL;
+ appearBuf = NULL;
+ borderStyle = NULL;
+
+ //----- parse the type
+
+ if (dict->lookup("Subtype", &obj1)->isName()) {
+ type = new GString(obj1.getName());
+ }
+ obj1.free();
+
+ //----- parse the rectangle
+
+ if (dict->lookup("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");
+ ok = gFalse;
+ }
+ obj1.free();
+
+ //----- parse the flags
+
+ if (dict->lookup("F", &obj1)->isInt()) {
+ flags = obj1.getInt();
+ } else {
+ flags = 0;
+ }
+ obj1.free();
+
+ //----- parse the border style
+
+ borderType = annotBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ 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")) {
+ 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 (dict->lookup("Border", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() >= 3) {
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGetLength() >= 4) {
+ if (obj1.arrayGet(3, &obj2)->isArray()) {
+ borderType = 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();
+ }
+ } else {
+ // an empty Border array also means "no border"
+ borderWidth = 0;
+ }
+ }
+ }
+ obj1.free();
+ 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();
+ borderStyle = new AnnotBorderStyle(borderType, borderWidth,
+ borderDash, borderDashLength,
+ borderColor, nBorderColorComps);
+
+ //----- get the appearance state
+
+ dict->lookup("AP", &apObj);
+ dict->lookup("AS", &asObj);
+ 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();
+
+ //----- get the annotation appearance
+
+ if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ apObj.dictLookupNF("N", &obj2);
+ if (obj1.isDict()) {
+ if (obj1.dictLookupNF(appearanceState->getCString(), &obj3)->isRef()) {
+ obj3.copy(&appearance);
+ }
+ obj3.free();
+ } else if (obj2.isRef()) {
+ obj2.copy(&appearance);
+ }
+ obj1.free();
+ obj2.free();
+ }
+ apObj.free();
+
+ //----- get the optional content entry
+
+ dict->lookupNF("OC", &ocObj);
+}
+
+Annot::~Annot() {
+ if (type) {
+ delete type;
+ }
+ if (appearanceState) {
+ delete appearanceState;
+ }
+ appearance.free();
+ if (appearBuf) {
+ delete appearBuf;
+ }
+ if (borderStyle) {
+ delete borderStyle;
+ }
+ ocObj.free();
+}
+
+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();
+ } else if (!type->cmp("FreeText")) {
+ generateFreeTextAppearance();
+ }
+ }
+ }
+ obj.free();
+}
+
+//~ 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();
+
+ //----- 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();
+
+ //----- 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 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;
+ }
+ 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 {
+ leaderLen = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LLE", &obj1)->isNum()) {
+ leaderExtLen = obj1.getNum();
+ } else {
+ leaderExtLen = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LLO", &obj1)->isNum()) {
+ leaderOffLen = obj1.getNum();
+ } else {
+ leaderOffLen = 0;
+ }
+ 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;
+ }
+ 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 {
+ 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);
+ }
+
+ //----- 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
+ 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);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+//~ this doesn't handle line ends (arrows)
+void Annot::generatePolyLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, w;
+ int i;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- 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();
+
+ //----- 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;
+ }
+ 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 {
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
+ }
+ }
+ appearBuf->append("S\n");
+ obj1.free();
+
+ //----- 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);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+void Annot::generatePolygonAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1;
+ int i;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- 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();
+
+ //----- set fill color
+ if (!annotObj.dictLookup("IC", &obj1)->isArray() ||
+ !setFillColor(&obj1)) {
+ obj1.free();
+ goto err1;
+ }
+ obj1.free();
+
+ //----- fill polygon
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
+ }
+ 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 {
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
+ }
+ }
+ appearBuf->append("f\n");
+ obj1.free();
+
+ //----- 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);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+//~ this doesn't handle rich text
+//~ this doesn't handle the callout
+//~ this doesn't handle the RD field
+void Annot::generateFreeTextAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ Object resources, gsResources, fontResources, defaultFont;
+ GString *text, *da;
+ double lineWidth;
+ int quadding, rot;
+ MemStream *appearStream;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- 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 text
+ if (annotObj.dictLookup("Contents", &obj1)->isString()) {
+ text = obj1.getString()->copy();
+ } else {
+ text = new GString();
+ }
+ obj1.free();
+ if (annotObj.dictLookup("Q", &obj1)->isInt()) {
+ quadding = obj1.getInt();
+ } else {
+ quadding = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("DA", &obj1)->isString()) {
+ da = obj1.getString()->copy();
+ } else {
+ da = new GString();
+ }
+ obj1.free();
+ // the "Rotate" field is not defined in the PDF spec, but Acrobat
+ // looks at it
+ if (annotObj.dictLookup("Rotate", &obj1)->isInt()) {
+ rot = obj1.getInt();
+ } else {
+ rot = 0;
+ }
+ obj1.free();
+ drawText(text, da, quadding, 0, rot);
+ delete text;
+ delete da;
+
+ //----- draw the border
+ if (borderStyle->getWidth() != 0) {
+ setLineStyle(borderStyle, &lineWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re s\n",
+ 0.5 * lineWidth, 0.5 * lineWidth,
+ xMax - xMin - lineWidth, yMax - yMin - lineWidth);
+ }
+
+ //----- 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);
+ resources.initDict(doc->getXRef());
+ defaultFont.initDict(doc->getXRef());
+ defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
+ defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
+ defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
+ defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
+ fontResources.initDict(doc->getXRef());
+ fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
+ resources.dictAdd(copyString("Font"), &fontResources);
+ if (gfxStateDict.isDict()) {
+ gsResources.initDict(doc->getXRef());
+ gsResources.dictAdd(copyString("GS1"), &gfxStateDict);
+ resources.dictAdd(copyString("ExtGState"), &gsResources);
+ }
+ appearDict.dictAdd(copyString("Resources"), &resources);
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ annotObj.free();
+}
+
+void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
+ double *dash;
+ double w;
+ int dashLength, i;
+
+ 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("] 0 d\n");
+ }
+ appearBuf->append("0 j\n0 J\n");
+}
+
+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;
+ }
+}
+
+GBool Annot::setFillColor(Object *colorObj) {
+ Object obj;
+ double color[4];
+ int i;
+
+ if (!colorObj->isArray()) {
+ return gFalse;
+ }
+ for (i = 0; i < colorObj->arrayGetLength() && i < 4; ++i) {
+ if (colorObj->arrayGet(i, &obj)->isNum()) {
+ color[i] = obj.getNum();
+ } else {
+ color[i] = 0;
+ }
+ obj.free();
+ }
+ 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;
+ }
+ return gFalse;
+}
+
+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;
+ }
+}
+
+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;
+ }
+ *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;
+ }
+}
+
+// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
+// <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:.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 Annot::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 Annot::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");
+}
+
+void Annot::drawText(GString *text, GString *da, int quadding, double margin,
+ int rot) {
+ GString *text2, *tok;
+ GList *daToks;
+ const char *charName;
+ double dx, dy, fontSize, fontSize2, x, y, w;
+ Gushort charWidth;
+ int tfPos, tmPos, i, j, c;
+
+ // 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;
+ }
+
+ // get the font and font size
+ fontSize = 0;
+ if (tfPos >= 0) {
+ //~ where do we look up the font?
+ tok = (GString *)daToks->get(tfPos);
+ tok->clear();
+ tok->append("/xpdf_default_font");
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1,
+ "Missing 'Tf' operator in annotation's DA string");
+ daToks->append(new GString("/xpdf_default_font"));
+ daToks->append(new GString("10"));
+ daToks->append(new GString("Tf"));
+ }
+
+ // setup
+ 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");
+
+ // compute string width
+ //~ this assumes we're substituting Helvetica/WinAnsiEncoding for everything
+ w = 0;
+ for (i = 0; i < text2->getLength(); ++i) {
+ charName = winAnsiEncoding[text->getChar(i) & 0xff];
+ if (charName && builtinFonts[4].widths->getWidth(charName, &charWidth)) {
+ w += charWidth;
+ } else {
+ w += 0.5;
+ }
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * margin;
+ fontSize2 = (dx - 2 * margin) / 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 0:
+ default:
+ x = margin + 2;
+ break;
+ case 1:
+ x = (dx - w) / 2;
+ break;
+ case 2:
+ x = dx - margin - 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((char)c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("\\{0:03o}", c);
+ } else {
+ appearBuf->append((char)c);
+ }
+ }
+ appearBuf->append(") Tj\n");
+
+ // cleanup
+ appearBuf->append("ET\n");
+ appearBuf->append("Q\n");
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+ if (text2 != text) {
+ delete text2;
+ }
+}
+
+void Annot::draw(Gfx *gfx, GBool printing) {
+ GBool oc, isLink;
+
+ // check the flags
+ if ((flags & annotFlagHidden) ||
+ (printing && !(flags & annotFlagPrint)) ||
+ (!printing && (flags & annotFlagNoView))) {
+ return;
+ }
+
+ // check the optional content entry
+ if (doc->getOptionalContent()->evalOCObject(&ocObj, &oc) && !oc) {
+ return;
+ }
+
+ // draw the appearance stream
+ isLink = type && !type->cmp("Link");
+ gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
+ xMin, yMin, xMax, yMax);
+}
+
+Object *Annot::getObject(Object *obj) {
+ if (ref.num >= 0) {
+ xref->fetch(ref.num, ref.gen, obj);
+ } else {
+ obj->initNull();
+ }
+ return obj;
+}
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+Annots::Annots(PDFDoc *docA, Object *annotsObj) {
+ Annot *annot;
+ Object obj1, obj2;
+ Ref ref;
+ GBool drawWidgetAnnots;
+ int size;
+ int i;
+
+ doc = docA;
+ annots = NULL;
+ size = 0;
+ 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();
+ obj1.free();
+ annotsObj->arrayGet(i, &obj1);
+ } else {
+ ref.num = ref.gen = -1;
+ }
+ if (obj1.isDict()) {
+ 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;
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+ }
+}
+
+Annots::~Annots() {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ delete annots[i];
+ }
+ gfree(annots);
+}
+
+Annot *Annots::find(double x, double y) {
+ int i;
+
+ for (i = nAnnots - 1; i >= 0; --i) {
+ if (annots[i]->inRect(x, y)) {
+ return annots[i];
+ }
+ }
+ return NULL;
+}
+
+int Annots::findIdx(double x, double y) {
+ int i;
+
+ for (i = nAnnots - 1; i >= 0; --i) {
+ if (annots[i]->inRect(x, y)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void Annots::generateAnnotAppearances() {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ annots[i]->generateAnnotAppearance();
+ }
+}
+
+Annot *Annots::findAnnot(Ref *ref) {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ if (annots[i]->match(ref)) {
+ return annots[i];
+ }
+ }
+ return NULL;
+}