mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-20 00:03:33 +08:00
1444 lines
38 KiB
C++
1444 lines
38 KiB
C++
//========================================================================
|
|
//
|
|
// 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, const char* AP, const char* AS) {
|
|
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(AS ? AS : asObj.getName());
|
|
} else if (apObj.isDict()) {
|
|
apObj.dictLookup(AP, &obj1);
|
|
if (obj1.isDict() && obj1.dictGetLength() == 1) {
|
|
appearanceState = new GString(AS ? AS : obj1.dictGetKey(0));
|
|
}
|
|
obj1.free();
|
|
}
|
|
if (!appearanceState) {
|
|
appearanceState = new GString("Off");
|
|
}
|
|
asObj.free();
|
|
|
|
//----- get the annotation appearance
|
|
|
|
if (apObj.isDict()) {
|
|
apObj.dictLookup(AP, &obj1);
|
|
apObj.dictLookupNF(AP, &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;
|
|
}
|