blob: 1d12aeecb04de436064c877f932bc62417cd7638 [file] [log] [blame] [edit]
//========================================================================
//
// Annot.cc
//
// Copyright 2000-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
// Copyright (C) 2007, 2008 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2007-2011 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2007-2011 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
// Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
// Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
// Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
// Copyright (C) 2011 José Aliste <jaliste@src.gnome.org>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#include <config.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "goo/gmem.h"
#include "goo/gstrtod.h"
#include "GooList.h"
#include "Error.h"
#include "Object.h"
#include "Catalog.h"
#include "Gfx.h"
#include "Lexer.h"
#include "Page.h"
#include "Annot.h"
#include "GfxFont.h"
#include "CharCodeToUnicode.h"
#include "PDFDocEncoding.h"
#include "Form.h"
#include "Error.h"
#include "XRef.h"
#include "Movie.h"
#include "OptionalContent.h"
#include "Sound.h"
#include "FileSpec.h"
#include "DateInfo.h"
#include "Link.h"
#include <string.h>
#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
// Ensures that x is between the limits set by low and high.
// If low is greater than high the result is undefined.
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
AnnotLineEndingStyle parseAnnotLineEndingStyle(GooString *string) {
if (string != NULL) {
if (!string->cmp("Square")) {
return annotLineEndingSquare;
} else if (!string->cmp("Circle")) {
return annotLineEndingCircle;
} else if (!string->cmp("Diamond")) {
return annotLineEndingDiamond;
} else if (!string->cmp("OpenArrow")) {
return annotLineEndingOpenArrow;
} else if (!string->cmp("ClosedArrow")) {
return annotLineEndingClosedArrow;
} else if (!string->cmp("Butt")) {
return annotLineEndingButt;
} else if (!string->cmp("ROpenArrow")) {
return annotLineEndingROpenArrow;
} else if (!string->cmp("RClosedArrow")) {
return annotLineEndingRClosedArrow;
} else if (!string->cmp("Slash")) {
return annotLineEndingSlash;
} else {
return annotLineEndingNone;
}
} else {
return annotLineEndingNone;
}
}
static AnnotExternalDataType parseAnnotExternalData(Dict* dict) {
Object obj1;
AnnotExternalDataType type;
if (dict->lookup("Subtype", &obj1)->isName()) {
const char *typeName = obj1.getName();
if (!strcmp(typeName, "Markup3D")) {
type = annotExternalDataMarkup3D;
} else {
type = annotExternalDataMarkupUnknown;
}
} else {
type = annotExternalDataMarkupUnknown;
}
obj1.free();
return type;
}
PDFRectangle *parseDiffRectangle(Array *array, PDFRectangle *rect) {
PDFRectangle *newRect = NULL;
if (array->getLength() == 4) {
// deltas
Object obj1;
double dx1 = (array->get(0, &obj1)->isNum() ? obj1.getNum() : 0);
obj1.free();
double dy1 = (array->get(1, &obj1)->isNum() ? obj1.getNum() : 0);
obj1.free();
double dx2 = (array->get(2, &obj1)->isNum() ? obj1.getNum() : 0);
obj1.free();
double dy2 = (array->get(3, &obj1)->isNum() ? obj1.getNum() : 0);
obj1.free();
// checking that the numbers are valid (i.e. >= 0),
// and that applying the differences still give us a valid rect
if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2
&& (rect->x2 - rect->x1 - dx1 - dx2) >= 0
&& (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
newRect = new PDFRectangle();
newRect->x1 = rect->x1 + dx1;
newRect->y1 = rect->y1 + dy1;
newRect->x2 = rect->x2 - dx2;
newRect->y2 = rect->y2 - dy2;
}
}
return newRect;
}
//------------------------------------------------------------------------
// AnnotBorderEffect
//------------------------------------------------------------------------
AnnotBorderEffect::AnnotBorderEffect(Dict *dict) {
Object obj1;
if (dict->lookup("S", &obj1)->isName()) {
const char *effectName = obj1.getName();
if (!strcmp(effectName, "C"))
effectType = borderEffectCloudy;
else
effectType = borderEffectNoEffect;
} else {
effectType = borderEffectNoEffect;
}
obj1.free();
if ((dict->lookup("I", &obj1)->isNum()) && effectType == borderEffectCloudy) {
intensity = obj1.getNum();
} else {
intensity = 0;
}
obj1.free();
}
//------------------------------------------------------------------------
// AnnotPath
//------------------------------------------------------------------------
AnnotPath::AnnotPath() {
coords = NULL;
coordsLength = 0;
}
AnnotPath::AnnotPath(Array *array) {
coords = NULL;
coordsLength = 0;
parsePathArray(array);
}
AnnotPath::AnnotPath(AnnotCoord **coords, int coordsLength) {
this->coords = coords;
this->coordsLength = coordsLength;
}
AnnotPath::~AnnotPath() {
if (coords) {
for (int i = 0; i < coordsLength; ++i)
delete coords[i];
gfree(coords);
}
}
double AnnotPath::getX(int coord) const {
if (coord >= 0 && coord < coordsLength)
return coords[coord]->getX();
return 0;
}
double AnnotPath::getY(int coord) const {
if (coord >= 0 && coord < coordsLength)
return coords[coord]->getY();
return 0;
}
AnnotCoord *AnnotPath::getCoord(int coord) const {
if (coord >= 0 && coord < coordsLength)
return coords[coord];
return NULL;
}
void AnnotPath::parsePathArray(Array *array) {
int tempLength;
AnnotCoord **tempCoords;
GBool correct = gTrue;
if (array->getLength() % 2) {
error(-1, "Bad Annot Path");
return;
}
tempLength = array->getLength() / 2;
tempCoords = (AnnotCoord **) gmallocn (tempLength, sizeof(AnnotCoord *));
memset(tempCoords, 0, tempLength * sizeof(AnnotCoord *));
for (int i = 0; i < tempLength && correct; i++) {
Object obj1;
double x = 0, y = 0;
if (array->get(i * 2, &obj1)->isNum()) {
x = obj1.getNum();
} else {
correct = gFalse;
}
obj1.free();
if (array->get((i * 2) + 1, &obj1)->isNum()) {
y = obj1.getNum();
} else {
correct = gFalse;
}
obj1.free();
if (!correct) {
for (int j = i - 1; j >= 0; j--)
delete tempCoords[j];
gfree (tempCoords);
return;
}
tempCoords[i] = new AnnotCoord(x, y);
}
coords = tempCoords;
coordsLength = tempLength;
}
//------------------------------------------------------------------------
// AnnotCalloutLine
//------------------------------------------------------------------------
AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2)
: coord1(x1, y1), coord2(x2, y2) {
}
//------------------------------------------------------------------------
// AnnotCalloutMultiLine
//------------------------------------------------------------------------
AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2,
double y2, double x3, double y3)
: AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) {
}
//------------------------------------------------------------------------
// AnnotQuadrilateral
//------------------------------------------------------------------------
AnnotQuadrilaterals::AnnotQuadrilaterals(Array *array, PDFRectangle *rect) {
int arrayLength = array->getLength();
GBool correct = gTrue;
int quadsLength = 0;
AnnotQuadrilateral **quads;
double quadArray[8];
// default values
quadrilaterals = NULL;
quadrilateralsLength = 0;
if ((arrayLength % 8) == 0) {
int i;
quadsLength = arrayLength / 8;
quads = (AnnotQuadrilateral **) gmallocn
((quadsLength), sizeof(AnnotQuadrilateral *));
memset(quads, 0, quadsLength * sizeof(AnnotQuadrilateral *));
for (i = 0; i < quadsLength; i++) {
for (int j = 0; j < 8; j++) {
Object obj;
if (array->get(i * 8 + j, &obj)->isNum()) {
if (j % 2 == 1)
quadArray[j] = CLAMP (obj.getNum(), rect->y1, rect->y2);
else
quadArray[j] = CLAMP (obj.getNum(), rect->x1, rect->x2);
} else {
correct = gFalse;
obj.free();
error (-1, "Invalid QuadPoint in annot");
break;
}
obj.free();
}
if (!correct)
break;
quads[i] = new AnnotQuadrilateral(quadArray[0], quadArray[1],
quadArray[2], quadArray[3],
quadArray[4], quadArray[5],
quadArray[6], quadArray[7]);
}
if (correct) {
quadrilateralsLength = quadsLength;
quadrilaterals = quads;
} else {
for (int j = 0; j < i; j++)
delete quads[j];
gfree (quads);
}
}
}
AnnotQuadrilaterals::~AnnotQuadrilaterals() {
if (quadrilaterals) {
for(int i = 0; i < quadrilateralsLength; i++)
delete quadrilaterals[i];
gfree (quadrilaterals);
}
}
double AnnotQuadrilaterals::getX1(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord1.getX();
return 0;
}
double AnnotQuadrilaterals::getY1(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord1.getY();
return 0;
}
double AnnotQuadrilaterals::getX2(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord2.getX();
return 0;
}
double AnnotQuadrilaterals::getY2(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord2.getY();
return 0;
}
double AnnotQuadrilaterals::getX3(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord3.getX();
return 0;
}
double AnnotQuadrilaterals::getY3(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord3.getY();
return 0;
}
double AnnotQuadrilaterals::getX4(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord4.getX();
return 0;
}
double AnnotQuadrilaterals::getY4(int quadrilateral) {
if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
return quadrilaterals[quadrilateral]->coord4.getY();
return 0;
}
AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1,
double x2, double y2, double x3, double y3, double x4, double y4)
: coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) {
}
//------------------------------------------------------------------------
// AnnotBorder
//------------------------------------------------------------------------
AnnotBorder::AnnotBorder() {
type = typeUnknown;
width = 1;
dashLength = 0;
dash = NULL;
style = borderSolid;
}
GBool AnnotBorder::parseDashArray(Object *dashObj) {
GBool correct = gTrue;
int tempLength = dashObj->arrayGetLength();
double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
// TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
for (int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
Object obj1;
if (dashObj->arrayGet(i, &obj1)->isNum()) {
tempDash[i] = obj1.getNum();
correct = tempDash[i] >= 0;
obj1.free();
}
}
if (correct) {
dashLength = tempLength;
dash = tempDash;
style = borderDashed;
} else {
gfree (tempDash);
}
return correct;
}
AnnotBorder::~AnnotBorder() {
if (dash)
gfree (dash);
}
//------------------------------------------------------------------------
// AnnotBorderArray
//------------------------------------------------------------------------
AnnotBorderArray::AnnotBorderArray() {
type = typeArray;
horizontalCorner = 0;
verticalCorner = 0;
}
AnnotBorderArray::AnnotBorderArray(Array *array) {
Object obj1;
int arrayLength = array->getLength();
GBool correct = gTrue;
if (arrayLength == 3 || arrayLength == 4) {
// implementation note 81 in Appendix H.
if (array->get(0, &obj1)->isNum())
horizontalCorner = obj1.getNum();
else
correct = gFalse;
obj1.free();
if (array->get(1, &obj1)->isNum())
verticalCorner = obj1.getNum();
else
correct = gFalse;
obj1.free();
if (array->get(2, &obj1)->isNum())
width = obj1.getNum();
else
correct = gFalse;
obj1.free();
if (arrayLength == 4) {
if (array->get(3, &obj1)->isArray())
correct = parseDashArray(&obj1);
else
correct = gFalse;
obj1.free();
}
} else {
correct = gFalse;
}
if (!correct) {
width = 0;
}
}
//------------------------------------------------------------------------
// AnnotBorderBS
//------------------------------------------------------------------------
AnnotBorderBS::AnnotBorderBS() {
type = typeBS;
}
AnnotBorderBS::AnnotBorderBS(Dict *dict) {
Object obj1, obj2;
// acroread 8 seems to need both W and S entries for
// any border to be drawn, even though the spec
// doesn't claim anything of that sort. We follow
// that behaviour by veryifying both entries exist
// otherwise we set the borderWidth to 0
// --jrmuizel
dict->lookup("W", &obj1);
dict->lookup("S", &obj2);
if (obj1.isNum() && obj2.isName()) {
const char *styleName = obj2.getName();
width = obj1.getNum();
if (!strcmp(styleName, "S")) {
style = borderSolid;
} else if (!strcmp(styleName, "D")) {
style = borderDashed;
} else if (!strcmp(styleName, "B")) {
style = borderBeveled;
} else if (!strcmp(styleName, "I")) {
style = borderInset;
} else if (!strcmp(styleName, "U")) {
style = borderUnderlined;
} else {
style = borderSolid;
}
} else {
width = 0;
}
obj2.free();
obj1.free();
if (style == borderDashed) {
if (dict->lookup("D", &obj1)->isArray())
parseDashArray(&obj1);
obj1.free();
if (!dash) {
dashLength = 1;
dash = (double *) gmallocn (dashLength, sizeof (double));
dash[0] = 3;
}
}
}
//------------------------------------------------------------------------
// AnnotColor
//------------------------------------------------------------------------
AnnotColor::AnnotColor() {
length = 0;
}
AnnotColor::AnnotColor(double gray) {
length = 1;
values[0] = gray;
}
AnnotColor::AnnotColor(double r, double g, double b) {
length = 3;
values[0] = r;
values[1] = g;
values[2] = b;
}
AnnotColor::AnnotColor(double c, double m, double y, double k) {
length = 4;
values[0] = c;
values[1] = m;
values[2] = y;
values[3] = k;
}
// If <adjust> is +1, color is brightened;
// if <adjust> is -1, color is darkened;
// otherwise color is not modified.
AnnotColor::AnnotColor(Array *array, int adjust) {
int i;
length = array->getLength();
if (length > 4)
length = 4;
for (i = 0; i < length; i++) {
Object obj1;
if (array->get(i, &obj1)->isNum()) {
values[i] = obj1.getNum();
if (values[i] < 0 || values[i] > 1)
values[i] = 0;
} else {
values[i] = 0;
}
obj1.free();
}
if (adjust != 0)
adjustColor(adjust);
}
void AnnotColor::adjustColor(int adjust) {
int i;
if (length == 4) {
adjust = -adjust;
}
if (adjust > 0) {
for (i = 0; i < length; ++i) {
values[i] = 0.5 * values[i] + 0.5;
}
} else if (adjust < 0) {
for (i = 0; i < length; ++i) {
values[i] = 0.5 * values[i];
}
}
}
//------------------------------------------------------------------------
// AnnotIconFit
//------------------------------------------------------------------------
AnnotIconFit::AnnotIconFit(Dict* dict) {
Object obj1;
if (dict->lookup("SW", &obj1)->isName()) {
const char *scaleName = obj1.getName();
if(!strcmp(scaleName, "B")) {
scaleWhen = scaleBigger;
} else if(!strcmp(scaleName, "S")) {
scaleWhen = scaleSmaller;
} else if(!strcmp(scaleName, "N")) {
scaleWhen = scaleNever;
} else {
scaleWhen = scaleAlways;
}
} else {
scaleWhen = scaleAlways;
}
obj1.free();
if (dict->lookup("S", &obj1)->isName()) {
const char *scaleName = obj1.getName();
if(!strcmp(scaleName, "A")) {
scale = scaleAnamorphic;
} else {
scale = scaleProportional;
}
} else {
scale = scaleProportional;
}
obj1.free();
if (dict->lookup("A", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
Object obj2;
(obj1.arrayGet(0, &obj2)->isNum() ? left = obj2.getNum() : left = 0);
obj2.free();
(obj1.arrayGet(1, &obj2)->isNum() ? bottom = obj2.getNum() : bottom = 0);
obj2.free();
if (left < 0 || left > 1)
left = 0.5;
if (bottom < 0 || bottom > 1)
bottom = 0.5;
} else {
left = bottom = 0.5;
}
obj1.free();
if (dict->lookup("FB", &obj1)->isBool()) {
fullyBounds = obj1.getBool();
} else {
fullyBounds = gFalse;
}
obj1.free();
}
//------------------------------------------------------------------------
// AnnotAppearanceCharacs
//------------------------------------------------------------------------
AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) {
Object obj1;
if (dict->lookup("R", &obj1)->isInt()) {
rotation = obj1.getInt();
} else {
rotation = 0;
}
obj1.free();
if (dict->lookup("BC", &obj1)->isArray()) {
borderColor = new AnnotColor(obj1.getArray());
} else {
borderColor = NULL;
}
obj1.free();
if (dict->lookup("BG", &obj1)->isArray()) {
backColor = new AnnotColor(obj1.getArray());
} else {
backColor = NULL;
}
obj1.free();
if (dict->lookup("CA", &obj1)->isString()) {
normalCaption = new GooString(obj1.getString());
} else {
normalCaption = NULL;
}
obj1.free();
if (dict->lookup("RC", &obj1)->isString()) {
rolloverCaption = new GooString(obj1.getString());
} else {
rolloverCaption = NULL;
}
obj1.free();
if (dict->lookup("AC", &obj1)->isString()) {
alternateCaption = new GooString(obj1.getString());
} else {
alternateCaption = NULL;
}
obj1.free();
if (dict->lookup("IF", &obj1)->isDict()) {
iconFit = new AnnotIconFit(obj1.getDict());
} else {
iconFit = NULL;
}
obj1.free();
if (dict->lookup("TP", &obj1)->isInt()) {
position = (AnnotAppearanceCharacsTextPos) obj1.getInt();
} else {
position = captionNoIcon;
}
obj1.free();
}
AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
if (borderColor)
delete borderColor;
if (backColor)
delete backColor;
if (normalCaption)
delete normalCaption;
if (rolloverCaption)
delete rolloverCaption;
if (alternateCaption)
delete alternateCaption;
if (iconFit)
delete iconFit;
}
//------------------------------------------------------------------------
// Annot
//------------------------------------------------------------------------
Annot::Annot(XRef *xrefA, PDFRectangle *rectA, Catalog *catalog) {
Object obj1;
refCnt = 1;
flags = flagUnknown;
type = typeUnknown;
obj1.initArray (xrefA);
Object obj2;
obj1.arrayAdd (obj2.initReal (rectA->x1));
obj1.arrayAdd (obj2.initReal (rectA->y1));
obj1.arrayAdd (obj2.initReal (rectA->x2));
obj1.arrayAdd (obj2.initReal (rectA->y2));
obj2.free ();
annotObj.initDict (xrefA);
annotObj.dictSet ("Type", obj2.initName ("Annot"));
annotObj.dictSet ("Rect", &obj1);
// obj1 is owned by the dict
ref = xrefA->addIndirectObject (&annotObj);
initialize (xrefA, annotObj.getDict(), catalog);
}
Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog) {
refCnt = 1;
hasRef = false;
flags = flagUnknown;
type = typeUnknown;
annotObj.initDict (dict);
initialize (xrefA, dict, catalog);
}
Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog, Object *obj) {
refCnt = 1;
if (obj->isRef()) {
hasRef = gTrue;
ref = obj->getRef();
} else {
hasRef = gFalse;
}
flags = flagUnknown;
type = typeUnknown;
annotObj.initDict (dict);
initialize (xrefA, dict, catalog);
}
void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
Object asObj, obj1, obj2, obj3;
appRef.num = 0;
appRef.gen = 65535;
ok = gTrue;
xref = xrefA;
appearBuf = NULL;
fontSize = 0;
appearance.initNull();
//----- parse the rectangle
rect = new PDFRectangle();
if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
Object obj2;
(obj1.arrayGet(0, &obj2)->isNum() ? rect->x1 = obj2.getNum() : rect->x1 = 0);
obj2.free();
(obj1.arrayGet(1, &obj2)->isNum() ? rect->y1 = obj2.getNum() : rect->y1 = 0);
obj2.free();
(obj1.arrayGet(2, &obj2)->isNum() ? rect->x2 = obj2.getNum() : rect->x2 = 1);
obj2.free();
(obj1.arrayGet(3, &obj2)->isNum() ? rect->y2 = obj2.getNum() : rect->y2 = 1);
obj2.free();
if (rect->x1 > rect->x2) {
double t = rect->x1;
rect->x1 = rect->x2;
rect->x2 = t;
}
if (rect->y1 > rect->y2) {
double t = rect->y1;
rect->y1 = rect->y2;
rect->y2 = t;
}
} else {
rect->x1 = rect->y1 = 0;
rect->x2 = rect->y2 = 1;
error(-1, "Bad bounding box for annotation");
ok = gFalse;
}
obj1.free();
if (dict->lookup("Contents", &obj1)->isString()) {
contents = obj1.getString()->copy();
} else {
contents = NULL;
}
obj1.free();
if (dict->lookupNF("P", &obj1)->isRef()) {
Ref ref = obj1.getRef();
page = catalog ? catalog->findPage (ref.num, ref.gen) : -1;
} else {
page = 0;
}
obj1.free();
if (dict->lookup("NM", &obj1)->isString()) {
name = obj1.getString()->copy();
} else {
name = NULL;
}
obj1.free();
if (dict->lookup("M", &obj1)->isString()) {
modified = obj1.getString()->copy();
} else {
modified = NULL;
}
obj1.free();
//----- get the flags
if (dict->lookup("F", &obj1)->isInt()) {
flags |= obj1.getInt();
} else {
flags = flagUnknown;
}
obj1.free();
if (dict->lookup("AP", &obj1)->isDict()) {
Object obj2;
if (dict->lookup("AS", &obj2)->isName()) {
Object obj3;
appearState = new GooString(obj2.getName());
if (obj1.dictLookup("N", &obj3)->isDict()) {
Object obj4;
if (obj3.dictLookupNF(appearState->getCString(), &obj4)->isRef()) {
obj4.copy(&appearance);
} else {
obj4.free();
if (obj3.dictLookupNF("Off", &obj4)->isRef()) {
obj4.copy(&appearance);
}
}
obj4.free();
}
obj3.free();
} else {
obj2.free();
appearState = NULL;
if (obj1.dictLookupNF("N", &obj2)->isRef()) {
obj2.copy(&appearance);
}
}
obj2.free();
} else {
appearState = NULL;
}
obj1.free();
//----- parse the border style
if (dict->lookup("BS", &obj1)->isDict()) {
border = new AnnotBorderBS(obj1.getDict());
} else {
obj1.free();
if (dict->lookup("Border", &obj1)->isArray())
border = new AnnotBorderArray(obj1.getArray());
else
// Adobe draws no border at all if the last element is of
// the wrong type.
border = NULL;
}
obj1.free();
if (dict->lookup("C", &obj1)->isArray()) {
color = new AnnotColor(obj1.getArray());
} else {
color = NULL;
}
obj1.free();
if (dict->lookup("StructParent", &obj1)->isInt()) {
treeKey = obj1.getInt();
} else {
treeKey = 0;
}
obj1.free();
optContentConfig = catalog ? catalog->getOptContentConfig() : NULL;
dict->lookupNF("OC", &oc);
if (!oc.isRef() && !oc.isNull()) {
error (-1, "Annotation OC value not null or dict: %i", oc.getType());
}
}
void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const {
*x1 = rect->x1;
*y1 = rect->y1;
*x2 = rect->x2;
*y2 = rect->y2;
}
GBool Annot::inRect(double x, double y) const {
return rect->contains(x, y);
}
void Annot::update(const char *key, Object *value) {
/* Set M to current time */
delete modified;
modified = timeToDateString(NULL);
Object obj1;
obj1.initString (modified->copy());
annotObj.dictSet("M", &obj1);
annotObj.dictSet(const_cast<char*>(key), value);
xref->setModifiedObject(&annotObj, ref);
}
void Annot::setContents(GooString *new_content) {
delete contents;
if (new_content) {
contents = new GooString(new_content);
//append the unicode marker <FE FF> if needed
if (!contents->hasUnicodeMarker()) {
contents->insert(0, 0xff);
contents->insert(0, 0xfe);
}
} else {
contents = new GooString();
}
Object obj1;
obj1.initString(contents->copy());
update ("Contents", &obj1);
}
void Annot::setColor(AnnotColor *new_color) {
delete color;
if (new_color) {
Object obj1, obj2;
const double *values = new_color->getValues();
obj1.initArray(xref);
for (int i = 0; i < (int)new_color->getSpace(); i++)
obj1.arrayAdd(obj2.initReal (values[i]));
update ("C", &obj1);
color = new_color;
} else {
color = NULL;
}
}
void Annot::setPage(Ref *pageRef, int pageIndex)
{
Object obj1;
obj1.initRef(pageRef->num, pageRef->gen);
update("P", &obj1);
page = pageIndex;
}
void Annot::setAppearanceState(char *state) {
if (!state)
return;
if (appearState && appearState->cmp(state) == 0)
return;
delete appearState;
appearState = new GooString(state);
Object obj1;
obj1.initName(state);
update ("AS", &obj1);
// The appearance state determines the current appearance stream
Object obj2;
if (annotObj.dictLookup("AP", &obj2)->isDict()) {
Object obj3;
if (obj2.dictLookup("N", &obj3)->isDict()) {
Object obj4;
appearance.free();
if (obj3.dictLookupNF(state, &obj4)->isRef())
obj4.copy(&appearance);
else
appearance.initNull();
obj4.free();
}
obj3.free();
}
obj2.free();
}
double Annot::getXMin() {
return rect->x1;
}
double Annot::getYMin() {
return rect->y1;
}
void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
Object valueObject;
pdfArray->arrayGet(key, &valueObject);
if (valueObject.isNum()) {
*value = valueObject.getNum();
} else {
*value = 0;
ok = gFalse;
}
valueObject.free();
}
void Annot::incRefCnt() {
refCnt++;
}
void Annot::decRefCnt() {
if (--refCnt == 0)
delete this;
}
Annot::~Annot() {
annotObj.free();
delete rect;
if (contents)
delete contents;
if (name)
delete name;
if (modified)
delete modified;
appearance.free();
if (appearState)
delete appearState;
if (border)
delete border;
if (color)
delete color;
oc.free();
}
void Annot::setColor(AnnotColor *color, GBool fill) {
const double *values = color->getValues();
switch (color->getSpace()) {
case AnnotColor::colorCMYK:
appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
values[0], values[1], values[2], values[3],
fill ? 'k' : 'K');
break;
case AnnotColor::colorRGB:
appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
values[0], values[1], values[2],
fill ? "rg" : "RG");
break;
case AnnotColor::colorGray:
appearBuf->appendf("{0:.2f} {1:c}\n",
values[0],
fill ? 'g' : 'G');
break;
case AnnotColor::colorTransparent:
default:
break;
}
}
// 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",
cx + r, cy);
appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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",
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",
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",
cx + bezierCircle * r, cy - r,
cx + r, cy - bezierCircle * r,
cx + r, cy);
appearBuf->append(fill ? "f\n" : "s\n");
}
// 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:.2f} {1:.2f} m\n",
cx + r2, cy + r2);
appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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",
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:.2f} {1:.2f} m\n",
cx - r2, cy - r2);
appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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",
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::createForm(double *bbox, GBool transparencyGroup, Object *resDict, Object *aStream) {
Object obj1, obj2;
Object appearDict;
appearDict.initDict(xref);
appearDict.dictSet("Length", obj1.initInt(appearBuf->getLength()));
appearDict.dictSet("Subtype", obj1.initName("Form"));
obj1.initArray(xref);
obj1.arrayAdd(obj2.initReal(bbox[0]));
obj1.arrayAdd(obj2.initReal(bbox[1]));
obj1.arrayAdd(obj2.initReal(bbox[2]));
obj1.arrayAdd(obj2.initReal(bbox[3]));
appearDict.dictSet("BBox", &obj1);
if (transparencyGroup) {
Object transDict;
transDict.initDict(xref);
transDict.dictSet("S", obj1.initName("Transparency"));
appearDict.dictSet("Group", &transDict);
}
if (resDict)
appearDict.dictSet("Resources", resDict);
MemStream *mStream = new MemStream(copyString(appearBuf->getCString()), 0,
appearBuf->getLength(), &appearDict);
mStream->setNeedFree(gTrue);
aStream->initStream(mStream);
}
void Annot::createResourcesDict(char *formName, Object *formStream,
char *stateName,
double opacity, char *blendMode,
Object *resDict) {
Object gsDict, stateDict, formDict, obj1;
gsDict.initDict(xref);
if (opacity != 1) {
gsDict.dictSet("CA", obj1.initReal(opacity));
gsDict.dictSet("ca", obj1.initReal(opacity));
}
if (blendMode)
gsDict.dictSet("BM", obj1.initName(blendMode));
stateDict.initDict(xref);
stateDict.dictSet(stateName, &gsDict);
formDict.initDict(xref);
formDict.dictSet(formName, formStream);
resDict->initDict(xref);
resDict->dictSet("ExtGState", &stateDict);
resDict->dictSet("XObject", &formDict);
}
GBool Annot::isVisible(GBool printing) {
// check the flags
if ((flags & flagHidden) ||
(printing && !(flags & flagPrint)) ||
(!printing && (flags & flagNoView))) {
return gFalse;
}
// check the OC
if (optContentConfig && oc.isRef()) {
if (! optContentConfig->optContentIsVisible(&oc))
return gFalse;
}
return gTrue;
}
void Annot::draw(Gfx *gfx, GBool printing) {
Object obj;
if (!isVisible (printing))
return;
// draw the appearance stream
appearance.fetch(xref, &obj);
gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
rect->x1, rect->y1, rect->x2, rect->y2);
obj.free();
}
//------------------------------------------------------------------------
// AnnotPopup
//------------------------------------------------------------------------
AnnotPopup::AnnotPopup(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
Annot(xrefA, rect, catalog) {
Object obj1;
type = typePopup;
annotObj.dictSet ("Subtype", obj1.initName ("Popup"));
initialize (xrefA, annotObj.getDict(), catalog);
}
AnnotPopup::AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
Annot(xrefA, dict, catalog, obj) {
type = typePopup;
initialize(xrefA, dict, catalog);
}
AnnotPopup::~AnnotPopup() {
parent.free();
}
void AnnotPopup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
Object obj1;
if (!dict->lookupNF("Parent", &parent)->isRef()) {
parent.initNull();
}
if (dict->lookup("Open", &obj1)->isBool()) {
open = obj1.getBool();
} else {
open = gFalse;
}
obj1.free();
}
void AnnotPopup::setParent(Object *parentA) {
parentA->copy(&parent);
update ("Parent", &parent);
}
void AnnotPopup::setParent(Annot *parentA) {
Ref parentRef = parentA->getRef();
parent.initRef(parentRef.num, parentRef.gen);
update ("Parent", &parent);
}
void AnnotPopup::setOpen(GBool openA) {
Object obj1;
open = openA;
obj1.initBool(open);
update ("Open", &obj1);
}
//------------------------------------------------------------------------
// AnnotMarkup
//------------------------------------------------------------------------
AnnotMarkup::AnnotMarkup(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
Annot(xrefA, rect, catalog) {
initialize(xrefA, annotObj.getDict(), catalog, &annotObj);
}
AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
Annot(xrefA, dict, catalog, obj) {
initialize(xrefA, dict, catalog, obj);
}
AnnotMarkup::~AnnotMarkup() {
if (label)
delete label;
if (popup)
delete popup;
if (date)
delete date;
if (subject)
delete subject;
}
void AnnotMarkup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
Object obj1;
if (dict->lookup("T", &obj1)->isString()) {
label = obj1.getString()->copy();
} else {
label = NULL;
}
obj1.free();
if (dict->lookup("Popup", &obj1)->isDict()) {
popup = new AnnotPopup(xrefA, obj1.getDict(), catalog, obj);
} else {
popup = NULL;
}
obj1.free();
if (dict->lookup("CA", &obj1)->isNum()) {
opacity = obj1.getNum();
} else {
opacity = 1.0;
}
obj1.free();
if (dict->lookup("CreationDate", &obj1)->isString()) {
date = obj1.getString()->copy();
} else {
date = NULL;
}
obj1.free();
if (dict->lookupNF("IRT", &obj1)->isRef()) {
inReplyTo = obj1.getRef();
} else {
inReplyTo.num = 0;
inReplyTo.gen = 0;
}
obj1.free();
if (dict->lookup("Subj", &obj1)->isString()) {
subject = obj1.getString()->copy();
} else {
subject = NULL;
}
obj1.free();
if (dict->lookup("RT", &obj1)->isName()) {
const char *replyName = obj1.getName();
if (!strcmp(replyName, "R")) {
replyTo = replyTypeR;
} else if (!strcmp(replyName, "Group")) {
replyTo = replyTypeGroup;
} else {
replyTo = replyTypeR;
}
} else {
replyTo = replyTypeR;
}
obj1.free();
if (dict->lookup("ExData", &obj1)->isDict()) {
exData = parseAnnotExternalData(obj1.getDict());
} else {
exData = annotExternalDataMarkupUnknown;
}
obj1.free();
}
void AnnotMarkup::setLabel(GooString *new_label) {
delete label;
if (new_label) {
label = new GooString(new_label);
//append the unicode marker <FE FF> if needed
if (!label->hasUnicodeMarker()) {
label->insert(0, 0xff);
label->insert(0, 0xfe);
}
} else {
label = new GooString();
}
Object obj1;
obj1.initString(label->copy());
update ("T", &obj1);
}
void AnnotMarkup::setPopup(AnnotPopup *new_popup) {
delete popup;
if (new_popup) {
Object obj1;
Ref popupRef = new_popup->getRef();
obj1.initRef (popupRef.num, popupRef.gen);
update ("Popup", &obj1);
new_popup->setParent(this);
popup = new_popup;
} else {
popup = NULL;
}
}
void AnnotMarkup::setOpacity(double opacityA) {
Object obj1;
opacity = opacityA;
obj1.initReal(opacity);
update ("CA", &obj1);
}
//------------------------------------------------------------------------
// AnnotText
//------------------------------------------------------------------------
AnnotText::AnnotText(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
AnnotMarkup(xrefA, rect, catalog) {
Object obj1;
type = typeText;
flags |= flagNoZoom | flagNoRotate;
annotObj.dictSet ("Subtype", obj1.initName ("Text"));
initialize (xrefA, catalog, annotObj.getDict());
}
AnnotText::AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
AnnotMarkup(xrefA, dict, catalog, obj) {
type = typeText;
flags |= flagNoZoom | flagNoRotate;
initialize (xrefA, catalog, dict);
}
AnnotText::~AnnotText() {
delete icon;
}
void AnnotText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
if (dict->lookup("Open", &obj1)->isBool())
open = obj1.getBool();
else
open = gFalse;
obj1.free();
if (dict->lookup("Name", &obj1)->isName()) {
icon = new GooString(obj1.getName());
} else {
icon = new GooString("Note");
}
obj1.free();
if (dict->lookup("StateModel", &obj1)->isString()) {
Object obj2;
GooString *modelName = obj1.getString();
if (dict->lookup("State", &obj2)->isString()) {
GooString *stateName = obj2.getString();
if (!stateName->cmp("Marked")) {
state = stateMarked;
} else if (!stateName->cmp("Unmarked")) {
state = stateUnmarked;
} else if (!stateName->cmp("Accepted")) {
state = stateAccepted;
} else if (!stateName->cmp("Rejected")) {
state = stateRejected;
} else if (!stateName->cmp("Cancelled")) {
state = stateCancelled;
} else if (!stateName->cmp("Completed")) {
state = stateCompleted;
} else if (!stateName->cmp("None")) {
state = stateNone;
} else {
state = stateUnknown;
}
} else {
state = stateUnknown;
}
obj2.free();
if (!modelName->cmp("Marked")) {
switch (state) {
case stateUnknown:
state = stateMarked;
break;
case stateAccepted:
case stateRejected:
case stateCancelled:
case stateCompleted:
case stateNone:
state = stateUnknown;
break;
default:
break;
}
} else if (!modelName->cmp("Review")) {
switch (state) {
case stateUnknown:
state = stateNone;
break;
case stateMarked:
case stateUnmarked:
state = stateUnknown;
break;
default:
break;
}
} else {
state = stateUnknown;
}
} else {
state = stateUnknown;
}
obj1.free();
}
void AnnotText::setOpen(GBool openA) {
Object obj1;
open = openA;
obj1.initBool(open);
update ("Open", &obj1);
}
void AnnotText::setIcon(GooString *new_icon) {
if (new_icon && icon->cmp(new_icon) == 0)
return;
delete icon;
if (new_icon) {
icon = new GooString (new_icon);
} else {
icon = new GooString("Note");
}
Object obj1;
obj1.initName (icon->getCString());
update("Name", &obj1);
}
#define ANNOT_TEXT_AP_NOTE \
"3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \
"1.613 22.387 0 20.398 0 c 3.602 0 l 1.613 0 0 1.613 0 3.602 c 0 20.398\n" \
"l 0 22.387 1.613 24 3.602 24 c h\n" \
"3.602 24 m f\n" \
"0.533333 0.541176 0.521569 RG 2 w\n" \
"1 J\n" \
"1 j\n" \
"[] 0.0 d\n" \
"4 M 9 18 m 4 18 l 4 7 4 4 6 3 c 20 3 l 18 4 18 7 18 18 c 17 18 l S\n" \
"1.5 w\n" \
"0 j\n" \
"10 16 m 14 21 l S\n" \
"1.85625 w\n" \
"1 j\n" \
"15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \
"11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \
"14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \
"15.07 20.523 m S\n" \
"1 w\n" \
"0 j\n" \
"6.5 13.5 m 15.5 13.5 l S\n" \
"6.5 10.5 m 13.5 10.5 l S\n" \
"6.801 7.5 m 15.5 7.5 l S\n" \
"0.729412 0.741176 0.713725 RG 2 w\n" \
"1 j\n" \
"9 19 m 4 19 l 4 8 4 5 6 4 c 20 4 l 18 5 18 8 18 19 c 17 19 l S\n" \
"1.5 w\n" \
"0 j\n" \
"10 17 m 14 22 l S\n" \
"1.85625 w\n" \
"1 j\n" \
"15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \
"11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \
"14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \
"15.07 21.523 m S\n" \
"1 w\n" \
"0 j\n" \
"6.5 14.5 m 15.5 14.5 l S\n" \
"6.5 11.5 m 13.5 11.5 l S\n" \
"6.801 8.5 m 15.5 8.5 l S\n"
#define ANNOT_TEXT_AP_COMMENT \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2 w\n" \
"0 J\n" \
"1 j\n" \
"[] 0.0 d\n" \
"4 M 8 20 m 16 20 l 18.363 20 20 18.215 20 16 c 20 13 l 20 10.785 18.363 9\n" \
"16 9 c 13 9 l 8 3 l 8 9 l 8 9 l 5.637 9 4 10.785 4 13 c 4 16 l 4 18.215\n" \
"5.637 20 8 20 c h\n" \
"8 20 m S\n" \
"0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \
"c 20 14 l 20 11.785 18.363 10\n" \
"16 10 c 13 10 l 8 4 l 8 10 l 8 10 l 5.637 10 4 11.785 4 14 c 4 17 l 4\n" \
"19.215 5.637 21 8 21 c h\n" \
"8 21 m S\n"
#define ANNOT_TEXT_AP_KEY \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2 w\n" \
"1 J\n" \
"0 j\n" \
"[] 0.0 d\n" \
"4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \
"16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \
"10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \
"7.059 6.059 l 6.34 4.121 l 5.543 3.668 l 3.375 4.207 l 2.938 6.156 l\n" \
"10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \
"11.895 18.754 m S\n" \
"1.5 w\n" \
"16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \
"15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \
"17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \
"16.059 15.586 m S\n" \
"0.729412 0.741176 0.713725 RG 2 w\n" \
"11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \
"17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \
"11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \
"7.059 7.059 l 6.34 5.121 l 5.543 4.668 l 3.375 5.207 l 2.938 7.156 l\n" \
"10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \
"11.895 19.754 m S\n" \
"1.5 w\n" \
"16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \
"16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \
"18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \
"16.059 16.586 m S\n"
#define ANNOT_TEXT_AP_HELP \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2.5 w\n" \
"1 J\n" \
"1 j\n" \
"[] 0.0 d\n" \
"4 M 8.289 16.488 m 8.824 17.828 10.043 18.773 11.473 18.965 c 12.902 19.156\n" \
"14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \
"c S\n" \
"0 j\n" \
"12 8 m 12 12 16 11 16 15 c S\n" \
"1.539286 w\n" \
"1 j\n" \
"q 1 0 0 -0.999991 0 24 cm\n" \
"12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \
"20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \
"19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \
"0.729412 0.741176 0.713725 RG 2.5 w\n" \
"8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \
"16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \
"c S\n" \
"0 j\n" \
"12 9 m 12 13 16 12 16 16 c S\n" \
"1.539286 w\n" \
"1 j\n" \
"q 1 0 0 -0.999991 0 24 cm\n" \
"12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \
"19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \
"18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
#define ANNOT_TEXT_AP_NEW_PARAGRAPH \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 4 w\n" \
"0 J\n" \
"2 j\n" \
"[] 0.0 d\n" \
"4 M q 1 0 0 -1 0 24 cm\n" \
"9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \
"6.898 9.59 7.305 8.941 c 7.711 8.293 8.449 7.93 9.211 8.012 c S Q\n" \
"1.004413 w\n" \
"1 J\n" \
"1 j\n" \
"q 1 0 0 -0.991232 0 24 cm\n" \
"18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \
"6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \
"17.461 8.252 l 18.07 11.511 l h\n" \
"18.07 11.511 m S Q\n" \
"2 w\n" \
"0 j\n" \
"11 17 m 10 17 l 10 3 l S\n" \
"14 3 m 14 13 l S\n" \
"0.729412 0.741176 0.713725 RG 4 w\n" \
"0 J\n" \
"2 j\n" \
"q 1 0 0 -1 0 24 cm\n" \
"9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \
"7.691 7.125 8.789 7.012 c 8.93 6.996 9.07 6.996 9.211 7.012 c S Q\n" \
"1.004413 w\n" \
"1 J\n" \
"1 j\n" \
"q 1 0 0 -0.991232 0 24 cm\n" \
"18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \
"5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \
"17.461 7.243 l 18.07 10.502 l h\n" \
"18.07 10.502 m S Q\n" \
"2 w\n" \
"0 j\n" \
"11 18 m 10 18 l 10 4 l S\n" \
"14 4 m 14 14 l S\n"
#define ANNOT_TEXT_AP_PARAGRAPH \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2 w\n" \
"1 J\n" \
"1 j\n" \
"[] 0.0 d\n" \
"4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
"4 w\n" \
"q 1 0 0 -1 0 24 cm\n" \
"9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \
"7.066 9.879 7.004 c S Q\n" \
"0.729412 0.741176 0.713725 RG 2 w\n" \
"15 4 m 15 19 l 11 19 l 11 4 l S\n" \
"4 w\n" \
"q 1 0 0 -1 0 24 cm\n" \
"9.777 9.988 m 8.746 9.871 7.973 8.988 8 7.949 c 8.027 6.91 8.844 6.066\n" \
"9.879 6.004 c S Q\n"
#define ANNOT_TEXT_AP_INSERT \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2 w\n" \
"1 J\n" \
"0 j\n" \
"[] 0.0 d\n" \
"4 M 12 18.012 m 20 18 l S\n" \
"9 10 m 17 10 l S\n" \
"12 14.012 m 20 14 l S\n" \
"12 6.012 m 20 6.012 l S\n" \
"4 12 m 6 10 l 4 8 l S\n" \
"4 12 m 4 8 l S\n" \
"0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \
"9 11 m 17 11 l S\n" \
"12 15.012 m 20 15 l S\n" \
"12 7.012 m 20 7.012 l S\n" \
"4 13 m 6 11 l 4 9 l S\n" \
"4 13 m 4 9 l S\n"
#define ANNOT_TEXT_AP_CROSS \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2.5 w\n" \
"1 J\n" \
"0 j\n" \
"[] 0.0 d\n" \
"4 M 18 5 m 6 17 l S\n" \
"6 5 m 18 17 l S\n" \
"0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \
"6 6 m 18 18 l S\n"
#define ANNOT_TEXT_AP_CIRCLE \
"4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
"2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
"l 1 21.523 2.477 23 4.301 23 c h\n" \
"4.301 23 m f\n" \
"0.533333 0.541176 0.521569 RG 2.5 w\n" \
"1 J\n" \
"1 j\n" \
"[] 0.0 d\n" \
"4 M 19.5 11.5 m 19.5 7.359 16.141 4 12 4 c 7.859 4 4.5 7.359 4.5 11.5 c 4.5\n" \
"15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \
"19.5 11.5 m S\n" \
"0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \
"7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \
"16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \
"19.5 12.5 m S\n"
void AnnotText::draw(Gfx *gfx, GBool printing) {
Object obj;
double ca = 1;
if (!isVisible (printing))
return;
double rectx2 = rect->x2;
double recty2 = rect->y2;
if (appearance.isNull()) {
ca = opacity;
appearBuf = new GooString ();
appearBuf->append ("q\n");
if (color)
setColor(color, gTrue);
else
appearBuf->append ("1 1 1 rg\n");
if (!icon->cmp("Note"))
appearBuf->append (ANNOT_TEXT_AP_NOTE);
else if (!icon->cmp("Comment"))
appearBuf->append (ANNOT_TEXT_AP_COMMENT);
else if (!icon->cmp("Key"))
appearBuf->append (ANNOT_TEXT_AP_KEY);
else if (!icon->cmp("Help"))
appearBuf->append (ANNOT_TEXT_AP_HELP);
else if (!icon->cmp("NewParagraph"))
appearBuf->append (ANNOT_TEXT_AP_NEW_PARAGRAPH);
else if (!icon->cmp("Paragraph"))
appearBuf->append (ANNOT_TEXT_AP_PARAGRAPH);
else if (!icon->cmp("Insert"))
appearBuf->append (ANNOT_TEXT_AP_INSERT);
else if (!icon->cmp("Cross"))
appearBuf->append (ANNOT_TEXT_AP_CROSS);
else if (!icon->cmp("Circle"))
appearBuf->append (ANNOT_TEXT_AP_CIRCLE);
appearBuf->append ("Q\n");
double bbox[4];
bbox[0] = bbox[1] = 0;
bbox[2] = bbox[3] = 24;
if (ca == 1) {
createForm(bbox, gFalse, NULL, &appearance);
} else {
Object aStream, resDict;
createForm(bbox, gTrue, NULL, &aStream);
delete appearBuf;
appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
createForm(bbox, gFalse, &resDict, &appearance);
}
delete appearBuf;
rectx2 = rect->x1 + 24;
recty2 = rect->y1 + 24;
}
// draw the appearance stream
appearance.fetch(xref, &obj);
gfx->drawAnnot(&obj, border, color,
rect->x1, rect->y1, rectx2, recty2);
obj.free();
}
//------------------------------------------------------------------------
// AnnotLink
//------------------------------------------------------------------------
AnnotLink::AnnotLink(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
Annot(xrefA, rect, catalog) {
Object obj1;
type = typeLink;
annotObj.dictSet ("Subtype", obj1.initName ("Link"));
initialize (xrefA, catalog, annotObj.getDict());
}
AnnotLink::AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
Annot(xrefA, dict, catalog, obj) {
type = typeLink;
initialize (xrefA, catalog, dict);
}
AnnotLink::~AnnotLink() {
delete action;
/*
if (uriAction)
delete uriAction;
*/
if (quadrilaterals)
delete quadrilaterals;
}
void AnnotLink::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
action = NULL;
// look for destination
if (!dict->lookup("Dest", &obj1)->isNull()) {
action = LinkAction::parseDest(&obj1);
// look for action
} else {
obj1.free();
if (dict->lookup("A", &obj1)->isDict()) {
action = LinkAction::parseAction(&obj1, catalog->getBaseURI());
}
}
obj1.free();
if (dict->lookup("H", &obj1)->isName()) {
const char *effect = obj1.getName();
if (!strcmp(effect, "N")) {
linkEffect = effectNone;
} else if (!strcmp(effect, "I")) {
linkEffect = effectInvert;
} else if (!strcmp(effect, "O")) {
linkEffect = effectOutline;
} else if (!strcmp(effect, "P")) {
linkEffect = effectPush;
} else {
linkEffect = effectInvert;
}
} else {
linkEffect = effectInvert;
}
obj1.free();
/*
if (dict->lookup("PA", &obj1)->isDict()) {
uriAction = NULL;
} else {
uriAction = NULL;
}
obj1.free();
*/
if (dict->lookup("QuadPoints", &obj1)->isArray()) {
quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
} else {
quadrilaterals = NULL;
}
obj1.free();
}
void AnnotLink::draw(Gfx *gfx, GBool printing) {
Object obj;
if (!isVisible (printing))
return;
// draw the appearance stream
appearance.fetch(xref, &obj);
gfx->drawAnnot(&obj, border, color,
rect->x1, rect->y1, rect->x2, rect->y2);
obj.free();
}
//------------------------------------------------------------------------
// AnnotFreeText
//------------------------------------------------------------------------
AnnotFreeText::AnnotFreeText(XRef *xrefA, PDFRectangle *rect, GooString *da, Catalog *catalog) :
AnnotMarkup(xrefA, rect, catalog) {
Object obj1;
type = typeFreeText;
annotObj.dictSet ("Subtype", obj1.initName ("FreeText"));
Object obj2;
obj2.initString (da->copy());
annotObj.dictSet("DA", &obj2);
initialize (xrefA, catalog, annotObj.getDict());
}
AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
AnnotMarkup(xrefA, dict, catalog, obj) {
type = typeFreeText;
initialize(xrefA, catalog, dict);
}
AnnotFreeText::~AnnotFreeText() {
delete appearanceString;
if (styleString)
delete styleString;
if (calloutLine)
delete calloutLine;
if (borderEffect)
delete borderEffect;
if (rectangle)
delete rectangle;
}
void AnnotFreeText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
if (dict->lookup("DA", &obj1)->isString()) {
appearanceString = obj1.getString()->copy();
} else {
appearanceString = new GooString();
error(-1, "Bad appearance for annotation");
ok = gFalse;
}
obj1.free();
if (dict->lookup("Q", &obj1)->isInt()) {
quadding = (AnnotFreeTextQuadding) obj1.getInt();
} else {
quadding = quaddingLeftJustified;
}
obj1.free();
if (dict->lookup("DS", &obj1)->isString()) {
styleString = obj1.getString()->copy();
} else {
styleString = NULL;
}
obj1.free();
if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
double x1, y1, x2, y2;
Object obj2;
(obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
obj2.free();
(obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
obj2.free();
(obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
obj2.free();
(obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
obj2.free();
if (obj1.arrayGetLength() == 6) {
double x3, y3;
(obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
obj2.free();
(obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
obj2.free();
calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
} else {
calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
}
} else {
calloutLine = NULL;
}
obj1.free();
if (dict->lookup("IT", &obj1)->isName()) {
const char *intentName = obj1.getName();
if (!strcmp(intentName, "FreeText")) {
intent = intentFreeText;
} else if (!strcmp(intentName, "FreeTextCallout")) {
intent = intentFreeTextCallout;
} else if (!strcmp(intentName, "FreeTextTypeWriter")) {
intent = intentFreeTextTypeWriter;
} else {
intent = intentFreeText;
}
} else {
intent = intentFreeText;
}
obj1.free();
if (dict->lookup("BE", &obj1)->isDict()) {
borderEffect = new AnnotBorderEffect(obj1.getDict());
} else {
borderEffect = NULL;
}
obj1.free();
if (dict->lookup("RD", &obj1)->isArray()) {
rectangle = parseDiffRectangle(obj1.getArray(), rect);
} else {
rectangle = NULL;
}
obj1.free();
if (dict->lookup("LE", &obj1)->isName()) {
GooString styleName(obj1.getName());
endStyle = parseAnnotLineEndingStyle(&styleName);
} else {
endStyle = annotLineEndingNone;
}
obj1.free();
}
//------------------------------------------------------------------------
// AnnotLine
//------------------------------------------------------------------------
AnnotLine::AnnotLine(XRef *xrefA, PDFRectangle *rect, PDFRectangle *lRect, Catalog *catalog) :
AnnotMarkup(xrefA, rect, catalog) {
Object obj1;
type = typeLine;
annotObj.dictSet ("Subtype", obj1.initName ("Line"));
Object obj2, obj3;
obj2.initArray (xrefA);
obj2.arrayAdd (obj3.initReal (lRect->x1));
obj2.arrayAdd (obj3.initReal (lRect->y1));
obj2.arrayAdd (obj3.initReal (lRect->x2));
obj2.arrayAdd (obj3.initReal (lRect->y2));
annotObj.dictSet ("L", &obj2);
initialize (xrefA, catalog, annotObj.getDict());
}
AnnotLine::AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
AnnotMarkup(xrefA, dict, catalog, obj) {
type = typeLine;
initialize(xrefA, catalog, dict);
}
AnnotLine::~AnnotLine() {
delete coord1;
delete coord2;
if (interiorColor)
delete interiorColor;
if (measure)
delete measure;
}
void AnnotLine::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
Object obj2;
double x1, y1, x2, y2;
(obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
obj2.free();
(obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
obj2.free();
(obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
obj2.free();
(obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
obj2.free();
coord1 = new AnnotCoord(x1, y1);
coord2 = new AnnotCoord(x2, y2);
} else {
coord1 = new AnnotCoord();
coord2 = new AnnotCoord();
}
obj1.free();
if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
Object obj2;
if(obj1.arrayGet(0, &obj2)->isString())
startStyle = parseAnnotLineEndingStyle(obj2.getString());
else
startStyle = annotLineEndingNone;
obj2.free();
if(obj1.arrayGet(1, &obj2)->isString())
endStyle = parseAnnotLineEndingStyle(obj2.getString());
else
endStyle = annotLineEndingNone;
obj2.free();
} else {
startStyle = endStyle = annotLineEndingNone;
}
obj1.free();
if (dict->lookup("IC", &obj1)->isArray()) {
interiorColor = new AnnotColor(obj1.getArray());
} else {
interiorColor = NULL;
}
obj1.free();
if (dict->lookup("LL", &obj1)->isNum()) {
leaderLineLength = obj1.getNum();
} else {
leaderLineLength = 0;
}
obj1.free();
if (dict->lookup("LLE", &obj1)->isNum()) {
leaderLineExtension = obj1.getNum();
if (leaderLineExtension < 0)
leaderLineExtension = 0;
} else {
leaderLineExtension = 0;
}
obj1.free();
if (dict->lookup("Cap", &obj1)->isBool()) {
caption = obj1.getBool();
} else {
caption = gFalse;
}
obj1.free();
if (dict->lookup("IT", &obj1)->isName()) {
const char *intentName = obj1.getName();
if(!strcmp(intentName, "LineArrow")) {
intent = intentLineArrow;
} else if(!strcmp(intentName, "LineDimension")) {
intent = intentLineDimension;
} else {
intent = intentLineArrow;
}
} else {
intent = intentLineArrow;
}
obj1.free();
if (dict->lookup("LLO", &obj1)->isNum()) {
leaderLineOffset = obj1.getNum();
if (leaderLineOffset < 0)
leaderLineOffset = 0;
} else {
leaderLineOffset = 0;
}
obj1.free();
if (dict->lookup("CP", &obj1)->isName()) {
const char *captionName = obj1.getName();
if(!strcmp(captionName, "Inline")) {
captionPos = captionPosInline;
} else if(!strcmp(captionName, "Top")) {
captionPos = captionPosTop;
} else {
captionPos = captionPosInline;
}
} else {
captionPos = captionPosInline;
}
obj1.free();
if (dict->lookup("Measure", &obj1)->isDict()) {
measure = NULL;
} else {
measure = NULL;
}
obj1.free();
if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
Object obj2;
(obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
captionTextHorizontal = 0);
obj2.free();
(obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
captionTextVertical = 0);
obj2.free();
} else {
captionTextHorizontal = captionTextVertical = 0;
}
obj1.free();
}
void AnnotLine::draw(Gfx *gfx, GBool printing) {
Object obj;
double ca = 1;
if (!isVisible (printing))
return;
/* Some documents like pdf_commenting_new.pdf,
* have y1 = y2 but line_width > 0, acroread
* renders the lines in such cases even though
* the annot bbox is empty. We adjust the bbox here
* to avoid having an empty bbox so that lines
* are rendered
*/
if (rect->y1 == rect->y2)
rect->y2 += border ? border->getWidth() : 1;
if (appearance.isNull()) {
ca = opacity;
appearBuf = new GooString ();
appearBuf->append ("q\n");
if (color)
setColor(color, gFalse);
if (border) {
int i, dashLength;
double *dash;
switch (border->getStyle()) {
case AnnotBorder::borderDashed:
appearBuf->append("[");
dashLength = border->getDashLength();
dash = border->getDash();
for (i = 0; i < dashLength; ++i)
appearBuf->appendf(" {0:.2f}", dash[i]);
appearBuf->append(" ] 0 d\n");
break;
default:
appearBuf->append("[] 0 d\n");
break;
}
appearBuf->appendf("{0:.2f} w\n", border->getWidth());
}
appearBuf->appendf ("{0:.2f} {1:.2f} m\n", coord1->getX() - rect->x1, coord1->getY() - rect->y1);
appearBuf->appendf ("{0:.2f} {1:.2f} l\n", coord2->getX() - rect->x1, coord2->getY() - rect->y1);
// TODO: Line ending, caption, leader lines
appearBuf->append ("S\n");
appearBuf->append ("Q\n");
double bbox[4];
bbox[0] = bbox[1] = 0;
bbox[2] = rect->x2 - rect->x1;
bbox[3] = rect->y2 - rect->y1;
if (ca == 1) {
createForm(bbox, gFalse, NULL, &appearance);
} else {
Object aStream, resDict;
createForm(bbox, gTrue, NULL, &aStream);
delete appearBuf;
appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
createForm(bbox, gFalse, &resDict, &appearance);
}
delete appearBuf;
}
// draw the appearance stream
appearance.fetch(xref, &obj);
gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
rect->x1, rect->y1, rect->x2, rect->y2);
obj.free();
}
//------------------------------------------------------------------------
// AnnotTextMarkup
//------------------------------------------------------------------------
AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, PDFRectangle *rect, AnnotSubtype subType,
AnnotQuadrilaterals *quadPoints, Catalog *catalog) :
AnnotMarkup(xrefA, rect, catalog) {
Object obj1;
switch (subType) {
case typeHighlight:
annotObj.dictSet ("Subtype", obj1.initName ("Highlight"));
break;
case typeUnderline:
annotObj.dictSet ("Subtype", obj1.initName ("Underline"));
break;
case typeSquiggly:
annotObj.dictSet ("Subtype", obj1.initName ("Squiggly"));
break;
case typeStrikeOut:
annotObj.dictSet ("Subtype", obj1.initName ("StrikeOut"));
break;
default:
assert (0 && "Invalid subtype for AnnotTextMarkup\n");
}
Object obj2;
obj2.initArray (xrefA);
for (int i = 0; i < quadPoints->getQuadrilateralsLength(); ++i) {
Object obj3;
obj2.arrayAdd (obj3.initReal (quadPoints->getX1(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getY1(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getX2(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getY2(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getX3(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getY3(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getX4(i)));
obj2.arrayAdd (obj3.initReal (quadPoints->getY4(i)));
}
annotObj.dictSet ("QuadPoints", &obj2);
initialize(xrefA, catalog, annotObj.getDict());
}
AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
AnnotMarkup(xrefA, dict, catalog, obj) {
// the real type will be read in initialize()
type = typeHighlight;
initialize(xrefA, catalog, dict);
}
void AnnotTextMarkup::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
if (dict->lookup("Subtype", &obj1)->isName()) {
GooString typeName(obj1.getName());
if (!typeName.cmp("Highlight")) {
type = typeHighlight;
} else if (!typeName.cmp("Underline")) {
type = typeUnderline;
} else if (!typeName.cmp("Squiggly")) {
type = typeSquiggly;
} else if (!typeName.cmp("StrikeOut")) {
type = typeStrikeOut;
}
}
obj1.free();
if(dict->lookup("QuadPoints", &obj1)->isArray()) {
quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
} else {
error(-1, "Bad Annot Text Markup QuadPoints");
quadrilaterals = NULL;
ok = gFalse;
}
obj1.free();
}
AnnotTextMarkup::~AnnotTextMarkup() {
if(quadrilaterals) {
delete quadrilaterals;
}
}
void AnnotTextMarkup::draw(Gfx *gfx, GBool printing) {
Object obj;
double ca = 1;
int i;
Object obj1, obj2;
if (!isVisible (printing))
return;
if (appearance.isNull() || type == typeHighlight) {
ca = opacity;
appearBuf = new GooString ();
switch (type) {
case typeUnderline:
if (color) {
setColor(color, gFalse);
setColor(color, gTrue);
}
for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
double x1, x2, y3;
double x, y;
x1 = quadrilaterals->getX1(i);
x2 = quadrilaterals->getX2(i);
y3 = quadrilaterals->getY3(i);
x = x1 - rect->x1;
y = y3 - rect->y1;
appearBuf->append ("[]0 d 2 w\n");
appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x, y);
appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x + (x2 - x1), y);
appearBuf->append ("S\n");
}
break;
case typeStrikeOut:
if (color) {
setColor(color, gFalse);
setColor(color, gTrue);
}
for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
double x1, y1, x2, y3;
double x, y;
double h2;
x1 = quadrilaterals->getX1(i);
y1 = quadrilaterals->getY1(i);
x2 = quadrilaterals->getX2(i);
y3 = quadrilaterals->getY3(i);
h2 = (y1 - y3) / 2.0;
x = x1 - rect->x1;
y = (y3 - rect->y1) + h2;
appearBuf->append ("[]0 d 2 w\n");
appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x, y);
appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x + (x2 - x1), y);
appearBuf->append ("S\n");
}
break;
case typeSquiggly:
// TODO
default:
case typeHighlight:
appearance.free();
if (color)
setColor(color, gTrue);
for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
double x1, y1, x2, y2, x3, y3, x4, y4;
double h4;
x1 = quadrilaterals->getX1(i);
y1 = quadrilaterals->getY1(i);
x2 = quadrilaterals->getX2(i);
y2 = quadrilaterals->getY2(i);
x3 = quadrilaterals->getX3(i);
y3 = quadrilaterals->getY3(i);
x4 = quadrilaterals->getX4(i);
y4 = quadrilaterals->getY4(i);
h4 = abs(y1 - y3) / 4.0;
appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x3, y3);
appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
x3 - h4, y3 + h4, x1 - h4, y1 - h4, x1, y1);
appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x2, y2);
appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
x2 + h4, y2 - h4, x4 + h4, y4 + h4, x4, y4);
appearBuf->append ("f\n");
}
Object aStream, resDict;
double bbox[4];
bbox[0] = rect->x1;
bbox[1] = rect->y1;
bbox[2] = rect->x2;
bbox[3] = rect->y2;
createForm(bbox, gTrue, NULL, &aStream);
delete appearBuf;
appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
createResourcesDict("Fm0", &aStream, "GS0", 1, "Multiply", &resDict);
if (ca == 1) {
createForm(bbox, gFalse, &resDict, &appearance);
} else {
createForm(bbox, gTrue, &resDict, &aStream);
delete appearBuf;
appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
createForm(bbox, gFalse, &resDict, &appearance);
}
delete appearBuf;
break;
}
}
// draw the appearance stream
appearance.fetch(xref, &obj);
gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
rect->x1, rect->y1, rect->x2, rect->y2);
obj.free();
}
//------------------------------------------------------------------------
// AnnotWidget
//------------------------------------------------------------------------
AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
Annot(xrefA, dict, catalog, obj) {
type = typeWidget;
field = NULL;
initialize(xrefA, catalog, dict);
}
AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj, FormField *fieldA) :
Annot(xrefA, dict, catalog, obj) {
type = typeWidget;
field = fieldA;
initialize(xrefA, catalog, dict);
}
AnnotWidget::~AnnotWidget() {
if (appearCharacs)
delete appearCharacs;
if (action)
delete action;
if (additionActions)
delete additionActions;
if (parent)
delete parent;
}
void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
Object obj1;
form = catalog->getForm();
if(dict->lookup("H", &obj1)->isName()) {
const char *modeName = obj1.getName();
if(!strcmp(modeName, "N")) {
mode = highlightModeNone;
} else if(!strcmp(modeName, "O")) {
mode = highlightModeOutline;
} else if(!strcmp(modeName, "P") || !strcmp(modeName, "T")) {
mode = highlightModePush;
} else {
mode = highlightModeInvert;
}
} else {
mode = highlightModeInvert;
}
obj1.free();
if(dict->lookup("MK", &obj1)->isDict()) {
appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
} else {
appearCharacs = NULL;
}
obj1.free();
action = NULL;
if(dict->lookup("A", &obj1)->isDict()) {
action = LinkAction::parseAction(&obj1, catalog->getBaseURI());
}
obj1.free();
if(dict->lookup("AA", &obj1)->isDict()) {
additionActions = NULL;
} else {
additionActions = NULL;
}
obj1.free();
if(dict->lookup("Parent", &obj1)->isDict()) {
parent = NULL;
} else {
parent = NULL;
}
obj1.free();
}
// Grand unified handler for preparing text strings to be drawn into form
// fields. Takes as input a text string (in PDFDocEncoding or UTF-16).
// Converts some or all of this string to the appropriate encoding for the
// specified font, and computes the width of the text. Can optionally stop
// converting when a specified width has been reached, to perform line-breaking
// for multi-line fields.
//
// Parameters:
// text: input text string to convert
// outBuf: buffer for writing re-encoded string
// i: index at which to start converting; will be updated to point just after
// last character processed
// font: the font which will be used for display
// width: computed width (unscaled by font size) will be stored here
// widthLimit: if non-zero, stop converting to keep width under this value
// (should be scaled down by font size)
// charCount: count of number of characters will be stored here
// noReencode: if set, do not try to translate the character encoding
// (useful for Zapf Dingbats or other unusual encodings)
// can only be used with simple fonts, not CID-keyed fonts
//
// TODO: Handle surrogate pairs in UTF-16.
// Should be able to generate output for any CID-keyed font.
// Doesn't handle vertical fonts--should it?
void AnnotWidget::layoutText(GooString *text, GooString *outBuf, int *i,
GfxFont *font, double *width, double widthLimit,
int *charCount, GBool noReencode)
{
CharCode c;
Unicode uChar, *uAux;
double w = 0.0;
int uLen, n;
double dx, dy, ox, oy;
GBool unicode = text->hasUnicodeMarker();
GBool spacePrev; // previous character was a space
// State for backtracking when more text has been processed than fits within
// widthLimit. We track for both input (text) and output (outBuf) the offset
// to the first character to discard.
//
// We keep track of several points:
// 1 - end of previous completed word which fits
// 2 - previous character which fit
int last_i1, last_i2, last_o1, last_o2;
if (unicode && text->getLength() % 2 != 0) {
error(-1, "AnnotWidget::layoutText, bad unicode string");
return;
}
// skip Unicode marker on string if needed
if (unicode && *i == 0)
*i = 2;
// Start decoding and copying characters, until either:
// we reach the end of the string
// we reach the maximum width
// we reach a newline character
// As we copy characters, keep track of the last full word to fit, so that we
// can backtrack if we exceed the maximum width.
last_i1 = last_i2 = *i;
last_o1 = last_o2 = 0;
spacePrev = gFalse;
outBuf->clear();
while (*i < text->getLength()) {
last_i2 = *i;
last_o2 = outBuf->getLength();
if (unicode) {
uChar = (unsigned char)(text->getChar(*i)) << 8;
uChar += (unsigned char)(text->getChar(*i + 1));
*i += 2;
} else {
if (noReencode)
uChar = text->getChar(*i) & 0xff;
else
uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
*i += 1;
}
// Explicit line break?
if (uChar == '\r' || uChar == '\n') {
// Treat a <CR><LF> sequence as a single line break
if (uChar == '\r' && *i < text->getLength()) {
if (unicode && text->getChar(*i) == '\0'
&& text->getChar(*i + 1) == '\n')
*i += 2;
else if (!unicode && text->getChar(*i) == '\n')
*i += 1;
}
break;
}
if (noReencode)