1 //========================================================================
5 // Copyright 2000-2003 Glyph & Cog, LLC
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
17 // Copyright (C) 2007, 2008 Julien Rebetez <julienr@svn.gnome.org>
18 // Copyright (C) 2007-2013, 2015 Albert Astals Cid <aacid@kde.org>
19 // Copyright (C) 2007-2013 Carlos Garcia Campos <carlosgc@gnome.org>
20 // Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
21 // Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
22 // Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
23 // Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
24 // Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
25 // Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
26 // Copyright (C) 2011, 2013 José Aliste <jaliste@src.gnome.org>
27 // Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
28 // Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
29 // Copyright (C) 2012, 2015 Tobias Koenig <tokoe@kdab.com>
30 // Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de>
31 // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
32 // Copyright (C) 2014, 2015 Marek Kasik <mkasik@redhat.com>
33 // Copyright (C) 2014 Jiri Slaby <jirislaby@gmail.com>
34 // Copyright (C) 2014 Anuj Khare <khareanuj18@gmail.com>
35 // Copyright (C) 2015 Petr Gajdos <pgajdos@suse.cz>
36 // Copyright (C) 2015 Philipp Reinkemeier <philipp.reinkemeier@offis.de>
37 // Copyright (C) 2015 Tamas Szekeres <szekerest@gmail.com>
39 // To see a description of the changes please see the Changelog file that
40 // came with your tarball or type make ChangeLog if you are building from git
42 //========================================================================
46 #ifdef USE_GCC_PRAGMAS
47 #pragma implementation
54 #include "goo/gstrtod.h"
65 #include "CharCodeToUnicode.h"
66 #include "PDFDocEncoding.h"
71 #include "OptionalContent.h"
80 # define annotLocker() MutexLocker locker(&mutex)
81 # define annotCondLocker(X) MutexLocker locker(&mutex, (X))
83 # define annotLocker()
84 # define annotCondLocker(X)
87 #define fieldFlagReadOnly 0x00000001
88 #define fieldFlagRequired 0x00000002
89 #define fieldFlagNoExport 0x00000004
90 #define fieldFlagMultiline 0x00001000
91 #define fieldFlagPassword 0x00002000
92 #define fieldFlagNoToggleToOff 0x00004000
93 #define fieldFlagRadio 0x00008000
94 #define fieldFlagPushbutton 0x00010000
95 #define fieldFlagCombo 0x00020000
96 #define fieldFlagEdit 0x00040000
97 #define fieldFlagSort 0x00080000
98 #define fieldFlagFileSelect 0x00100000
99 #define fieldFlagMultiSelect 0x00200000
100 #define fieldFlagDoNotSpellCheck 0x00400000
101 #define fieldFlagDoNotScroll 0x00800000
102 #define fieldFlagComb 0x01000000
103 #define fieldFlagRichText 0x02000000
104 #define fieldFlagRadiosInUnison 0x02000000
105 #define fieldFlagCommitOnSelChange 0x04000000
107 #define fieldQuadLeft 0
108 #define fieldQuadCenter 1
109 #define fieldQuadRight 2
111 // distance of Bezier control point from center for circle approximation
112 // = (4 * (sqrt(2) - 1) / 3) * r
113 #define bezierCircle 0.55228475
115 AnnotLineEndingStyle
parseAnnotLineEndingStyle(GooString
*string
) {
116 if (string
!= NULL
) {
117 if (!string
->cmp("Square")) {
118 return annotLineEndingSquare
;
119 } else if (!string
->cmp("Circle")) {
120 return annotLineEndingCircle
;
121 } else if (!string
->cmp("Diamond")) {
122 return annotLineEndingDiamond
;
123 } else if (!string
->cmp("OpenArrow")) {
124 return annotLineEndingOpenArrow
;
125 } else if (!string
->cmp("ClosedArrow")) {
126 return annotLineEndingClosedArrow
;
127 } else if (!string
->cmp("Butt")) {
128 return annotLineEndingButt
;
129 } else if (!string
->cmp("ROpenArrow")) {
130 return annotLineEndingROpenArrow
;
131 } else if (!string
->cmp("RClosedArrow")) {
132 return annotLineEndingRClosedArrow
;
133 } else if (!string
->cmp("Slash")) {
134 return annotLineEndingSlash
;
136 return annotLineEndingNone
;
139 return annotLineEndingNone
;
143 const char* convertAnnotLineEndingStyle(AnnotLineEndingStyle style
) {
145 case annotLineEndingSquare
:
147 case annotLineEndingCircle
:
149 case annotLineEndingDiamond
:
151 case annotLineEndingOpenArrow
:
153 case annotLineEndingClosedArrow
:
154 return "ClosedArrow";
155 case annotLineEndingButt
:
157 case annotLineEndingROpenArrow
:
159 case annotLineEndingRClosedArrow
:
160 return "RClosedArrow";
161 case annotLineEndingSlash
:
168 static AnnotExternalDataType
parseAnnotExternalData(Dict
* dict
) {
170 AnnotExternalDataType type
;
172 if (dict
->lookup("Subtype", &obj1
)->isName()) {
173 const char *typeName
= obj1
.getName();
175 if (!strcmp(typeName
, "Markup3D")) {
176 type
= annotExternalDataMarkup3D
;
178 type
= annotExternalDataMarkupUnknown
;
181 type
= annotExternalDataMarkupUnknown
;
188 PDFRectangle
*parseDiffRectangle(Array
*array
, PDFRectangle
*rect
) {
189 PDFRectangle
*newRect
= NULL
;
190 if (array
->getLength() == 4) {
193 double dx1
= (array
->get(0, &obj1
)->isNum() ? obj1
.getNum() : 0);
195 double dy1
= (array
->get(1, &obj1
)->isNum() ? obj1
.getNum() : 0);
197 double dx2
= (array
->get(2, &obj1
)->isNum() ? obj1
.getNum() : 0);
199 double dy2
= (array
->get(3, &obj1
)->isNum() ? obj1
.getNum() : 0);
202 // checking that the numbers are valid (i.e. >= 0),
203 // and that applying the differences still give us a valid rect
204 if (dx1
>= 0 && dy1
>= 0 && dx2
>= 0 && dy2
205 && (rect
->x2
- rect
->x1
- dx1
- dx2
) >= 0
206 && (rect
->y2
- rect
->y1
- dy1
- dy2
) >= 0) {
207 newRect
= new PDFRectangle();
208 newRect
->x1
= rect
->x1
+ dx1
;
209 newRect
->y1
= rect
->y1
+ dy1
;
210 newRect
->x2
= rect
->x2
- dx2
;
211 newRect
->y2
= rect
->y2
- dy2
;
217 static LinkAction
* getAdditionalAction(Annot::AdditionalActionsType type
, Object
*additionalActions
, PDFDoc
*doc
) {
218 Object additionalActionsObject
;
219 LinkAction
*linkAction
= NULL
;
221 if (additionalActions
->fetch(doc
->getXRef(), &additionalActionsObject
)->isDict()) {
222 const char *key
= (type
== Annot::actionCursorEntering
? "E" :
223 type
== Annot::actionCursorLeaving
? "X" :
224 type
== Annot::actionMousePressed
? "D" :
225 type
== Annot::actionMouseReleased
? "U" :
226 type
== Annot::actionFocusIn
? "Fo" :
227 type
== Annot::actionFocusOut
? "BI" :
228 type
== Annot::actionPageOpening
? "PO" :
229 type
== Annot::actionPageClosing
? "PC" :
230 type
== Annot::actionPageVisible
? "PV" :
231 type
== Annot::actionPageInvisible
? "PI" : NULL
);
235 if (additionalActionsObject
.dictLookup(key
, &actionObject
)->isDict())
236 linkAction
= LinkAction::parseAction(&actionObject
, doc
->getCatalog()->getBaseURI());
240 additionalActionsObject
.free();
245 static LinkAction
* getFormAdditionalAction(Annot::FormAdditionalActionsType type
, Object
*additionalActions
, PDFDoc
*doc
) {
246 Object additionalActionsObject
;
247 LinkAction
*linkAction
= NULL
;
249 if (additionalActions
->fetch(doc
->getXRef(), &additionalActionsObject
)->isDict()) {
250 const char *key
= (type
== Annot::actionFieldModified
? "K" :
251 type
== Annot::actionFormatField
? "F" :
252 type
== Annot::actionValidateField
? "V" :
253 type
== Annot::actionCalculateField
? "C" : NULL
);
257 if (additionalActionsObject
.dictLookup(key
, &actionObject
)->isDict())
258 linkAction
= LinkAction::parseAction(&actionObject
, doc
->getCatalog()->getBaseURI());
262 additionalActionsObject
.free();
267 //------------------------------------------------------------------------
269 //------------------------------------------------------------------------
271 AnnotBorderEffect::AnnotBorderEffect(Dict
*dict
) {
274 if (dict
->lookup("S", &obj1
)->isName()) {
275 const char *effectName
= obj1
.getName();
277 if (!strcmp(effectName
, "C"))
278 effectType
= borderEffectCloudy
;
280 effectType
= borderEffectNoEffect
;
282 effectType
= borderEffectNoEffect
;
286 if ((dict
->lookup("I", &obj1
)->isNum()) && effectType
== borderEffectCloudy
) {
287 intensity
= obj1
.getNum();
294 //------------------------------------------------------------------------
296 //------------------------------------------------------------------------
298 AnnotPath::AnnotPath() {
303 AnnotPath::AnnotPath(Array
*array
) {
306 parsePathArray(array
);
309 AnnotPath::AnnotPath(AnnotCoord
**coords
, int coordsLength
) {
310 this->coords
= coords
;
311 this->coordsLength
= coordsLength
;
314 AnnotPath::~AnnotPath() {
316 for (int i
= 0; i
< coordsLength
; ++i
)
322 double AnnotPath::getX(int coord
) const {
323 if (coord
>= 0 && coord
< coordsLength
)
324 return coords
[coord
]->getX();
328 double AnnotPath::getY(int coord
) const {
329 if (coord
>= 0 && coord
< coordsLength
)
330 return coords
[coord
]->getY();
334 AnnotCoord
*AnnotPath::getCoord(int coord
) const {
335 if (coord
>= 0 && coord
< coordsLength
)
336 return coords
[coord
];
340 void AnnotPath::parsePathArray(Array
*array
) {
342 AnnotCoord
**tempCoords
;
343 GBool correct
= gTrue
;
345 if (array
->getLength() % 2) {
346 error(errSyntaxError
, -1, "Bad Annot Path");
350 tempLength
= array
->getLength() / 2;
351 tempCoords
= (AnnotCoord
**) gmallocn (tempLength
, sizeof(AnnotCoord
*));
352 memset(tempCoords
, 0, tempLength
* sizeof(AnnotCoord
*));
353 for (int i
= 0; i
< tempLength
&& correct
; i
++) {
357 if (array
->get(i
* 2, &obj1
)->isNum()) {
364 if (array
->get((i
* 2) + 1, &obj1
)->isNum()) {
372 for (int j
= i
- 1; j
>= 0; j
--)
373 delete tempCoords
[j
];
378 tempCoords
[i
] = new AnnotCoord(x
, y
);
382 coordsLength
= tempLength
;
385 //------------------------------------------------------------------------
387 //------------------------------------------------------------------------
389 AnnotCalloutLine::AnnotCalloutLine(double x1
, double y1
, double x2
, double y2
)
390 : coord1(x1
, y1
), coord2(x2
, y2
) {
393 //------------------------------------------------------------------------
394 // AnnotCalloutMultiLine
395 //------------------------------------------------------------------------
397 AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1
, double y1
, double x2
,
398 double y2
, double x3
, double y3
)
399 : AnnotCalloutLine(x1
, y1
, x2
, y2
), coord3(x3
, y3
) {
402 //------------------------------------------------------------------------
403 // AnnotQuadrilateral
404 //------------------------------------------------------------------------
406 AnnotQuadrilaterals::AnnotQuadrilaterals(Array
*array
, PDFRectangle
*rect
) {
407 int arrayLength
= array
->getLength();
408 GBool correct
= gTrue
;
410 AnnotQuadrilateral
**quads
;
414 quadrilaterals
= NULL
;
415 quadrilateralsLength
= 0;
417 if ((arrayLength
% 8) == 0) {
420 quadsLength
= arrayLength
/ 8;
421 quads
= (AnnotQuadrilateral
**) gmallocn
422 ((quadsLength
), sizeof(AnnotQuadrilateral
*));
423 memset(quads
, 0, quadsLength
* sizeof(AnnotQuadrilateral
*));
425 for (i
= 0; i
< quadsLength
; i
++) {
426 for (int j
= 0; j
< 8; j
++) {
428 if (array
->get(i
* 8 + j
, &obj
)->isNum()) {
429 quadArray
[j
] = obj
.getNum();
433 error (errSyntaxError
, -1, "Invalid QuadPoint in annot");
442 quads
[i
] = new AnnotQuadrilateral(quadArray
[0], quadArray
[1],
443 quadArray
[2], quadArray
[3],
444 quadArray
[4], quadArray
[5],
445 quadArray
[6], quadArray
[7]);
449 quadrilateralsLength
= quadsLength
;
450 quadrilaterals
= quads
;
452 for (int j
= 0; j
< i
; j
++)
459 AnnotQuadrilaterals::AnnotQuadrilaterals(AnnotQuadrilaterals::AnnotQuadrilateral
**quads
, int quadsLength
) {
460 quadrilaterals
= quads
;
461 quadrilateralsLength
= quadsLength
;
464 AnnotQuadrilaterals::~AnnotQuadrilaterals() {
465 if (quadrilaterals
) {
466 for(int i
= 0; i
< quadrilateralsLength
; i
++)
467 delete quadrilaterals
[i
];
469 gfree (quadrilaterals
);
473 double AnnotQuadrilaterals::getX1(int quadrilateral
) {
474 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
475 return quadrilaterals
[quadrilateral
]->coord1
.getX();
479 double AnnotQuadrilaterals::getY1(int quadrilateral
) {
480 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
481 return quadrilaterals
[quadrilateral
]->coord1
.getY();
485 double AnnotQuadrilaterals::getX2(int quadrilateral
) {
486 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
487 return quadrilaterals
[quadrilateral
]->coord2
.getX();
491 double AnnotQuadrilaterals::getY2(int quadrilateral
) {
492 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
493 return quadrilaterals
[quadrilateral
]->coord2
.getY();
497 double AnnotQuadrilaterals::getX3(int quadrilateral
) {
498 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
499 return quadrilaterals
[quadrilateral
]->coord3
.getX();
503 double AnnotQuadrilaterals::getY3(int quadrilateral
) {
504 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
505 return quadrilaterals
[quadrilateral
]->coord3
.getY();
509 double AnnotQuadrilaterals::getX4(int quadrilateral
) {
510 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
511 return quadrilaterals
[quadrilateral
]->coord4
.getX();
515 double AnnotQuadrilaterals::getY4(int quadrilateral
) {
516 if (quadrilateral
>= 0 && quadrilateral
< quadrilateralsLength
)
517 return quadrilaterals
[quadrilateral
]->coord4
.getY();
521 AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1
, double y1
,
522 double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
523 : coord1(x1
, y1
), coord2(x2
, y2
), coord3(x3
, y3
), coord4(x4
, y4
) {
526 //------------------------------------------------------------------------
528 //------------------------------------------------------------------------
529 AnnotBorder::AnnotBorder() {
536 GBool
AnnotBorder::parseDashArray(Object
*dashObj
) {
537 GBool correct
= gTrue
;
538 int tempLength
= dashObj
->arrayGetLength();
539 double *tempDash
= (double *) gmallocn (tempLength
, sizeof (double));
541 // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
542 for (int i
= 0; i
< tempLength
&& i
< DASH_LIMIT
&& correct
; i
++) {
545 if (dashObj
->arrayGet(i
, &obj1
)->isNum()) {
546 tempDash
[i
] = obj1
.getNum();
548 correct
= tempDash
[i
] >= 0;
554 dashLength
= tempLength
;
556 style
= borderDashed
;
564 AnnotBorder::~AnnotBorder() {
569 //------------------------------------------------------------------------
571 //------------------------------------------------------------------------
573 AnnotBorderArray::AnnotBorderArray() {
574 horizontalCorner
= 0;
578 AnnotBorderArray::AnnotBorderArray(Array
*array
) {
580 int arrayLength
= array
->getLength();
582 GBool correct
= gTrue
;
583 if (arrayLength
== 3 || arrayLength
== 4) {
584 // implementation note 81 in Appendix H.
586 if (array
->get(0, &obj1
)->isNum())
587 horizontalCorner
= obj1
.getNum();
592 if (array
->get(1, &obj1
)->isNum())
593 verticalCorner
= obj1
.getNum();
598 if (array
->get(2, &obj1
)->isNum())
599 width
= obj1
.getNum();
604 if (arrayLength
== 4) {
605 if (array
->get(3, &obj1
)->isArray())
606 correct
= parseDashArray(&obj1
);
620 void AnnotBorderArray::writeToObject(XRef
*xref
, Object
*obj1
) const {
623 obj1
->initArray(xref
);
624 obj1
->arrayAdd(obj2
.initReal(horizontalCorner
));
625 obj1
->arrayAdd(obj2
.initReal(verticalCorner
));
626 obj1
->arrayAdd(obj2
.initReal(width
));
628 if (dashLength
> 0) {
631 obj1
->arrayAdd(obj3
.initArray(xref
));
632 for (int i
= 0; i
< dashLength
; i
++)
633 obj3
.arrayAdd(obj2
.initReal(dash
[i
]));
637 //------------------------------------------------------------------------
639 //------------------------------------------------------------------------
641 AnnotBorderBS::AnnotBorderBS() {
644 AnnotBorderBS::AnnotBorderBS(Dict
*dict
) {
647 // acroread 8 seems to need both W and S entries for
648 // any border to be drawn, even though the spec
649 // doesn't claim anything of that sort. We follow
650 // that behaviour by veryifying both entries exist
651 // otherwise we set the borderWidth to 0
653 dict
->lookup("W", &obj1
);
654 dict
->lookup("S", &obj2
);
655 if (obj1
.isNum() && obj2
.isName()) {
656 const char *styleName
= obj2
.getName();
658 width
= obj1
.getNum();
660 if (!strcmp(styleName
, "S")) {
662 } else if (!strcmp(styleName
, "D")) {
663 style
= borderDashed
;
664 } else if (!strcmp(styleName
, "B")) {
665 style
= borderBeveled
;
666 } else if (!strcmp(styleName
, "I")) {
668 } else if (!strcmp(styleName
, "U")) {
669 style
= borderUnderlined
;
679 if (style
== borderDashed
) {
680 if (dict
->lookup("D", &obj1
)->isArray())
681 parseDashArray(&obj1
);
686 dash
= (double *) gmallocn (dashLength
, sizeof (double));
692 const char *AnnotBorderBS::getStyleName() const {
702 case borderUnderlined
:
709 void AnnotBorderBS::writeToObject(XRef
*xref
, Object
*obj1
) const {
712 obj1
->initDict(xref
);
713 obj1
->dictSet("W", obj2
.initReal(width
));
714 obj1
->dictSet("S", obj2
.initName(getStyleName()));
715 if (style
== borderDashed
&& dashLength
> 0) {
718 obj1
->dictSet("D", obj3
.initArray(xref
));
719 for (int i
= 0; i
< dashLength
; i
++)
720 obj3
.arrayAdd(obj2
.initReal(dash
[i
]));
724 //------------------------------------------------------------------------
726 //------------------------------------------------------------------------
728 AnnotColor::AnnotColor() {
732 AnnotColor::AnnotColor(double gray
) {
738 AnnotColor::AnnotColor(double r
, double g
, double b
) {
746 AnnotColor::AnnotColor(double c
, double m
, double y
, double k
) {
755 // If <adjust> is +1, color is brightened;
756 // if <adjust> is -1, color is darkened;
757 // otherwise color is not modified.
758 AnnotColor::AnnotColor(Array
*array
, int adjust
) {
761 length
= array
->getLength();
765 for (i
= 0; i
< length
; i
++) {
768 if (array
->get(i
, &obj1
)->isNum()) {
769 values
[i
] = obj1
.getNum();
771 if (values
[i
] < 0 || values
[i
] > 1)
783 void AnnotColor::adjustColor(int adjust
) {
790 for (i
= 0; i
< length
; ++i
) {
791 values
[i
] = 0.5 * values
[i
] + 0.5;
793 } else if (adjust
< 0) {
794 for (i
= 0; i
< length
; ++i
) {
795 values
[i
] = 0.5 * values
[i
];
800 void AnnotColor::writeToObject(XRef
*xref
, Object
*obj1
) const {
805 obj1
->initNull(); // Transparent (no color)
807 obj1
->initArray(xref
);
808 for (i
= 0; i
< length
; ++i
)
809 obj1
->arrayAdd( obj2
.initReal( values
[i
] ) );
813 //------------------------------------------------------------------------
815 //------------------------------------------------------------------------
817 AnnotIconFit::AnnotIconFit(Dict
* dict
) {
820 if (dict
->lookup("SW", &obj1
)->isName()) {
821 const char *scaleName
= obj1
.getName();
823 if(!strcmp(scaleName
, "B")) {
824 scaleWhen
= scaleBigger
;
825 } else if(!strcmp(scaleName
, "S")) {
826 scaleWhen
= scaleSmaller
;
827 } else if(!strcmp(scaleName
, "N")) {
828 scaleWhen
= scaleNever
;
830 scaleWhen
= scaleAlways
;
833 scaleWhen
= scaleAlways
;
837 if (dict
->lookup("S", &obj1
)->isName()) {
838 const char *scaleName
= obj1
.getName();
840 if(!strcmp(scaleName
, "A")) {
841 scale
= scaleAnamorphic
;
843 scale
= scaleProportional
;
846 scale
= scaleProportional
;
850 if (dict
->lookup("A", &obj1
)->isArray() && obj1
.arrayGetLength() == 2) {
852 (obj1
.arrayGet(0, &obj2
)->isNum() ? left
= obj2
.getNum() : left
= 0);
854 (obj1
.arrayGet(1, &obj2
)->isNum() ? bottom
= obj2
.getNum() : bottom
= 0);
857 if (left
< 0 || left
> 1)
860 if (bottom
< 0 || bottom
> 1)
868 if (dict
->lookup("FB", &obj1
)->isBool()) {
869 fullyBounds
= obj1
.getBool();
871 fullyBounds
= gFalse
;
876 //------------------------------------------------------------------------
878 //------------------------------------------------------------------------
880 AnnotAppearance::AnnotAppearance(PDFDoc
*docA
, Object
*dict
) {
881 assert(dict
->isDict());
883 xref
= docA
->getXRef();
884 dict
->copy(&appearDict
);
887 AnnotAppearance::~AnnotAppearance() {
891 void AnnotAppearance::getAppearanceStream(AnnotAppearanceType type
, const char *state
, Object
*dest
) {
892 Object apData
, stream
;
895 // Obtain dictionary or stream associated to appearance type
898 if (appearDict
.dictLookupNF("R", &apData
)->isNull())
899 appearDict
.dictLookupNF("N", &apData
);
902 if (appearDict
.dictLookupNF("D", &apData
)->isNull())
903 appearDict
.dictLookupNF("N", &apData
);
906 appearDict
.dictLookupNF("N", &apData
);
911 if (apData
.isDict() && state
)
912 apData
.dictLookupNF(state
, dest
);
913 else if (apData
.isRef())
918 GooString
* AnnotAppearance::getStateKey(int i
) {
920 GooString
* res
= NULL
;
921 if (appearDict
.dictLookupNF("N", &obj1
)->isDict())
922 res
= new GooString(obj1
.dictGetKey(i
));
927 int AnnotAppearance::getNumStates() {
930 if (appearDict
.dictLookupNF("N", &obj1
)->isDict())
931 res
= obj1
.dictGetLength();
936 // Test if stateObj (a Ref or a Dict) points to the specified stream
937 GBool
AnnotAppearance::referencesStream(Object
*stateObj
, Ref refToStream
) {
938 if (stateObj
->isRef()) {
939 Ref r
= stateObj
->getRef();
940 if (r
.num
== refToStream
.num
&& r
.gen
== refToStream
.gen
) {
943 } else if (stateObj
->isDict()) { // Test each value
944 const int size
= stateObj
->dictGetLength();
945 for (int i
= 0; i
< size
; ++i
) {
947 stateObj
->dictGetValNF(i
, &obj1
);
949 Ref r
= obj1
.getRef();
950 if (r
.num
== refToStream
.num
&& r
.gen
== refToStream
.gen
) {
957 return gFalse
; // Not found
960 // Test if this AnnotAppearance references the specified stream
961 GBool
AnnotAppearance::referencesStream(Ref refToStream
) {
965 // Scan each state's ref/subdictionary
966 appearDict
.dictLookupNF("N", &obj1
);
967 found
= referencesStream(&obj1
, refToStream
);
972 appearDict
.dictLookupNF("R", &obj1
);
973 found
= referencesStream(&obj1
, refToStream
);
978 appearDict
.dictLookupNF("D", &obj1
);
979 found
= referencesStream(&obj1
, refToStream
);
984 // If this is the only annotation in the document that references the
985 // specified appearance stream, remove the appearance stream
986 void AnnotAppearance::removeStream(Ref refToStream
) {
987 const int lastpage
= doc
->getNumPages();
988 for (int pg
= 1; pg
<= lastpage
; ++pg
) { // Scan all annotations in the document
989 Page
*page
= doc
->getPage(pg
);
991 error(errSyntaxError
, -1, "Failed check for shared annotation stream at page {0:d}", pg
);
994 Annots
*annots
= page
->getAnnots();
995 for (int i
= 0; i
< annots
->getNumAnnots(); ++i
) {
996 AnnotAppearance
*annotAp
= annots
->getAnnot(i
)->getAppearStreams();
997 if (annotAp
&& annotAp
!= this && annotAp
->referencesStream(refToStream
)) {
998 return; // Another annotation points to the stream -> Don't delete it
1003 // TODO: stream resources (e.g. font), AP name tree
1004 xref
->removeIndirectObject(refToStream
);
1007 // Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict
1008 void AnnotAppearance::removeStateStreams(Object
*obj1
) {
1009 if (obj1
->isRef()) {
1010 removeStream(obj1
->getRef());
1011 } else if (obj1
->isDict()) {
1012 const int size
= obj1
->dictGetLength();
1013 for (int i
= 0; i
< size
; ++i
) {
1015 obj1
->dictGetValNF(i
, &obj2
);
1017 removeStream(obj2
.getRef());
1024 void AnnotAppearance::removeAllStreams() {
1026 appearDict
.dictLookupNF("N", &obj1
);
1027 removeStateStreams(&obj1
);
1029 appearDict
.dictLookupNF("R", &obj1
);
1030 removeStateStreams(&obj1
);
1032 appearDict
.dictLookupNF("D", &obj1
);
1033 removeStateStreams(&obj1
);
1037 //------------------------------------------------------------------------
1038 // AnnotAppearanceCharacs
1039 //------------------------------------------------------------------------
1041 AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict
*dict
) {
1044 if (dict
->lookup("R", &obj1
)->isInt()) {
1045 rotation
= obj1
.getInt();
1051 if (dict
->lookup("BC", &obj1
)->isArray()) {
1052 Array
*colorComponents
= obj1
.getArray();
1053 if (colorComponents
->getLength() > 0) {
1054 borderColor
= new AnnotColor(colorComponents
);
1063 if (dict
->lookup("BG", &obj1
)->isArray()) {
1064 Array
*colorComponents
= obj1
.getArray();
1065 if (colorComponents
->getLength() > 0) {
1066 backColor
= new AnnotColor(colorComponents
);
1075 if (dict
->lookup("CA", &obj1
)->isString()) {
1076 normalCaption
= new GooString(obj1
.getString());
1078 normalCaption
= NULL
;
1082 if (dict
->lookup("RC", &obj1
)->isString()) {
1083 rolloverCaption
= new GooString(obj1
.getString());
1085 rolloverCaption
= NULL
;
1089 if (dict
->lookup("AC", &obj1
)->isString()) {
1090 alternateCaption
= new GooString(obj1
.getString());
1092 alternateCaption
= NULL
;
1096 if (dict
->lookup("IF", &obj1
)->isDict()) {
1097 iconFit
= new AnnotIconFit(obj1
.getDict());
1103 if (dict
->lookup("TP", &obj1
)->isInt()) {
1104 position
= (AnnotAppearanceCharacsTextPos
) obj1
.getInt();
1106 position
= captionNoIcon
;
1111 AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
1119 delete normalCaption
;
1121 if (rolloverCaption
)
1122 delete rolloverCaption
;
1124 if (alternateCaption
)
1125 delete alternateCaption
;
1131 //------------------------------------------------------------------------
1132 // AnnotAppearanceBBox
1133 //------------------------------------------------------------------------
1135 AnnotAppearanceBBox::AnnotAppearanceBBox(PDFRectangle
*rect
) {
1140 // Initially set the same size as rect
1143 maxX
= rect
->x2
- rect
->x1
;
1144 maxY
= rect
->y2
- rect
->y1
;
1147 void AnnotAppearanceBBox::extendTo(double x
, double y
) {
1150 } else if (x
> maxX
) {
1155 } else if (y
> maxY
) {
1160 void AnnotAppearanceBBox::getBBoxRect(double bbox
[4]) const {
1161 bbox
[0] = minX
- borderWidth
;
1162 bbox
[1] = minY
- borderWidth
;
1163 bbox
[2] = maxX
+ borderWidth
;
1164 bbox
[3] = maxY
+ borderWidth
;
1167 double AnnotAppearanceBBox::getPageXMin() const {
1168 return origX
+ minX
- borderWidth
;
1171 double AnnotAppearanceBBox::getPageYMin() const {
1172 return origY
+ minY
- borderWidth
;
1175 double AnnotAppearanceBBox::getPageXMax() const {
1176 return origX
+ maxX
+ borderWidth
;
1179 double AnnotAppearanceBBox::getPageYMax() const {
1180 return origY
+ maxY
+ borderWidth
;
1183 //------------------------------------------------------------------------
1185 //------------------------------------------------------------------------
1187 Annot::Annot(PDFDoc
*docA
, PDFRectangle
*rectA
) {
1191 flags
= flagUnknown
;
1194 obj1
.initArray (docA
->getXRef());
1196 obj1
.arrayAdd (obj2
.initReal (rectA
->x1
));
1197 obj1
.arrayAdd (obj2
.initReal (rectA
->y1
));
1198 obj1
.arrayAdd (obj2
.initReal (rectA
->x2
));
1199 obj1
.arrayAdd (obj2
.initReal (rectA
->y2
));
1202 annotObj
.initDict (docA
->getXRef());
1203 annotObj
.dictSet ("Type", obj2
.initName ("Annot"));
1204 annotObj
.dictSet ("Rect", &obj1
);
1205 // obj1 is owned by the dict
1207 ref
= docA
->getXRef()->addIndirectObject (&annotObj
);
1209 initialize (docA
, annotObj
.getDict());
1212 Annot::Annot(PDFDoc
*docA
, Dict
*dict
) {
1215 flags
= flagUnknown
;
1217 annotObj
.initDict (dict
);
1218 initialize (docA
, dict
);
1221 Annot::Annot(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) {
1225 ref
= obj
->getRef();
1229 flags
= flagUnknown
;
1231 annotObj
.initDict (dict
);
1232 initialize (docA
, dict
);
1235 void Annot::initialize(PDFDoc
*docA
, Dict
*dict
) {
1236 Object apObj
, asObj
, obj1
, obj2
;
1240 xref
= doc
->getXRef();
1241 appearStreams
= NULL
;
1247 appearance
.initNull();
1249 //----- parse the rectangle
1250 rect
= new PDFRectangle();
1251 if (dict
->lookup("Rect", &obj1
)->isArray() && obj1
.arrayGetLength() == 4) {
1253 (obj1
.arrayGet(0, &obj2
)->isNum() ? rect
->x1
= obj2
.getNum() : rect
->x1
= 0);
1255 (obj1
.arrayGet(1, &obj2
)->isNum() ? rect
->y1
= obj2
.getNum() : rect
->y1
= 0);
1257 (obj1
.arrayGet(2, &obj2
)->isNum() ? rect
->x2
= obj2
.getNum() : rect
->x2
= 1);
1259 (obj1
.arrayGet(3, &obj2
)->isNum() ? rect
->y2
= obj2
.getNum() : rect
->y2
= 1);
1262 if (rect
->x1
> rect
->x2
) {
1263 double t
= rect
->x1
;
1264 rect
->x1
= rect
->x2
;
1268 if (rect
->y1
> rect
->y2
) {
1269 double t
= rect
->y1
;
1270 rect
->y1
= rect
->y2
;
1274 rect
->x1
= rect
->y1
= 0;
1275 rect
->x2
= rect
->y2
= 1;
1276 error(errSyntaxError
, -1, "Bad bounding box for annotation");
1281 if (dict
->lookup("Contents", &obj1
)->isString()) {
1282 contents
= obj1
.getString()->copy();
1284 contents
= new GooString();
1288 // Note: This value is overwritten by Annots ctor
1289 if (dict
->lookupNF("P", &obj1
)->isRef()) {
1290 Ref ref
= obj1
.getRef();
1292 page
= doc
->getCatalog()->findPage (ref
.num
, ref
.gen
);
1298 if (dict
->lookup("NM", &obj1
)->isString()) {
1299 name
= obj1
.getString()->copy();
1305 if (dict
->lookup("M", &obj1
)->isString()) {
1306 modified
= obj1
.getString()->copy();
1312 //----- get the flags
1313 if (dict
->lookup("F", &obj1
)->isInt()) {
1314 flags
|= obj1
.getInt();
1316 flags
= flagUnknown
;
1320 //----- get the annotation appearance dictionary
1321 dict
->lookup("AP", &apObj
);
1322 if (apObj
.isDict()) {
1323 appearStreams
= new AnnotAppearance(doc
, &apObj
);
1327 //----- get the appearance state
1328 dict
->lookup("AS", &asObj
);
1329 if (asObj
.isName()) {
1330 appearState
= new GooString(asObj
.getName());
1331 } else if (appearStreams
&& appearStreams
->getNumStates() != 0) {
1332 error (errSyntaxError
, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries");
1333 // AS value is required in this case, but if the
1334 // N dictionary contains only one entry
1335 // take it as default appearance.
1336 if (appearStreams
->getNumStates() == 1) {
1337 appearState
= appearStreams
->getStateKey(0);
1341 appearState
= new GooString("Off");
1345 //----- get the annotation appearance
1346 if (appearStreams
) {
1347 appearStreams
->getAppearanceStream(AnnotAppearance::appearNormal
, appearState
->getCString(), &appearance
);
1350 //----- parse the border style
1351 // According to the spec if neither the Border nor the BS entry is present,
1352 // the border shall be drawn as a solid line with a width of 1 point. But acroread
1353 // seems to ignore the Border entry for annots that can't have a BS entry. So, we only
1354 // follow this rule for annots tha can have a BS entry.
1355 if (dict
->lookup("Border", &obj1
)->isArray())
1356 border
= new AnnotBorderArray(obj1
.getArray());
1361 if (dict
->lookup("C", &obj1
)->isArray()) {
1362 color
= new AnnotColor(obj1
.getArray());
1368 if (dict
->lookup("StructParent", &obj1
)->isInt()) {
1369 treeKey
= obj1
.getInt();
1375 dict
->lookupNF("OC", &oc
);
1382 void Annot::getRect(double *x1
, double *y1
, double *x2
, double *y2
) const {
1389 void Annot::setRect(PDFRectangle
*rect
) {
1390 setRect(rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
);
1393 void Annot::setRect(double x1
, double y1
, double x2
, double y2
) {
1412 obj1
.initArray (xref
);
1413 obj1
.arrayAdd (obj2
.initReal (rect
->x1
));
1414 obj1
.arrayAdd (obj2
.initReal (rect
->y1
));
1415 obj1
.arrayAdd (obj2
.initReal (rect
->x2
));
1416 obj1
.arrayAdd (obj2
.initReal (rect
->y2
));
1418 update("Rect", &obj1
);
1419 invalidateAppearance();
1422 GBool
Annot::inRect(double x
, double y
) const {
1423 return rect
->contains(x
, y
);
1426 void Annot::update(const char *key
, Object
*value
) {
1428 /* Set M to current time, unless we are updating M itself */
1429 if (strcmp(key
, "M") != 0) {
1431 modified
= timeToDateString(NULL
);
1434 obj1
.initString (modified
->copy());
1435 annotObj
.dictSet("M", &obj1
);
1438 annotObj
.dictSet(const_cast<char*>(key
), value
);
1440 xref
->setModifiedObject(&annotObj
, ref
);
1443 void Annot::setContents(GooString
*new_content
) {
1448 contents
= new GooString(new_content
);
1449 //append the unicode marker <FE FF> if needed
1450 if (!contents
->hasUnicodeMarker()) {
1451 contents
->insert(0, 0xff);
1452 contents
->insert(0, 0xfe);
1455 contents
= new GooString();
1459 obj1
.initString(contents
->copy());
1460 update ("Contents", &obj1
);
1463 void Annot::setName(GooString
*new_name
) {
1468 name
= new GooString(new_name
);
1470 name
= new GooString();
1474 obj1
.initString(name
->copy());
1475 update ("NM", &obj1
);
1478 void Annot::setModified(GooString
*new_modified
) {
1483 modified
= new GooString(new_modified
);
1485 modified
= new GooString();
1488 obj1
.initString(modified
->copy());
1489 update ("M", &obj1
);
1492 void Annot::setFlags(Guint new_flags
) {
1496 obj1
.initInt(flags
);
1497 update ("F", &obj1
);
1500 void Annot::setBorder(AnnotBorder
*new_border
) {
1506 new_border
->writeToObject(xref
, &obj1
);
1507 update(new_border
->getType() == AnnotBorder::typeArray
? "Border" : "BS", &obj1
);
1508 border
= new_border
;
1512 invalidateAppearance();
1515 void Annot::setColor(AnnotColor
*new_color
) {
1521 new_color
->writeToObject(xref
, &obj1
);
1522 update ("C", &obj1
);
1527 invalidateAppearance();
1530 void Annot::setPage(int pageIndex
, GBool updateP
) {
1532 Page
*pageobj
= doc
->getPage(pageIndex
);
1536 Ref pageRef
= pageobj
->getRef();
1537 obj1
.initRef(pageRef
.num
, pageRef
.gen
);
1549 void Annot::setAppearanceState(const char *state
) {
1555 appearState
= new GooString(state
);
1561 obj1
.initName(state
);
1562 update ("AS", &obj1
);
1564 // The appearance state determines the current appearance stream
1566 if (appearStreams
) {
1567 appearStreams
->getAppearanceStream(AnnotAppearance::appearNormal
, appearState
->getCString(), &appearance
);
1569 appearance
.initNull();
1573 void Annot::invalidateAppearance() {
1575 if (appearStreams
) { // Remove existing appearance streams
1576 appearStreams
->removeAllStreams();
1578 delete appearStreams
;
1579 appearStreams
= NULL
;
1588 appearance
.initNull();
1592 if (!annotObj
.dictLookup("AP", &obj2
)->isNull())
1593 update ("AP", &obj1
); // Remove AP
1596 if (!annotObj
.dictLookup("AS", &obj2
)->isNull())
1597 update ("AS", &obj1
); // Remove AS
1601 double Annot::getXMin() {
1605 double Annot::getYMin() {
1609 double Annot::getXMax() {
1613 double Annot::getYMax() {
1617 void Annot::readArrayNum(Object
*pdfArray
, int key
, double *value
) {
1620 pdfArray
->arrayGet(key
, &valueObject
);
1621 if (valueObject
.isNum()) {
1622 *value
= valueObject
.getNum();
1630 void Annot::removeReferencedObjects() {
1631 // Remove appearance streams (if any)
1632 invalidateAppearance();
1635 void Annot::incRefCnt() {
1640 void Annot::decRefCnt() {
1644 if (--refCnt
== 0) {
1646 gUnlockMutex(&mutex
);
1652 gUnlockMutex(&mutex
);
1668 delete appearStreams
;
1684 gDestroyMutex(&mutex
);
1688 void Annot::setColor(AnnotColor
*color
, GBool fill
) {
1689 const double *values
= color
->getValues();
1691 switch (color
->getSpace()) {
1692 case AnnotColor::colorCMYK
:
1693 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1694 values
[0], values
[1], values
[2], values
[3],
1697 case AnnotColor::colorRGB
:
1698 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1699 values
[0], values
[1], values
[2],
1700 fill
? "rg" : "RG");
1702 case AnnotColor::colorGray
:
1703 appearBuf
->appendf("{0:.2f} {1:c}\n",
1707 case AnnotColor::colorTransparent
:
1713 void Annot::setLineStyleForBorder(AnnotBorder
*border
) {
1717 switch (border
->getStyle()) {
1718 case AnnotBorder::borderDashed
:
1719 appearBuf
->append("[");
1720 dashLength
= border
->getDashLength();
1721 dash
= border
->getDash();
1722 for (i
= 0; i
< dashLength
; ++i
)
1723 appearBuf
->appendf(" {0:.2f}", dash
[i
]);
1724 appearBuf
->append(" ] 0 d\n");
1727 appearBuf
->append("[] 0 d\n");
1730 appearBuf
->appendf("{0:.2f} w\n", border
->getWidth());
1733 // Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1734 // If <fill> is true, the circle is filled; otherwise it is stroked.
1735 void Annot::drawCircle(double cx
, double cy
, double r
, GBool fill
) {
1736 appearBuf
->appendf("{0:.2f} {1:.2f} m\n",
1738 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1739 cx
+ r
, cy
+ bezierCircle
* r
,
1740 cx
+ bezierCircle
* r
, cy
+ r
,
1742 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1743 cx
- bezierCircle
* r
, cy
+ r
,
1744 cx
- r
, cy
+ bezierCircle
* r
,
1746 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1747 cx
- r
, cy
- bezierCircle
* r
,
1748 cx
- bezierCircle
* r
, cy
- r
,
1750 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1751 cx
+ bezierCircle
* r
, cy
- r
,
1752 cx
+ r
, cy
- bezierCircle
* r
,
1754 appearBuf
->append(fill
? "f\n" : "s\n");
1757 // Draw the top-left half of an (approximate) circle of radius <r>
1758 // centered at (<cx>, <cy>).
1759 void Annot::drawCircleTopLeft(double cx
, double cy
, double r
) {
1763 appearBuf
->appendf("{0:.2f} {1:.2f} m\n",
1765 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1766 cx
+ (1 - bezierCircle
) * r2
,
1767 cy
+ (1 + bezierCircle
) * r2
,
1768 cx
- (1 - bezierCircle
) * r2
,
1769 cy
+ (1 + bezierCircle
) * r2
,
1772 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1773 cx
- (1 + bezierCircle
) * r2
,
1774 cy
+ (1 - bezierCircle
) * r2
,
1775 cx
- (1 + bezierCircle
) * r2
,
1776 cy
- (1 - bezierCircle
) * r2
,
1779 appearBuf
->append("S\n");
1782 // Draw the bottom-right half of an (approximate) circle of radius <r>
1783 // centered at (<cx>, <cy>).
1784 void Annot::drawCircleBottomRight(double cx
, double cy
, double r
) {
1788 appearBuf
->appendf("{0:.2f} {1:.2f} m\n",
1790 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1791 cx
- (1 - bezierCircle
) * r2
,
1792 cy
- (1 + bezierCircle
) * r2
,
1793 cx
+ (1 - bezierCircle
) * r2
,
1794 cy
- (1 + bezierCircle
) * r2
,
1797 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1798 cx
+ (1 + bezierCircle
) * r2
,
1799 cy
- (1 - bezierCircle
) * r2
,
1800 cx
+ (1 + bezierCircle
) * r2
,
1801 cy
+ (1 - bezierCircle
) * r2
,
1804 appearBuf
->append("S\n");
1807 void Annot::createForm(double *bbox
, GBool transparencyGroup
, Object
*resDict
, Object
*aStream
) {
1811 appearDict
.initDict(xref
);
1812 appearDict
.dictSet("Length", obj1
.initInt(appearBuf
->getLength()));
1813 appearDict
.dictSet("Subtype", obj1
.initName("Form"));
1814 obj1
.initArray(xref
);
1815 obj1
.arrayAdd(obj2
.initReal(bbox
[0]));
1816 obj1
.arrayAdd(obj2
.initReal(bbox
[1]));
1817 obj1
.arrayAdd(obj2
.initReal(bbox
[2]));
1818 obj1
.arrayAdd(obj2
.initReal(bbox
[3]));
1819 appearDict
.dictSet("BBox", &obj1
);
1820 if (transparencyGroup
) {
1822 transDict
.initDict(xref
);
1823 transDict
.dictSet("S", obj1
.initName("Transparency"));
1824 appearDict
.dictSet("Group", &transDict
);
1827 appearDict
.dictSet("Resources", resDict
);
1829 MemStream
*mStream
= new MemStream(copyString(appearBuf
->getCString()), 0,
1830 appearBuf
->getLength(), &appearDict
);
1831 mStream
->setNeedFree(gTrue
);
1832 aStream
->initStream(mStream
);
1835 void Annot::createResourcesDict(const char *formName
, Object
*formStream
,
1836 const char *stateName
,
1837 double opacity
, const char *blendMode
,
1839 Object gsDict
, stateDict
, formDict
, obj1
;
1841 gsDict
.initDict(xref
);
1843 gsDict
.dictSet("CA", obj1
.initReal(opacity
));
1844 gsDict
.dictSet("ca", obj1
.initReal(opacity
));
1847 gsDict
.dictSet("BM", obj1
.initName(blendMode
));
1848 stateDict
.initDict(xref
);
1849 stateDict
.dictSet(stateName
, &gsDict
);
1850 formDict
.initDict(xref
);
1851 formDict
.dictSet(formName
, formStream
);
1853 resDict
->initDict(xref
);
1854 resDict
->dictSet("ExtGState", &stateDict
);
1855 resDict
->dictSet("XObject", &formDict
);
1858 Object
*Annot::getAppearanceResDict(Object
*dest
) {
1861 dest
->initNull(); // Default value
1863 // Fetch appearance's resource dict (if any)
1864 appearance
.fetch(xref
, &obj1
);
1865 if (obj1
.isStream()) {
1866 obj1
.streamGetDict()->lookup("Resources", &obj2
);
1867 if (obj2
.isDict()) {
1877 GBool
Annot::isVisible(GBool printing
) {
1879 if ((flags
& flagHidden
) ||
1880 (printing
&& !(flags
& flagPrint
)) ||
1881 (!printing
&& (flags
& flagNoView
))) {
1886 OCGs
*optContentConfig
= doc
->getCatalog()->getOptContentConfig();
1887 if (optContentConfig
) {
1888 if (! optContentConfig
->optContentIsVisible(&oc
))
1895 int Annot::getRotation() const
1897 Page
*pageobj
= doc
->getPage(page
);
1898 assert(pageobj
!= NULL
);
1900 if (flags
& flagNoRotate
) {
1901 return (360 - pageobj
->getRotate()) % 360;
1907 void Annot::draw(Gfx
*gfx
, GBool printing
) {
1911 if (!isVisible (printing
))
1914 // draw the appearance stream
1915 appearance
.fetch(gfx
->getXRef(), &obj
);
1916 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
1917 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
1921 //------------------------------------------------------------------------
1923 //------------------------------------------------------------------------
1925 AnnotPopup::AnnotPopup(PDFDoc
*docA
, PDFRectangle
*rect
) :
1931 annotObj
.dictSet ("Subtype", obj1
.initName ("Popup"));
1932 initialize (docA
, annotObj
.getDict());
1935 AnnotPopup::AnnotPopup(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
1936 Annot(docA
, dict
, obj
) {
1938 initialize(docA
, dict
);
1941 AnnotPopup::~AnnotPopup() {
1945 void AnnotPopup::initialize(PDFDoc
*docA
, Dict
*dict
) {
1948 if (!dict
->lookupNF("Parent", &parent
)->isRef()) {
1952 if (dict
->lookup("Open", &obj1
)->isBool()) {
1953 open
= obj1
.getBool();
1960 void AnnotPopup::setParent(Object
*parentA
) {
1961 parentA
->copy(&parent
);
1962 update ("Parent", &parent
);
1965 void AnnotPopup::setParent(Annot
*parentA
) {
1966 Ref parentRef
= parentA
->getRef();
1967 parent
.initRef(parentRef
.num
, parentRef
.gen
);
1968 update ("Parent", &parent
);
1971 void AnnotPopup::setOpen(GBool openA
) {
1975 obj1
.initBool(open
);
1976 update ("Open", &obj1
);
1979 //------------------------------------------------------------------------
1981 //------------------------------------------------------------------------
1982 AnnotMarkup::AnnotMarkup(PDFDoc
*docA
, PDFRectangle
*rect
) :
1984 initialize(docA
, annotObj
.getDict(), &annotObj
);
1987 AnnotMarkup::AnnotMarkup(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
1988 Annot(docA
, dict
, obj
) {
1989 initialize(docA
, dict
, obj
);
1992 AnnotMarkup::~AnnotMarkup() {
2006 void AnnotMarkup::initialize(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) {
2009 if (dict
->lookup("T", &obj1
)->isString()) {
2010 label
= obj1
.getString()->copy();
2016 if (dict
->lookup("Popup", &obj1
)->isDict() && dict
->lookupNF("Popup", &obj2
)->isRef()) {
2017 popup
= new AnnotPopup(docA
, obj1
.getDict(), &obj2
);
2023 if (dict
->lookup("CA", &obj1
)->isNum()) {
2024 opacity
= obj1
.getNum();
2030 if (dict
->lookup("CreationDate", &obj1
)->isString()) {
2031 date
= obj1
.getString()->copy();
2037 if (dict
->lookupNF("IRT", &obj1
)->isRef()) {
2038 inReplyTo
= obj1
.getRef();
2045 if (dict
->lookup("Subj", &obj1
)->isString()) {
2046 subject
= obj1
.getString()->copy();
2052 if (dict
->lookup("RT", &obj1
)->isName()) {
2053 const char *replyName
= obj1
.getName();
2055 if (!strcmp(replyName
, "R")) {
2056 replyTo
= replyTypeR
;
2057 } else if (!strcmp(replyName
, "Group")) {
2058 replyTo
= replyTypeGroup
;
2060 replyTo
= replyTypeR
;
2063 replyTo
= replyTypeR
;
2067 if (dict
->lookup("ExData", &obj1
)->isDict()) {
2068 exData
= parseAnnotExternalData(obj1
.getDict());
2070 exData
= annotExternalDataMarkupUnknown
;
2075 void AnnotMarkup::setLabel(GooString
*new_label
) {
2079 label
= new GooString(new_label
);
2080 //append the unicode marker <FE FF> if needed
2081 if (!label
->hasUnicodeMarker()) {
2082 label
->insert(0, 0xff);
2083 label
->insert(0, 0xfe);
2086 label
= new GooString();
2090 obj1
.initString(label
->copy());
2091 update ("T", &obj1
);
2094 void AnnotMarkup::setPopup(AnnotPopup
*new_popup
) {
2095 // If there exists an old popup annotation that is already
2096 // associated with a page, then we need to remove that
2097 // popup annotation from the page. Otherwise we would have
2098 // dangling references to it.
2099 if (popup
!= NULL
&& popup
->getPageNum() != 0) {
2100 Page
*pageobj
= doc
->getPage(popup
->getPageNum());
2102 pageobj
->removeAnnot(popup
);
2109 Ref popupRef
= new_popup
->getRef();
2111 obj1
.initRef (popupRef
.num
, popupRef
.gen
);
2112 update ("Popup", &obj1
);
2114 new_popup
->setParent(this);
2117 // If this annotation is already added to a page, then we
2118 // add the new popup annotation to the same page.
2120 Page
*pageobj
= doc
->getPage(page
);
2121 assert(pageobj
!= NULL
); // pageobj should exist in doc (see setPage())
2123 pageobj
->addAnnot(popup
);
2130 void AnnotMarkup::setOpacity(double opacityA
) {
2134 obj1
.initReal(opacity
);
2135 update ("CA", &obj1
);
2136 invalidateAppearance();
2139 void AnnotMarkup::setDate(GooString
*new_date
) {
2143 date
= new GooString(new_date
);
2145 date
= new GooString();
2148 obj1
.initString(date
->copy());
2149 update ("CreationDate", &obj1
);
2152 void AnnotMarkup::removeReferencedObjects() {
2153 Page
*pageobj
= doc
->getPage(page
);
2154 assert(pageobj
!= NULL
); // We're called when removing an annot from a page
2158 pageobj
->removeAnnot(popup
);
2161 Annot::removeReferencedObjects();
2164 //------------------------------------------------------------------------
2166 //------------------------------------------------------------------------
2168 AnnotText::AnnotText(PDFDoc
*docA
, PDFRectangle
*rect
) :
2169 AnnotMarkup(docA
, rect
) {
2173 flags
|= flagNoZoom
| flagNoRotate
;
2175 annotObj
.dictSet ("Subtype", obj1
.initName ("Text"));
2176 initialize (docA
, annotObj
.getDict());
2179 AnnotText::AnnotText(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
2180 AnnotMarkup(docA
, dict
, obj
) {
2183 flags
|= flagNoZoom
| flagNoRotate
;
2184 initialize (docA
, dict
);
2187 AnnotText::~AnnotText() {
2191 void AnnotText::initialize(PDFDoc
*docA
, Dict
*dict
) {
2194 if (dict
->lookup("Open", &obj1
)->isBool())
2195 open
= obj1
.getBool();
2200 if (dict
->lookup("Name", &obj1
)->isName()) {
2201 icon
= new GooString(obj1
.getName());
2203 icon
= new GooString("Note");
2207 if (dict
->lookup("StateModel", &obj1
)->isString()) {
2209 GooString
*modelName
= obj1
.getString();
2211 if (dict
->lookup("State", &obj2
)->isString()) {
2212 GooString
*stateName
= obj2
.getString();
2214 if (!stateName
->cmp("Marked")) {
2215 state
= stateMarked
;
2216 } else if (!stateName
->cmp("Unmarked")) {
2217 state
= stateUnmarked
;
2218 } else if (!stateName
->cmp("Accepted")) {
2219 state
= stateAccepted
;
2220 } else if (!stateName
->cmp("Rejected")) {
2221 state
= stateRejected
;
2222 } else if (!stateName
->cmp("Cancelled")) {
2223 state
= stateCancelled
;
2224 } else if (!stateName
->cmp("Completed")) {
2225 state
= stateCompleted
;
2226 } else if (!stateName
->cmp("None")) {
2229 state
= stateUnknown
;
2232 state
= stateUnknown
;
2236 if (!modelName
->cmp("Marked")) {
2239 state
= stateMarked
;
2243 case stateCancelled
:
2244 case stateCompleted
:
2246 state
= stateUnknown
;
2251 } else if (!modelName
->cmp("Review")) {
2258 state
= stateUnknown
;
2264 state
= stateUnknown
;
2267 state
= stateUnknown
;
2272 void AnnotText::setOpen(GBool openA
) {
2276 obj1
.initBool(open
);
2277 update ("Open", &obj1
);
2280 void AnnotText::setIcon(GooString
*new_icon
) {
2281 if (new_icon
&& icon
->cmp(new_icon
) == 0)
2287 icon
= new GooString (new_icon
);
2289 icon
= new GooString("Note");
2293 obj1
.initName (icon
->getCString());
2294 update("Name", &obj1
);
2295 invalidateAppearance();
2298 #define ANNOT_TEXT_AP_NOTE \
2299 "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \
2300 "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" \
2301 "l 0 22.387 1.613 24 3.602 24 c h\n" \
2303 "0.533333 0.541176 0.521569 RG 2 w\n" \
2307 "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" \
2310 "10 16 m 14 21 l S\n" \
2313 "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \
2314 "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \
2315 "14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \
2316 "15.07 20.523 m S\n" \
2319 "6.5 13.5 m 15.5 13.5 l S\n" \
2320 "6.5 10.5 m 13.5 10.5 l S\n" \
2321 "6.801 7.5 m 15.5 7.5 l S\n" \
2322 "0.729412 0.741176 0.713725 RG 2 w\n" \
2324 "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" \
2327 "10 17 m 14 22 l S\n" \
2330 "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \
2331 "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \
2332 "14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \
2333 "15.07 21.523 m S\n" \
2336 "6.5 14.5 m 15.5 14.5 l S\n" \
2337 "6.5 11.5 m 13.5 11.5 l S\n" \
2338 "6.801 8.5 m 15.5 8.5 l S\n"
2340 #define ANNOT_TEXT_AP_COMMENT \
2341 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2342 "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" \
2343 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2345 "0.533333 0.541176 0.521569 RG 2 w\n" \
2349 "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" \
2350 "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" \
2351 "5.637 20 8 20 c h\n" \
2353 "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \
2354 "c 20 14 l 20 11.785 18.363 10\n" \
2355 "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" \
2356 "19.215 5.637 21 8 21 c h\n" \
2359 #define ANNOT_TEXT_AP_KEY \
2360 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2361 "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" \
2362 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2364 "0.533333 0.541176 0.521569 RG 2 w\n" \
2368 "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \
2369 "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \
2370 "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \
2371 "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" \
2372 "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \
2373 "11.895 18.754 m S\n" \
2375 "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \
2376 "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \
2377 "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \
2378 "16.059 15.586 m S\n" \
2379 "0.729412 0.741176 0.713725 RG 2 w\n" \
2380 "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \
2381 "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \
2382 "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \
2383 "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" \
2384 "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \
2385 "11.895 19.754 m S\n" \
2387 "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \
2388 "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \
2389 "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \
2390 "16.059 16.586 m S\n"
2392 #define ANNOT_TEXT_AP_HELP \
2393 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2394 "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" \
2395 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2397 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2401 "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" \
2402 "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \
2405 "12 8 m 12 12 16 11 16 15 c S\n" \
2408 "q 1 0 0 -0.999991 0 24 cm\n" \
2409 "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \
2410 "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \
2411 "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \
2412 "0.729412 0.741176 0.713725 RG 2.5 w\n" \
2413 "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \
2414 "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \
2417 "12 9 m 12 13 16 12 16 16 c S\n" \
2420 "q 1 0 0 -0.999991 0 24 cm\n" \
2421 "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \
2422 "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \
2423 "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
2425 #define ANNOT_TEXT_AP_NEW_PARAGRAPH \
2426 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2427 "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" \
2428 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2430 "0.533333 0.541176 0.521569 RG 4 w\n" \
2434 "4 M q 1 0 0 -1 0 24 cm\n" \
2435 "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \
2436 "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" \
2440 "q 1 0 0 -0.991232 0 24 cm\n" \
2441 "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \
2442 "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \
2443 "17.461 8.252 l 18.07 11.511 l h\n" \
2444 "18.07 11.511 m S Q\n" \
2447 "11 17 m 10 17 l 10 3 l S\n" \
2448 "14 3 m 14 13 l S\n" \
2449 "0.729412 0.741176 0.713725 RG 4 w\n" \
2452 "q 1 0 0 -1 0 24 cm\n" \
2453 "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \
2454 "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" \
2458 "q 1 0 0 -0.991232 0 24 cm\n" \
2459 "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \
2460 "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \
2461 "17.461 7.243 l 18.07 10.502 l h\n" \
2462 "18.07 10.502 m S Q\n" \
2465 "11 18 m 10 18 l 10 4 l S\n" \
2466 "14 4 m 14 14 l S\n"
2468 #define ANNOT_TEXT_AP_PARAGRAPH \
2469 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2470 "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" \
2471 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2473 "0.533333 0.541176 0.521569 RG 2 w\n" \
2477 "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
2479 "q 1 0 0 -1 0 24 cm\n" \
2480 "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \
2481 "7.066 9.879 7.004 c S Q\n" \
2482 "0.729412 0.741176 0.713725 RG 2 w\n" \
2483 "15 4 m 15 19 l 11 19 l 11 4 l S\n" \
2485 "q 1 0 0 -1 0 24 cm\n" \
2486 "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" \
2487 "9.879 6.004 c S Q\n"
2489 #define ANNOT_TEXT_AP_INSERT \
2490 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2491 "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" \
2492 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2494 "0.533333 0.541176 0.521569 RG 2 w\n" \
2498 "4 M 12 18.012 m 20 18 l S\n" \
2499 "9 10 m 17 10 l S\n" \
2500 "12 14.012 m 20 14 l S\n" \
2501 "12 6.012 m 20 6.012 l S\n" \
2502 "4 12 m 6 10 l 4 8 l S\n" \
2503 "4 12 m 4 8 l S\n" \
2504 "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \
2505 "9 11 m 17 11 l S\n" \
2506 "12 15.012 m 20 15 l S\n" \
2507 "12 7.012 m 20 7.012 l S\n" \
2508 "4 13 m 6 11 l 4 9 l S\n" \
2511 #define ANNOT_TEXT_AP_CROSS \
2512 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2513 "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" \
2514 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2516 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2520 "4 M 18 5 m 6 17 l S\n" \
2521 "6 5 m 18 17 l S\n" \
2522 "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \
2525 #define ANNOT_TEXT_AP_CIRCLE \
2526 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2527 "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" \
2528 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2530 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2534 "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" \
2535 "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \
2537 "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \
2538 "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \
2539 "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \
2542 void AnnotText::draw(Gfx
*gfx
, GBool printing
) {
2546 if (!isVisible (printing
))
2550 if (appearance
.isNull()) {
2553 appearBuf
= new GooString ();
2555 appearBuf
->append ("q\n");
2557 setColor(color
, gTrue
);
2559 appearBuf
->append ("1 1 1 rg\n");
2560 if (!icon
->cmp("Note"))
2561 appearBuf
->append (ANNOT_TEXT_AP_NOTE
);
2562 else if (!icon
->cmp("Comment"))
2563 appearBuf
->append (ANNOT_TEXT_AP_COMMENT
);
2564 else if (!icon
->cmp("Key"))
2565 appearBuf
->append (ANNOT_TEXT_AP_KEY
);
2566 else if (!icon
->cmp("Help"))
2567 appearBuf
->append (ANNOT_TEXT_AP_HELP
);
2568 else if (!icon
->cmp("NewParagraph"))
2569 appearBuf
->append (ANNOT_TEXT_AP_NEW_PARAGRAPH
);
2570 else if (!icon
->cmp("Paragraph"))
2571 appearBuf
->append (ANNOT_TEXT_AP_PARAGRAPH
);
2572 else if (!icon
->cmp("Insert"))
2573 appearBuf
->append (ANNOT_TEXT_AP_INSERT
);
2574 else if (!icon
->cmp("Cross"))
2575 appearBuf
->append (ANNOT_TEXT_AP_CROSS
);
2576 else if (!icon
->cmp("Circle"))
2577 appearBuf
->append (ANNOT_TEXT_AP_CIRCLE
);
2578 appearBuf
->append ("Q\n");
2580 // Force 24x24 rectangle
2581 PDFRectangle
fixedRect(rect
->x1
, rect
->y2
- 24, rect
->x1
+ 24, rect
->y2
);
2582 appearBBox
= new AnnotAppearanceBBox(&fixedRect
);
2584 appearBBox
->getBBoxRect(bbox
);
2586 createForm(bbox
, gFalse
, NULL
, &appearance
);
2588 Object aStream
, resDict
;
2590 createForm(bbox
, gTrue
, NULL
, &aStream
);
2593 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
2594 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
2595 createForm(bbox
, gFalse
, &resDict
, &appearance
);
2600 // draw the appearance stream
2601 appearance
.fetch(gfx
->getXRef(), &obj
);
2603 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
2604 appearBBox
->getPageXMin(), appearBBox
->getPageYMin(),
2605 appearBBox
->getPageXMax(), appearBBox
->getPageYMax(),
2608 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
2609 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
2614 //------------------------------------------------------------------------
2616 //------------------------------------------------------------------------
2617 AnnotLink::AnnotLink(PDFDoc
*docA
, PDFRectangle
*rect
) :
2622 annotObj
.dictSet ("Subtype", obj1
.initName ("Link"));
2623 initialize (docA
, annotObj
.getDict());
2626 AnnotLink::AnnotLink(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
2627 Annot(docA
, dict
, obj
) {
2630 initialize (docA
, dict
);
2633 AnnotLink::~AnnotLink() {
2640 delete quadrilaterals
;
2643 void AnnotLink::initialize(PDFDoc
*docA
, Dict
*dict
) {
2648 // look for destination
2649 if (!dict
->lookup("Dest", &obj1
)->isNull()) {
2650 action
= LinkAction::parseDest(&obj1
);
2654 if (dict
->lookup("A", &obj1
)->isDict()) {
2655 action
= LinkAction::parseAction(&obj1
, doc
->getCatalog()->getBaseURI());
2660 if (dict
->lookup("H", &obj1
)->isName()) {
2661 const char *effect
= obj1
.getName();
2663 if (!strcmp(effect
, "N")) {
2664 linkEffect
= effectNone
;
2665 } else if (!strcmp(effect
, "I")) {
2666 linkEffect
= effectInvert
;
2667 } else if (!strcmp(effect
, "O")) {
2668 linkEffect
= effectOutline
;
2669 } else if (!strcmp(effect
, "P")) {
2670 linkEffect
= effectPush
;
2672 linkEffect
= effectInvert
;
2675 linkEffect
= effectInvert
;
2679 if (dict->lookup("PA", &obj1)->isDict()) {
2686 if (dict
->lookup("QuadPoints", &obj1
)->isArray()) {
2687 quadrilaterals
= new AnnotQuadrilaterals(obj1
.getArray(), rect
);
2689 quadrilaterals
= NULL
;
2693 if (dict
->lookup("BS", &obj1
)->isDict()) {
2695 border
= new AnnotBorderBS(obj1
.getDict());
2696 } else if (!border
) {
2697 border
= new AnnotBorderBS();
2702 void AnnotLink::draw(Gfx
*gfx
, GBool printing
) {
2705 if (!isVisible (printing
))
2709 // draw the appearance stream
2710 appearance
.fetch(gfx
->getXRef(), &obj
);
2711 gfx
->drawAnnot(&obj
, border
, color
,
2712 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
2716 //------------------------------------------------------------------------
2718 //------------------------------------------------------------------------
2719 AnnotFreeText::AnnotFreeText(PDFDoc
*docA
, PDFRectangle
*rect
, GooString
*da
) :
2720 AnnotMarkup(docA
, rect
) {
2723 type
= typeFreeText
;
2725 annotObj
.dictSet ("Subtype", obj1
.initName ("FreeText"));
2728 obj2
.initString (da
->copy());
2729 annotObj
.dictSet("DA", &obj2
);
2731 initialize (docA
, annotObj
.getDict());
2734 AnnotFreeText::AnnotFreeText(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
2735 AnnotMarkup(docA
, dict
, obj
) {
2736 type
= typeFreeText
;
2737 initialize(docA
, dict
);
2740 AnnotFreeText::~AnnotFreeText() {
2741 delete appearanceString
;
2750 delete borderEffect
;
2756 void AnnotFreeText::initialize(PDFDoc
*docA
, Dict
*dict
) {
2759 if (dict
->lookup("DA", &obj1
)->isString()) {
2760 appearanceString
= obj1
.getString()->copy();
2762 appearanceString
= new GooString();
2763 error(errSyntaxError
, -1, "Bad appearance for annotation");
2768 if (dict
->lookup("Q", &obj1
)->isInt()) {
2769 quadding
= (AnnotFreeTextQuadding
) obj1
.getInt();
2771 quadding
= quaddingLeftJustified
;
2775 if (dict
->lookup("DS", &obj1
)->isString()) {
2776 styleString
= obj1
.getString()->copy();
2782 if (dict
->lookup("CL", &obj1
)->isArray() && obj1
.arrayGetLength() >= 4) {
2783 double x1
, y1
, x2
, y2
;
2786 (obj1
.arrayGet(0, &obj2
)->isNum() ? x1
= obj2
.getNum() : x1
= 0);
2788 (obj1
.arrayGet(1, &obj2
)->isNum() ? y1
= obj2
.getNum() : y1
= 0);
2790 (obj1
.arrayGet(2, &obj2
)->isNum() ? x2
= obj2
.getNum() : x2
= 0);
2792 (obj1
.arrayGet(3, &obj2
)->isNum() ? y2
= obj2
.getNum() : y2
= 0);
2795 if (obj1
.arrayGetLength() == 6) {
2797 (obj1
.arrayGet(4, &obj2
)->isNum() ? x3
= obj2
.getNum() : x3
= 0);
2799 (obj1
.arrayGet(5, &obj2
)->isNum() ? y3
= obj2
.getNum() : y3
= 0);
2801 calloutLine
= new AnnotCalloutMultiLine(x1
, y1
, x2
, y2
, x3
, y3
);
2803 calloutLine
= new AnnotCalloutLine(x1
, y1
, x2
, y2
);
2810 if (dict
->lookup("IT", &obj1
)->isName()) {
2811 const char *intentName
= obj1
.getName();
2813 if (!strcmp(intentName
, "FreeText")) {
2814 intent
= intentFreeText
;
2815 } else if (!strcmp(intentName
, "FreeTextCallout")) {
2816 intent
= intentFreeTextCallout
;
2817 } else if (!strcmp(intentName
, "FreeTextTypeWriter")) {
2818 intent
= intentFreeTextTypeWriter
;
2820 intent
= intentFreeText
;
2823 intent
= intentFreeText
;
2827 if (dict
->lookup("BS", &obj1
)->isDict()) {
2829 border
= new AnnotBorderBS(obj1
.getDict());
2830 } else if (!border
) {
2831 border
= new AnnotBorderBS();
2835 if (dict
->lookup("BE", &obj1
)->isDict()) {
2836 borderEffect
= new AnnotBorderEffect(obj1
.getDict());
2838 borderEffect
= NULL
;
2842 if (dict
->lookup("RD", &obj1
)->isArray()) {
2843 rectangle
= parseDiffRectangle(obj1
.getArray(), rect
);
2849 if (dict
->lookup("LE", &obj1
)->isName()) {
2850 GooString
styleName(obj1
.getName());
2851 endStyle
= parseAnnotLineEndingStyle(&styleName
);
2853 endStyle
= annotLineEndingNone
;
2858 void AnnotFreeText::setContents(GooString
*new_content
) {
2859 Annot::setContents(new_content
);
2860 invalidateAppearance();
2863 void AnnotFreeText::setAppearanceString(GooString
*new_string
) {
2864 delete appearanceString
;
2867 appearanceString
= new GooString(new_string
);
2869 appearanceString
= new GooString();
2873 obj1
.initString(appearanceString
->copy());
2874 update ("DA", &obj1
);
2875 invalidateAppearance();
2878 void AnnotFreeText::setQuadding(AnnotFreeTextQuadding new_quadding
) {
2880 quadding
= new_quadding
;
2881 obj1
.initInt((int)quadding
);
2882 update ("Q", &obj1
);
2883 invalidateAppearance();
2886 void AnnotFreeText::setStyleString(GooString
*new_string
) {
2890 styleString
= new GooString(new_string
);
2891 //append the unicode marker <FE FF> if needed
2892 if (!styleString
->hasUnicodeMarker()) {
2893 styleString
->insert(0, 0xff);
2894 styleString
->insert(0, 0xfe);
2897 styleString
= new GooString();
2901 obj1
.initString(styleString
->copy());
2902 update ("DS", &obj1
);
2905 void AnnotFreeText::setCalloutLine(AnnotCalloutLine
*line
) {
2913 double x1
= line
->getX1(), y1
= line
->getY1();
2914 double x2
= line
->getX2(), y2
= line
->getY2();
2916 obj1
.initArray(xref
);
2917 obj1
.arrayAdd( obj2
.initReal(x1
) );
2918 obj1
.arrayAdd( obj2
.initReal(y1
) );
2919 obj1
.arrayAdd( obj2
.initReal(x2
) );
2920 obj1
.arrayAdd( obj2
.initReal(y2
) );
2922 AnnotCalloutMultiLine
*mline
= dynamic_cast<AnnotCalloutMultiLine
*>(line
);
2924 double x3
= mline
->getX3(), y3
= mline
->getY3();
2925 obj1
.arrayAdd( obj2
.initReal(x3
) );
2926 obj1
.arrayAdd( obj2
.initReal(y3
) );
2927 calloutLine
= new AnnotCalloutMultiLine(x1
, y1
, x2
, y2
, x3
, y3
);
2929 calloutLine
= new AnnotCalloutLine(x1
, y1
, x2
, y2
);
2933 update("CL", &obj1
);
2934 invalidateAppearance();
2937 void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent
) {
2940 intent
= new_intent
;
2941 if (new_intent
== intentFreeText
)
2942 obj1
.initName("FreeText");
2943 else if (new_intent
== intentFreeTextCallout
)
2944 obj1
.initName("FreeTextCallout");
2945 else // intentFreeTextTypeWriter
2946 obj1
.initName("FreeTextTypeWriter");
2947 update ("IT", &obj1
);
2950 static GfxFont
* createAnnotDrawFont(XRef
* xref
, Object
*fontResDict
)
2952 Ref dummyRef
= { -1, -1 };
2954 Object baseFontObj
, subtypeObj
, encodingObj
;
2955 baseFontObj
.initName("Helvetica");
2956 subtypeObj
.initName("Type0");
2957 encodingObj
.initName("WinAnsiEncoding");
2960 Dict
*fontDict
= new Dict(xref
);
2962 fontDict
->add(copyString("BaseFont"), &baseFontObj
);
2963 fontDict
->add(copyString("Subtype"), &subtypeObj
);
2964 fontDict
->add(copyString("Encoding"), &encodingObj
);
2965 fontDictObj
.initDict(fontDict
);
2967 Object fontsDictObj
;
2968 Dict
*fontsDict
= new Dict(xref
);
2969 fontsDict
->decRef();
2970 fontsDict
->add(copyString("AnnotDrawFont"), &fontDictObj
);
2971 fontsDictObj
.initDict(fontsDict
);
2973 Dict
*dict
= new Dict(xref
);
2974 dict
->add(copyString("Font"), &fontsDictObj
);
2976 fontResDict
->initDict(dict
);
2977 return GfxFont::makeFont(xref
, "AnnotDrawFont", dummyRef
, fontDict
);
2980 void AnnotFreeText::parseAppearanceString(GooString
*da
, double &fontsize
, AnnotColor
* &fontcolor
) {
2984 GooList
* daToks
= new GooList();
2988 while (i
< da
->getLength()) {
2989 while (i
< da
->getLength() && Lexer::isSpace(da
->getChar(i
))) {
2992 if (i
< da
->getLength()) {
2993 for (j
= i
+ 1; j
< da
->getLength() && !Lexer::isSpace(da
->getChar(j
)); ++j
) {
2995 daToks
->append(new GooString(da
, i
, j
- i
));
3000 // Scan backwards: we are looking for the last set value
3001 for (i
= daToks
->getLength()-1; i
>= 0; --i
) {
3002 if (fontsize
== -1) {
3003 if (!((GooString
*)daToks
->get(i
))->cmp("Tf") && i
>= 2) {
3005 fontsize
= gatof(( (GooString
*)daToks
->get(i
-1) )->getCString());
3008 if (fontcolor
== NULL
) {
3009 if (!((GooString
*)daToks
->get(i
))->cmp("g") && i
>= 1) {
3010 fontcolor
= new AnnotColor(gatof(( (GooString
*)daToks
->get(i
-1) )->getCString()));
3011 } else if (!((GooString
*)daToks
->get(i
))->cmp("rg") && i
>= 3) {
3012 fontcolor
= new AnnotColor(gatof(( (GooString
*)daToks
->get(i
-3) )->getCString()),
3013 gatof(( (GooString
*)daToks
->get(i
-2) )->getCString()),
3014 gatof(( (GooString
*)daToks
->get(i
-1) )->getCString()));
3015 } else if (!((GooString
*)daToks
->get(i
))->cmp("k") && i
>= 4) {
3016 fontcolor
= new AnnotColor(gatof(( (GooString
*)daToks
->get(i
-4) )->getCString()),
3017 gatof(( (GooString
*)daToks
->get(i
-3) )->getCString()),
3018 gatof(( (GooString
*)daToks
->get(i
-2) )->getCString()),
3019 gatof(( (GooString
*)daToks
->get(i
-1) )->getCString()));
3023 deleteGooList(daToks
, GooString
);
3027 void AnnotFreeText::generateFreeTextAppearance()
3029 double borderWidth
, ca
= opacity
;
3031 appearBuf
= new GooString ();
3032 appearBuf
->append ("q\n");
3034 borderWidth
= border
->getWidth();
3035 if (borderWidth
> 0)
3036 setLineStyleForBorder(border
);
3039 const double width
= rect
->x2
- rect
->x1
;
3040 const double height
= rect
->y2
- rect
->y1
;
3042 // Parse some properties from the appearance string
3044 AnnotColor
*fontcolor
;
3045 parseAppearanceString(appearanceString
, fontsize
, fontcolor
);
3049 if (fontcolor
== NULL
)
3050 fontcolor
= new AnnotColor(0, 0, 0); // Black
3052 contents
= new GooString ();
3055 GBool doFill
= (color
&& color
->getSpace() != AnnotColor::colorTransparent
);
3056 GBool doStroke
= (borderWidth
!= 0);
3057 if (doFill
|| doStroke
) {
3059 setColor(fontcolor
, gFalse
); // Border color: same as font color
3061 appearBuf
->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth
/2, width
-borderWidth
, height
-borderWidth
);
3063 setColor(color
, gTrue
);
3064 appearBuf
->append(doStroke
? "B\n" : "f\n");
3066 appearBuf
->append("S\n");
3070 // Setup text clipping
3071 const double textmargin
= borderWidth
* 2;
3072 const double textwidth
= width
- 2*textmargin
;
3073 appearBuf
->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin
, textwidth
, height
- 2*textmargin
);
3076 GfxFont
*font
= createAnnotDrawFont(xref
, &fontResDict
);
3079 setColor(fontcolor
, gTrue
);
3080 appearBuf
->appendf ("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin
, height
- textmargin
- fontsize
* font
->getDescent());
3081 appearBuf
->appendf ("/AnnotDrawFont {0:.2f} Tf\n", fontsize
);
3084 double xposPrev
= 0;
3085 while (i
< contents
->getLength()) {
3087 double linewidth
, xpos
;
3088 layoutText(contents
, &out
, &i
, font
, &linewidth
, textwidth
/fontsize
, NULL
, gFalse
);
3089 linewidth
*= fontsize
;
3091 case quaddingCentered
:
3092 xpos
= (textwidth
- linewidth
) / 2;
3094 case quaddingRightJustified
:
3095 xpos
= textwidth
- linewidth
;
3097 default: // quaddingLeftJustified:
3101 appearBuf
->appendf("{0:.2f} {1:.2f} Td\n", xpos
- xposPrev
, -fontsize
);
3102 writeString(&out
, appearBuf
);
3103 appearBuf
->append("Tj\n");
3109 appearBuf
->append ("ET Q\n");
3112 bbox
[0] = bbox
[1] = 0;
3113 bbox
[2] = rect
->x2
- rect
->x1
;
3114 bbox
[3] = rect
->y2
- rect
->y1
;
3117 createForm(bbox
, gFalse
, &fontResDict
, &appearance
);
3119 Object aStream
, resDict
;
3121 createForm(bbox
, gTrue
, &fontResDict
, &aStream
);
3124 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
3125 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
3126 createForm(bbox
, gFalse
, &resDict
, &appearance
);
3131 void AnnotFreeText::draw(Gfx
*gfx
, GBool printing
) {
3134 if (!isVisible (printing
))
3138 if (appearance
.isNull()) {
3139 generateFreeTextAppearance();
3142 // draw the appearance stream
3143 appearance
.fetch(gfx
->getXRef(), &obj
);
3144 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
3145 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
3149 // Before retrieving the res dict, regenerate the appearance stream if needed,
3150 // because AnnotFreeText::draw needs to store font info in the res dict
3151 Object
*AnnotFreeText::getAppearanceResDict(Object
*dest
) {
3152 if (appearance
.isNull()) {
3153 generateFreeTextAppearance();
3155 return Annot::getAppearanceResDict(dest
);
3158 //------------------------------------------------------------------------
3160 //------------------------------------------------------------------------
3162 AnnotLine::AnnotLine(PDFDoc
*docA
, PDFRectangle
*rect
) :
3163 AnnotMarkup(docA
, rect
) {
3167 annotObj
.dictSet ("Subtype", obj1
.initName ("Line"));
3169 initialize (docA
, annotObj
.getDict());
3172 AnnotLine::AnnotLine(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
3173 AnnotMarkup(docA
, dict
, obj
) {
3175 initialize(docA
, dict
);
3178 AnnotLine::~AnnotLine() {
3183 delete interiorColor
;
3189 void AnnotLine::initialize(PDFDoc
*docA
, Dict
*dict
) {
3192 if (dict
->lookup("L", &obj1
)->isArray() && obj1
.arrayGetLength() == 4) {
3194 double x1
, y1
, x2
, y2
;
3196 (obj1
.arrayGet(0, &obj2
)->isNum() ? x1
= obj2
.getNum() : x1
= 0);
3198 (obj1
.arrayGet(1, &obj2
)->isNum() ? y1
= obj2
.getNum() : y1
= 0);
3200 (obj1
.arrayGet(2, &obj2
)->isNum() ? x2
= obj2
.getNum() : x2
= 0);
3202 (obj1
.arrayGet(3, &obj2
)->isNum() ? y2
= obj2
.getNum() : y2
= 0);
3205 coord1
= new AnnotCoord(x1
, y1
);
3206 coord2
= new AnnotCoord(x2
, y2
);
3208 coord1
= new AnnotCoord();
3209 coord2
= new AnnotCoord();
3213 if (dict
->lookup("LE", &obj1
)->isArray() && obj1
.arrayGetLength() == 2) {
3216 if(obj1
.arrayGet(0, &obj2
)->isString())
3217 startStyle
= parseAnnotLineEndingStyle(obj2
.getString());
3219 startStyle
= annotLineEndingNone
;
3222 if(obj1
.arrayGet(1, &obj2
)->isString())
3223 endStyle
= parseAnnotLineEndingStyle(obj2
.getString());
3225 endStyle
= annotLineEndingNone
;
3229 startStyle
= endStyle
= annotLineEndingNone
;
3233 if (dict
->lookup("IC", &obj1
)->isArray()) {
3234 interiorColor
= new AnnotColor(obj1
.getArray());
3236 interiorColor
= NULL
;
3240 if (dict
->lookup("LL", &obj1
)->isNum()) {
3241 leaderLineLength
= obj1
.getNum();
3243 leaderLineLength
= 0;
3247 if (dict
->lookup("LLE", &obj1
)->isNum()) {
3248 leaderLineExtension
= obj1
.getNum();
3250 if (leaderLineExtension
< 0)
3251 leaderLineExtension
= 0;
3253 leaderLineExtension
= 0;
3257 if (dict
->lookup("Cap", &obj1
)->isBool()) {
3258 caption
= obj1
.getBool();
3264 if (dict
->lookup("IT", &obj1
)->isName()) {
3265 const char *intentName
= obj1
.getName();
3267 if(!strcmp(intentName
, "LineArrow")) {
3268 intent
= intentLineArrow
;
3269 } else if(!strcmp(intentName
, "LineDimension")) {
3270 intent
= intentLineDimension
;
3272 intent
= intentLineArrow
;
3275 intent
= intentLineArrow
;
3279 if (dict
->lookup("LLO", &obj1
)->isNum()) {
3280 leaderLineOffset
= obj1
.getNum();
3282 if (leaderLineOffset
< 0)
3283 leaderLineOffset
= 0;
3285 leaderLineOffset
= 0;
3289 if (dict
->lookup("CP", &obj1
)->isName()) {
3290 const char *captionName
= obj1
.getName();
3292 if(!strcmp(captionName
, "Inline")) {
3293 captionPos
= captionPosInline
;
3294 } else if(!strcmp(captionName
, "Top")) {
3295 captionPos
= captionPosTop
;
3297 captionPos
= captionPosInline
;
3300 captionPos
= captionPosInline
;
3304 if (dict
->lookup("Measure", &obj1
)->isDict()) {
3311 if ((dict
->lookup("CO", &obj1
)->isArray()) && (obj1
.arrayGetLength() == 2)) {
3314 (obj1
.arrayGet(0, &obj2
)->isNum() ? captionTextHorizontal
= obj2
.getNum() :
3315 captionTextHorizontal
= 0);
3317 (obj1
.arrayGet(1, &obj2
)->isNum() ? captionTextVertical
= obj2
.getNum() :
3318 captionTextVertical
= 0);
3321 captionTextHorizontal
= captionTextVertical
= 0;
3325 if (dict
->lookup("BS", &obj1
)->isDict()) {
3327 border
= new AnnotBorderBS(obj1
.getDict());
3328 } else if (!border
) {
3329 border
= new AnnotBorderBS();
3334 void AnnotLine::setContents(GooString
*new_content
) {
3335 Annot::setContents(new_content
);
3337 invalidateAppearance();
3340 void AnnotLine::setVertices(double x1
, double y1
, double x2
, double y2
) {
3344 coord1
= new AnnotCoord(x1
, y1
);
3346 coord2
= new AnnotCoord(x2
, y2
);
3348 obj1
.initArray(xref
);
3349 obj1
.arrayAdd( obj2
.initReal(x1
) );
3350 obj1
.arrayAdd( obj2
.initReal(y1
) );
3351 obj1
.arrayAdd( obj2
.initReal(x2
) );
3352 obj1
.arrayAdd( obj2
.initReal(y2
) );
3355 invalidateAppearance();
3358 void AnnotLine::setStartEndStyle(AnnotLineEndingStyle start
, AnnotLineEndingStyle end
) {
3364 obj1
.initArray(xref
);
3365 obj1
.arrayAdd( obj2
.initName(convertAnnotLineEndingStyle( startStyle
)) );
3366 obj1
.arrayAdd( obj2
.initName(convertAnnotLineEndingStyle( endStyle
)) );
3368 update("LE", &obj1
);
3369 invalidateAppearance();
3372 void AnnotLine::setInteriorColor(AnnotColor
*new_color
) {
3373 delete interiorColor
;
3377 new_color
->writeToObject(xref
, &obj1
);
3378 update ("IC", &obj1
);
3379 interiorColor
= new_color
;
3381 interiorColor
= NULL
;
3383 invalidateAppearance();
3386 void AnnotLine::setLeaderLineLength(double len
) {
3389 leaderLineLength
= len
;
3391 update ("LL", &obj1
);
3392 invalidateAppearance();
3395 void AnnotLine::setLeaderLineExtension(double len
) {
3398 leaderLineExtension
= len
;
3400 update ("LLE", &obj1
);
3402 // LL is required if LLE is present
3403 obj1
.initReal(leaderLineLength
);
3404 update ("LL", &obj1
);
3405 invalidateAppearance();
3408 void AnnotLine::setCaption(bool new_cap
) {
3412 obj1
.initBool(new_cap
);
3413 update ("Cap", &obj1
);
3414 invalidateAppearance();
3417 void AnnotLine::setIntent(AnnotLineIntent new_intent
) {
3420 intent
= new_intent
;
3421 if (new_intent
== intentLineArrow
)
3422 obj1
.initName("LineArrow");
3423 else // intentLineDimension
3424 obj1
.initName("LineDimension");
3425 update ("IT", &obj1
);
3428 void AnnotLine::generateLineAppearance()
3430 double borderWidth
, ca
= opacity
;
3432 appearBBox
= new AnnotAppearanceBBox(rect
);
3433 appearBuf
= new GooString ();
3434 appearBuf
->append ("q\n");
3436 setColor(color
, gFalse
);
3439 setLineStyleForBorder(border
);
3440 borderWidth
= border
->getWidth();
3441 appearBBox
->setBorderWidth(std::max(1., borderWidth
));
3443 const double x1
= coord1
->getX();
3444 const double y1
= coord1
->getY();
3445 const double x2
= coord2
->getX();
3446 const double y2
= coord2
->getY();
3448 // Main segment length
3449 const double main_len
= sqrt((x2
-x1
)*(x2
-x1
) + (y2
-y1
)*(y2
-y1
));
3451 // Main segment becomes positive x direction, coord1 becomes (0,0)
3453 const double angle
= atan2(y2
- y1
, x2
- x1
);
3454 matr
.m
[0] = matr
.m
[3] = cos(angle
);
3455 matr
.m
[1] = sin(angle
);
3456 matr
.m
[2] = -matr
.m
[1];
3457 matr
.m
[4] = x1
-rect
->x1
;
3458 matr
.m
[5] = y1
-rect
->y1
;
3460 double tx
, ty
, captionwidth
= 0, captionheight
= 0;
3461 AnnotLineCaptionPos actualCaptionPos
= captionPos
;
3462 const double fontsize
= 9;
3463 const double captionhmargin
= 2; // Left and right margin (inline caption only)
3464 const double captionmaxwidth
= main_len
- 2 * captionhmargin
;
3469 // Calculate caption width and height
3471 font
= createAnnotDrawFont(xref
, &fontResDict
);
3474 while (i
< contents
->getLength()) {
3477 layoutText(contents
, &out
, &i
, font
, &linewidth
, 0, NULL
, gFalse
);
3478 linewidth
*= fontsize
;
3479 if (linewidth
> captionwidth
) {
3480 captionwidth
= linewidth
;
3484 captionheight
= lines
* fontsize
;
3485 // If text is longer than available space, turn into captionPosTop
3486 if (captionwidth
> captionmaxwidth
) {
3487 actualCaptionPos
= captionPosTop
;
3490 fontResDict
.initNull();
3494 // Draw main segment
3495 matr
.transform (0, leaderLineLength
, &tx
, &ty
);
3496 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", tx
, ty
);
3497 appearBBox
->extendTo (tx
, ty
);
3499 if (captionwidth
!= 0 && actualCaptionPos
== captionPosInline
) { // Break in the middle
3500 matr
.transform ((main_len
-captionwidth
)/2 - captionhmargin
, leaderLineLength
, &tx
, &ty
);
3501 appearBuf
->appendf ("{0:.2f} {1:.2f} l S\n", tx
, ty
);
3503 matr
.transform ((main_len
+captionwidth
)/2 + captionhmargin
, leaderLineLength
, &tx
, &ty
);
3504 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", tx
, ty
);
3507 matr
.transform (main_len
, leaderLineLength
, &tx
, &ty
);
3508 appearBuf
->appendf ("{0:.2f} {1:.2f} l S\n", tx
, ty
);
3509 appearBBox
->extendTo (tx
, ty
);
3511 // TODO: Line endings
3513 // Draw caption text
3515 double tlx
= (main_len
- captionwidth
) / 2, tly
; // Top-left coords
3516 if (actualCaptionPos
== captionPosInline
) {
3517 tly
= leaderLineLength
+ captionheight
/ 2;
3519 tly
= leaderLineLength
+ captionheight
+ 2*borderWidth
;
3522 tlx
+= captionTextHorizontal
;
3523 tly
+= captionTextVertical
;
3525 // Adjust bounding box
3526 matr
.transform (tlx
, tly
-captionheight
, &tx
, &ty
);
3527 appearBBox
->extendTo (tx
, ty
);
3528 matr
.transform (tlx
+captionwidth
, tly
-captionheight
, &tx
, &ty
);
3529 appearBBox
->extendTo (tx
, ty
);
3530 matr
.transform (tlx
+captionwidth
, tly
, &tx
, &ty
);
3531 appearBBox
->extendTo (tx
, ty
);
3532 matr
.transform (tlx
, tly
, &tx
, &ty
);
3533 appearBBox
->extendTo (tx
, ty
);
3535 // Setup text state (reusing transformed top-left coord)
3536 appearBuf
->appendf ("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize
); // Font color: black
3537 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} Tm\n",
3538 matr
.m
[0], matr
.m
[1], matr
.m
[2], matr
.m
[3], tx
, ty
);
3539 appearBuf
->appendf ("0 {0:.2f} Td\n", -fontsize
* font
->getDescent());
3542 double xposPrev
= 0;
3543 while (i
< contents
->getLength()) {
3545 double linewidth
, xpos
;
3546 layoutText(contents
, &out
, &i
, font
, &linewidth
, 0, NULL
, gFalse
);
3547 linewidth
*= fontsize
;
3548 xpos
= (captionwidth
- linewidth
) / 2;
3549 appearBuf
->appendf("{0:.2f} {1:.2f} Td\n", xpos
- xposPrev
, -fontsize
);
3550 writeString(&out
, appearBuf
);
3551 appearBuf
->append ("Tj\n");
3554 appearBuf
->append ("ET\n");
3558 // Draw leader lines
3559 double ll_len
= fabs(leaderLineLength
) + leaderLineExtension
;
3560 double sign
= leaderLineLength
>= 0 ? 1 : -1;
3562 matr
.transform (0, 0, &tx
, &ty
);
3563 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", tx
, ty
);
3564 appearBBox
->extendTo (tx
, ty
);
3565 matr
.transform (0, sign
*ll_len
, &tx
, &ty
);
3566 appearBuf
->appendf ("{0:.2f} {1:.2f} l S\n", tx
, ty
);
3567 appearBBox
->extendTo (tx
, ty
);
3569 matr
.transform (main_len
, 0, &tx
, &ty
);
3570 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", tx
, ty
);
3571 appearBBox
->extendTo (tx
, ty
);
3572 matr
.transform (main_len
, sign
*ll_len
, &tx
, &ty
);
3573 appearBuf
->appendf ("{0:.2f} {1:.2f} l S\n", tx
, ty
);
3574 appearBBox
->extendTo (tx
, ty
);
3577 appearBuf
->append ("Q\n");
3580 appearBBox
->getBBoxRect(bbox
);
3582 createForm(bbox
, gFalse
, &fontResDict
, &appearance
);
3584 Object aStream
, resDict
;
3586 createForm(bbox
, gTrue
, &fontResDict
, &aStream
);
3589 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
3590 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
3591 createForm(bbox
, gFalse
, &resDict
, &appearance
);
3596 void AnnotLine::draw(Gfx
*gfx
, GBool printing
) {
3599 if (!isVisible (printing
))
3603 if (appearance
.isNull()) {
3604 generateLineAppearance();
3607 // draw the appearance stream
3608 appearance
.fetch(gfx
->getXRef(), &obj
);
3610 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
3611 appearBBox
->getPageXMin(), appearBBox
->getPageYMin(),
3612 appearBBox
->getPageXMax(), appearBBox
->getPageYMax(),
3615 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
3616 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
3621 // Before retrieving the res dict, regenerate the appearance stream if needed,
3622 // because AnnotLine::draw may need to store font info in the res dict
3623 Object
*AnnotLine::getAppearanceResDict(Object
*dest
) {
3624 if (appearance
.isNull()) {
3625 generateLineAppearance();
3627 return Annot::getAppearanceResDict(dest
);
3630 //------------------------------------------------------------------------
3632 //------------------------------------------------------------------------
3633 AnnotTextMarkup::AnnotTextMarkup(PDFDoc
*docA
, PDFRectangle
*rect
, AnnotSubtype subType
) :
3634 AnnotMarkup(docA
, rect
) {
3639 annotObj
.dictSet ("Subtype", obj1
.initName ("Highlight"));
3642 annotObj
.dictSet ("Subtype", obj1
.initName ("Underline"));
3645 annotObj
.dictSet ("Subtype", obj1
.initName ("Squiggly"));
3648 annotObj
.dictSet ("Subtype", obj1
.initName ("StrikeOut"));
3651 assert (0 && "Invalid subtype for AnnotTextMarkup\n");
3654 // Store dummy quadrilateral with null coordinates
3656 obj2
.initArray (doc
->getXRef());
3657 for (int i
= 0; i
< 4*2; ++i
) {
3658 obj2
.arrayAdd (obj3
.initReal (0));
3660 annotObj
.dictSet ("QuadPoints", &obj2
);
3662 initialize(docA
, annotObj
.getDict());
3665 AnnotTextMarkup::AnnotTextMarkup(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
3666 AnnotMarkup(docA
, dict
, obj
) {
3667 // the real type will be read in initialize()
3668 type
= typeHighlight
;
3669 initialize(docA
, dict
);
3672 void AnnotTextMarkup::initialize(PDFDoc
*docA
, Dict
*dict
) {
3675 if (dict
->lookup("Subtype", &obj1
)->isName()) {
3676 GooString
typeName(obj1
.getName());
3677 if (!typeName
.cmp("Highlight")) {
3678 type
= typeHighlight
;
3679 } else if (!typeName
.cmp("Underline")) {
3680 type
= typeUnderline
;
3681 } else if (!typeName
.cmp("Squiggly")) {
3682 type
= typeSquiggly
;
3683 } else if (!typeName
.cmp("StrikeOut")) {
3684 type
= typeStrikeOut
;
3689 if(dict
->lookup("QuadPoints", &obj1
)->isArray()) {
3690 quadrilaterals
= new AnnotQuadrilaterals(obj1
.getArray(), rect
);
3692 error(errSyntaxError
, -1, "Bad Annot Text Markup QuadPoints");
3693 quadrilaterals
= NULL
;
3699 AnnotTextMarkup::~AnnotTextMarkup() {
3700 if(quadrilaterals
) {
3701 delete quadrilaterals
;
3705 void AnnotTextMarkup::setType(AnnotSubtype new_type
) {
3710 obj1
.initName("Highlight");
3713 obj1
.initName("Underline");
3716 obj1
.initName("Squiggly");
3719 obj1
.initName("StrikeOut");
3722 assert(!"Invalid subtype");
3726 update("Subtype", &obj1
);
3727 invalidateAppearance();
3730 void AnnotTextMarkup::setQuadrilaterals(AnnotQuadrilaterals
*quadPoints
) {
3732 obj1
.initArray (xref
);
3734 for (int i
= 0; i
< quadPoints
->getQuadrilateralsLength(); ++i
) {
3735 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getX1(i
)));
3736 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getY1(i
)));
3737 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getX2(i
)));
3738 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getY2(i
)));
3739 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getX3(i
)));
3740 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getY3(i
)));
3741 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getX4(i
)));
3742 obj1
.arrayAdd (obj2
.initReal (quadPoints
->getY4(i
)));
3745 delete quadrilaterals
;
3746 quadrilaterals
= new AnnotQuadrilaterals(obj1
.getArray(), rect
);
3748 annotObj
.dictSet ("QuadPoints", &obj1
);
3749 invalidateAppearance();
3752 void AnnotTextMarkup::draw(Gfx
*gfx
, GBool printing
) {
3758 if (!isVisible (printing
))
3762 if (appearance
.isNull() || type
== typeHighlight
) {
3763 GBool blendMultiply
= gTrue
;
3766 appearBuf
= new GooString ();
3767 appearBuf
->append ("q\n");
3771 appearBBox
= new AnnotAppearanceBBox(rect
);
3772 for (i
= 0; i
< quadrilaterals
->getQuadrilateralsLength(); ++i
) {
3773 appearBBox
->extendTo (quadrilaterals
->getX1(i
) - rect
->x1
, quadrilaterals
->getY1(i
) - rect
->y1
);
3774 appearBBox
->extendTo (quadrilaterals
->getX2(i
) - rect
->x1
, quadrilaterals
->getY2(i
) - rect
->y1
);
3775 appearBBox
->extendTo (quadrilaterals
->getX3(i
) - rect
->x1
, quadrilaterals
->getY3(i
) - rect
->y1
);
3776 appearBBox
->extendTo (quadrilaterals
->getX4(i
) - rect
->x1
, quadrilaterals
->getY4(i
) - rect
->y1
);
3782 setColor(color
, gFalse
);
3784 appearBuf
->append ("[] 0 d 1 w\n");
3786 for (i
= 0; i
< quadrilaterals
->getQuadrilateralsLength(); ++i
) {
3787 double x3
, y3
, x4
, y4
;
3789 x3
= quadrilaterals
->getX3(i
);
3790 y3
= quadrilaterals
->getY3(i
);
3791 x4
= quadrilaterals
->getX4(i
);
3792 y4
= quadrilaterals
->getY4(i
);
3794 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", x3
, y3
);
3795 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", x4
, y4
);
3796 appearBuf
->append ("S\n");
3801 setColor(color
, gFalse
);
3803 blendMultiply
= gFalse
;
3804 appearBuf
->append ("[] 0 d 1 w\n");
3806 for (i
= 0; i
< quadrilaterals
->getQuadrilateralsLength(); ++i
) {
3807 double x1
, y1
, x2
, y2
;
3808 double x3
, y3
, x4
, y4
;
3810 x1
= quadrilaterals
->getX1(i
);
3811 y1
= quadrilaterals
->getY1(i
);
3812 x2
= quadrilaterals
->getX2(i
);
3813 y2
= quadrilaterals
->getY2(i
);
3815 x3
= quadrilaterals
->getX3(i
);
3816 y3
= quadrilaterals
->getY3(i
);
3817 x4
= quadrilaterals
->getX4(i
);
3818 y4
= quadrilaterals
->getY4(i
);
3820 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", (x1
+x3
)/2., (y1
+y3
)/2.);
3821 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", (x2
+x4
)/2., (y2
+y4
)/2.);
3822 appearBuf
->append ("S\n");
3827 setColor(color
, gFalse
);
3829 appearBuf
->append ("[] 0 d 1 w\n");
3831 for (i
= 0; i
< quadrilaterals
->getQuadrilateralsLength(); ++i
) {
3832 double x1
, y1
, x2
, y3
;
3835 x1
= quadrilaterals
->getX1(i
);
3836 y1
= quadrilaterals
->getY1(i
);
3837 x2
= quadrilaterals
->getX2(i
);
3838 y3
= quadrilaterals
->getY3(i
);
3839 h6
= (y1
- y3
) / 6.0;
3841 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", x1
, y3
+h6
);
3844 down
= !down
; // Zigzag line
3846 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", x1
, y3
+ (down
? 0 : h6
));
3848 appearBuf
->append ("S\n");
3856 setColor(color
, gTrue
);
3858 double biggestBorder
= 0;
3859 for (i
= 0; i
< quadrilaterals
->getQuadrilateralsLength(); ++i
) {
3860 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
3863 x1
= quadrilaterals
->getX1(i
);
3864 y1
= quadrilaterals
->getY1(i
);
3865 x2
= quadrilaterals
->getX2(i
);
3866 y2
= quadrilaterals
->getY2(i
);
3867 x3
= quadrilaterals
->getX3(i
);
3868 y3
= quadrilaterals
->getY3(i
);
3869 x4
= quadrilaterals
->getX4(i
);
3870 y4
= quadrilaterals
->getY4(i
);
3871 h4
= fabs(y1
- y3
) / 4.0;
3873 if (h4
> biggestBorder
) {
3877 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", x3
, y3
);
3878 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
3879 x3
- h4
, y3
+ h4
, x1
- h4
, y1
- h4
, x1
, y1
);
3880 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", x2
, y2
);
3881 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
3882 x2
+ h4
, y2
- h4
, x4
+ h4
, y4
+ h4
, x4
, y4
);
3883 appearBuf
->append ("f\n");
3885 appearBBox
->setBorderWidth(biggestBorder
);
3888 appearBuf
->append ("Q\n");
3890 Object aStream
, resDict
;
3892 bbox
[0] = appearBBox
->getPageXMin();
3893 bbox
[1] = appearBBox
->getPageYMin();
3894 bbox
[2] = appearBBox
->getPageXMax();
3895 bbox
[3] = appearBBox
->getPageYMax();
3896 createForm(bbox
, gTrue
, NULL
, &aStream
);
3899 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
3900 createResourcesDict("Fm0", &aStream
, "GS0", 1, blendMultiply
? "Multiply" : NULL
, &resDict
);
3902 createForm(bbox
, gFalse
, &resDict
, &appearance
);
3904 createForm(bbox
, gTrue
, &resDict
, &aStream
);
3907 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
3908 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
3909 createForm(bbox
, gFalse
, &resDict
, &appearance
);
3914 // draw the appearance stream
3915 appearance
.fetch(gfx
->getXRef(), &obj
);
3917 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
3918 appearBBox
->getPageXMin(), appearBBox
->getPageYMin(),
3919 appearBBox
->getPageXMax(), appearBBox
->getPageYMax(),
3922 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
3923 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
3928 //------------------------------------------------------------------------
3930 //------------------------------------------------------------------------
3932 AnnotWidget::AnnotWidget(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
3933 Annot(docA
, dict
, obj
) {
3936 initialize(docA
, dict
);
3939 AnnotWidget::AnnotWidget(PDFDoc
*docA
, Dict
*dict
, Object
*obj
, FormField
*fieldA
) :
3940 Annot(docA
, dict
, obj
) {
3943 initialize(docA
, dict
);
3946 AnnotWidget::~AnnotWidget() {
3948 delete appearCharacs
;
3953 additionalActions
.free();
3959 void AnnotWidget::initialize(PDFDoc
*docA
, Dict
*dict
) {
3962 form
= doc
->getCatalog()->getForm();
3964 if(dict
->lookup("H", &obj1
)->isName()) {
3965 const char *modeName
= obj1
.getName();
3967 if(!strcmp(modeName
, "N")) {
3968 mode
= highlightModeNone
;
3969 } else if(!strcmp(modeName
, "O")) {
3970 mode
= highlightModeOutline
;
3971 } else if(!strcmp(modeName
, "P") || !strcmp(modeName
, "T")) {
3972 mode
= highlightModePush
;
3974 mode
= highlightModeInvert
;
3977 mode
= highlightModeInvert
;
3981 if(dict
->lookup("MK", &obj1
)->isDict()) {
3982 appearCharacs
= new AnnotAppearanceCharacs(obj1
.getDict());
3984 appearCharacs
= NULL
;
3989 if(dict
->lookup("A", &obj1
)->isDict()) {
3990 action
= LinkAction::parseAction(&obj1
, doc
->getCatalog()->getBaseURI());
3994 dict
->lookupNF("AA", &additionalActions
);
3996 if(dict
->lookup("Parent", &obj1
)->isDict()) {
4003 if (dict
->lookup("BS", &obj1
)->isDict()) {
4005 border
= new AnnotBorderBS(obj1
.getDict());
4009 updatedAppearanceStream
.num
= updatedAppearanceStream
.gen
= -1;
4012 LinkAction
* AnnotWidget::getAdditionalAction(AdditionalActionsType type
)
4014 return ::getAdditionalAction(type
, &additionalActions
, doc
);
4017 LinkAction
* AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType type
)
4019 return ::getFormAdditionalAction(type
, &additionalActions
, doc
);
4022 // Grand unified handler for preparing text strings to be drawn into form
4023 // fields. Takes as input a text string (in PDFDocEncoding or UTF-16).
4024 // Converts some or all of this string to the appropriate encoding for the
4025 // specified font, and computes the width of the text. Can optionally stop
4026 // converting when a specified width has been reached, to perform line-breaking
4027 // for multi-line fields.
4030 // text: input text string to convert
4031 // outBuf: buffer for writing re-encoded string
4032 // i: index at which to start converting; will be updated to point just after
4033 // last character processed
4034 // font: the font which will be used for display
4035 // width: computed width (unscaled by font size) will be stored here
4036 // widthLimit: if non-zero, stop converting to keep width under this value
4037 // (should be scaled down by font size)
4038 // charCount: count of number of characters will be stored here
4039 // noReencode: if set, do not try to translate the character encoding
4040 // (useful for Zapf Dingbats or other unusual encodings)
4041 // can only be used with simple fonts, not CID-keyed fonts
4043 // TODO: Handle surrogate pairs in UTF-16.
4044 // Should be able to generate output for any CID-keyed font.
4045 // Doesn't handle vertical fonts--should it?
4046 void Annot::layoutText(GooString
*text
, GooString
*outBuf
, int *i
,
4047 GfxFont
*font
, double *width
, double widthLimit
,
4048 int *charCount
, GBool noReencode
)
4051 Unicode uChar
, *uAux
;
4054 double dx
, dy
, ox
, oy
;
4058 GBool unicode
= text
->hasUnicodeMarker();
4059 GBool spacePrev
; // previous character was a space
4061 // State for backtracking when more text has been processed than fits within
4062 // widthLimit. We track for both input (text) and output (outBuf) the offset
4063 // to the first character to discard.
4065 // We keep track of several points:
4066 // 1 - end of previous completed word which fits
4067 // 2 - previous character which fit
4068 int last_i1
, last_i2
, last_o1
, last_o2
;
4070 if (unicode
&& text
->getLength() % 2 != 0) {
4071 error(errSyntaxError
, -1, "AnnotWidget::layoutText, bad unicode string");
4075 // skip Unicode marker on string if needed
4076 if (unicode
&& *i
== 0)
4079 // Start decoding and copying characters, until either:
4080 // we reach the end of the string
4081 // we reach the maximum width
4082 // we reach a newline character
4083 // As we copy characters, keep track of the last full word to fit, so that we
4084 // can backtrack if we exceed the maximum width.
4085 last_i1
= last_i2
= *i
;
4086 last_o1
= last_o2
= 0;
4090 while (*i
< text
->getLength()) {
4092 last_o2
= outBuf
->getLength();
4095 uChar
= (unsigned char)(text
->getChar(*i
)) << 8;
4096 uChar
+= (unsigned char)(text
->getChar(*i
+ 1));
4100 uChar
= text
->getChar(*i
) & 0xff;
4102 uChar
= pdfDocEncoding
[text
->getChar(*i
) & 0xff];
4106 // Explicit line break?
4107 if (uChar
== '\r' || uChar
== '\n') {
4108 // Treat a <CR><LF> sequence as a single line break
4109 if (uChar
== '\r' && *i
< text
->getLength()) {
4110 if (unicode
&& text
->getChar(*i
) == '\0'
4111 && text
->getChar(*i
+ 1) == '\n')
4113 else if (!unicode
&& text
->getChar(*i
) == '\n')
4121 outBuf
->append(uChar
);
4123 CharCodeToUnicode
*ccToUnicode
= font
->getToUnicode();
4125 // This assumes an identity CMap.
4126 outBuf
->append((uChar
>> 8) & 0xff);
4127 outBuf
->append(uChar
& 0xff);
4128 } else if (ccToUnicode
->mapToCharCode(&uChar
, &c
, 1)) {
4129 ccToUnicode
->decRefCnt();
4130 if (font
->isCIDFont()) {
4131 // TODO: This assumes an identity CMap. It should be extended to
4132 // handle the general case.
4133 outBuf
->append((c
>> 8) & 0xff);
4134 outBuf
->append(c
& 0xff);
4140 ccToUnicode
->decRefCnt();
4141 error(errSyntaxError
, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar
);
4145 // If we see a space, then we have a linebreak opportunity.
4155 // Compute width of character just output
4156 if (outBuf
->getLength() > last_o2
) {
4158 font
->getNextChar(outBuf
->getCString() + last_o2
,
4159 outBuf
->getLength() - last_o2
,
4160 &c
, &uAux
, &uLen
, &dx
, &dy
, &ox
, &oy
);
4164 // Current line over-full now?
4165 if (widthLimit
> 0.0 && w
> widthLimit
) {
4167 // Back up to the previous word which fit, if there was a previous
4170 outBuf
->del(last_o1
, outBuf
->getLength() - last_o1
);
4171 } else if (last_o2
> 0) {
4172 // Otherwise, back up to the previous character (in the only word on
4175 outBuf
->del(last_o2
, outBuf
->getLength() - last_o2
);
4177 // Otherwise, we were trying to fit the first character; include it
4178 // anyway even if it overflows the space--no updates to make.
4184 // If splitting input into lines because we reached the width limit, then
4185 // consume any remaining trailing spaces that would go on this line from the
4186 // input. If in doing so we reach a newline, consume that also. This code
4187 // won't run if we stopped at a newline above, since in that case w <=
4188 // widthLimit still.
4189 if (widthLimit
> 0.0 && w
> widthLimit
) {
4191 while (*i
< text
->getLength()
4192 && text
->getChar(*i
) == '\0' && text
->getChar(*i
+ 1) == ' ')
4194 if (*i
< text
->getLength()
4195 && text
->getChar(*i
) == '\0' && text
->getChar(*i
+ 1) == '\r')
4197 if (*i
< text
->getLength()
4198 && text
->getChar(*i
) == '\0' && text
->getChar(*i
+ 1) == '\n')
4201 while (*i
< text
->getLength() && text
->getChar(*i
) == ' ')
4203 if (*i
< text
->getLength() && text
->getChar(*i
) == '\r')
4205 if (*i
< text
->getLength() && text
->getChar(*i
) == '\n')
4210 // Compute the actual width and character count of the final string, based on
4211 // breakpoint, if this information is requested by the caller.
4212 if (width
!= NULL
|| charCount
!= NULL
) {
4213 char *s
= outBuf
->getCString();
4214 int len
= outBuf
->getLength();
4218 if (charCount
!= NULL
)
4223 n
= font
->getNextChar(s
, len
, &c
, &uAux
, &uLen
, &dx
, &dy
, &ox
, &oy
);
4231 if (charCount
!= NULL
)
4240 // Copy the given string to appearBuf, adding parentheses around it and
4241 // escaping characters as appropriate.
4242 void Annot::writeString(GooString
*str
, GooString
*appearBuf
)
4247 appearBuf
->append('(');
4249 for (i
= 0; i
< str
->getLength(); ++i
) {
4250 c
= str
->getChar(i
);
4251 if (c
== '(' || c
== ')' || c
== '\\') {
4252 appearBuf
->append('\\');
4253 appearBuf
->append(c
);
4254 } else if (c
< 0x20) {
4255 appearBuf
->appendf("\\{0:03o}", (unsigned char)c
);
4257 appearBuf
->append(c
);
4261 appearBuf
->append(')');
4264 // Draw the variable text or caption for a field.
4265 void AnnotWidget::drawText(GooString
*text
, GooString
*da
, GfxResources
*resources
,
4266 GBool multiline
, int comb
, int quadding
,
4267 GBool txField
, GBool forceZapfDingbats
,
4270 GooString
*tok
, *convertedText
;
4273 double fontSize
, fontSize2
, borderWidth
, x
, xPrev
, y
, w
, wMax
;
4274 int tfPos
, tmPos
, i
, j
;
4276 GBool freeText
= gFalse
; // true if text should be freed before return
4277 GBool freeFont
= gFalse
;
4279 //~ if there is no MK entry, this should use the existing content stream,
4280 //~ and only replace the marked content portion of it
4281 //~ (this is only relevant for Tx fields)
4283 // parse the default appearance string
4286 daToks
= new GooList();
4288 while (i
< da
->getLength()) {
4289 while (i
< da
->getLength() && Lexer::isSpace(da
->getChar(i
))) {
4292 if (i
< da
->getLength()) {
4294 j
< da
->getLength() && !Lexer::isSpace(da
->getChar(j
));
4296 daToks
->append(new GooString(da
, i
, j
- i
));
4300 for (i
= 2; i
< daToks
->getLength(); ++i
) {
4301 if (i
>= 2 && !((GooString
*)daToks
->get(i
))->cmp("Tf")) {
4303 } else if (i
>= 6 && !((GooString
*)daToks
->get(i
))->cmp("Tm")) {
4311 // force ZapfDingbats
4312 if (forceZapfDingbats
) {
4314 tok
= (GooString
*)daToks
->get(tfPos
);
4315 if (tok
->cmp("/ZaDb")) {
4317 tok
->append("/ZaDb");
4321 // get the font and font size
4325 tok
= (GooString
*)daToks
->get(tfPos
);
4326 if (tok
->getLength() >= 1 && tok
->getChar(0) == '/') {
4327 if (!resources
|| !(font
= resources
->lookupFont(tok
->getCString() + 1))) {
4328 if (forceZapfDingbats
) {
4329 // We are forcing ZaDb but the font does not exist
4330 // so create a fake one
4331 Ref r
; // dummy Ref, it's not used at all in this codepath
4334 Dict
*d
= new Dict(xref
);
4335 font
= new Gfx8BitFont(xref
, "ZaDb", r
, new GooString("ZapfDingbats"), fontType1
, r
, d
);
4338 addDingbatsResource
= gTrue
;
4340 error(errSyntaxError
, -1, "Unknown font in field's DA string");
4344 error(errSyntaxError
, -1, "Invalid font name in 'Tf' operator in field's DA string");
4346 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4347 fontSize
= gatof(tok
->getCString());
4349 error(errSyntaxError
, -1, "Missing 'Tf' operator in field's DA string");
4353 deleteGooList(daToks
, GooString
);
4358 // get the border width
4359 borderWidth
= border
? border
->getWidth() : 0;
4361 // for a password field, replace all characters with asterisks
4364 if (text
->hasUnicodeMarker())
4365 len
= (text
->getLength() - 2) / 2;
4367 len
= text
->getLength();
4369 text
= new GooString
;
4370 for (i
= 0; i
< len
; ++i
)
4375 convertedText
= new GooString
;
4379 appearBuf
->append("/Tx BMC\n");
4381 appearBuf
->append("q\n");
4382 rot
= appearCharacs
? appearCharacs
->getRotation() : 0;
4385 appearBuf
->appendf("0 1 -1 0 {0:.2f} 0 cm\n", rect
->x2
- rect
->x1
);
4386 dx
= rect
->y2
- rect
->y1
;
4387 dy
= rect
->x2
- rect
->x1
;
4390 appearBuf
->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n",
4391 rect
->x2
- rect
->x1
, rect
->y2
- rect
->y1
);
4392 dx
= rect
->x2
- rect
->y2
;
4393 dy
= rect
->y2
- rect
->y1
;
4396 appearBuf
->appendf("0 -1 1 0 0 {0:.2f} cm\n", rect
->y2
- rect
->y1
);
4397 dx
= rect
->y2
- rect
->y1
;
4398 dy
= rect
->x2
- rect
->x1
;
4400 default: // assume rot == 0
4401 dx
= rect
->x2
- rect
->x1
;
4402 dy
= rect
->y2
- rect
->y1
;
4405 appearBuf
->append("BT\n");
4408 // note: the comb flag is ignored in multiline mode
4410 wMax
= dx
- 2 * borderWidth
- 4;
4412 // compute font autosize
4413 if (fontSize
== 0) {
4414 for (fontSize
= 20; fontSize
> 1; --fontSize
) {
4417 while (i
< text
->getLength()) {
4418 layoutText(text
, convertedText
, &i
, font
, &w
, wMax
/ fontSize
, NULL
,
4422 // approximate the descender for the last line
4423 if (y
>= 0.33 * fontSize
) {
4428 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4430 tok
->appendf("{0:.2f}", fontSize
);
4434 // starting y coordinate
4435 // (note: each line of text starts with a Td operator that moves
4439 // set the font matrix
4441 tok
= (GooString
*)daToks
->get(tmPos
+ 4);
4444 tok
= (GooString
*)daToks
->get(tmPos
+ 5);
4446 tok
->appendf("{0:.2f}", y
);
4449 // write the DA string
4451 for (i
= 0; i
< daToks
->getLength(); ++i
) {
4452 appearBuf
->append((GooString
*)daToks
->get(i
))->append(' ');
4456 // write the font matrix (if not part of the DA string)
4458 appearBuf
->appendf("1 0 0 1 0 {0:.2f} Tm\n", y
);
4461 // write a series of lines of text
4464 while (i
< text
->getLength()) {
4465 layoutText(text
, convertedText
, &i
, font
, &w
, wMax
/ fontSize
, NULL
,
4469 // compute text start position
4471 case quaddingLeftJustified
:
4473 x
= borderWidth
+ 2;
4475 case quaddingCentered
:
4478 case quaddingRightJustified
:
4479 x
= dx
- borderWidth
- 2 - w
;
4484 appearBuf
->appendf("{0:.2f} {1:.2f} Td\n", x
- xPrev
, -fontSize
);
4485 writeString(convertedText
, appearBuf
);
4486 appearBuf
->append(" Tj\n");
4494 //~ replace newlines with spaces? - what does Acrobat do?
4500 // compute comb spacing
4501 w
= (dx
- 2 * borderWidth
) / comb
;
4503 // compute font autosize
4504 if (fontSize
== 0) {
4505 fontSize
= dy
- 2 * borderWidth
;
4509 fontSize
= floor(fontSize
);
4511 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4513 tok
->appendf("{0:.2f}", fontSize
);
4518 layoutText(text
, convertedText
, &i
, font
, NULL
, 0.0, &charCount
,
4520 if (charCount
> comb
)
4523 // compute starting text cell
4525 case quaddingLeftJustified
:
4529 case quaddingCentered
:
4530 x
= borderWidth
+ (comb
- charCount
) / 2 * w
;
4532 case quaddingRightJustified
:
4533 x
= borderWidth
+ (comb
- charCount
) * w
;
4536 y
= 0.5 * dy
- 0.4 * fontSize
;
4538 // set the font matrix
4540 tok
= (GooString
*)daToks
->get(tmPos
+ 4);
4542 tok
->appendf("{0:.2f}", x
);
4543 tok
= (GooString
*)daToks
->get(tmPos
+ 5);
4545 tok
->appendf("{0:.2f}", y
);
4548 // write the DA string
4550 for (i
= 0; i
< daToks
->getLength(); ++i
) {
4551 appearBuf
->append((GooString
*)daToks
->get(i
))->append(' ');
4555 // write the font matrix (if not part of the DA string)
4557 appearBuf
->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x
, y
);
4560 // write the text string
4561 char *s
= convertedText
->getCString();
4562 int len
= convertedText
->getLength();
4564 xPrev
= w
; // so that first character is placed properly
4565 while (i
< comb
&& len
> 0) {
4569 double dx
, dy
, ox
, oy
;
4572 n
= font
->getNextChar(s
, len
, &code
, &uAux
, &uLen
, &dx
, &dy
, &ox
, &oy
);
4575 // center each character within its cell, by advancing the text
4576 // position the appropriate amount relative to the start of the
4577 // previous character
4579 appearBuf
->appendf("{0:.2f} 0 Td\n", x
- xPrev
+ w
);
4581 GooString
*charBuf
= new GooString(s
, n
);
4582 writeString(charBuf
, appearBuf
);
4583 appearBuf
->append(" Tj\n");
4592 // regular (non-comb) formatting
4595 layoutText(text
, convertedText
, &i
, font
, &w
, 0.0, NULL
,
4598 // compute font autosize
4599 if (fontSize
== 0) {
4600 fontSize
= dy
- 2 * borderWidth
;
4601 fontSize2
= (dx
- 4 - 2 * borderWidth
) / w
;
4602 if (fontSize2
< fontSize
) {
4603 fontSize
= fontSize2
;
4605 fontSize
= floor(fontSize
);
4607 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4609 tok
->appendf("{0:.2f}", fontSize
);
4613 // compute text start position
4616 case quaddingLeftJustified
:
4618 x
= borderWidth
+ 2;
4620 case quaddingCentered
:
4623 case quaddingRightJustified
:
4624 x
= dx
- borderWidth
- 2 - w
;
4627 y
= 0.5 * dy
- 0.4 * fontSize
;
4629 // set the font matrix
4631 tok
= (GooString
*)daToks
->get(tmPos
+ 4);
4633 tok
->appendf("{0:.2f}", x
);
4634 tok
= (GooString
*)daToks
->get(tmPos
+ 5);
4636 tok
->appendf("{0:.2f}", y
);
4639 // write the DA string
4641 for (i
= 0; i
< daToks
->getLength(); ++i
) {
4642 appearBuf
->append((GooString
*)daToks
->get(i
))->append(' ');
4646 // write the font matrix (if not part of the DA string)
4648 appearBuf
->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x
, y
);
4651 // write the text string
4652 writeString(convertedText
, appearBuf
);
4653 appearBuf
->append(" Tj\n");
4657 appearBuf
->append("ET\n");
4658 appearBuf
->append("Q\n");
4660 appearBuf
->append("EMC\n");
4663 deleteGooList(daToks
, GooString
);
4668 delete convertedText
;
4674 // Draw the variable text or caption for a field.
4675 void AnnotWidget::drawListBox(FormFieldChoice
*fieldChoice
,
4676 GooString
*da
, GfxResources
*resources
, int quadding
) {
4678 GooString
*tok
, *convertedText
;
4680 double fontSize
, fontSize2
, borderWidth
, x
, y
, w
, wMax
;
4681 int tfPos
, tmPos
, i
, j
;
4683 //~ if there is no MK entry, this should use the existing content stream,
4684 //~ and only replace the marked content portion of it
4685 //~ (this is only relevant for Tx fields)
4687 // parse the default appearance string
4690 daToks
= new GooList();
4692 while (i
< da
->getLength()) {
4693 while (i
< da
->getLength() && Lexer::isSpace(da
->getChar(i
))) {
4696 if (i
< da
->getLength()) {
4698 j
< da
->getLength() && !Lexer::isSpace(da
->getChar(j
));
4700 daToks
->append(new GooString(da
, i
, j
- i
));
4704 for (i
= 2; i
< daToks
->getLength(); ++i
) {
4705 if (i
>= 2 && !((GooString
*)daToks
->get(i
))->cmp("Tf")) {
4707 } else if (i
>= 6 && !((GooString
*)daToks
->get(i
))->cmp("Tm")) {
4715 // get the font and font size
4719 tok
= (GooString
*)daToks
->get(tfPos
);
4720 if (tok
->getLength() >= 1 && tok
->getChar(0) == '/') {
4721 if (!resources
|| !(font
= resources
->lookupFont(tok
->getCString() + 1))) {
4722 error(errSyntaxError
, -1, "Unknown font in field's DA string");
4725 error(errSyntaxError
, -1, "Invalid font name in 'Tf' operator in field's DA string");
4727 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4728 fontSize
= gatof(tok
->getCString());
4730 error(errSyntaxError
, -1, "Missing 'Tf' operator in field's DA string");
4734 deleteGooList(daToks
, GooString
);
4739 convertedText
= new GooString
;
4741 // get the border width
4742 borderWidth
= border
? border
->getWidth() : 0;
4744 // compute font autosize
4745 if (fontSize
== 0) {
4747 for (i
= 0; i
< fieldChoice
->getNumChoices(); ++i
) {
4749 if (fieldChoice
->getChoice(i
) == NULL
) {
4750 error(errSyntaxError
, -1, "Invalid annotation listbox");
4752 deleteGooList(daToks
, GooString
);
4754 delete convertedText
;
4757 layoutText(fieldChoice
->getChoice(i
), convertedText
, &j
, font
, &w
, 0.0, NULL
, gFalse
);
4762 fontSize
= rect
->y2
- rect
->y1
- 2 * borderWidth
;
4763 fontSize2
= (rect
->x2
- rect
->x1
- 4 - 2 * borderWidth
) / wMax
;
4764 if (fontSize2
< fontSize
) {
4765 fontSize
= fontSize2
;
4767 fontSize
= floor(fontSize
);
4769 tok
= (GooString
*)daToks
->get(tfPos
+ 1);
4771 tok
->appendf("{0:.2f}", fontSize
);
4775 y
= rect
->y2
- rect
->y1
- 1.1 * fontSize
;
4776 for (i
= fieldChoice
->getTopIndex(); i
< fieldChoice
->getNumChoices(); ++i
) {
4778 appearBuf
->append("q\n");
4780 // draw the background if selected
4781 if (fieldChoice
->isSelected(i
)) {
4782 appearBuf
->append("0 g f\n");
4783 appearBuf
->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
4786 rect
->x2
- rect
->x1
- 2 * borderWidth
,
4791 appearBuf
->append("BT\n");
4793 // compute text width and start position
4795 layoutText(fieldChoice
->getChoice(i
), convertedText
, &j
, font
, &w
, 0.0, NULL
, gFalse
);
4798 case quaddingLeftJustified
:
4800 x
= borderWidth
+ 2;
4802 case quaddingCentered
:
4803 x
= (rect
->x2
- rect
->x1
- w
) / 2;
4805 case quaddingRightJustified
:
4806 x
= rect
->x2
- rect
->x1
- borderWidth
- 2 - w
;
4810 // set the font matrix
4812 tok
= (GooString
*)daToks
->get(tmPos
+ 4);
4814 tok
->appendf("{0:.2f}", x
);
4815 tok
= (GooString
*)daToks
->get(tmPos
+ 5);
4817 tok
->appendf("{0:.2f}", y
);
4820 // write the DA string
4822 for (j
= 0; j
< daToks
->getLength(); ++j
) {
4823 appearBuf
->append((GooString
*)daToks
->get(j
))->append(' ');
4827 // write the font matrix (if not part of the DA string)
4829 appearBuf
->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x
, y
);
4832 // change the text color if selected
4833 if (fieldChoice
->isSelected(i
)) {
4834 appearBuf
->append("1 g\n");
4837 // write the text string
4838 writeString(convertedText
, appearBuf
);
4839 appearBuf
->append(" Tj\n");
4842 appearBuf
->append("ET\n");
4843 appearBuf
->append("Q\n");
4846 y
-= 1.1 * fontSize
;
4850 deleteGooList(daToks
, GooString
);
4853 delete convertedText
;
4856 void AnnotWidget::drawBorder() {
4859 AnnotColor adjustedColor
;
4860 double w
= border
->getWidth();
4862 AnnotColor
*aColor
= appearCharacs
->getBorderColor();
4864 aColor
= appearCharacs
->getBackColor();
4868 double dx
= rect
->x2
- rect
->x1
;
4869 double dy
= rect
->y2
- rect
->y1
;
4871 // radio buttons with no caption have a round border
4872 GBool hasCaption
= appearCharacs
->getNormalCaption() != NULL
;
4873 if (field
->getType() == formButton
&&
4874 static_cast<FormFieldButton
*>(field
)->getButtonType() == formButtonRadio
&& !hasCaption
) {
4875 double r
= 0.5 * (dx
< dy
? dx
: dy
);
4876 switch (border
->getStyle()) {
4877 case AnnotBorder::borderDashed
:
4878 appearBuf
->append("[");
4879 dashLength
= border
->getDashLength();
4880 dash
= border
->getDash();
4881 for (int i
= 0; i
< dashLength
; ++i
) {
4882 appearBuf
->appendf(" {0:.2f}", dash
[i
]);
4884 appearBuf
->append("] 0 d\n");
4885 // fall through to the solid case
4886 case AnnotBorder::borderSolid
:
4887 case AnnotBorder::borderUnderlined
:
4888 appearBuf
->appendf("{0:.2f} w\n", w
);
4889 setColor(aColor
, gFalse
);
4890 drawCircle(0.5 * dx
, 0.5 * dy
, r
- 0.5 * w
, gFalse
);
4892 case AnnotBorder::borderBeveled
:
4893 case AnnotBorder::borderInset
:
4894 appearBuf
->appendf("{0:.2f} w\n", 0.5 * w
);
4895 setColor(aColor
, gFalse
);
4896 drawCircle(0.5 * dx
, 0.5 * dy
, r
- 0.25 * w
, gFalse
);
4897 adjustedColor
= AnnotColor(*aColor
);
4898 adjustedColor
.adjustColor(border
->getStyle() == AnnotBorder::borderBeveled
? 1 : -1);
4899 setColor(&adjustedColor
, gFalse
);
4900 drawCircleTopLeft(0.5 * dx
, 0.5 * dy
, r
- 0.75 * w
);
4901 adjustedColor
= AnnotColor(*aColor
);
4902 adjustedColor
.adjustColor(border
->getStyle() == AnnotBorder::borderBeveled
? -1 : 1);
4903 setColor(&adjustedColor
, gFalse
);
4904 drawCircleBottomRight(0.5 * dx
, 0.5 * dy
, r
- 0.75 * w
);
4908 switch (border
->getStyle()) {
4909 case AnnotBorder::borderDashed
:
4910 appearBuf
->append("[");
4911 dashLength
= border
->getDashLength();
4912 dash
= border
->getDash();
4913 for (int i
= 0; i
< dashLength
; ++i
) {
4914 appearBuf
->appendf(" {0:.2f}", dash
[i
]);
4916 appearBuf
->append("] 0 d\n");
4917 // fall through to the solid case
4918 case AnnotBorder::borderSolid
:
4919 appearBuf
->appendf("{0:.2f} w\n", w
);
4920 setColor(aColor
, gFalse
);
4921 appearBuf
->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
4922 0.5 * w
, dx
- w
, dy
- w
);
4924 case AnnotBorder::borderBeveled
:
4925 case AnnotBorder::borderInset
:
4926 adjustedColor
= AnnotColor(*aColor
);
4927 adjustedColor
.adjustColor(border
->getStyle() == AnnotBorder::borderBeveled
? 1 : -1);
4928 setColor(&adjustedColor
, gTrue
);
4929 appearBuf
->append("0 0 m\n");
4930 appearBuf
->appendf("0 {0:.2f} l\n", dy
);
4931 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", dx
, dy
);
4932 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", dx
- w
, dy
- w
);
4933 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", w
, dy
- w
);
4934 appearBuf
->appendf("{0:.2f} {0:.2f} l\n", w
);
4935 appearBuf
->append("f\n");
4936 adjustedColor
= AnnotColor(*aColor
);
4937 adjustedColor
.adjustColor(border
->getStyle() == AnnotBorder::borderBeveled
? -1 : 1);
4938 setColor(&adjustedColor
, gTrue
);
4939 appearBuf
->append("0 0 m\n");
4940 appearBuf
->appendf("{0:.2f} 0 l\n", dx
);
4941 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", dx
, dy
);
4942 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", dx
- w
, dy
- w
);
4943 appearBuf
->appendf("{0:.2f} {1:.2f} l\n", dx
- w
, w
);
4944 appearBuf
->appendf("{0:.2f} {0:.2f} l\n", w
);
4945 appearBuf
->append("f\n");
4947 case AnnotBorder::borderUnderlined
:
4948 appearBuf
->appendf("{0:.2f} w\n", w
);
4949 setColor(aColor
, gFalse
);
4950 appearBuf
->appendf("0 0 m {0:.2f} 0 l s\n", dx
);
4954 // clip to the inside of the border
4955 appearBuf
->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
4956 w
, dx
- 2 * w
, dy
- 2 * w
);
4960 void AnnotWidget::drawFormFieldButton(GfxResources
*resources
, GooString
*da
) {
4961 GooString
*caption
= NULL
;
4963 caption
= appearCharacs
->getNormalCaption();
4965 switch (static_cast<FormFieldButton
*>(field
)->getButtonType()) {
4966 case formButtonRadio
: {
4967 //~ Acrobat doesn't draw a caption if there is no AP dict (?)
4968 if (appearState
&& appearState
->cmp("Off") != 0 &&
4969 static_cast<FormFieldButton
*>(field
)->getState(appearState
->getCString())) {
4971 drawText(caption
, da
, resources
, gFalse
, 0, fieldQuadCenter
,
4973 } else if (appearCharacs
) {
4974 AnnotColor
*aColor
= appearCharacs
->getBorderColor();
4976 double dx
= rect
->x2
- rect
->x1
;
4977 double dy
= rect
->y2
- rect
->y1
;
4978 setColor(aColor
, gTrue
);
4979 drawCircle(0.5 * dx
, 0.5 * dy
, 0.2 * (dx
< dy
? dx
: dy
), gTrue
);
4985 case formButtonPush
:
4987 drawText(caption
, da
, resources
, gFalse
, 0, fieldQuadCenter
, gFalse
, gFalse
);
4989 case formButtonCheck
:
4990 if (appearState
&& appearState
->cmp("Off") != 0) {
4992 GooString
checkMark("3");
4993 drawText(&checkMark
, da
, resources
, gFalse
, 0, fieldQuadCenter
, gFalse
, gTrue
);
4995 drawText(caption
, da
, resources
, gFalse
, 0, fieldQuadCenter
, gFalse
, gTrue
);
5002 void AnnotWidget::drawFormFieldText(GfxResources
*resources
, GooString
*da
) {
5003 VariableTextQuadding quadding
;
5004 GooString
*contents
;
5005 FormFieldText
*fieldText
= static_cast<FormFieldText
*>(field
);
5007 contents
= fieldText
->getContent();
5009 quadding
= field
->hasTextQuadding() ? field
->getTextQuadding() : form
->getTextQuadding();
5012 if (fieldText
->isComb())
5013 comb
= fieldText
->getMaxLen();
5015 drawText(contents
, da
, resources
,
5016 fieldText
->isMultiline(), comb
, quadding
, gTrue
, gFalse
, fieldText
->isPassword());
5020 void AnnotWidget::drawFormFieldChoice(GfxResources
*resources
, GooString
*da
) {
5021 GooString
*selected
;
5022 VariableTextQuadding quadding
;
5023 FormFieldChoice
*fieldChoice
= static_cast<FormFieldChoice
*>(field
);
5025 quadding
= field
->hasTextQuadding() ? field
->getTextQuadding() : form
->getTextQuadding();
5027 if (fieldChoice
->isCombo()) {
5028 selected
= fieldChoice
->getSelectedChoice();
5030 drawText(selected
, da
, resources
, gFalse
, 0, quadding
, gTrue
, gFalse
);
5031 //~ Acrobat draws a popup icon on the right side
5035 drawListBox(fieldChoice
, da
, resources
, quadding
);
5039 void AnnotWidget::generateFieldAppearance() {
5040 Object appearDict
, obj1
, obj2
;
5041 GfxResources
*resources
;
5042 MemStream
*appearStream
;
5045 appearBuf
= new GooString ();
5047 // draw the background
5048 if (appearCharacs
) {
5049 AnnotColor
*aColor
= appearCharacs
->getBackColor();
5051 setColor(aColor
, gTrue
);
5052 appearBuf
->appendf("0 0 {0:.2f} {1:.2f} re f\n",
5053 rect
->x2
- rect
->x1
, rect
->y2
- rect
->y1
);
5058 if (appearCharacs
&& border
&& border
->getWidth() > 0)
5061 da
= field
->getDefaultAppearance();
5063 da
= form
->getDefaultAppearance();
5065 resources
= form
->getDefaultResources();
5067 // draw the field contents
5068 switch (field
->getType()) {
5070 drawFormFieldButton(resources
, da
);
5073 drawFormFieldText(resources
, da
);
5076 drawFormFieldChoice(resources
, da
);
5083 error(errSyntaxError
, -1, "Unknown field type");
5086 // build the appearance stream dictionary
5087 appearDict
.initDict(xref
);
5088 appearDict
.dictAdd(copyString("Length"),
5089 obj1
.initInt(appearBuf
->getLength()));
5090 appearDict
.dictAdd(copyString("Subtype"), obj1
.initName("Form"));
5091 obj1
.initArray(xref
);
5092 obj1
.arrayAdd(obj2
.initReal(0));
5093 obj1
.arrayAdd(obj2
.initReal(0));
5094 obj1
.arrayAdd(obj2
.initReal(rect
->x2
- rect
->x1
));
5095 obj1
.arrayAdd(obj2
.initReal(rect
->y2
- rect
->y1
));
5096 appearDict
.dictAdd(copyString("BBox"), &obj1
);
5098 // set the resource dictionary
5099 Object
*resDict
= form
->getDefaultResourcesObj();
5100 if (resDict
->isDict()) {
5101 appearDict
.dictAdd(copyString("Resources"), resDict
->copy(&obj1
));
5104 // build the appearance stream
5105 appearStream
= new MemStream(copyString(appearBuf
->getCString()), 0,
5106 appearBuf
->getLength(), &appearDict
);
5108 appearance
.initStream(appearStream
);
5111 appearStream
->setNeedFree(gTrue
);
5114 void AnnotWidget::updateAppearanceStream()
5116 // If this the first time updateAppearanceStream() is called on this widget,
5117 // destroy the AP dictionary because we are going to create a new one.
5118 if (updatedAppearanceStream
.num
== -1) {
5119 invalidateAppearance(); // Delete AP dictionary and all referenced streams
5122 // There's no need to create a new appearance stream if NeedAppearances is
5123 // set, because it will be ignored next time anyway.
5124 if (form
&& form
->getNeedAppearances())
5127 // Create the new appearance
5128 generateFieldAppearance();
5130 // Fetch the appearance stream we've just created
5132 appearance
.fetch(xref
, &obj1
);
5134 // If this the first time updateAppearanceStream() is called on this widget,
5135 // create a new AP dictionary containing the new appearance stream.
5136 // Otherwise, just update the stream we had created previously.
5137 if (updatedAppearanceStream
.num
== -1) {
5138 // Write the appearance stream
5139 updatedAppearanceStream
= xref
->addIndirectObject(&obj1
);
5142 // Write the AP dictionary
5144 obj1
.initDict(xref
);
5145 obj1
.dictAdd(copyString("N"), obj2
.initRef(updatedAppearanceStream
.num
, updatedAppearanceStream
.gen
));
5146 update("AP", &obj1
);
5148 // Update our internal pointers to the appearance dictionary
5149 appearStreams
= new AnnotAppearance(doc
, &obj1
);
5151 // Replace the existing appearance stream
5152 xref
->setModifiedObject(&obj1
, updatedAppearanceStream
);
5157 void AnnotWidget::draw(Gfx
*gfx
, GBool printing
) {
5160 if (!isVisible (printing
))
5164 addDingbatsResource
= gFalse
;
5166 // Only construct the appearance stream when
5167 // - annot doesn't have an AP or
5168 // - NeedAppearances is true
5170 if (appearance
.isNull() || (form
&& form
->getNeedAppearances()))
5171 generateFieldAppearance();
5174 // draw the appearance stream
5175 appearance
.fetch(gfx
->getXRef(), &obj
);
5176 if (addDingbatsResource
) {
5177 // We are forcing ZaDb but the font does not exist
5178 // so create a fake one
5179 Object baseFontObj
, subtypeObj
;
5180 baseFontObj
.initName("ZapfDingbats");
5181 subtypeObj
.initName("Type1");
5184 Dict
*fontDict
= new Dict(gfx
->getXRef());
5186 fontDict
->add(copyString("BaseFont"), &baseFontObj
);
5187 fontDict
->add(copyString("Subtype"), &subtypeObj
);
5188 fontDictObj
.initDict(fontDict
);
5190 Object fontsDictObj
;
5191 Dict
*fontsDict
= new Dict(gfx
->getXRef());
5192 fontsDict
->decRef();
5193 fontsDict
->add(copyString("ZaDb"), &fontDictObj
);
5194 fontsDictObj
.initDict(fontsDict
);
5196 Dict
*dict
= new Dict(gfx
->getXRef());
5197 dict
->add(copyString("Font"), &fontsDictObj
);
5198 gfx
->pushResources(dict
);
5201 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
5202 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
5203 if (addDingbatsResource
) {
5204 gfx
->popResources();
5210 //------------------------------------------------------------------------
5212 //------------------------------------------------------------------------
5213 AnnotMovie::AnnotMovie(PDFDoc
*docA
, PDFRectangle
*rect
, Movie
*movieA
) :
5218 annotObj
.dictSet ("Subtype", obj1
.initName ("Movie"));
5220 movie
= movieA
->copy();
5221 // TODO: create movie dict from movieA
5223 initialize(docA
, annotObj
.getDict());
5226 AnnotMovie::AnnotMovie(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5227 Annot(docA
, dict
, obj
) {
5229 initialize(docA
, dict
);
5232 AnnotMovie::~AnnotMovie() {
5238 void AnnotMovie::initialize(PDFDoc
*docA
, Dict
* dict
) {
5241 if (dict
->lookup("T", &obj1
)->isString()) {
5242 title
= obj1
.getString()->copy();
5249 if (dict
->lookup("Movie", &movieDict
)->isDict()) {
5251 dict
->lookup("A", &obj2
);
5253 movie
= new Movie (&movieDict
, &obj2
);
5255 movie
= new Movie (&movieDict
);
5256 if (!movie
->isOk()) {
5263 error(errSyntaxError
, -1, "Bad Annot Movie");
5270 void AnnotMovie::draw(Gfx
*gfx
, GBool printing
) {
5273 if (!isVisible (printing
))
5277 if (appearance
.isNull() && movie
->getShowPoster()) {
5280 movie
->getPoster(&poster
);
5281 movie
->getAspect(&width
, &height
);
5283 if (width
!= -1 && height
!= -1 && !poster
.isNone()) {
5286 appearBuf
= new GooString ();
5287 appearBuf
->append ("q\n");
5288 appearBuf
->appendf ("{0:d} 0 0 {1:d} 0 0 cm\n", width
, height
);
5289 appearBuf
->append ("/MImg Do\n");
5290 appearBuf
->append ("Q\n");
5293 imgDict
.initDict(gfx
->getXRef());
5294 imgDict
.dictSet ("MImg", &poster
);
5297 resDict
.initDict(gfx
->getXRef());
5298 resDict
.dictSet ("XObject", &imgDict
);
5300 Object formDict
, obj1
, obj2
;
5301 formDict
.initDict(gfx
->getXRef());
5302 formDict
.dictSet("Length", obj1
.initInt(appearBuf
->getLength()));
5303 formDict
.dictSet("Subtype", obj1
.initName("Form"));
5304 formDict
.dictSet("Name", obj1
.initName("FRM"));
5305 obj1
.initArray(gfx
->getXRef());
5306 obj1
.arrayAdd(obj2
.initInt(0));
5307 obj1
.arrayAdd(obj2
.initInt(0));
5308 obj1
.arrayAdd(obj2
.initInt(width
));
5309 obj1
.arrayAdd(obj2
.initInt(height
));
5310 formDict
.dictSet("BBox", &obj1
);
5311 obj1
.initArray(gfx
->getXRef());
5312 obj1
.arrayAdd(obj2
.initInt(1));
5313 obj1
.arrayAdd(obj2
.initInt(0));
5314 obj1
.arrayAdd(obj2
.initInt(0));
5315 obj1
.arrayAdd(obj2
.initInt(1));
5316 obj1
.arrayAdd(obj2
.initInt(-width
/ 2));
5317 obj1
.arrayAdd(obj2
.initInt(-height
/ 2));
5318 formDict
.dictSet("Matrix", &obj1
);
5319 formDict
.dictSet("Resources", &resDict
);
5322 mStream
= new MemStream(copyString(appearBuf
->getCString()), 0,
5323 appearBuf
->getLength(), &formDict
);
5324 mStream
->setNeedFree(gTrue
);
5325 aStream
.initStream(mStream
);
5329 objDict
.initDict(gfx
->getXRef());
5330 objDict
.dictSet ("FRM", &aStream
);
5332 resDict
.initDict(gfx
->getXRef());
5333 resDict
.dictSet ("XObject", &objDict
);
5335 appearBuf
= new GooString ();
5336 appearBuf
->append ("q\n");
5337 appearBuf
->appendf ("0 0 {0:d} {1:d} re W n\n", width
, height
);
5338 appearBuf
->append ("q\n");
5339 appearBuf
->appendf ("0 0 {0:d} {1:d} re W n\n", width
, height
);
5340 appearBuf
->appendf ("1 0 0 1 {0:d} {1:d} cm\n", width
/ 2, height
/ 2);
5341 appearBuf
->append ("/FRM Do\n");
5342 appearBuf
->append ("Q\n");
5343 appearBuf
->append ("Q\n");
5346 bbox
[0] = bbox
[1] = 0;
5349 createForm(bbox
, gFalse
, &resDict
, &appearance
);
5355 // draw the appearance stream
5356 appearance
.fetch(gfx
->getXRef(), &obj
);
5357 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
5358 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
5362 //------------------------------------------------------------------------
5364 //------------------------------------------------------------------------
5365 AnnotScreen::AnnotScreen(PDFDoc
*docA
, PDFRectangle
*rect
) :
5371 annotObj
.dictSet ("Subtype", obj1
.initName ("Screen"));
5372 initialize(docA
, annotObj
.getDict());
5375 AnnotScreen::AnnotScreen(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5376 Annot(docA
, dict
, obj
) {
5378 initialize(docA
, dict
);
5381 AnnotScreen::~AnnotScreen() {
5385 delete appearCharacs
;
5389 additionalActions
.free();
5392 void AnnotScreen::initialize(PDFDoc
*docA
, Dict
* dict
) {
5396 if (dict
->lookup("T", &obj1
)->isString()) {
5397 title
= obj1
.getString()->copy();
5402 if (dict
->lookup("A", &obj1
)->isDict()) {
5403 action
= LinkAction::parseAction(&obj1
, doc
->getCatalog()->getBaseURI());
5404 if (action
->getKind() == actionRendition
&& page
== 0) {
5405 error (errSyntaxError
, -1, "Invalid Rendition action: associated screen annotation without P");
5413 dict
->lookupNF("AA", &additionalActions
);
5415 appearCharacs
= NULL
;
5416 if(dict
->lookup("MK", &obj1
)->isDict()) {
5417 appearCharacs
= new AnnotAppearanceCharacs(obj1
.getDict());
5422 LinkAction
* AnnotScreen::getAdditionalAction(AdditionalActionsType type
)
5424 if (type
== actionFocusIn
|| type
== actionFocusOut
) // not defined for screen annotation
5427 return ::getAdditionalAction(type
, &additionalActions
, doc
);
5430 //------------------------------------------------------------------------
5432 //------------------------------------------------------------------------
5433 AnnotStamp::AnnotStamp(PDFDoc
*docA
, PDFRectangle
*rect
) :
5434 AnnotMarkup(docA
, rect
) {
5438 annotObj
.dictSet ("Subtype", obj1
.initName ("Stamp"));
5439 initialize(docA
, annotObj
.getDict());
5442 AnnotStamp::AnnotStamp(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5443 AnnotMarkup(docA
, dict
, obj
) {
5445 initialize(docA
, dict
);
5448 AnnotStamp::~AnnotStamp() {
5452 void AnnotStamp::initialize(PDFDoc
*docA
, Dict
* dict
) {
5455 if (dict
->lookup("Name", &obj1
)->isName()) {
5456 icon
= new GooString(obj1
.getName());
5458 icon
= new GooString("Draft");
5464 void AnnotStamp::setIcon(GooString
*new_icon
) {
5468 icon
= new GooString (new_icon
);
5470 icon
= new GooString();
5474 obj1
.initName (icon
->getCString());
5475 update("Name", &obj1
);
5476 invalidateAppearance();
5479 //------------------------------------------------------------------------
5481 //------------------------------------------------------------------------
5482 AnnotGeometry::AnnotGeometry(PDFDoc
*docA
, PDFRectangle
*rect
, AnnotSubtype subType
) :
5483 AnnotMarkup(docA
, rect
) {
5488 annotObj
.dictSet ("Subtype", obj1
.initName ("Square"));
5491 annotObj
.dictSet ("Subtype", obj1
.initName ("Circle"));
5494 assert (0 && "Invalid subtype for AnnotGeometry\n");
5497 initialize(docA
, annotObj
.getDict());
5500 AnnotGeometry::AnnotGeometry(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5501 AnnotMarkup(docA
, dict
, obj
) {
5502 // the real type will be read in initialize()
5504 initialize(docA
, dict
);
5507 AnnotGeometry::~AnnotGeometry() {
5508 delete interiorColor
;
5509 delete borderEffect
;
5510 delete geometryRect
;
5513 void AnnotGeometry::initialize(PDFDoc
*docA
, Dict
* dict
) {
5516 if (dict
->lookup("Subtype", &obj1
)->isName()) {
5517 GooString
typeName(obj1
.getName());
5518 if (!typeName
.cmp("Square")) {
5520 } else if (!typeName
.cmp("Circle")) {
5526 if (dict
->lookup("IC", &obj1
)->isArray()) {
5527 interiorColor
= new AnnotColor(obj1
.getArray());
5529 interiorColor
= NULL
;
5533 if (dict
->lookup("BS", &obj1
)->isDict()) {
5535 border
= new AnnotBorderBS(obj1
.getDict());
5536 } else if (!border
) {
5537 border
= new AnnotBorderBS();
5541 if (dict
->lookup("BE", &obj1
)->isDict()) {
5542 borderEffect
= new AnnotBorderEffect(obj1
.getDict());
5544 borderEffect
= NULL
;
5548 geometryRect
= NULL
;
5549 if (dict
->lookup("RD", &obj1
)->isArray()) {
5550 geometryRect
= parseDiffRectangle(obj1
.getArray(), rect
);
5556 void AnnotGeometry::setType(AnnotSubtype new_type
) {
5561 obj1
.initName("Square");
5564 obj1
.initName("Circle");
5567 assert(!"Invalid subtype");
5571 update("Subtype", &obj1
);
5572 invalidateAppearance();
5575 void AnnotGeometry::setInteriorColor(AnnotColor
*new_color
) {
5576 delete interiorColor
;
5580 new_color
->writeToObject(xref
, &obj1
);
5581 update ("IC", &obj1
);
5582 interiorColor
= new_color
;
5584 interiorColor
= NULL
;
5586 invalidateAppearance();
5589 void AnnotGeometry::draw(Gfx
*gfx
, GBool printing
) {
5593 if (!isVisible (printing
))
5597 if (appearance
.isNull()) {
5600 appearBuf
= new GooString ();
5601 appearBuf
->append ("q\n");
5603 setColor(color
, gFalse
);
5605 double borderWidth
= border
->getWidth();
5606 setLineStyleForBorder(border
);
5609 setColor(interiorColor
, gTrue
);
5611 if (type
== typeSquare
) {
5612 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re\n",
5613 borderWidth
/ 2.0, borderWidth
/ 2.0,
5614 (rect
->x2
- rect
->x1
) - borderWidth
,
5615 (rect
->y2
- rect
->y1
) - borderWidth
);
5617 double width
, height
;
5619 double x1
, y1
, x2
, y2
, x3
, y3
;
5621 width
= rect
->x2
- rect
->x1
;
5622 height
= rect
->y2
- rect
->y1
;
5623 b
= borderWidth
/ 2.0;
5627 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", x1
, y1
);
5634 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5635 x1
, y1
, x2
, y2
, x3
, y3
);
5638 x1
= x3
+ (width
/ 4.0);
5642 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5643 x1
, y1
, x2
, y2
, x3
, y3
);
5651 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5652 x1
, y1
, x2
, y2
, x3
, y3
);
5660 appearBuf
->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5661 x1
, y1
, x2
, y2
, x3
, y3
);
5664 if (interiorColor
&& interiorColor
->getSpace() != AnnotColor::colorTransparent
)
5665 appearBuf
->append ("b\n");
5667 appearBuf
->append ("S\n");
5669 appearBuf
->append ("Q\n");
5672 bbox
[0] = bbox
[1] = 0;
5673 bbox
[2] = rect
->x2
- rect
->x1
;
5674 bbox
[3] = rect
->y2
- rect
->y1
;
5676 createForm(bbox
, gFalse
, NULL
, &appearance
);
5680 createForm(bbox
, gTrue
, NULL
, &aStream
);
5684 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
5685 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
5686 createForm(bbox
, gFalse
, &resDict
, &appearance
);
5691 // draw the appearance stream
5692 appearance
.fetch(gfx
->getXRef(), &obj
);
5693 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
5694 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
5698 //------------------------------------------------------------------------
5700 //------------------------------------------------------------------------
5701 AnnotPolygon::AnnotPolygon(PDFDoc
*docA
, PDFRectangle
*rect
, AnnotSubtype subType
) :
5702 AnnotMarkup(docA
, rect
) {
5707 annotObj
.dictSet ("Subtype", obj1
.initName ("Polygon"));
5710 annotObj
.dictSet ("Subtype", obj1
.initName ("PolyLine"));
5713 assert (0 && "Invalid subtype for AnnotGeometry\n");
5716 // Store dummy path with one null vertex only
5718 obj2
.initArray (doc
->getXRef());
5719 obj2
.arrayAdd (obj3
.initReal (0));
5720 obj2
.arrayAdd (obj3
.initReal (0));
5721 annotObj
.dictSet ("Vertices", &obj2
);
5723 initialize(docA
, annotObj
.getDict());
5726 AnnotPolygon::AnnotPolygon(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5727 AnnotMarkup(docA
, dict
, obj
) {
5728 // the real type will be read in initialize()
5730 initialize(docA
, dict
);
5733 AnnotPolygon::~AnnotPolygon() {
5737 delete interiorColor
;
5740 delete borderEffect
;
5743 void AnnotPolygon::initialize(PDFDoc
*docA
, Dict
* dict
) {
5746 if (dict
->lookup("Subtype", &obj1
)->isName()) {
5747 GooString
typeName(obj1
.getName());
5748 if (!typeName
.cmp("Polygon")) {
5750 } else if (!typeName
.cmp("PolyLine")) {
5751 type
= typePolyLine
;
5756 if (dict
->lookup("Vertices", &obj1
)->isArray()) {
5757 vertices
= new AnnotPath(obj1
.getArray());
5759 vertices
= new AnnotPath();
5760 error(errSyntaxError
, -1, "Bad Annot Polygon Vertices");
5765 if (dict
->lookup("LE", &obj1
)->isArray() && obj1
.arrayGetLength() == 2) {
5768 if(obj1
.arrayGet(0, &obj2
)->isString())
5769 startStyle
= parseAnnotLineEndingStyle(obj2
.getString());
5771 startStyle
= annotLineEndingNone
;
5774 if(obj1
.arrayGet(1, &obj2
)->isString())
5775 endStyle
= parseAnnotLineEndingStyle(obj2
.getString());
5777 endStyle
= annotLineEndingNone
;
5781 startStyle
= endStyle
= annotLineEndingNone
;
5785 if (dict
->lookup("IC", &obj1
)->isArray()) {
5786 interiorColor
= new AnnotColor(obj1
.getArray());
5788 interiorColor
= NULL
;
5792 if (dict
->lookup("BS", &obj1
)->isDict()) {
5794 border
= new AnnotBorderBS(obj1
.getDict());
5795 } else if (!border
) {
5796 border
= new AnnotBorderBS();
5800 if (dict
->lookup("BE", &obj1
)->isDict()) {
5801 borderEffect
= new AnnotBorderEffect(obj1
.getDict());
5803 borderEffect
= NULL
;
5807 if (dict
->lookup("IT", &obj1
)->isName()) {
5808 const char *intentName
= obj1
.getName();
5810 if(!strcmp(intentName
, "PolygonCloud")) {
5811 intent
= polygonCloud
;
5812 } else if(!strcmp(intentName
, "PolyLineDimension")) {
5813 intent
= polylineDimension
;
5815 intent
= polygonDimension
;
5818 intent
= polygonCloud
;
5823 void AnnotPolygon::setType(AnnotSubtype new_type
) {
5828 obj1
.initName("Polygon");
5831 obj1
.initName("PolyLine");
5834 assert(!"Invalid subtype");
5838 update("Subtype", &obj1
);
5839 invalidateAppearance();
5842 void AnnotPolygon::setVertices(AnnotPath
*path
) {
5846 obj1
.initArray(xref
);
5848 for (int i
= 0; i
< path
->getCoordsLength(); i
++) {
5849 obj1
.arrayAdd (obj2
.initReal (path
->getX(i
)));
5850 obj1
.arrayAdd (obj2
.initReal (path
->getY(i
)));
5853 vertices
= new AnnotPath(obj1
.getArray());
5855 update("Vertices", &obj1
);
5856 invalidateAppearance();
5859 void AnnotPolygon::setStartEndStyle(AnnotLineEndingStyle start
, AnnotLineEndingStyle end
) {
5865 obj1
.initArray(xref
);
5866 obj1
.arrayAdd( obj2
.initName(convertAnnotLineEndingStyle( startStyle
)) );
5867 obj1
.arrayAdd( obj2
.initName(convertAnnotLineEndingStyle( endStyle
)) );
5869 update("LE", &obj1
);
5870 invalidateAppearance();
5873 void AnnotPolygon::setInteriorColor(AnnotColor
*new_color
) {
5874 delete interiorColor
;
5878 new_color
->writeToObject(xref
, &obj1
);
5879 update ("IC", &obj1
);
5880 interiorColor
= new_color
;
5882 interiorColor
= NULL
;
5884 invalidateAppearance();
5887 void AnnotPolygon::setIntent(AnnotPolygonIntent new_intent
) {
5890 intent
= new_intent
;
5891 if (new_intent
== polygonCloud
)
5892 obj1
.initName("PolygonCloud");
5893 else if (new_intent
== polylineDimension
)
5894 obj1
.initName("PolyLineDimension");
5895 else // polygonDimension
5896 obj1
.initName("PolygonDimension");
5897 update ("IT", &obj1
);
5900 void AnnotPolygon::draw(Gfx
*gfx
, GBool printing
) {
5904 if (!isVisible (printing
))
5908 if (appearance
.isNull()) {
5909 appearBBox
= new AnnotAppearanceBBox(rect
);
5912 appearBuf
= new GooString ();
5913 appearBuf
->append ("q\n");
5916 setColor(color
, gFalse
);
5919 setLineStyleForBorder(border
);
5920 appearBBox
->setBorderWidth(std::max(1., border
->getWidth()));
5922 if (interiorColor
) {
5923 setColor(interiorColor
, gTrue
);
5926 if (vertices
->getCoordsLength() != 0) {
5927 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", vertices
->getX(0) - rect
->x1
, vertices
->getY(0) - rect
->y1
);
5928 appearBBox
->extendTo (vertices
->getX(0) - rect
->x1
, vertices
->getY(0) - rect
->y1
);
5930 for (int i
= 1; i
< vertices
->getCoordsLength(); ++i
) {
5931 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", vertices
->getX(i
) - rect
->x1
, vertices
->getY(i
) - rect
->y1
);
5932 appearBBox
->extendTo (vertices
->getX(i
) - rect
->x1
, vertices
->getY(i
) - rect
->y1
);
5935 if (type
== typePolygon
) {
5936 if (interiorColor
&& interiorColor
->getSpace() != AnnotColor::colorTransparent
) {
5937 appearBuf
->append ("b\n");
5939 appearBuf
->append ("s\n");
5942 appearBuf
->append ("S\n");
5946 appearBuf
->append ("Q\n");
5949 appearBBox
->getBBoxRect(bbox
);
5951 createForm(bbox
, gFalse
, NULL
, &appearance
);
5953 Object aStream
, resDict
;
5955 createForm(bbox
, gTrue
, NULL
, &aStream
);
5958 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
5959 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
5960 createForm(bbox
, gFalse
, &resDict
, &appearance
);
5965 // draw the appearance stream
5966 appearance
.fetch(gfx
->getXRef(), &obj
);
5968 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
5969 appearBBox
->getPageXMin(), appearBBox
->getPageYMin(),
5970 appearBBox
->getPageXMax(), appearBBox
->getPageYMax(),
5973 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
5974 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
5979 //------------------------------------------------------------------------
5981 //------------------------------------------------------------------------
5982 AnnotCaret::AnnotCaret(PDFDoc
*docA
, PDFRectangle
*rect
) :
5983 AnnotMarkup(docA
, rect
) {
5988 annotObj
.dictSet ("Subtype", obj1
.initName ("Caret"));
5989 initialize(docA
, annotObj
.getDict());
5992 AnnotCaret::AnnotCaret(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
5993 AnnotMarkup(docA
, dict
, obj
) {
5995 initialize(docA
, dict
);
5998 AnnotCaret::~AnnotCaret() {
6002 void AnnotCaret::initialize(PDFDoc
*docA
, Dict
* dict
) {
6005 symbol
= symbolNone
;
6006 if (dict
->lookup("Sy", &obj1
)->isName()) {
6007 GooString
typeName(obj1
.getName());
6008 if (!typeName
.cmp("P")) {
6010 } else if (!typeName
.cmp("None")) {
6011 symbol
= symbolNone
;
6016 if (dict
->lookup("RD", &obj1
)->isArray()) {
6017 caretRect
= parseDiffRectangle(obj1
.getArray(), rect
);
6025 void AnnotCaret::setSymbol(AnnotCaretSymbol new_symbol
) {
6027 obj1
.initName( new_symbol
== symbolP
? "P" : "None" );
6028 symbol
= new_symbol
;
6029 update("Sy", &obj1
);
6030 invalidateAppearance();
6033 //------------------------------------------------------------------------
6035 //------------------------------------------------------------------------
6036 AnnotInk::AnnotInk(PDFDoc
*docA
, PDFRectangle
*rect
) :
6037 AnnotMarkup(docA
, rect
) {
6042 annotObj
.dictSet ("Subtype", obj1
.initName ("Ink"));
6044 // Store dummy path with one null vertex only
6045 Object obj2
, obj3
, obj4
;
6046 obj2
.initArray (doc
->getXRef());
6047 obj2
.arrayAdd (obj3
.initArray (doc
->getXRef()));
6048 obj3
.arrayAdd (obj4
.initReal (0));
6049 obj3
.arrayAdd (obj4
.initReal (0));
6050 annotObj
.dictSet ("InkList", &obj2
);
6052 initialize(docA
, annotObj
.getDict());
6055 AnnotInk::AnnotInk(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
6056 AnnotMarkup(docA
, dict
, obj
) {
6058 initialize(docA
, dict
);
6061 AnnotInk::~AnnotInk() {
6065 void AnnotInk::initialize(PDFDoc
*docA
, Dict
* dict
) {
6068 if (dict
->lookup("InkList", &obj1
)->isArray()) {
6069 parseInkList(obj1
.getArray());
6073 error(errSyntaxError
, -1, "Bad Annot Ink List");
6078 if (dict
->lookup("BS", &obj1
)->isDict()) {
6080 border
= new AnnotBorderBS(obj1
.getDict());
6081 } else if (!border
) {
6082 border
= new AnnotBorderBS();
6087 void AnnotInk::writeInkList(AnnotPath
**paths
, int n_paths
, Array
*dest_array
) {
6089 for (int i
= 0; i
< n_paths
; ++i
) {
6090 AnnotPath
*path
= paths
[i
];
6091 obj1
.initArray (xref
);
6092 for (int j
= 0; j
< path
->getCoordsLength(); ++j
) {
6093 obj1
.arrayAdd (obj2
.initReal (path
->getX(j
)));
6094 obj1
.arrayAdd (obj2
.initReal (path
->getY(j
)));
6096 dest_array
->add (&obj1
);
6100 void AnnotInk::parseInkList(Array
*array
) {
6101 inkListLength
= array
->getLength();
6102 inkList
= (AnnotPath
**) gmallocn ((inkListLength
), sizeof(AnnotPath
*));
6103 memset(inkList
, 0, inkListLength
* sizeof(AnnotPath
*));
6104 for (int i
= 0; i
< inkListLength
; i
++) {
6106 if (array
->get(i
, &obj2
)->isArray())
6107 inkList
[i
] = new AnnotPath(obj2
.getArray());
6112 void AnnotInk::freeInkList() {
6114 for (int i
= 0; i
< inkListLength
; ++i
)
6120 void AnnotInk::setInkList(AnnotPath
**paths
, int n_paths
) {
6125 obj1
.initArray (xref
);
6126 writeInkList(paths
, n_paths
, obj1
.getArray());
6128 parseInkList(obj1
.getArray());
6129 annotObj
.dictSet ("InkList", &obj1
);
6130 invalidateAppearance();
6133 void AnnotInk::draw(Gfx
*gfx
, GBool printing
) {
6137 if (!isVisible (printing
))
6141 if (appearance
.isNull()) {
6142 appearBBox
= new AnnotAppearanceBBox(rect
);
6145 appearBuf
= new GooString ();
6146 appearBuf
->append ("q\n");
6149 setColor(color
, gFalse
);
6152 setLineStyleForBorder(border
);
6153 appearBBox
->setBorderWidth(std::max(1., border
->getWidth()));
6155 for (int i
= 0; i
< inkListLength
; ++i
) {
6156 const AnnotPath
* path
= inkList
[i
];
6157 if (path
&& path
->getCoordsLength() != 0) {
6158 appearBuf
->appendf ("{0:.2f} {1:.2f} m\n", path
->getX(0) - rect
->x1
, path
->getY(0) - rect
->y1
);
6159 appearBBox
->extendTo (path
->getX(0) - rect
->x1
, path
->getY(0) - rect
->y1
);
6161 for (int j
= 1; j
< path
->getCoordsLength(); ++j
) {
6162 appearBuf
->appendf ("{0:.2f} {1:.2f} l\n", path
->getX(j
) - rect
->x1
, path
->getY(j
) - rect
->y1
);
6163 appearBBox
->extendTo (path
->getX(j
) - rect
->x1
, path
->getY(j
) - rect
->y1
);
6166 appearBuf
->append ("S\n");
6170 appearBuf
->append ("Q\n");
6173 appearBBox
->getBBoxRect(bbox
);
6175 createForm(bbox
, gFalse
, NULL
, &appearance
);
6177 Object aStream
, resDict
;
6179 createForm(bbox
, gTrue
, NULL
, &aStream
);
6182 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
6183 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
6184 createForm(bbox
, gFalse
, &resDict
, &appearance
);
6189 // draw the appearance stream
6190 appearance
.fetch(gfx
->getXRef(), &obj
);
6192 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
6193 appearBBox
->getPageXMin(), appearBBox
->getPageYMin(),
6194 appearBBox
->getPageXMax(), appearBBox
->getPageYMax(),
6197 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
6198 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
6203 //------------------------------------------------------------------------
6204 // AnnotFileAttachment
6205 //------------------------------------------------------------------------
6206 AnnotFileAttachment::AnnotFileAttachment(PDFDoc
*docA
, PDFRectangle
*rect
, GooString
*filename
) :
6207 AnnotMarkup(docA
, rect
) {
6210 type
= typeFileAttachment
;
6212 annotObj
.dictSet ("Subtype", obj1
.initName ("FileAttachment"));
6215 obj2
.initString(filename
->copy());
6216 annotObj
.dictSet ("FS", &obj2
);
6218 initialize(docA
, annotObj
.getDict());
6221 AnnotFileAttachment::AnnotFileAttachment(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
6222 AnnotMarkup(docA
, dict
, obj
) {
6223 type
= typeFileAttachment
;
6224 initialize(docA
, dict
);
6227 AnnotFileAttachment::~AnnotFileAttachment() {
6234 void AnnotFileAttachment::initialize(PDFDoc
*docA
, Dict
* dict
) {
6237 if (dict
->lookup("FS", &obj1
)->isDict() || dict
->lookup("FS", &obj1
)->isString()) {
6240 error(errSyntaxError
, -1, "Bad Annot File Attachment");
6245 if (dict
->lookup("Name", &obj1
)->isName()) {
6246 name
= new GooString(obj1
.getName());
6248 name
= new GooString("PushPin");
6253 #define ANNOT_FILE_ATTACHMENT_AP_PUSHPIN \
6254 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6255 "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" \
6256 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6258 "0.533333 0.541176 0.521569 RG 2 w\n" \
6262 "4 M 5 4 m 6 5 l S\n" \
6264 "11 14 m 9 12 l 6 12 l 13 5 l 13 8 l 15 10 l 18 11 l 20 11 l 12 19 l 12\n" \
6265 "17 l 11 14 l h\n" \
6269 "0.729412 0.741176 0.713725 RG 2 w\n" \
6272 "11 15 m 9 13 l 6 13 l 13 6 l 13 9 l 15 11 l 18 12 l 20 12 l 12 20 l 12\n" \
6273 "18 l 11 15 l h\n" \
6278 #define ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP \
6279 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6280 "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" \
6281 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6283 "0.533333 0.541176 0.521569 RG 2 w\n" \
6287 "4 M 16.645 12.035 m 12.418 7.707 l 10.902 6.559 6.402 11.203 8.09 12.562 c\n" \
6288 "14.133 18.578 l 14.949 19.387 16.867 19.184 17.539 18.465 c 20.551\n" \
6289 "15.23 l 21.191 14.66 21.336 12.887 20.426 12.102 c 13.18 4.824 l 12.18\n" \
6290 "3.82 6.25 2.566 4.324 4.461 c 3 6.395 3.383 11.438 4.711 12.801 c 9.648\n" \
6292 "0.729412 0.741176 0.713725 RG 16.645 13.035 m 12.418 8.707 l\n" \
6293 "10.902 7.559 6.402 12.203 8.09 13.562 c\n" \
6294 "14.133 19.578 l 14.949 20.387 16.867 20.184 17.539 19.465 c 20.551\n" \
6295 "16.23 l 21.191 15.66 21.336 13.887 20.426 13.102 c 13.18 5.824 l 12.18\n" \
6296 "4.82 6.25 3.566 4.324 5.461 c 3 7.395 3.383 12.438 4.711 13.801 c 9.648\n" \
6299 #define ANNOT_FILE_ATTACHMENT_AP_GRAPH \
6300 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6301 "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" \
6302 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6304 "0.533333 0.541176 0.521569 RG 1 w\n" \
6308 "4 M 18.5 15.5 m 18.5 13.086 l 16.086 15.5 l 18.5 15.5 l h\n" \
6310 "7 7 m 10 11 l 13 9 l 18 15 l S\n" \
6311 "0.729412 0.741176 0.713725 RG 7 8 m 10 12 l 13 10 l 18 16 l S\n" \
6312 "18.5 16.5 m 18.5 14.086 l 16.086 16.5 l 18.5 16.5 l h\n" \
6314 "0.533333 0.541176 0.521569 RG 2 w\n" \
6316 "3 19 m 3 3 l 21 3 l S\n" \
6317 "0.729412 0.741176 0.713725 RG 3 20 m 3 4 l 21 4 l S\n"
6319 #define ANNOT_FILE_ATTACHMENT_AP_TAG \
6320 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6321 "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" \
6322 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6324 "0.533333 0.541176 0.521569 RG 0.999781 w\n" \
6328 "4 M q 1 0 0 -1 0 24 cm\n" \
6329 "8.492 8.707 m 8.492 9.535 7.82 10.207 6.992 10.207 c 6.164 10.207 5.492\n" \
6330 "9.535 5.492 8.707 c 5.492 7.879 6.164 7.207 6.992 7.207 c 7.82 7.207\n" \
6331 "8.492 7.879 8.492 8.707 c h\n" \
6332 "8.492 8.707 m S Q\n" \
6334 "20.078 11.414 m 20.891 10.602 20.785 9.293 20.078 8.586 c 14.422 2.93 l\n" \
6335 "13.715 2.223 12.301 2.223 11.594 2.93 c 3.816 10.707 l 3.109 11.414\n" \
6336 "2.402 17.781 3.816 19.195 c 5.23 20.609 11.594 19.902 12.301 19.195 c\n" \
6337 "20.078 11.414 l h\n" \
6338 "20.078 11.414 m S\n" \
6339 "0.729412 0.741176 0.713725 RG 20.078 12.414 m\n" \
6340 "20.891 11.605 20.785 10.293 20.078 9.586 c 14.422 3.93 l\n" \
6341 "13.715 3.223 12.301 3.223 11.594 3.93 c 3.816 11.707 l 3.109 12.414\n" \
6342 "2.402 18.781 3.816 20.195 c 5.23 21.609 11.594 20.902 12.301 20.195 c\n" \
6343 "20.078 12.414 l h\n" \
6344 "20.078 12.414 m S\n" \
6345 "0.533333 0.541176 0.521569 RG 1 w\n" \
6347 "11.949 13.184 m 16.191 8.941 l S\n" \
6348 "0.729412 0.741176 0.713725 RG 11.949 14.184 m 16.191 9.941 l S\n" \
6349 "0.533333 0.541176 0.521569 RG 14.07 6.82 m 9.828 11.062 l S\n" \
6350 "0.729412 0.741176 0.713725 RG 14.07 7.82 m 9.828 12.062 l S\n" \
6351 "0.533333 0.541176 0.521569 RG 6.93 15.141 m 8 20 14.27 20.5 16 20.5 c\n" \
6352 "18.094 20.504 19.5 20 19.5 18 c 19.5 16.699 20.91 16.418 22.5 16.5 c S\n" \
6353 "0.729412 0.741176 0.713725 RG 0.999781 w\n" \
6355 "q 1 0 0 -1 0 24 cm\n" \
6356 "8.492 7.707 m 8.492 8.535 7.82 9.207 6.992 9.207 c 6.164 9.207 5.492\n" \
6357 "8.535 5.492 7.707 c 5.492 6.879 6.164 6.207 6.992 6.207 c 7.82 6.207\n" \
6358 "8.492 6.879 8.492 7.707 c h\n" \
6359 "8.492 7.707 m S Q\n" \
6362 "6.93 16.141 m 8 21 14.27 21.5 16 21.5 c 18.094 21.504 19.5 21 19.5 19 c\n" \
6363 "19.5 17.699 20.91 17.418 22.5 17.5 c S\n"
6365 void AnnotFileAttachment::draw(Gfx
*gfx
, GBool printing
) {
6369 if (!isVisible (printing
))
6373 if (appearance
.isNull()) {
6376 appearBuf
= new GooString ();
6378 appearBuf
->append ("q\n");
6380 setColor(color
, gTrue
);
6382 appearBuf
->append ("1 1 1 rg\n");
6383 if (!name
->cmp("PushPin"))
6384 appearBuf
->append (ANNOT_FILE_ATTACHMENT_AP_PUSHPIN
);
6385 else if (!name
->cmp("Paperclip"))
6386 appearBuf
->append (ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP
);
6387 else if (!name
->cmp("Graph"))
6388 appearBuf
->append (ANNOT_FILE_ATTACHMENT_AP_GRAPH
);
6389 else if (!name
->cmp("Tag"))
6390 appearBuf
->append (ANNOT_FILE_ATTACHMENT_AP_TAG
);
6391 appearBuf
->append ("Q\n");
6394 bbox
[0] = bbox
[1] = 0;
6395 bbox
[2] = bbox
[3] = 24;
6397 createForm (bbox
, gFalse
, NULL
, &appearance
);
6401 createForm (bbox
, gTrue
, NULL
, &aStream
);
6405 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
6406 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
6407 createForm(bbox
, gFalse
, &resDict
, &appearance
);
6412 // draw the appearance stream
6413 appearance
.fetch(gfx
->getXRef(), &obj
);
6414 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
6415 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
6419 //------------------------------------------------------------------------
6421 //------------------------------------------------------------------------
6422 AnnotSound::AnnotSound(PDFDoc
*docA
, PDFRectangle
*rect
, Sound
*soundA
) :
6423 AnnotMarkup(docA
, rect
) {
6428 annotObj
.dictSet ("Subtype", obj1
.initName ("Sound"));
6431 Stream
*str
= soundA
->getStream();
6432 obj2
.initStream (str
);
6433 str
->incRef(); //FIXME: initStream should do this?
6434 annotObj
.dictSet ("Sound", &obj2
);
6436 initialize(docA
, annotObj
.getDict());
6439 AnnotSound::AnnotSound(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
6440 AnnotMarkup(docA
, dict
, obj
) {
6442 initialize(docA
, dict
);
6445 AnnotSound::~AnnotSound() {
6451 void AnnotSound::initialize(PDFDoc
*docA
, Dict
* dict
) {
6454 sound
= Sound::parseSound(dict
->lookup("Sound", &obj1
));
6456 error(errSyntaxError
, -1, "Bad Annot Sound");
6461 if (dict
->lookup("Name", &obj1
)->isName()) {
6462 name
= new GooString(obj1
.getName());
6464 name
= new GooString("Speaker");
6469 #define ANNOT_SOUND_AP_SPEAKER \
6470 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6471 "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" \
6472 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6474 "0.533333 0.541176 0.521569 RG 2 w\n" \
6478 "4 M 4 14 m 4.086 8.043 l 7 8 l 11 4 l 11 18 l 7 14 l 4 14 l h\n" \
6483 "13.699 15.398 m 14.699 13.398 14.699 9.398 13.699 7.398 c S\n" \
6484 "18.199 19.398 m 21.199 17.398 21.199 5.398 18.199 3.398 c S\n" \
6485 "16 17.398 m 18 16.398 18 7.398 16 5.398 c S\n" \
6486 "0.729412 0.741176 0.713725 RG 2 w\n" \
6489 "4 15 m 4.086 9.043 l 7 9 l 11 5 l 11 19 l 7 15 l 4 15 l h\n" \
6494 "13.699 16 m 14.699 14 14.699 10 13.699 8 c S\n" \
6495 "18.199 20 m 21.199 18 21.199 6 18.199 4 c S\n" \
6496 "16 18 m 18 17 18 8 16 6 c S\n"
6498 #define ANNOT_SOUND_AP_MIC \
6499 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6500 "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" \
6501 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6503 "0.533333 0.541176 0.521569 RG 2 w\n" \
6507 "4 M 12 20 m 12 20 l 13.656 20 15 18.656 15 17 c 15 13 l 15 11.344 13.656 10\n" \
6508 "12 10 c 12 10 l 10.344 10 9 11.344 9 13 c 9 17 l 9 18.656 10.344 20 12\n" \
6512 "17.5 14.5 m 17.5 11.973 l 17.5 8.941 15.047 6.5 12 6.5 c 8.953 6.5 6.5\n" \
6513 "8.941 6.5 11.973 c 6.5 14.5 l S\n" \
6516 "12 6.52 m 12 3 l S\n" \
6518 "8 3 m 16 3 l S\n" \
6519 "0.729412 0.741176 0.713725 RG 12 21 m 12 21 l 13.656 21 15 19.656 15 18 c\n" \
6520 "15 14 l 15 12.344 13.656 11 12 11 c 12 11 l 10.344 11 9 12.344 9 14 c\n" \
6521 "9 18 l 9 19.656 10.344 21 12 21 c h\n" \
6524 "17.5 15.5 m 17.5 12.973 l 17.5 9.941 15.047 7.5 12 7.5 c 8.953 7.5 6.5\n" \
6525 "9.941 6.5 12.973 c 6.5 15.5 l S\n" \
6528 "12 7.52 m 12 4 l S\n" \
6532 void AnnotSound::draw(Gfx
*gfx
, GBool printing
) {
6536 if (!isVisible (printing
))
6540 if (appearance
.isNull()) {
6543 appearBuf
= new GooString ();
6545 appearBuf
->append ("q\n");
6547 setColor(color
, gTrue
);
6549 appearBuf
->append ("1 1 1 rg\n");
6550 if (!name
->cmp("Speaker"))
6551 appearBuf
->append (ANNOT_SOUND_AP_SPEAKER
);
6552 else if (!name
->cmp("Mic"))
6553 appearBuf
->append (ANNOT_SOUND_AP_MIC
);
6554 appearBuf
->append ("Q\n");
6557 bbox
[0] = bbox
[1] = 0;
6558 bbox
[2] = bbox
[3] = 24;
6560 createForm(bbox
, gFalse
, NULL
, &appearance
);
6562 Object aStream
, resDict
;
6564 createForm(bbox
, gTrue
, NULL
, &aStream
);
6567 appearBuf
= new GooString ("/GS0 gs\n/Fm0 Do");
6568 createResourcesDict("Fm0", &aStream
, "GS0", ca
, NULL
, &resDict
);
6569 createForm(bbox
, gFalse
, &resDict
, &appearance
);
6574 // draw the appearance stream
6575 appearance
.fetch(gfx
->getXRef(), &obj
);
6576 gfx
->drawAnnot(&obj
, (AnnotBorder
*)NULL
, color
,
6577 rect
->x1
, rect
->y1
, rect
->x2
, rect
->y2
, getRotation());
6581 //------------------------------------------------------------------------
6583 //------------------------------------------------------------------------
6584 Annot3D::Annot3D(PDFDoc
*docA
, PDFRectangle
*rect
) :
6590 annotObj
.dictSet ("Subtype", obj1
.initName ("3D"));
6592 initialize(docA
, annotObj
.getDict());
6595 Annot3D::Annot3D(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
6596 Annot(docA
, dict
, obj
) {
6598 initialize(docA
, dict
);
6601 Annot3D::~Annot3D() {
6606 void Annot3D::initialize(PDFDoc
*docA
, Dict
* dict
) {
6609 if (dict
->lookup("3DA", &obj1
)->isDict()) {
6610 activation
= new Activation(obj1
.getDict());
6617 Annot3D::Activation::Activation(Dict
*dict
) {
6620 if (dict
->lookup("A", &obj1
)->isName()) {
6621 const char *name
= obj1
.getName();
6623 if(!strcmp(name
, "PO")) {
6624 aTrigger
= aTriggerPageOpened
;
6625 } else if(!strcmp(name
, "PV")) {
6626 aTrigger
= aTriggerPageVisible
;
6627 } else if(!strcmp(name
, "XA")) {
6628 aTrigger
= aTriggerUserAction
;
6630 aTrigger
= aTriggerUnknown
;
6633 aTrigger
= aTriggerUnknown
;
6637 if(dict
->lookup("AIS", &obj1
)->isName()) {
6638 const char *name
= obj1
.getName();
6640 if(!strcmp(name
, "I")) {
6641 aState
= aStateEnabled
;
6642 } else if(!strcmp(name
, "L")) {
6643 aState
= aStateDisabled
;
6645 aState
= aStateUnknown
;
6648 aState
= aStateUnknown
;
6652 if(dict
->lookup("D", &obj1
)->isName()) {
6653 const char *name
= obj1
.getName();
6655 if(!strcmp(name
, "PC")) {
6656 dTrigger
= dTriggerPageClosed
;
6657 } else if(!strcmp(name
, "PI")) {
6658 dTrigger
= dTriggerPageInvisible
;
6659 } else if(!strcmp(name
, "XD")) {
6660 dTrigger
= dTriggerUserAction
;
6662 dTrigger
= dTriggerUnknown
;
6665 dTrigger
= dTriggerUnknown
;
6669 if(dict
->lookup("DIS", &obj1
)->isName()) {
6670 const char *name
= obj1
.getName();
6672 if(!strcmp(name
, "U")) {
6673 dState
= dStateUninstantiaded
;
6674 } else if(!strcmp(name
, "I")) {
6675 dState
= dStateInstantiated
;
6676 } else if(!strcmp(name
, "L")) {
6677 dState
= dStateLive
;
6679 dState
= dStateUnknown
;
6682 dState
= dStateUnknown
;
6686 if (dict
->lookup("TB", &obj1
)->isBool()) {
6687 displayToolbar
= obj1
.getBool();
6689 displayToolbar
= gTrue
;
6693 if (dict
->lookup("NP", &obj1
)->isBool()) {
6694 displayNavigation
= obj1
.getBool();
6696 displayNavigation
= gFalse
;
6701 //------------------------------------------------------------------------
6703 //------------------------------------------------------------------------
6704 AnnotRichMedia::AnnotRichMedia(PDFDoc
*docA
, PDFRectangle
*rect
) :
6708 type
= typeRichMedia
;
6710 annotObj
.dictSet ("Subtype", obj1
.initName ("RichMedia"));
6712 initialize(docA
, annotObj
.getDict());
6715 AnnotRichMedia::AnnotRichMedia(PDFDoc
*docA
, Dict
*dict
, Object
*obj
) :
6716 Annot(docA
, dict
, obj
) {
6717 type
= typeRichMedia
;
6718 initialize(docA
, dict
);
6721 AnnotRichMedia::~AnnotRichMedia() {
6726 void AnnotRichMedia::initialize(PDFDoc
*docA
, Dict
* dict
) {
6729 if (dict
->lookup("RichMediaContent", &obj1
)->isDict()) {
6730 content
= new AnnotRichMedia::Content(obj1
.getDict());
6736 if (dict
->lookup("RichMediaSettings", &obj1
)->isDict()) {
6737 settings
= new AnnotRichMedia::Settings(obj1
.getDict());
6744 AnnotRichMedia::Content
* AnnotRichMedia::getContent() const {
6748 AnnotRichMedia::Settings
* AnnotRichMedia::getSettings() const {
6752 AnnotRichMedia::Settings::Settings(Dict
*dict
) {
6755 if (dict
->lookup("Activation", &obj1
)->isDict()) {
6756 activation
= new AnnotRichMedia::Activation(obj1
.getDict());
6762 if (dict
->lookup("Deactivation", &obj1
)->isDict()) {
6763 deactivation
= new AnnotRichMedia::Deactivation(obj1
.getDict());
6765 deactivation
= NULL
;
6770 AnnotRichMedia::Settings::~Settings() {
6772 delete deactivation
;
6775 AnnotRichMedia::Activation
* AnnotRichMedia::Settings::getActivation() const {
6779 AnnotRichMedia::Deactivation
* AnnotRichMedia::Settings::getDeactivation() const {
6780 return deactivation
;
6783 AnnotRichMedia::Activation::Activation(Dict
*dict
) {
6786 if (dict
->lookup("Condition", &obj1
)->isName()) {
6787 const char *name
= obj1
.getName();
6789 if (!strcmp(name
, "PO")) {
6790 condition
= conditionPageOpened
;
6791 } else if (!strcmp(name
, "PV")) {
6792 condition
= conditionPageVisible
;
6793 } else if (!strcmp(name
, "XA")) {
6794 condition
= conditionUserAction
;
6796 condition
= conditionUserAction
;
6799 condition
= conditionUserAction
;
6804 AnnotRichMedia::Activation::Condition
AnnotRichMedia::Activation::getCondition() const {
6808 AnnotRichMedia::Deactivation::Deactivation(Dict
*dict
) {
6811 if (dict
->lookup("Condition", &obj1
)->isName()) {
6812 const char *name
= obj1
.getName();
6814 if (!strcmp(name
, "PC")) {
6815 condition
= conditionPageClosed
;
6816 } else if (!strcmp(name
, "PI")) {
6817 condition
= conditionPageInvisible
;
6818 } else if (!strcmp(name
, "XD")) {
6819 condition
= conditionUserAction
;
6821 condition
= conditionUserAction
;
6824 condition
= conditionUserAction
;
6829 AnnotRichMedia::Deactivation::Condition
AnnotRichMedia::Deactivation::getCondition() const {
6833 AnnotRichMedia::Content::Content(Dict
*dict
) {
6836 if (dict
->lookup("Configurations", &obj1
)->isArray()) {
6837 nConfigurations
= obj1
.arrayGetLength();
6839 configurations
= (Configuration
**)gmallocn(nConfigurations
, sizeof(Configuration
*));
6841 for (int i
= 0; i
< nConfigurations
; ++i
) {
6844 if (obj1
.arrayGet(i
, &obj2
)->isDict()) {
6845 configurations
[i
] = new AnnotRichMedia::Configuration(obj2
.getDict());
6847 configurations
[i
] = NULL
;
6852 configurations
= NULL
;
6856 if (dict
->lookup("Assets", &obj1
)->isDict()) {
6859 if (obj1
.getDict()->lookup("Names", &obj2
)->isArray()) {
6860 nAssets
= obj2
.arrayGetLength() / 2;
6862 assets
= (Asset
**)gmallocn(nAssets
, sizeof(Asset
*));
6865 for (int i
= 0; i
< obj2
.arrayGetLength(); i
+= 2) {
6868 assets
[counter
] = new AnnotRichMedia::Asset
;
6870 obj2
.arrayGet(i
, &objKey
);
6871 obj2
.arrayGet(i
+ 1, &assets
[counter
]->fileSpec
);
6873 assets
[counter
]->name
= new GooString( objKey
.getString() );
6887 AnnotRichMedia::Content::~Content() {
6888 if (configurations
) {
6889 for (int i
= 0; i
< nConfigurations
; ++i
)
6890 delete configurations
[i
];
6891 gfree(configurations
);
6895 for (int i
= 0; i
< nAssets
; ++i
)
6901 int AnnotRichMedia::Content::getConfigurationsCount() const {
6902 return nConfigurations
;
6905 AnnotRichMedia::Configuration
* AnnotRichMedia::Content::getConfiguration(int index
) const {
6906 if (index
< 0 || index
>= nConfigurations
)
6909 return configurations
[index
];
6912 int AnnotRichMedia::Content::getAssetsCount() const {
6916 AnnotRichMedia::Asset
* AnnotRichMedia::Content::getAsset(int index
) const {
6917 if (index
< 0 || index
>= nAssets
)
6920 return assets
[index
];
6923 AnnotRichMedia::Asset::Asset()
6928 AnnotRichMedia::Asset::~Asset()
6934 GooString
* AnnotRichMedia::Asset::getName() const {
6938 Object
* AnnotRichMedia::Asset::getFileSpec() const {
6939 return const_cast<Object
*>(&fileSpec
);
6942 AnnotRichMedia::Configuration::Configuration(Dict
*dict
)
6946 if (dict
->lookup("Instances", &obj1
)->isArray()) {
6947 nInstances
= obj1
.arrayGetLength();
6949 instances
= (Instance
**)gmallocn(nInstances
, sizeof(Instance
*));
6951 for (int i
= 0; i
< nInstances
; ++i
) {
6954 if (obj1
.arrayGet(i
, &obj2
)->isDict()) {
6955 instances
[i
] = new AnnotRichMedia::Instance(obj2
.getDict());
6957 instances
[i
] = NULL
;
6966 if (dict
->lookup("Name", &obj1
)->isString()) {
6967 name
= new GooString(obj1
.getString());
6973 if (dict
->lookup("Subtype", &obj1
)->isName()) {
6974 const char *name
= obj1
.getName();
6976 if (!strcmp(name
, "3D")) {
6978 } else if (!strcmp(name
, "Flash")) {
6980 } else if (!strcmp(name
, "Sound")) {
6982 } else if (!strcmp(name
, "Video")) {
6985 // determine from first instance
6986 if (instances
&& nInstances
> 0) {
6987 AnnotRichMedia::Instance
*instance
= instances
[0];
6988 switch (instance
->getType()) {
6989 case AnnotRichMedia::Instance::type3D
:
6992 case AnnotRichMedia::Instance::typeFlash
:
6995 case AnnotRichMedia::Instance::typeSound
:
6998 case AnnotRichMedia::Instance::typeVideo
:
7011 AnnotRichMedia::Configuration::~Configuration()
7014 for (int i
= 0; i
< nInstances
; ++i
)
7015 delete instances
[i
];
7022 int AnnotRichMedia::Configuration::getInstancesCount() const {
7026 AnnotRichMedia::Instance
* AnnotRichMedia::Configuration::getInstance(int index
) const {
7027 if (index
< 0 || index
>= nInstances
)
7030 return instances
[index
];
7033 GooString
* AnnotRichMedia::Configuration::getName() const {
7037 AnnotRichMedia::Configuration::Type
AnnotRichMedia::Configuration::getType() const {
7041 AnnotRichMedia::Instance::Instance(Dict
*dict
)
7045 if (dict
->lookup("Subtype", &obj1
)->isName()) {
7046 const char *name
= obj1
.getName();
7048 if (!strcmp(name
, "3D")) {
7050 } else if (!strcmp(name
, "Flash")) {
7052 } else if (!strcmp(name
, "Sound")) {
7054 } else if (!strcmp(name
, "Video")) {
7062 if (dict
->lookup("Params", &obj1
)->isDict()) {
7063 params
= new AnnotRichMedia::Params(obj1
.getDict());
7069 AnnotRichMedia::Instance::~Instance()
7074 AnnotRichMedia::Instance::Type
AnnotRichMedia::Instance::getType() const {
7078 AnnotRichMedia::Params
* AnnotRichMedia::Instance::getParams() const {
7082 AnnotRichMedia::Params::Params(Dict
*dict
)
7086 if (dict
->lookup("FlashVars", &obj1
)->isString()) {
7087 flashVars
= new GooString(obj1
.getString());
7094 AnnotRichMedia::Params::~Params()
7099 GooString
* AnnotRichMedia::Params::getFlashVars() const {
7103 //------------------------------------------------------------------------
7105 //------------------------------------------------------------------------
7107 Annots::Annots(PDFDoc
*docA
, int page
, Object
*annotsObj
) {
7117 if (annotsObj
->isArray()) {
7118 for (i
= 0; i
< annotsObj
->arrayGetLength(); ++i
) {
7119 //get the Ref to this annot and pass it to Annot constructor
7120 //this way, it'll be possible for the annot to retrieve the corresponding
7123 if (annotsObj
->arrayGet(i
, &obj1
)->isDict()) {
7124 annotsObj
->arrayGetNF(i
, &obj2
);
7125 annot
= createAnnot (obj1
.getDict(), &obj2
);
7127 if (annot
->isOk()) {
7128 annot
->setPage(page
, gFalse
); // Don't change /P
7140 void Annots::appendAnnot(Annot
*annot
) {
7141 if (annot
&& annot
->isOk()) {
7142 if (nAnnots
>= size
) {
7144 annots
= (Annot
**)greallocn(annots
, size
, sizeof(Annot
*));
7146 annots
[nAnnots
++] = annot
;
7151 GBool
Annots::removeAnnot(Annot
*annot
) {
7153 // Search annot and determine its index
7154 for (int i
= 0; idx
== -1 && i
< nAnnots
; i
++) {
7155 if (annots
[i
] == annot
) {
7163 memmove( annots
+ idx
, annots
+ idx
+ 1, sizeof(annots
[0]) * (nAnnots
- idx
) );
7169 Annot
*Annots::createAnnot(Dict
* dict
, Object
*obj
) {
7170 Annot
*annot
= NULL
;
7173 if (dict
->lookup("Subtype", &obj1
)->isName()) {
7174 const char *typeName
= obj1
.getName();
7176 if (!strcmp(typeName
, "Text")) {
7177 annot
= new AnnotText(doc
, dict
, obj
);
7178 } else if (!strcmp(typeName
, "Link")) {
7179 annot
= new AnnotLink(doc
, dict
, obj
);
7180 } else if (!strcmp(typeName
, "FreeText")) {
7181 annot
= new AnnotFreeText(doc
, dict
, obj
);
7182 } else if (!strcmp(typeName
, "Line")) {
7183 annot
= new AnnotLine(doc
, dict
, obj
);
7184 } else if (!strcmp(typeName
, "Square")) {
7185 annot
= new AnnotGeometry(doc
, dict
, obj
);
7186 } else if (!strcmp(typeName
, "Circle")) {
7187 annot
= new AnnotGeometry(doc
, dict
, obj
);
7188 } else if (!strcmp(typeName
, "Polygon")) {
7189 annot
= new AnnotPolygon(doc
, dict
, obj
);
7190 } else if (!strcmp(typeName
, "PolyLine")) {
7191 annot
= new AnnotPolygon(doc
, dict
, obj
);
7192 } else if (!strcmp(typeName
, "Highlight")) {
7193 annot
= new AnnotTextMarkup(doc
, dict
, obj
);
7194 } else if (!strcmp(typeName
, "Underline")) {
7195 annot
= new AnnotTextMarkup(doc
, dict
, obj
);
7196 } else if (!strcmp(typeName
, "Squiggly")) {
7197 annot
= new AnnotTextMarkup(doc
, dict
, obj
);
7198 } else if (!strcmp(typeName
, "StrikeOut")) {
7199 annot
= new AnnotTextMarkup(doc
, dict
, obj
);
7200 } else if (!strcmp(typeName
, "Stamp")) {
7201 annot
= new AnnotStamp(doc
, dict
, obj
);
7202 } else if (!strcmp(typeName
, "Caret")) {
7203 annot
= new AnnotCaret(doc
, dict
, obj
);
7204 } else if (!strcmp(typeName
, "Ink")) {
7205 annot
= new AnnotInk(doc
, dict
, obj
);
7206 } else if (!strcmp(typeName
, "FileAttachment")) {
7207 annot
= new AnnotFileAttachment(doc
, dict
, obj
);
7208 } else if (!strcmp(typeName
, "Sound")) {
7209 annot
= new AnnotSound(doc
, dict
, obj
);
7210 } else if(!strcmp(typeName
, "Movie")) {
7211 annot
= new AnnotMovie(doc
, dict
, obj
);
7212 } else if(!strcmp(typeName
, "Widget")) {
7213 // Find the annot in forms
7215 Form
*form
= doc
->getCatalog()->getForm();
7217 FormWidget
*widget
= form
->findWidgetByRef(obj
->getRef());
7219 annot
= widget
->getWidgetAnnotation();
7225 annot
= new AnnotWidget(doc
, dict
, obj
);
7226 } else if(!strcmp(typeName
, "Screen")) {
7227 annot
= new AnnotScreen(doc
, dict
, obj
);
7228 } else if(!strcmp(typeName
, "PrinterMark")) {
7229 annot
= new Annot(doc
, dict
, obj
);
7230 } else if (!strcmp(typeName
, "TrapNet")) {
7231 annot
= new Annot(doc
, dict
, obj
);
7232 } else if (!strcmp(typeName
, "Watermark")) {
7233 annot
= new Annot(doc
, dict
, obj
);
7234 } else if (!strcmp(typeName
, "3D")) {
7235 annot
= new Annot3D(doc
, dict
, obj
);
7236 } else if (!strcmp(typeName
, "RichMedia")) {
7237 annot
= new AnnotRichMedia(doc
, dict
, obj
);
7238 } else if (!strcmp(typeName
, "Popup")) {
7239 /* Popup annots are already handled by markup annots
7240 * Here we only care about popup annots without a
7241 * markup annotation associated
7245 if (dict
->lookup("Parent", &obj2
)->isNull())
7246 annot
= new AnnotPopup(doc
, dict
, obj
);
7252 annot
= new Annot(doc
, dict
, obj
);
7260 Annot
*Annots::findAnnot(Ref
*ref
) {
7263 for (i
= 0; i
< nAnnots
; ++i
) {
7264 if (annots
[i
]->match(ref
)) {
7275 for (i
= 0; i
< nAnnots
; ++i
) {
7276 annots
[i
]->decRefCnt();