beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / Annot.cc
blobc20398bd936a1d1135fbe3d659c99a77f0789b1e
1 //========================================================================
2 //
3 // Annot.cc
4 //
5 // Copyright 2000-2003 Glyph & Cog, LLC
6 //
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 //========================================================================
44 #include <config.h>
46 #ifdef USE_GCC_PRAGMAS
47 #pragma implementation
48 #endif
50 #include <stdlib.h>
51 #include <math.h>
52 #include <assert.h>
53 #include "goo/gmem.h"
54 #include "goo/gstrtod.h"
55 #include "GooList.h"
56 #include "Error.h"
57 #include "Object.h"
58 #include "Catalog.h"
59 #include "Gfx.h"
60 #include "Lexer.h"
61 #include "PDFDoc.h"
62 #include "Page.h"
63 #include "Annot.h"
64 #include "GfxFont.h"
65 #include "CharCodeToUnicode.h"
66 #include "PDFDocEncoding.h"
67 #include "Form.h"
68 #include "Error.h"
69 #include "XRef.h"
70 #include "Movie.h"
71 #include "OptionalContent.h"
72 #include "Sound.h"
73 #include "FileSpec.h"
74 #include "DateInfo.h"
75 #include "Link.h"
76 #include <string.h>
77 #include <algorithm>
79 #if MULTITHREADED
80 # define annotLocker() MutexLocker locker(&mutex)
81 # define annotCondLocker(X) MutexLocker locker(&mutex, (X))
82 #else
83 # define annotLocker()
84 # define annotCondLocker(X)
85 #endif
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;
135 } else {
136 return annotLineEndingNone;
138 } else {
139 return annotLineEndingNone;
143 const char* convertAnnotLineEndingStyle(AnnotLineEndingStyle style) {
144 switch (style) {
145 case annotLineEndingSquare:
146 return "Square";
147 case annotLineEndingCircle:
148 return "Circle";
149 case annotLineEndingDiamond:
150 return "Diamond";
151 case annotLineEndingOpenArrow:
152 return "OpenArrow";
153 case annotLineEndingClosedArrow:
154 return "ClosedArrow";
155 case annotLineEndingButt:
156 return "Butt";
157 case annotLineEndingROpenArrow:
158 return "ROpenArrow";
159 case annotLineEndingRClosedArrow:
160 return "RClosedArrow";
161 case annotLineEndingSlash:
162 return "Slash";
163 default:
164 return "None";
168 static AnnotExternalDataType parseAnnotExternalData(Dict* dict) {
169 Object obj1;
170 AnnotExternalDataType type;
172 if (dict->lookup("Subtype", &obj1)->isName()) {
173 const char *typeName = obj1.getName();
175 if (!strcmp(typeName, "Markup3D")) {
176 type = annotExternalDataMarkup3D;
177 } else {
178 type = annotExternalDataMarkupUnknown;
180 } else {
181 type = annotExternalDataMarkupUnknown;
183 obj1.free();
185 return type;
188 PDFRectangle *parseDiffRectangle(Array *array, PDFRectangle *rect) {
189 PDFRectangle *newRect = NULL;
190 if (array->getLength() == 4) {
191 // deltas
192 Object obj1;
193 double dx1 = (array->get(0, &obj1)->isNum() ? obj1.getNum() : 0);
194 obj1.free();
195 double dy1 = (array->get(1, &obj1)->isNum() ? obj1.getNum() : 0);
196 obj1.free();
197 double dx2 = (array->get(2, &obj1)->isNum() ? obj1.getNum() : 0);
198 obj1.free();
199 double dy2 = (array->get(3, &obj1)->isNum() ? obj1.getNum() : 0);
200 obj1.free();
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;
214 return newRect;
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);
233 Object actionObject;
235 if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
236 linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
237 actionObject.free();
240 additionalActionsObject.free();
242 return linkAction;
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);
255 Object actionObject;
257 if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
258 linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
259 actionObject.free();
262 additionalActionsObject.free();
264 return linkAction;
267 //------------------------------------------------------------------------
268 // AnnotBorderEffect
269 //------------------------------------------------------------------------
271 AnnotBorderEffect::AnnotBorderEffect(Dict *dict) {
272 Object obj1;
274 if (dict->lookup("S", &obj1)->isName()) {
275 const char *effectName = obj1.getName();
277 if (!strcmp(effectName, "C"))
278 effectType = borderEffectCloudy;
279 else
280 effectType = borderEffectNoEffect;
281 } else {
282 effectType = borderEffectNoEffect;
284 obj1.free();
286 if ((dict->lookup("I", &obj1)->isNum()) && effectType == borderEffectCloudy) {
287 intensity = obj1.getNum();
288 } else {
289 intensity = 0;
291 obj1.free();
294 //------------------------------------------------------------------------
295 // AnnotPath
296 //------------------------------------------------------------------------
298 AnnotPath::AnnotPath() {
299 coords = NULL;
300 coordsLength = 0;
303 AnnotPath::AnnotPath(Array *array) {
304 coords = NULL;
305 coordsLength = 0;
306 parsePathArray(array);
309 AnnotPath::AnnotPath(AnnotCoord **coords, int coordsLength) {
310 this->coords = coords;
311 this->coordsLength = coordsLength;
314 AnnotPath::~AnnotPath() {
315 if (coords) {
316 for (int i = 0; i < coordsLength; ++i)
317 delete coords[i];
318 gfree(coords);
322 double AnnotPath::getX(int coord) const {
323 if (coord >= 0 && coord < coordsLength)
324 return coords[coord]->getX();
325 return 0;
328 double AnnotPath::getY(int coord) const {
329 if (coord >= 0 && coord < coordsLength)
330 return coords[coord]->getY();
331 return 0;
334 AnnotCoord *AnnotPath::getCoord(int coord) const {
335 if (coord >= 0 && coord < coordsLength)
336 return coords[coord];
337 return NULL;
340 void AnnotPath::parsePathArray(Array *array) {
341 int tempLength;
342 AnnotCoord **tempCoords;
343 GBool correct = gTrue;
345 if (array->getLength() % 2) {
346 error(errSyntaxError, -1, "Bad Annot Path");
347 return;
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++) {
354 Object obj1;
355 double x = 0, y = 0;
357 if (array->get(i * 2, &obj1)->isNum()) {
358 x = obj1.getNum();
359 } else {
360 correct = gFalse;
362 obj1.free();
364 if (array->get((i * 2) + 1, &obj1)->isNum()) {
365 y = obj1.getNum();
366 } else {
367 correct = gFalse;
369 obj1.free();
371 if (!correct) {
372 for (int j = i - 1; j >= 0; j--)
373 delete tempCoords[j];
374 gfree (tempCoords);
375 return;
378 tempCoords[i] = new AnnotCoord(x, y);
381 coords = tempCoords;
382 coordsLength = tempLength;
385 //------------------------------------------------------------------------
386 // AnnotCalloutLine
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;
409 int quadsLength = 0;
410 AnnotQuadrilateral **quads;
411 double quadArray[8];
413 // default values
414 quadrilaterals = NULL;
415 quadrilateralsLength = 0;
417 if ((arrayLength % 8) == 0) {
418 int i;
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++) {
427 Object obj;
428 if (array->get(i * 8 + j, &obj)->isNum()) {
429 quadArray[j] = obj.getNum();
430 } else {
431 correct = gFalse;
432 obj.free();
433 error (errSyntaxError, -1, "Invalid QuadPoint in annot");
434 break;
436 obj.free();
439 if (!correct)
440 break;
442 quads[i] = new AnnotQuadrilateral(quadArray[0], quadArray[1],
443 quadArray[2], quadArray[3],
444 quadArray[4], quadArray[5],
445 quadArray[6], quadArray[7]);
448 if (correct) {
449 quadrilateralsLength = quadsLength;
450 quadrilaterals = quads;
451 } else {
452 for (int j = 0; j < i; j++)
453 delete quads[j];
454 gfree (quads);
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();
476 return 0;
479 double AnnotQuadrilaterals::getY1(int quadrilateral) {
480 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
481 return quadrilaterals[quadrilateral]->coord1.getY();
482 return 0;
485 double AnnotQuadrilaterals::getX2(int quadrilateral) {
486 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
487 return quadrilaterals[quadrilateral]->coord2.getX();
488 return 0;
491 double AnnotQuadrilaterals::getY2(int quadrilateral) {
492 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
493 return quadrilaterals[quadrilateral]->coord2.getY();
494 return 0;
497 double AnnotQuadrilaterals::getX3(int quadrilateral) {
498 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
499 return quadrilaterals[quadrilateral]->coord3.getX();
500 return 0;
503 double AnnotQuadrilaterals::getY3(int quadrilateral) {
504 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
505 return quadrilaterals[quadrilateral]->coord3.getY();
506 return 0;
509 double AnnotQuadrilaterals::getX4(int quadrilateral) {
510 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
511 return quadrilaterals[quadrilateral]->coord4.getX();
512 return 0;
515 double AnnotQuadrilaterals::getY4(int quadrilateral) {
516 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
517 return quadrilaterals[quadrilateral]->coord4.getY();
518 return 0;
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 //------------------------------------------------------------------------
527 // AnnotBorder
528 //------------------------------------------------------------------------
529 AnnotBorder::AnnotBorder() {
530 width = 1;
531 dashLength = 0;
532 dash = NULL;
533 style = borderSolid;
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++) {
543 Object obj1;
545 if (dashObj->arrayGet(i, &obj1)->isNum()) {
546 tempDash[i] = obj1.getNum();
548 correct = tempDash[i] >= 0;
549 obj1.free();
553 if (correct) {
554 dashLength = tempLength;
555 dash = tempDash;
556 style = borderDashed;
557 } else {
558 gfree (tempDash);
561 return correct;
564 AnnotBorder::~AnnotBorder() {
565 if (dash)
566 gfree (dash);
569 //------------------------------------------------------------------------
570 // AnnotBorderArray
571 //------------------------------------------------------------------------
573 AnnotBorderArray::AnnotBorderArray() {
574 horizontalCorner = 0;
575 verticalCorner = 0;
578 AnnotBorderArray::AnnotBorderArray(Array *array) {
579 Object obj1;
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();
588 else
589 correct = gFalse;
590 obj1.free();
592 if (array->get(1, &obj1)->isNum())
593 verticalCorner = obj1.getNum();
594 else
595 correct = gFalse;
596 obj1.free();
598 if (array->get(2, &obj1)->isNum())
599 width = obj1.getNum();
600 else
601 correct = gFalse;
602 obj1.free();
604 if (arrayLength == 4) {
605 if (array->get(3, &obj1)->isArray())
606 correct = parseDashArray(&obj1);
607 else
608 correct = gFalse;
609 obj1.free();
611 } else {
612 correct = gFalse;
615 if (!correct) {
616 width = 0;
620 void AnnotBorderArray::writeToObject(XRef *xref, Object *obj1) const {
621 Object obj2;
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) {
629 Object obj3;
631 obj1->arrayAdd(obj3.initArray(xref));
632 for (int i = 0; i < dashLength; i++)
633 obj3.arrayAdd(obj2.initReal(dash[i]));
637 //------------------------------------------------------------------------
638 // AnnotBorderBS
639 //------------------------------------------------------------------------
641 AnnotBorderBS::AnnotBorderBS() {
644 AnnotBorderBS::AnnotBorderBS(Dict *dict) {
645 Object obj1, obj2;
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
652 // --jrmuizel
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")) {
661 style = borderSolid;
662 } else if (!strcmp(styleName, "D")) {
663 style = borderDashed;
664 } else if (!strcmp(styleName, "B")) {
665 style = borderBeveled;
666 } else if (!strcmp(styleName, "I")) {
667 style = borderInset;
668 } else if (!strcmp(styleName, "U")) {
669 style = borderUnderlined;
670 } else {
671 style = borderSolid;
673 } else {
674 width = 0;
676 obj2.free();
677 obj1.free();
679 if (style == borderDashed) {
680 if (dict->lookup("D", &obj1)->isArray())
681 parseDashArray(&obj1);
682 obj1.free();
684 if (!dash) {
685 dashLength = 1;
686 dash = (double *) gmallocn (dashLength, sizeof (double));
687 dash[0] = 3;
692 const char *AnnotBorderBS::getStyleName() const {
693 switch (style) {
694 case borderSolid:
695 return "S";
696 case borderDashed:
697 return "D";
698 case borderBeveled:
699 return "B";
700 case borderInset:
701 return "I";
702 case borderUnderlined:
703 return "U";
706 return "S";
709 void AnnotBorderBS::writeToObject(XRef *xref, Object *obj1) const {
710 Object obj2;
712 obj1->initDict(xref);
713 obj1->dictSet("W", obj2.initReal(width));
714 obj1->dictSet("S", obj2.initName(getStyleName()));
715 if (style == borderDashed && dashLength > 0) {
716 Object obj3;
718 obj1->dictSet("D", obj3.initArray(xref));
719 for (int i = 0; i < dashLength; i++)
720 obj3.arrayAdd(obj2.initReal(dash[i]));
724 //------------------------------------------------------------------------
725 // AnnotColor
726 //------------------------------------------------------------------------
728 AnnotColor::AnnotColor() {
729 length = 0;
732 AnnotColor::AnnotColor(double gray) {
733 length = 1;
735 values[0] = gray;
738 AnnotColor::AnnotColor(double r, double g, double b) {
739 length = 3;
741 values[0] = r;
742 values[1] = g;
743 values[2] = b;
746 AnnotColor::AnnotColor(double c, double m, double y, double k) {
747 length = 4;
749 values[0] = c;
750 values[1] = m;
751 values[2] = y;
752 values[3] = 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) {
759 int i;
761 length = array->getLength();
762 if (length > 4)
763 length = 4;
765 for (i = 0; i < length; i++) {
766 Object obj1;
768 if (array->get(i, &obj1)->isNum()) {
769 values[i] = obj1.getNum();
771 if (values[i] < 0 || values[i] > 1)
772 values[i] = 0;
773 } else {
774 values[i] = 0;
776 obj1.free();
779 if (adjust != 0)
780 adjustColor(adjust);
783 void AnnotColor::adjustColor(int adjust) {
784 int i;
786 if (length == 4) {
787 adjust = -adjust;
789 if (adjust > 0) {
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 {
801 Object obj2;
802 int i;
804 if (length == 0) {
805 obj1->initNull(); // Transparent (no color)
806 } else {
807 obj1->initArray(xref);
808 for (i = 0; i < length; ++i)
809 obj1->arrayAdd( obj2.initReal( values[i] ) );
813 //------------------------------------------------------------------------
814 // AnnotIconFit
815 //------------------------------------------------------------------------
817 AnnotIconFit::AnnotIconFit(Dict* dict) {
818 Object obj1;
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;
829 } else {
830 scaleWhen = scaleAlways;
832 } else {
833 scaleWhen = scaleAlways;
835 obj1.free();
837 if (dict->lookup("S", &obj1)->isName()) {
838 const char *scaleName = obj1.getName();
840 if(!strcmp(scaleName, "A")) {
841 scale = scaleAnamorphic;
842 } else {
843 scale = scaleProportional;
845 } else {
846 scale = scaleProportional;
848 obj1.free();
850 if (dict->lookup("A", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
851 Object obj2;
852 (obj1.arrayGet(0, &obj2)->isNum() ? left = obj2.getNum() : left = 0);
853 obj2.free();
854 (obj1.arrayGet(1, &obj2)->isNum() ? bottom = obj2.getNum() : bottom = 0);
855 obj2.free();
857 if (left < 0 || left > 1)
858 left = 0.5;
860 if (bottom < 0 || bottom > 1)
861 bottom = 0.5;
863 } else {
864 left = bottom = 0.5;
866 obj1.free();
868 if (dict->lookup("FB", &obj1)->isBool()) {
869 fullyBounds = obj1.getBool();
870 } else {
871 fullyBounds = gFalse;
873 obj1.free();
876 //------------------------------------------------------------------------
877 // AnnotAppearance
878 //------------------------------------------------------------------------
880 AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict) {
881 assert(dict->isDict());
882 doc = docA;
883 xref = docA->getXRef();
884 dict->copy(&appearDict);
887 AnnotAppearance::~AnnotAppearance() {
888 appearDict.free();
891 void AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, const char *state, Object *dest) {
892 Object apData, stream;
893 apData.initNull();
895 // Obtain dictionary or stream associated to appearance type
896 switch (type) {
897 case appearRollover:
898 if (appearDict.dictLookupNF("R", &apData)->isNull())
899 appearDict.dictLookupNF("N", &apData);
900 break;
901 case appearDown:
902 if (appearDict.dictLookupNF("D", &apData)->isNull())
903 appearDict.dictLookupNF("N", &apData);
904 break;
905 case appearNormal:
906 appearDict.dictLookupNF("N", &apData);
907 break;
910 dest->initNull();
911 if (apData.isDict() && state)
912 apData.dictLookupNF(state, dest);
913 else if (apData.isRef())
914 apData.copy(dest);
915 apData.free();
918 GooString * AnnotAppearance::getStateKey(int i) {
919 Object obj1;
920 GooString * res = NULL;
921 if (appearDict.dictLookupNF("N", &obj1)->isDict())
922 res = new GooString(obj1.dictGetKey(i));
923 obj1.free();
924 return res;
927 int AnnotAppearance::getNumStates() {
928 Object obj1;
929 int res = 0;
930 if (appearDict.dictLookupNF("N", &obj1)->isDict())
931 res = obj1.dictGetLength();
932 obj1.free();
933 return res;
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) {
941 return gTrue;
943 } else if (stateObj->isDict()) { // Test each value
944 const int size = stateObj->dictGetLength();
945 for (int i = 0; i < size; ++i) {
946 Object obj1;
947 stateObj->dictGetValNF(i, &obj1);
948 if (obj1.isRef()) {
949 Ref r = obj1.getRef();
950 if (r.num == refToStream.num && r.gen == refToStream.gen) {
951 return gTrue;
954 obj1.free();
957 return gFalse; // Not found
960 // Test if this AnnotAppearance references the specified stream
961 GBool AnnotAppearance::referencesStream(Ref refToStream) {
962 Object obj1;
963 GBool found;
965 // Scan each state's ref/subdictionary
966 appearDict.dictLookupNF("N", &obj1);
967 found = referencesStream(&obj1, refToStream);
968 obj1.free();
969 if (found)
970 return gTrue;
972 appearDict.dictLookupNF("R", &obj1);
973 found = referencesStream(&obj1, refToStream);
974 obj1.free();
975 if (found)
976 return gTrue;
978 appearDict.dictLookupNF("D", &obj1);
979 found = referencesStream(&obj1, refToStream);
980 obj1.free();
981 return found;
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);
990 if (!page) {
991 error(errSyntaxError, -1, "Failed check for shared annotation stream at page {0:d}", pg);
992 continue;
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) {
1014 Object obj2;
1015 obj1->dictGetValNF(i, &obj2);
1016 if (obj2.isRef()) {
1017 removeStream(obj2.getRef());
1019 obj2.free();
1024 void AnnotAppearance::removeAllStreams() {
1025 Object obj1;
1026 appearDict.dictLookupNF("N", &obj1);
1027 removeStateStreams(&obj1);
1028 obj1.free();
1029 appearDict.dictLookupNF("R", &obj1);
1030 removeStateStreams(&obj1);
1031 obj1.free();
1032 appearDict.dictLookupNF("D", &obj1);
1033 removeStateStreams(&obj1);
1034 obj1.free();
1037 //------------------------------------------------------------------------
1038 // AnnotAppearanceCharacs
1039 //------------------------------------------------------------------------
1041 AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) {
1042 Object obj1;
1044 if (dict->lookup("R", &obj1)->isInt()) {
1045 rotation = obj1.getInt();
1046 } else {
1047 rotation = 0;
1049 obj1.free();
1051 if (dict->lookup("BC", &obj1)->isArray()) {
1052 Array *colorComponents = obj1.getArray();
1053 if (colorComponents->getLength() > 0) {
1054 borderColor = new AnnotColor(colorComponents);
1055 } else {
1056 borderColor = NULL;
1058 } else {
1059 borderColor = NULL;
1061 obj1.free();
1063 if (dict->lookup("BG", &obj1)->isArray()) {
1064 Array *colorComponents = obj1.getArray();
1065 if (colorComponents->getLength() > 0) {
1066 backColor = new AnnotColor(colorComponents);
1067 } else {
1068 backColor = NULL;
1070 } else {
1071 backColor = NULL;
1073 obj1.free();
1075 if (dict->lookup("CA", &obj1)->isString()) {
1076 normalCaption = new GooString(obj1.getString());
1077 } else {
1078 normalCaption = NULL;
1080 obj1.free();
1082 if (dict->lookup("RC", &obj1)->isString()) {
1083 rolloverCaption = new GooString(obj1.getString());
1084 } else {
1085 rolloverCaption = NULL;
1087 obj1.free();
1089 if (dict->lookup("AC", &obj1)->isString()) {
1090 alternateCaption = new GooString(obj1.getString());
1091 } else {
1092 alternateCaption = NULL;
1094 obj1.free();
1096 if (dict->lookup("IF", &obj1)->isDict()) {
1097 iconFit = new AnnotIconFit(obj1.getDict());
1098 } else {
1099 iconFit = NULL;
1101 obj1.free();
1103 if (dict->lookup("TP", &obj1)->isInt()) {
1104 position = (AnnotAppearanceCharacsTextPos) obj1.getInt();
1105 } else {
1106 position = captionNoIcon;
1108 obj1.free();
1111 AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
1112 if (borderColor)
1113 delete borderColor;
1115 if (backColor)
1116 delete backColor;
1118 if (normalCaption)
1119 delete normalCaption;
1121 if (rolloverCaption)
1122 delete rolloverCaption;
1124 if (alternateCaption)
1125 delete alternateCaption;
1127 if (iconFit)
1128 delete iconFit;
1131 //------------------------------------------------------------------------
1132 // AnnotAppearanceBBox
1133 //------------------------------------------------------------------------
1135 AnnotAppearanceBBox::AnnotAppearanceBBox(PDFRectangle *rect) {
1136 origX = rect->x1;
1137 origY = rect->y1;
1138 borderWidth = 0;
1140 // Initially set the same size as rect
1141 minX = 0;
1142 minY = 0;
1143 maxX = rect->x2 - rect->x1;
1144 maxY = rect->y2 - rect->y1;
1147 void AnnotAppearanceBBox::extendTo(double x, double y) {
1148 if (x < minX) {
1149 minX = x;
1150 } else if (x > maxX) {
1151 maxX = x;
1153 if (y < minY) {
1154 minY = y;
1155 } else if (y > maxY) {
1156 maxY = y;
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 //------------------------------------------------------------------------
1184 // Annot
1185 //------------------------------------------------------------------------
1187 Annot::Annot(PDFDoc *docA, PDFRectangle *rectA) {
1188 Object obj1;
1190 refCnt = 1;
1191 flags = flagUnknown;
1192 type = typeUnknown;
1194 obj1.initArray (docA->getXRef());
1195 Object obj2;
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));
1200 obj2.free ();
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) {
1213 refCnt = 1;
1214 hasRef = false;
1215 flags = flagUnknown;
1216 type = typeUnknown;
1217 annotObj.initDict (dict);
1218 initialize (docA, dict);
1221 Annot::Annot(PDFDoc *docA, Dict *dict, Object *obj) {
1222 refCnt = 1;
1223 if (obj->isRef()) {
1224 hasRef = gTrue;
1225 ref = obj->getRef();
1226 } else {
1227 hasRef = gFalse;
1229 flags = flagUnknown;
1230 type = typeUnknown;
1231 annotObj.initDict (dict);
1232 initialize (docA, dict);
1235 void Annot::initialize(PDFDoc *docA, Dict *dict) {
1236 Object apObj, asObj, obj1, obj2;
1238 ok = gTrue;
1239 doc = docA;
1240 xref = doc->getXRef();
1241 appearStreams = NULL;
1242 appearBBox = NULL;
1243 appearState = NULL;
1244 appearBuf = NULL;
1245 fontSize = 0;
1247 appearance.initNull();
1249 //----- parse the rectangle
1250 rect = new PDFRectangle();
1251 if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
1252 Object obj2;
1253 (obj1.arrayGet(0, &obj2)->isNum() ? rect->x1 = obj2.getNum() : rect->x1 = 0);
1254 obj2.free();
1255 (obj1.arrayGet(1, &obj2)->isNum() ? rect->y1 = obj2.getNum() : rect->y1 = 0);
1256 obj2.free();
1257 (obj1.arrayGet(2, &obj2)->isNum() ? rect->x2 = obj2.getNum() : rect->x2 = 1);
1258 obj2.free();
1259 (obj1.arrayGet(3, &obj2)->isNum() ? rect->y2 = obj2.getNum() : rect->y2 = 1);
1260 obj2.free();
1262 if (rect->x1 > rect->x2) {
1263 double t = rect->x1;
1264 rect->x1 = rect->x2;
1265 rect->x2 = t;
1268 if (rect->y1 > rect->y2) {
1269 double t = rect->y1;
1270 rect->y1 = rect->y2;
1271 rect->y2 = t;
1273 } else {
1274 rect->x1 = rect->y1 = 0;
1275 rect->x2 = rect->y2 = 1;
1276 error(errSyntaxError, -1, "Bad bounding box for annotation");
1277 ok = gFalse;
1279 obj1.free();
1281 if (dict->lookup("Contents", &obj1)->isString()) {
1282 contents = obj1.getString()->copy();
1283 } else {
1284 contents = new GooString();
1286 obj1.free();
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);
1293 } else {
1294 page = 0;
1296 obj1.free();
1298 if (dict->lookup("NM", &obj1)->isString()) {
1299 name = obj1.getString()->copy();
1300 } else {
1301 name = NULL;
1303 obj1.free();
1305 if (dict->lookup("M", &obj1)->isString()) {
1306 modified = obj1.getString()->copy();
1307 } else {
1308 modified = NULL;
1310 obj1.free();
1312 //----- get the flags
1313 if (dict->lookup("F", &obj1)->isInt()) {
1314 flags |= obj1.getInt();
1315 } else {
1316 flags = flagUnknown;
1318 obj1.free();
1320 //----- get the annotation appearance dictionary
1321 dict->lookup("AP", &apObj);
1322 if (apObj.isDict()) {
1323 appearStreams = new AnnotAppearance(doc, &apObj);
1325 apObj.free();
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);
1340 if (!appearState) {
1341 appearState = new GooString("Off");
1343 asObj.free();
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());
1357 else
1358 border = NULL;
1359 obj1.free();
1361 if (dict->lookup("C", &obj1)->isArray()) {
1362 color = new AnnotColor(obj1.getArray());
1363 } else {
1364 color = NULL;
1366 obj1.free();
1368 if (dict->lookup("StructParent", &obj1)->isInt()) {
1369 treeKey = obj1.getInt();
1370 } else {
1371 treeKey = 0;
1373 obj1.free();
1375 dict->lookupNF("OC", &oc);
1377 #if MULTITHREADED
1378 gInitMutex(&mutex);
1379 #endif
1382 void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const {
1383 *x1 = rect->x1;
1384 *y1 = rect->y1;
1385 *x2 = rect->x2;
1386 *y2 = rect->y2;
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) {
1394 Object obj1, obj2;
1396 if (x1 < x2) {
1397 rect->x1 = x1;
1398 rect->x2 = x2;
1399 } else {
1400 rect->x1 = x2;
1401 rect->x2 = x1;
1404 if (y1 < y2) {
1405 rect->y1 = y1;
1406 rect->y2 = y2;
1407 } else {
1408 rect->y1 = y2;
1409 rect->y2 = y1;
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) {
1427 annotLocker();
1428 /* Set M to current time, unless we are updating M itself */
1429 if (strcmp(key, "M") != 0) {
1430 delete modified;
1431 modified = timeToDateString(NULL);
1433 Object obj1;
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) {
1444 annotLocker();
1445 delete contents;
1447 if (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);
1454 } else {
1455 contents = new GooString();
1458 Object obj1;
1459 obj1.initString(contents->copy());
1460 update ("Contents", &obj1);
1463 void Annot::setName(GooString *new_name) {
1464 annotLocker();
1465 delete name;
1467 if (new_name) {
1468 name = new GooString(new_name);
1469 } else {
1470 name = new GooString();
1473 Object obj1;
1474 obj1.initString(name->copy());
1475 update ("NM", &obj1);
1478 void Annot::setModified(GooString *new_modified) {
1479 annotLocker();
1480 delete modified;
1482 if (new_modified)
1483 modified = new GooString(new_modified);
1484 else
1485 modified = new GooString();
1487 Object obj1;
1488 obj1.initString(modified->copy());
1489 update ("M", &obj1);
1492 void Annot::setFlags(Guint new_flags) {
1493 annotLocker();
1494 Object obj1;
1495 flags = new_flags;
1496 obj1.initInt(flags);
1497 update ("F", &obj1);
1500 void Annot::setBorder(AnnotBorder *new_border) {
1501 annotLocker();
1502 delete border;
1504 if (new_border) {
1505 Object obj1;
1506 new_border->writeToObject(xref, &obj1);
1507 update(new_border->getType() == AnnotBorder::typeArray ? "Border" : "BS", &obj1);
1508 border = new_border;
1509 } else {
1510 border = NULL;
1512 invalidateAppearance();
1515 void Annot::setColor(AnnotColor *new_color) {
1516 annotLocker();
1517 delete color;
1519 if (new_color) {
1520 Object obj1;
1521 new_color->writeToObject(xref, &obj1);
1522 update ("C", &obj1);
1523 color = new_color;
1524 } else {
1525 color = NULL;
1527 invalidateAppearance();
1530 void Annot::setPage(int pageIndex, GBool updateP) {
1531 annotLocker();
1532 Page *pageobj = doc->getPage(pageIndex);
1533 Object obj1;
1535 if (pageobj) {
1536 Ref pageRef = pageobj->getRef();
1537 obj1.initRef(pageRef.num, pageRef.gen);
1538 page = pageIndex;
1539 } else {
1540 obj1.initNull();
1541 page = 0;
1544 if (updateP) {
1545 update("P", &obj1);
1549 void Annot::setAppearanceState(const char *state) {
1550 annotLocker();
1551 if (!state)
1552 return;
1554 delete appearState;
1555 appearState = new GooString(state);
1557 delete appearBBox;
1558 appearBBox = NULL;
1560 Object obj1;
1561 obj1.initName(state);
1562 update ("AS", &obj1);
1564 // The appearance state determines the current appearance stream
1565 appearance.free();
1566 if (appearStreams) {
1567 appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->getCString(), &appearance);
1568 } else {
1569 appearance.initNull();
1573 void Annot::invalidateAppearance() {
1574 annotLocker();
1575 if (appearStreams) { // Remove existing appearance streams
1576 appearStreams->removeAllStreams();
1578 delete appearStreams;
1579 appearStreams = NULL;
1581 delete appearState;
1582 appearState = NULL;
1584 delete appearBBox;
1585 appearBBox = NULL;
1587 appearance.free();
1588 appearance.initNull();
1590 Object obj1, obj2;
1591 obj1.initNull();
1592 if (!annotObj.dictLookup("AP", &obj2)->isNull())
1593 update ("AP", &obj1); // Remove AP
1594 obj2.free();
1596 if (!annotObj.dictLookup("AS", &obj2)->isNull())
1597 update ("AS", &obj1); // Remove AS
1598 obj2.free();
1601 double Annot::getXMin() {
1602 return rect->x1;
1605 double Annot::getYMin() {
1606 return rect->y1;
1609 double Annot::getXMax() {
1610 return rect->x2;
1613 double Annot::getYMax() {
1614 return rect->y2;
1617 void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
1618 Object valueObject;
1620 pdfArray->arrayGet(key, &valueObject);
1621 if (valueObject.isNum()) {
1622 *value = valueObject.getNum();
1623 } else {
1624 *value = 0;
1625 ok = gFalse;
1627 valueObject.free();
1630 void Annot::removeReferencedObjects() {
1631 // Remove appearance streams (if any)
1632 invalidateAppearance();
1635 void Annot::incRefCnt() {
1636 annotLocker();
1637 refCnt++;
1640 void Annot::decRefCnt() {
1641 #if MULTITHREADED
1642 gLockMutex(&mutex);
1643 #endif
1644 if (--refCnt == 0) {
1645 #if MULTITHREADED
1646 gUnlockMutex(&mutex);
1647 #endif
1648 delete this;
1649 return;
1651 #if MULTITHREADED
1652 gUnlockMutex(&mutex);
1653 #endif
1656 Annot::~Annot() {
1657 annotObj.free();
1659 delete rect;
1660 delete contents;
1662 if (name)
1663 delete name;
1665 if (modified)
1666 delete modified;
1668 delete appearStreams;
1669 delete appearBBox;
1670 appearance.free();
1672 if (appearState)
1673 delete appearState;
1675 if (border)
1676 delete border;
1678 if (color)
1679 delete color;
1681 oc.free();
1683 #if MULTITHREADED
1684 gDestroyMutex(&mutex);
1685 #endif
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],
1695 fill ? 'k' : 'K');
1696 break;
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");
1701 break;
1702 case AnnotColor::colorGray:
1703 appearBuf->appendf("{0:.2f} {1:c}\n",
1704 values[0],
1705 fill ? 'g' : 'G');
1706 break;
1707 case AnnotColor::colorTransparent:
1708 default:
1709 break;
1713 void Annot::setLineStyleForBorder(AnnotBorder *border) {
1714 int i, dashLength;
1715 double *dash;
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");
1725 break;
1726 default:
1727 appearBuf->append("[] 0 d\n");
1728 break;
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",
1737 cx + r, cy);
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,
1741 cx, 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,
1745 cx - r, cy);
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,
1749 cx, 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,
1753 cx + r, cy);
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) {
1760 double r2;
1762 r2 = r / sqrt(2.0);
1763 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1764 cx + r2, cy + r2);
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,
1770 cx - r2,
1771 cy + 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,
1777 cx - r2,
1778 cy - 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) {
1785 double r2;
1787 r2 = r / sqrt(2.0);
1788 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1789 cx - r2, cy - r2);
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,
1795 cx + r2,
1796 cy - 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,
1802 cx + r2,
1803 cy + r2);
1804 appearBuf->append("S\n");
1807 void Annot::createForm(double *bbox, GBool transparencyGroup, Object *resDict, Object *aStream) {
1808 Object obj1, obj2;
1809 Object appearDict;
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) {
1821 Object transDict;
1822 transDict.initDict(xref);
1823 transDict.dictSet("S", obj1.initName("Transparency"));
1824 appearDict.dictSet("Group", &transDict);
1826 if (resDict)
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,
1838 Object *resDict) {
1839 Object gsDict, stateDict, formDict, obj1;
1841 gsDict.initDict(xref);
1842 if (opacity != 1) {
1843 gsDict.dictSet("CA", obj1.initReal(opacity));
1844 gsDict.dictSet("ca", obj1.initReal(opacity));
1846 if (blendMode)
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) {
1859 Object obj1, obj2;
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()) {
1868 obj2.copy(dest);
1870 obj2.free();
1872 obj1.free();
1874 return dest;
1877 GBool Annot::isVisible(GBool printing) {
1878 // check the flags
1879 if ((flags & flagHidden) ||
1880 (printing && !(flags & flagPrint)) ||
1881 (!printing && (flags & flagNoView))) {
1882 return gFalse;
1885 // check the OC
1886 OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig();
1887 if (optContentConfig) {
1888 if (! optContentConfig->optContentIsVisible(&oc))
1889 return gFalse;
1892 return gTrue;
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;
1902 } else {
1903 return 0;
1907 void Annot::draw(Gfx *gfx, GBool printing) {
1908 Object obj;
1910 annotLocker();
1911 if (!isVisible (printing))
1912 return;
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());
1918 obj.free();
1921 //------------------------------------------------------------------------
1922 // AnnotPopup
1923 //------------------------------------------------------------------------
1925 AnnotPopup::AnnotPopup(PDFDoc *docA, PDFRectangle *rect) :
1926 Annot(docA, rect) {
1927 Object obj1;
1929 type = typePopup;
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) {
1937 type = typePopup;
1938 initialize(docA, dict);
1941 AnnotPopup::~AnnotPopup() {
1942 parent.free();
1945 void AnnotPopup::initialize(PDFDoc *docA, Dict *dict) {
1946 Object obj1;
1948 if (!dict->lookupNF("Parent", &parent)->isRef()) {
1949 parent.initNull();
1952 if (dict->lookup("Open", &obj1)->isBool()) {
1953 open = obj1.getBool();
1954 } else {
1955 open = gFalse;
1957 obj1.free();
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) {
1972 Object obj1;
1974 open = openA;
1975 obj1.initBool(open);
1976 update ("Open", &obj1);
1979 //------------------------------------------------------------------------
1980 // AnnotMarkup
1981 //------------------------------------------------------------------------
1982 AnnotMarkup::AnnotMarkup(PDFDoc *docA, PDFRectangle *rect) :
1983 Annot(docA, 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() {
1993 if (label)
1994 delete label;
1996 if (popup)
1997 delete popup;
1999 if (date)
2000 delete date;
2002 if (subject)
2003 delete subject;
2006 void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict, Object *obj) {
2007 Object obj1, obj2;
2009 if (dict->lookup("T", &obj1)->isString()) {
2010 label = obj1.getString()->copy();
2011 } else {
2012 label = NULL;
2014 obj1.free();
2016 if (dict->lookup("Popup", &obj1)->isDict() && dict->lookupNF("Popup", &obj2)->isRef()) {
2017 popup = new AnnotPopup(docA, obj1.getDict(), &obj2);
2018 } else {
2019 popup = NULL;
2021 obj1.free();
2023 if (dict->lookup("CA", &obj1)->isNum()) {
2024 opacity = obj1.getNum();
2025 } else {
2026 opacity = 1.0;
2028 obj1.free();
2030 if (dict->lookup("CreationDate", &obj1)->isString()) {
2031 date = obj1.getString()->copy();
2032 } else {
2033 date = NULL;
2035 obj1.free();
2037 if (dict->lookupNF("IRT", &obj1)->isRef()) {
2038 inReplyTo = obj1.getRef();
2039 } else {
2040 inReplyTo.num = 0;
2041 inReplyTo.gen = 0;
2043 obj1.free();
2045 if (dict->lookup("Subj", &obj1)->isString()) {
2046 subject = obj1.getString()->copy();
2047 } else {
2048 subject = NULL;
2050 obj1.free();
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;
2059 } else {
2060 replyTo = replyTypeR;
2062 } else {
2063 replyTo = replyTypeR;
2065 obj1.free();
2067 if (dict->lookup("ExData", &obj1)->isDict()) {
2068 exData = parseAnnotExternalData(obj1.getDict());
2069 } else {
2070 exData = annotExternalDataMarkupUnknown;
2072 obj1.free();
2075 void AnnotMarkup::setLabel(GooString *new_label) {
2076 delete label;
2078 if (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);
2085 } else {
2086 label = new GooString();
2089 Object obj1;
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());
2101 if (pageobj) {
2102 pageobj->removeAnnot(popup);
2105 delete popup;
2107 if (new_popup) {
2108 Object obj1;
2109 Ref popupRef = new_popup->getRef();
2111 obj1.initRef (popupRef.num, popupRef.gen);
2112 update ("Popup", &obj1);
2114 new_popup->setParent(this);
2115 popup = new_popup;
2117 // If this annotation is already added to a page, then we
2118 // add the new popup annotation to the same page.
2119 if (page != 0) {
2120 Page *pageobj = doc->getPage(page);
2121 assert(pageobj != NULL); // pageobj should exist in doc (see setPage())
2123 pageobj->addAnnot(popup);
2125 } else {
2126 popup = NULL;
2130 void AnnotMarkup::setOpacity(double opacityA) {
2131 Object obj1;
2133 opacity = opacityA;
2134 obj1.initReal(opacity);
2135 update ("CA", &obj1);
2136 invalidateAppearance();
2139 void AnnotMarkup::setDate(GooString *new_date) {
2140 delete date;
2142 if (new_date)
2143 date = new GooString(new_date);
2144 else
2145 date = new GooString();
2147 Object obj1;
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
2156 // Remove popup
2157 if (popup) {
2158 pageobj->removeAnnot(popup);
2161 Annot::removeReferencedObjects();
2164 //------------------------------------------------------------------------
2165 // AnnotText
2166 //------------------------------------------------------------------------
2168 AnnotText::AnnotText(PDFDoc *docA, PDFRectangle *rect) :
2169 AnnotMarkup(docA, rect) {
2170 Object obj1;
2172 type = typeText;
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) {
2182 type = typeText;
2183 flags |= flagNoZoom | flagNoRotate;
2184 initialize (docA, dict);
2187 AnnotText::~AnnotText() {
2188 delete icon;
2191 void AnnotText::initialize(PDFDoc *docA, Dict *dict) {
2192 Object obj1;
2194 if (dict->lookup("Open", &obj1)->isBool())
2195 open = obj1.getBool();
2196 else
2197 open = gFalse;
2198 obj1.free();
2200 if (dict->lookup("Name", &obj1)->isName()) {
2201 icon = new GooString(obj1.getName());
2202 } else {
2203 icon = new GooString("Note");
2205 obj1.free();
2207 if (dict->lookup("StateModel", &obj1)->isString()) {
2208 Object obj2;
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")) {
2227 state = stateNone;
2228 } else {
2229 state = stateUnknown;
2231 } else {
2232 state = stateUnknown;
2234 obj2.free();
2236 if (!modelName->cmp("Marked")) {
2237 switch (state) {
2238 case stateUnknown:
2239 state = stateMarked;
2240 break;
2241 case stateAccepted:
2242 case stateRejected:
2243 case stateCancelled:
2244 case stateCompleted:
2245 case stateNone:
2246 state = stateUnknown;
2247 break;
2248 default:
2249 break;
2251 } else if (!modelName->cmp("Review")) {
2252 switch (state) {
2253 case stateUnknown:
2254 state = stateNone;
2255 break;
2256 case stateMarked:
2257 case stateUnmarked:
2258 state = stateUnknown;
2259 break;
2260 default:
2261 break;
2263 } else {
2264 state = stateUnknown;
2266 } else {
2267 state = stateUnknown;
2269 obj1.free();
2272 void AnnotText::setOpen(GBool openA) {
2273 Object obj1;
2275 open = 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)
2282 return;
2284 delete icon;
2286 if (new_icon) {
2287 icon = new GooString (new_icon);
2288 } else {
2289 icon = new GooString("Note");
2292 Object obj1;
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" \
2302 "3.602 24 m f\n" \
2303 "0.533333 0.541176 0.521569 RG 2 w\n" \
2304 "1 J\n" \
2305 "1 j\n" \
2306 "[] 0.0 d\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" \
2308 "1.5 w\n" \
2309 "0 j\n" \
2310 "10 16 m 14 21 l S\n" \
2311 "1.85625 w\n" \
2312 "1 j\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" \
2317 "1 w\n" \
2318 "0 j\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" \
2323 "1 j\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" \
2325 "1.5 w\n" \
2326 "0 j\n" \
2327 "10 17 m 14 22 l S\n" \
2328 "1.85625 w\n" \
2329 "1 j\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" \
2334 "1 w\n" \
2335 "0 j\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" \
2344 "4.301 23 m f\n" \
2345 "0.533333 0.541176 0.521569 RG 2 w\n" \
2346 "0 J\n" \
2347 "1 j\n" \
2348 "[] 0.0 d\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" \
2352 "8 20 m S\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" \
2357 "8 21 m S\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" \
2363 "4.301 23 m f\n" \
2364 "0.533333 0.541176 0.521569 RG 2 w\n" \
2365 "1 J\n" \
2366 "0 j\n" \
2367 "[] 0.0 d\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" \
2374 "1.5 w\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" \
2386 "1.5 w\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" \
2396 "4.301 23 m f\n" \
2397 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2398 "1 J\n" \
2399 "1 j\n" \
2400 "[] 0.0 d\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" \
2403 "c S\n" \
2404 "0 j\n" \
2405 "12 8 m 12 12 16 11 16 15 c S\n" \
2406 "1.539286 w\n" \
2407 "1 j\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" \
2415 "c S\n" \
2416 "0 j\n" \
2417 "12 9 m 12 13 16 12 16 16 c S\n" \
2418 "1.539286 w\n" \
2419 "1 j\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" \
2429 "4.301 23 m f\n" \
2430 "0.533333 0.541176 0.521569 RG 4 w\n" \
2431 "0 J\n" \
2432 "2 j\n" \
2433 "[] 0.0 d\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" \
2437 "1.004413 w\n" \
2438 "1 J\n" \
2439 "1 j\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" \
2445 "2 w\n" \
2446 "0 j\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" \
2450 "0 J\n" \
2451 "2 j\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" \
2455 "1.004413 w\n" \
2456 "1 J\n" \
2457 "1 j\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" \
2463 "2 w\n" \
2464 "0 j\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" \
2472 "4.301 23 m f\n" \
2473 "0.533333 0.541176 0.521569 RG 2 w\n" \
2474 "1 J\n" \
2475 "1 j\n" \
2476 "[] 0.0 d\n" \
2477 "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
2478 "4 w\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" \
2484 "4 w\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" \
2493 "4.301 23 m f\n" \
2494 "0.533333 0.541176 0.521569 RG 2 w\n" \
2495 "1 J\n" \
2496 "0 j\n" \
2497 "[] 0.0 d\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" \
2509 "4 13 m 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" \
2515 "4.301 23 m f\n" \
2516 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2517 "1 J\n" \
2518 "0 j\n" \
2519 "[] 0.0 d\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" \
2523 "6 6 m 18 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" \
2529 "4.301 23 m f\n" \
2530 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2531 "1 J\n" \
2532 "1 j\n" \
2533 "[] 0.0 d\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" \
2536 "19.5 11.5 m S\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" \
2540 "19.5 12.5 m S\n"
2542 void AnnotText::draw(Gfx *gfx, GBool printing) {
2543 Object obj;
2544 double ca = 1;
2546 if (!isVisible (printing))
2547 return;
2549 annotLocker();
2550 if (appearance.isNull()) {
2551 ca = opacity;
2553 appearBuf = new GooString ();
2555 appearBuf->append ("q\n");
2556 if (color)
2557 setColor(color, gTrue);
2558 else
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);
2583 double bbox[4];
2584 appearBBox->getBBoxRect(bbox);
2585 if (ca == 1) {
2586 createForm(bbox, gFalse, NULL, &appearance);
2587 } else {
2588 Object aStream, resDict;
2590 createForm(bbox, gTrue, NULL, &aStream);
2591 delete appearBuf;
2593 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
2594 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
2595 createForm(bbox, gFalse, &resDict, &appearance);
2597 delete appearBuf;
2600 // draw the appearance stream
2601 appearance.fetch(gfx->getXRef(), &obj);
2602 if (appearBBox) {
2603 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2604 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
2605 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
2606 getRotation());
2607 } else {
2608 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2609 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2611 obj.free();
2614 //------------------------------------------------------------------------
2615 // AnnotLink
2616 //------------------------------------------------------------------------
2617 AnnotLink::AnnotLink(PDFDoc *docA, PDFRectangle *rect) :
2618 Annot(docA, rect) {
2619 Object obj1;
2621 type = typeLink;
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) {
2629 type = typeLink;
2630 initialize (docA, dict);
2633 AnnotLink::~AnnotLink() {
2634 delete action;
2636 if (uriAction)
2637 delete uriAction;
2639 if (quadrilaterals)
2640 delete quadrilaterals;
2643 void AnnotLink::initialize(PDFDoc *docA, Dict *dict) {
2644 Object obj1;
2646 action = NULL;
2648 // look for destination
2649 if (!dict->lookup("Dest", &obj1)->isNull()) {
2650 action = LinkAction::parseDest(&obj1);
2651 // look for action
2652 } else {
2653 obj1.free();
2654 if (dict->lookup("A", &obj1)->isDict()) {
2655 action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
2658 obj1.free();
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;
2671 } else {
2672 linkEffect = effectInvert;
2674 } else {
2675 linkEffect = effectInvert;
2677 obj1.free();
2679 if (dict->lookup("PA", &obj1)->isDict()) {
2680 uriAction = NULL;
2681 } else {
2682 uriAction = NULL;
2684 obj1.free();
2686 if (dict->lookup("QuadPoints", &obj1)->isArray()) {
2687 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
2688 } else {
2689 quadrilaterals = NULL;
2691 obj1.free();
2693 if (dict->lookup("BS", &obj1)->isDict()) {
2694 delete border;
2695 border = new AnnotBorderBS(obj1.getDict());
2696 } else if (!border) {
2697 border = new AnnotBorderBS();
2699 obj1.free();
2702 void AnnotLink::draw(Gfx *gfx, GBool printing) {
2703 Object obj;
2705 if (!isVisible (printing))
2706 return;
2708 annotLocker();
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());
2713 obj.free();
2716 //------------------------------------------------------------------------
2717 // AnnotFreeText
2718 //------------------------------------------------------------------------
2719 AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rect, GooString *da) :
2720 AnnotMarkup(docA, rect) {
2721 Object obj1;
2723 type = typeFreeText;
2725 annotObj.dictSet ("Subtype", obj1.initName ("FreeText"));
2727 Object obj2;
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;
2743 if (styleString)
2744 delete styleString;
2746 if (calloutLine)
2747 delete calloutLine;
2749 if (borderEffect)
2750 delete borderEffect;
2752 if (rectangle)
2753 delete rectangle;
2756 void AnnotFreeText::initialize(PDFDoc *docA, Dict *dict) {
2757 Object obj1;
2759 if (dict->lookup("DA", &obj1)->isString()) {
2760 appearanceString = obj1.getString()->copy();
2761 } else {
2762 appearanceString = new GooString();
2763 error(errSyntaxError, -1, "Bad appearance for annotation");
2764 ok = gFalse;
2766 obj1.free();
2768 if (dict->lookup("Q", &obj1)->isInt()) {
2769 quadding = (AnnotFreeTextQuadding) obj1.getInt();
2770 } else {
2771 quadding = quaddingLeftJustified;
2773 obj1.free();
2775 if (dict->lookup("DS", &obj1)->isString()) {
2776 styleString = obj1.getString()->copy();
2777 } else {
2778 styleString = NULL;
2780 obj1.free();
2782 if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
2783 double x1, y1, x2, y2;
2784 Object obj2;
2786 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
2787 obj2.free();
2788 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
2789 obj2.free();
2790 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
2791 obj2.free();
2792 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
2793 obj2.free();
2795 if (obj1.arrayGetLength() == 6) {
2796 double x3, y3;
2797 (obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
2798 obj2.free();
2799 (obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
2800 obj2.free();
2801 calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
2802 } else {
2803 calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
2805 } else {
2806 calloutLine = NULL;
2808 obj1.free();
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;
2819 } else {
2820 intent = intentFreeText;
2822 } else {
2823 intent = intentFreeText;
2825 obj1.free();
2827 if (dict->lookup("BS", &obj1)->isDict()) {
2828 delete border;
2829 border = new AnnotBorderBS(obj1.getDict());
2830 } else if (!border) {
2831 border = new AnnotBorderBS();
2833 obj1.free();
2835 if (dict->lookup("BE", &obj1)->isDict()) {
2836 borderEffect = new AnnotBorderEffect(obj1.getDict());
2837 } else {
2838 borderEffect = NULL;
2840 obj1.free();
2842 if (dict->lookup("RD", &obj1)->isArray()) {
2843 rectangle = parseDiffRectangle(obj1.getArray(), rect);
2844 } else {
2845 rectangle = NULL;
2847 obj1.free();
2849 if (dict->lookup("LE", &obj1)->isName()) {
2850 GooString styleName(obj1.getName());
2851 endStyle = parseAnnotLineEndingStyle(&styleName);
2852 } else {
2853 endStyle = annotLineEndingNone;
2855 obj1.free();
2858 void AnnotFreeText::setContents(GooString *new_content) {
2859 Annot::setContents(new_content);
2860 invalidateAppearance();
2863 void AnnotFreeText::setAppearanceString(GooString *new_string) {
2864 delete appearanceString;
2866 if (new_string) {
2867 appearanceString = new GooString(new_string);
2868 } else {
2869 appearanceString = new GooString();
2872 Object obj1;
2873 obj1.initString(appearanceString->copy());
2874 update ("DA", &obj1);
2875 invalidateAppearance();
2878 void AnnotFreeText::setQuadding(AnnotFreeTextQuadding new_quadding) {
2879 Object obj1;
2880 quadding = new_quadding;
2881 obj1.initInt((int)quadding);
2882 update ("Q", &obj1);
2883 invalidateAppearance();
2886 void AnnotFreeText::setStyleString(GooString *new_string) {
2887 delete styleString;
2889 if (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);
2896 } else {
2897 styleString = new GooString();
2900 Object obj1;
2901 obj1.initString(styleString->copy());
2902 update ("DS", &obj1);
2905 void AnnotFreeText::setCalloutLine(AnnotCalloutLine *line) {
2906 delete calloutLine;
2908 Object obj1;
2909 if (line == NULL) {
2910 obj1.initNull();
2911 calloutLine = NULL;
2912 } else {
2913 double x1 = line->getX1(), y1 = line->getY1();
2914 double x2 = line->getX2(), y2 = line->getY2();
2915 Object obj2;
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);
2923 if (mline) {
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);
2928 } else {
2929 calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
2933 update("CL", &obj1);
2934 invalidateAppearance();
2937 void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent) {
2938 Object obj1;
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");
2959 Object fontDictObj;
2960 Dict *fontDict = new Dict(xref);
2961 fontDict->decRef();
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) {
2981 fontsize = -1;
2982 fontcolor = NULL;
2983 if (da) {
2984 GooList * daToks = new GooList();
2985 int j, i = 0;
2987 // Tokenize
2988 while (i < da->getLength()) {
2989 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2990 ++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));
2996 i = j;
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) {
3004 // TODO: Font name
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);
3038 // Box size
3039 const double width = rect->x2 - rect->x1;
3040 const double height = rect->y2 - rect->y1;
3042 // Parse some properties from the appearance string
3043 double fontsize;
3044 AnnotColor *fontcolor;
3045 parseAppearanceString(appearanceString, fontsize, fontcolor);
3046 // Default values
3047 if (fontsize <= 0)
3048 fontsize = 10;
3049 if (fontcolor == NULL)
3050 fontcolor = new AnnotColor(0, 0, 0); // Black
3051 if (!contents)
3052 contents = new GooString ();
3054 // Draw box
3055 GBool doFill = (color && color->getSpace() != AnnotColor::colorTransparent);
3056 GBool doStroke = (borderWidth != 0);
3057 if (doFill || doStroke) {
3058 if (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);
3062 if (doFill) {
3063 setColor(color, gTrue);
3064 appearBuf->append(doStroke ? "B\n" : "f\n");
3065 } else {
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);
3075 Object fontResDict;
3076 GfxFont *font = createAnnotDrawFont(xref, &fontResDict);
3078 // Set font state
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);
3083 int i = 0;
3084 double xposPrev = 0;
3085 while (i < contents->getLength()) {
3086 GooString out;
3087 double linewidth, xpos;
3088 layoutText(contents, &out, &i, font, &linewidth, textwidth/fontsize, NULL, gFalse);
3089 linewidth *= fontsize;
3090 switch (quadding) {
3091 case quaddingCentered:
3092 xpos = (textwidth - linewidth) / 2;
3093 break;
3094 case quaddingRightJustified:
3095 xpos = textwidth - linewidth;
3096 break;
3097 default: // quaddingLeftJustified:
3098 xpos = 0;
3099 break;
3101 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize);
3102 writeString(&out, appearBuf);
3103 appearBuf->append("Tj\n");
3104 xposPrev = xpos;
3107 font->decRefCnt();
3108 delete fontcolor;
3109 appearBuf->append ("ET Q\n");
3111 double bbox[4];
3112 bbox[0] = bbox[1] = 0;
3113 bbox[2] = rect->x2 - rect->x1;
3114 bbox[3] = rect->y2 - rect->y1;
3116 if (ca == 1) {
3117 createForm(bbox, gFalse, &fontResDict, &appearance);
3118 } else {
3119 Object aStream, resDict;
3121 createForm(bbox, gTrue, &fontResDict, &aStream);
3122 delete appearBuf;
3124 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3125 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3126 createForm(bbox, gFalse, &resDict, &appearance);
3128 delete appearBuf;
3131 void AnnotFreeText::draw(Gfx *gfx, GBool printing) {
3132 Object obj;
3134 if (!isVisible (printing))
3135 return;
3137 annotLocker();
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());
3146 obj.free();
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 //------------------------------------------------------------------------
3159 // AnnotLine
3160 //------------------------------------------------------------------------
3162 AnnotLine::AnnotLine(PDFDoc *docA, PDFRectangle *rect) :
3163 AnnotMarkup(docA, rect) {
3164 Object obj1;
3166 type = typeLine;
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) {
3174 type = typeLine;
3175 initialize(docA, dict);
3178 AnnotLine::~AnnotLine() {
3179 delete coord1;
3180 delete coord2;
3182 if (interiorColor)
3183 delete interiorColor;
3185 if (measure)
3186 delete measure;
3189 void AnnotLine::initialize(PDFDoc *docA, Dict *dict) {
3190 Object obj1;
3192 if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
3193 Object obj2;
3194 double x1, y1, x2, y2;
3196 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
3197 obj2.free();
3198 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
3199 obj2.free();
3200 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
3201 obj2.free();
3202 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
3203 obj2.free();
3205 coord1 = new AnnotCoord(x1, y1);
3206 coord2 = new AnnotCoord(x2, y2);
3207 } else {
3208 coord1 = new AnnotCoord();
3209 coord2 = new AnnotCoord();
3211 obj1.free();
3213 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
3214 Object obj2;
3216 if(obj1.arrayGet(0, &obj2)->isString())
3217 startStyle = parseAnnotLineEndingStyle(obj2.getString());
3218 else
3219 startStyle = annotLineEndingNone;
3220 obj2.free();
3222 if(obj1.arrayGet(1, &obj2)->isString())
3223 endStyle = parseAnnotLineEndingStyle(obj2.getString());
3224 else
3225 endStyle = annotLineEndingNone;
3226 obj2.free();
3228 } else {
3229 startStyle = endStyle = annotLineEndingNone;
3231 obj1.free();
3233 if (dict->lookup("IC", &obj1)->isArray()) {
3234 interiorColor = new AnnotColor(obj1.getArray());
3235 } else {
3236 interiorColor = NULL;
3238 obj1.free();
3240 if (dict->lookup("LL", &obj1)->isNum()) {
3241 leaderLineLength = obj1.getNum();
3242 } else {
3243 leaderLineLength = 0;
3245 obj1.free();
3247 if (dict->lookup("LLE", &obj1)->isNum()) {
3248 leaderLineExtension = obj1.getNum();
3250 if (leaderLineExtension < 0)
3251 leaderLineExtension = 0;
3252 } else {
3253 leaderLineExtension = 0;
3255 obj1.free();
3257 if (dict->lookup("Cap", &obj1)->isBool()) {
3258 caption = obj1.getBool();
3259 } else {
3260 caption = gFalse;
3262 obj1.free();
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;
3271 } else {
3272 intent = intentLineArrow;
3274 } else {
3275 intent = intentLineArrow;
3277 obj1.free();
3279 if (dict->lookup("LLO", &obj1)->isNum()) {
3280 leaderLineOffset = obj1.getNum();
3282 if (leaderLineOffset < 0)
3283 leaderLineOffset = 0;
3284 } else {
3285 leaderLineOffset = 0;
3287 obj1.free();
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;
3296 } else {
3297 captionPos = captionPosInline;
3299 } else {
3300 captionPos = captionPosInline;
3302 obj1.free();
3304 if (dict->lookup("Measure", &obj1)->isDict()) {
3305 measure = NULL;
3306 } else {
3307 measure = NULL;
3309 obj1.free();
3311 if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
3312 Object obj2;
3314 (obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
3315 captionTextHorizontal = 0);
3316 obj2.free();
3317 (obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
3318 captionTextVertical = 0);
3319 obj2.free();
3320 } else {
3321 captionTextHorizontal = captionTextVertical = 0;
3323 obj1.free();
3325 if (dict->lookup("BS", &obj1)->isDict()) {
3326 delete border;
3327 border = new AnnotBorderBS(obj1.getDict());
3328 } else if (!border) {
3329 border = new AnnotBorderBS();
3331 obj1.free();
3334 void AnnotLine::setContents(GooString *new_content) {
3335 Annot::setContents(new_content);
3336 if (caption)
3337 invalidateAppearance();
3340 void AnnotLine::setVertices(double x1, double y1, double x2, double y2) {
3341 Object obj1, obj2;
3343 delete coord1;
3344 coord1 = new AnnotCoord(x1, y1);
3345 delete coord2;
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) );
3354 update("L", &obj1);
3355 invalidateAppearance();
3358 void AnnotLine::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end) {
3359 Object obj1, obj2;
3361 startStyle = start;
3362 endStyle = 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;
3375 if (new_color) {
3376 Object obj1;
3377 new_color->writeToObject(xref, &obj1);
3378 update ("IC", &obj1);
3379 interiorColor = new_color;
3380 } else {
3381 interiorColor = NULL;
3383 invalidateAppearance();
3386 void AnnotLine::setLeaderLineLength(double len) {
3387 Object obj1;
3389 leaderLineLength = len;
3390 obj1.initReal(len);
3391 update ("LL", &obj1);
3392 invalidateAppearance();
3395 void AnnotLine::setLeaderLineExtension(double len) {
3396 Object obj1;
3398 leaderLineExtension = len;
3399 obj1.initReal(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) {
3409 Object obj1;
3411 caption = new_cap;
3412 obj1.initBool(new_cap);
3413 update ("Cap", &obj1);
3414 invalidateAppearance();
3417 void AnnotLine::setIntent(AnnotLineIntent new_intent) {
3418 Object obj1;
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");
3435 if (color) {
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)
3452 Matrix matr;
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;
3466 Object fontResDict;
3467 GfxFont *font;
3469 // Calculate caption width and height
3470 if (caption) {
3471 font = createAnnotDrawFont(xref, &fontResDict);
3472 int lines = 0;
3473 int i = 0;
3474 while (i < contents->getLength()) {
3475 GooString out;
3476 double linewidth;
3477 layoutText(contents, &out, &i, font, &linewidth, 0, NULL, gFalse);
3478 linewidth *= fontsize;
3479 if (linewidth > captionwidth) {
3480 captionwidth = linewidth;
3482 ++lines;
3484 captionheight = lines * fontsize;
3485 // If text is longer than available space, turn into captionPosTop
3486 if (captionwidth > captionmaxwidth) {
3487 actualCaptionPos = captionPosTop;
3489 } else {
3490 fontResDict.initNull();
3491 font = NULL;
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
3514 if (caption) {
3515 double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords
3516 if (actualCaptionPos == captionPosInline) {
3517 tly = leaderLineLength + captionheight / 2;
3518 } else {
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());
3540 // Draw text
3541 int i = 0;
3542 double xposPrev = 0;
3543 while (i < contents->getLength()) {
3544 GooString out;
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");
3552 xposPrev = xpos;
3554 appearBuf->append ("ET\n");
3555 font->decRefCnt();
3558 // Draw leader lines
3559 double ll_len = fabs(leaderLineLength) + leaderLineExtension;
3560 double sign = leaderLineLength >= 0 ? 1 : -1;
3561 if (ll_len != 0) {
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");
3579 double bbox[4];
3580 appearBBox->getBBoxRect(bbox);
3581 if (ca == 1) {
3582 createForm(bbox, gFalse, &fontResDict, &appearance);
3583 } else {
3584 Object aStream, resDict;
3586 createForm(bbox, gTrue, &fontResDict, &aStream);
3587 delete appearBuf;
3589 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3590 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3591 createForm(bbox, gFalse, &resDict, &appearance);
3593 delete appearBuf;
3596 void AnnotLine::draw(Gfx *gfx, GBool printing) {
3597 Object obj;
3599 if (!isVisible (printing))
3600 return;
3602 annotLocker();
3603 if (appearance.isNull()) {
3604 generateLineAppearance();
3607 // draw the appearance stream
3608 appearance.fetch(gfx->getXRef(), &obj);
3609 if (appearBBox) {
3610 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3611 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
3612 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
3613 getRotation());
3614 } else {
3615 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3616 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3618 obj.free();
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 //------------------------------------------------------------------------
3631 // AnnotTextMarkup
3632 //------------------------------------------------------------------------
3633 AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
3634 AnnotMarkup(docA, rect) {
3635 Object obj1;
3637 switch (subType) {
3638 case typeHighlight:
3639 annotObj.dictSet ("Subtype", obj1.initName ("Highlight"));
3640 break;
3641 case typeUnderline:
3642 annotObj.dictSet ("Subtype", obj1.initName ("Underline"));
3643 break;
3644 case typeSquiggly:
3645 annotObj.dictSet ("Subtype", obj1.initName ("Squiggly"));
3646 break;
3647 case typeStrikeOut:
3648 annotObj.dictSet ("Subtype", obj1.initName ("StrikeOut"));
3649 break;
3650 default:
3651 assert (0 && "Invalid subtype for AnnotTextMarkup\n");
3654 // Store dummy quadrilateral with null coordinates
3655 Object obj2, obj3;
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) {
3673 Object obj1;
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;
3687 obj1.free();
3689 if(dict->lookup("QuadPoints", &obj1)->isArray()) {
3690 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
3691 } else {
3692 error(errSyntaxError, -1, "Bad Annot Text Markup QuadPoints");
3693 quadrilaterals = NULL;
3694 ok = gFalse;
3696 obj1.free();
3699 AnnotTextMarkup::~AnnotTextMarkup() {
3700 if(quadrilaterals) {
3701 delete quadrilaterals;
3705 void AnnotTextMarkup::setType(AnnotSubtype new_type) {
3706 Object obj1;
3708 switch (new_type) {
3709 case typeHighlight:
3710 obj1.initName("Highlight");
3711 break;
3712 case typeUnderline:
3713 obj1.initName("Underline");
3714 break;
3715 case typeSquiggly:
3716 obj1.initName("Squiggly");
3717 break;
3718 case typeStrikeOut:
3719 obj1.initName("StrikeOut");
3720 break;
3721 default:
3722 assert(!"Invalid subtype");
3725 type = new_type;
3726 update("Subtype", &obj1);
3727 invalidateAppearance();
3730 void AnnotTextMarkup::setQuadrilaterals(AnnotQuadrilaterals *quadPoints) {
3731 Object obj1, obj2;
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) {
3753 Object obj;
3754 double ca = 1;
3755 int i;
3756 Object obj1, obj2;
3758 if (!isVisible (printing))
3759 return;
3761 annotLocker();
3762 if (appearance.isNull() || type == typeHighlight) {
3763 GBool blendMultiply = gTrue;
3764 ca = opacity;
3766 appearBuf = new GooString ();
3767 appearBuf->append ("q\n");
3769 /* Adjust BBox */
3770 delete appearBBox;
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);
3779 switch (type) {
3780 case typeUnderline:
3781 if (color) {
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");
3798 break;
3799 case typeStrikeOut:
3800 if (color) {
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");
3824 break;
3825 case typeSquiggly:
3826 if (color) {
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;
3833 double h6;
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);
3842 bool down = false;
3843 do {
3844 down = !down; // Zigzag line
3845 x1 += 2;
3846 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x1, y3 + (down ? 0 : h6));
3847 } while (x1 < x2);
3848 appearBuf->append ("S\n");
3850 break;
3851 default:
3852 case typeHighlight:
3853 appearance.free();
3855 if (color)
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;
3861 double h4;
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) {
3874 biggestBorder = h4;
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);
3886 break;
3888 appearBuf->append ("Q\n");
3890 Object aStream, resDict;
3891 double bbox[4];
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);
3897 delete appearBuf;
3899 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3900 createResourcesDict("Fm0", &aStream, "GS0", 1, blendMultiply ? "Multiply" : NULL, &resDict);
3901 if (ca == 1) {
3902 createForm(bbox, gFalse, &resDict, &appearance);
3903 } else {
3904 createForm(bbox, gTrue, &resDict, &aStream);
3905 delete appearBuf;
3907 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3908 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3909 createForm(bbox, gFalse, &resDict, &appearance);
3911 delete appearBuf;
3914 // draw the appearance stream
3915 appearance.fetch(gfx->getXRef(), &obj);
3916 if (appearBBox) {
3917 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3918 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
3919 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
3920 getRotation());
3921 } else {
3922 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3923 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3925 obj.free();
3928 //------------------------------------------------------------------------
3929 // AnnotWidget
3930 //------------------------------------------------------------------------
3932 AnnotWidget::AnnotWidget(PDFDoc *docA, Dict *dict, Object *obj) :
3933 Annot(docA, dict, obj) {
3934 type = typeWidget;
3935 field = NULL;
3936 initialize(docA, dict);
3939 AnnotWidget::AnnotWidget(PDFDoc *docA, Dict *dict, Object *obj, FormField *fieldA) :
3940 Annot(docA, dict, obj) {
3941 type = typeWidget;
3942 field = fieldA;
3943 initialize(docA, dict);
3946 AnnotWidget::~AnnotWidget() {
3947 if (appearCharacs)
3948 delete appearCharacs;
3950 if (action)
3951 delete action;
3953 additionalActions.free();
3955 if (parent)
3956 delete parent;
3959 void AnnotWidget::initialize(PDFDoc *docA, Dict *dict) {
3960 Object obj1;
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;
3973 } else {
3974 mode = highlightModeInvert;
3976 } else {
3977 mode = highlightModeInvert;
3979 obj1.free();
3981 if(dict->lookup("MK", &obj1)->isDict()) {
3982 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
3983 } else {
3984 appearCharacs = NULL;
3986 obj1.free();
3988 action = NULL;
3989 if(dict->lookup("A", &obj1)->isDict()) {
3990 action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
3992 obj1.free();
3994 dict->lookupNF("AA", &additionalActions);
3996 if(dict->lookup("Parent", &obj1)->isDict()) {
3997 parent = NULL;
3998 } else {
3999 parent = NULL;
4001 obj1.free();
4003 if (dict->lookup("BS", &obj1)->isDict()) {
4004 delete border;
4005 border = new AnnotBorderBS(obj1.getDict());
4007 obj1.free();
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.
4029 // Parameters:
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)
4050 CharCode c;
4051 Unicode uChar, *uAux;
4052 double w = 0.0;
4053 int uLen, n;
4054 double dx, dy, ox, oy;
4055 if (!text) {
4056 return;
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");
4072 return;
4075 // skip Unicode marker on string if needed
4076 if (unicode && *i == 0)
4077 *i = 2;
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;
4087 spacePrev = gFalse;
4088 outBuf->clear();
4090 while (*i < text->getLength()) {
4091 last_i2 = *i;
4092 last_o2 = outBuf->getLength();
4094 if (unicode) {
4095 uChar = (unsigned char)(text->getChar(*i)) << 8;
4096 uChar += (unsigned char)(text->getChar(*i + 1));
4097 *i += 2;
4098 } else {
4099 if (noReencode)
4100 uChar = text->getChar(*i) & 0xff;
4101 else
4102 uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
4103 *i += 1;
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')
4112 *i += 2;
4113 else if (!unicode && text->getChar(*i) == '\n')
4114 *i += 1;
4117 break;
4120 if (noReencode) {
4121 outBuf->append(uChar);
4122 } else {
4123 CharCodeToUnicode *ccToUnicode = font->getToUnicode();
4124 if (!ccToUnicode) {
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);
4135 } else {
4136 // 8-bit font
4137 outBuf->append(c);
4139 } else {
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.
4146 if (uChar == ' ') {
4147 last_i1 = *i;
4148 if (!spacePrev)
4149 last_o1 = last_o2;
4150 spacePrev = gTrue;
4151 } else {
4152 spacePrev = gFalse;
4155 // Compute width of character just output
4156 if (outBuf->getLength() > last_o2) {
4157 dx = 0.0;
4158 font->getNextChar(outBuf->getCString() + last_o2,
4159 outBuf->getLength() - last_o2,
4160 &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4161 w += dx;
4164 // Current line over-full now?
4165 if (widthLimit > 0.0 && w > widthLimit) {
4166 if (last_o1 > 0) {
4167 // Back up to the previous word which fit, if there was a previous
4168 // word.
4169 *i = last_i1;
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
4173 // this line)
4174 *i = last_i2;
4175 outBuf->del(last_o2, outBuf->getLength() - last_o2);
4176 } else {
4177 // Otherwise, we were trying to fit the first character; include it
4178 // anyway even if it overflows the space--no updates to make.
4180 break;
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) {
4190 if (unicode) {
4191 while (*i < text->getLength()
4192 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
4193 *i += 2;
4194 if (*i < text->getLength()
4195 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
4196 *i += 2;
4197 if (*i < text->getLength()
4198 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
4199 *i += 2;
4200 } else {
4201 while (*i < text->getLength() && text->getChar(*i) == ' ')
4202 *i += 1;
4203 if (*i < text->getLength() && text->getChar(*i) == '\r')
4204 *i += 1;
4205 if (*i < text->getLength() && text->getChar(*i) == '\n')
4206 *i += 1;
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();
4216 if (width != NULL)
4217 *width = 0.0;
4218 if (charCount != NULL)
4219 *charCount = 0;
4221 while (len > 0) {
4222 dx = 0.0;
4223 n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4225 if (n == 0) {
4226 break;
4229 if (width != NULL)
4230 *width += dx;
4231 if (charCount != NULL)
4232 *charCount += 1;
4234 s += n;
4235 len -= n;
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)
4244 char c;
4245 int i;
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);
4256 } else {
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,
4268 GBool password) {
4269 GooList *daToks;
4270 GooString *tok, *convertedText;
4271 GfxFont *font;
4272 double dx, dy;
4273 double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
4274 int tfPos, tmPos, i, j;
4275 int rot;
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
4284 tfPos = tmPos = -1;
4285 if (da) {
4286 daToks = new GooList();
4287 i = 0;
4288 while (i < da->getLength()) {
4289 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
4290 ++i;
4292 if (i < da->getLength()) {
4293 for (j = i + 1;
4294 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
4295 ++j) ;
4296 daToks->append(new GooString(da, i, j - i));
4297 i = j;
4300 for (i = 2; i < daToks->getLength(); ++i) {
4301 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
4302 tfPos = i - 2;
4303 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
4304 tmPos = i - 6;
4307 } else {
4308 daToks = NULL;
4311 // force ZapfDingbats
4312 if (forceZapfDingbats) {
4313 if (tfPos >= 0) {
4314 tok = (GooString *)daToks->get(tfPos);
4315 if (tok->cmp("/ZaDb")) {
4316 tok->clear();
4317 tok->append("/ZaDb");
4321 // get the font and font size
4322 font = NULL;
4323 fontSize = 0;
4324 if (tfPos >= 0) {
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
4332 r.num = -1;
4333 r.gen = -1;
4334 Dict *d = new Dict(xref);
4335 font = new Gfx8BitFont(xref, "ZaDb", r, new GooString("ZapfDingbats"), fontType1, r, d);
4336 delete d;
4337 freeFont = gTrue;
4338 addDingbatsResource = gTrue;
4339 } else {
4340 error(errSyntaxError, -1, "Unknown font in field's DA string");
4343 } else {
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());
4348 } else {
4349 error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4351 if (!font) {
4352 if (daToks) {
4353 deleteGooList(daToks, GooString);
4355 return;
4358 // get the border width
4359 borderWidth = border ? border->getWidth() : 0;
4361 // for a password field, replace all characters with asterisks
4362 if (password) {
4363 int len;
4364 if (text->hasUnicodeMarker())
4365 len = (text->getLength() - 2) / 2;
4366 else
4367 len = text->getLength();
4369 text = new GooString;
4370 for (i = 0; i < len; ++i)
4371 text->append('*');
4372 freeText = gTrue;
4375 convertedText = new GooString;
4377 // setup
4378 if (txField) {
4379 appearBuf->append("/Tx BMC\n");
4381 appearBuf->append("q\n");
4382 rot = appearCharacs ? appearCharacs->getRotation() : 0;
4383 switch (rot) {
4384 case 90:
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;
4388 break;
4389 case 180:
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;
4394 break;
4395 case 270:
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;
4399 break;
4400 default: // assume rot == 0
4401 dx = rect->x2 - rect->x1;
4402 dy = rect->y2 - rect->y1;
4403 break;
4405 appearBuf->append("BT\n");
4406 // multi-line text
4407 if (multiline) {
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) {
4415 y = dy - 3;
4416 i = 0;
4417 while (i < text->getLength()) {
4418 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
4419 forceZapfDingbats);
4420 y -= fontSize;
4422 // approximate the descender for the last line
4423 if (y >= 0.33 * fontSize) {
4424 break;
4427 if (tfPos >= 0) {
4428 tok = (GooString *)daToks->get(tfPos + 1);
4429 tok->clear();
4430 tok->appendf("{0:.2f}", fontSize);
4434 // starting y coordinate
4435 // (note: each line of text starts with a Td operator that moves
4436 // down a line)
4437 y = dy - 3;
4439 // set the font matrix
4440 if (tmPos >= 0) {
4441 tok = (GooString *)daToks->get(tmPos + 4);
4442 tok->clear();
4443 tok->append('0');
4444 tok = (GooString *)daToks->get(tmPos + 5);
4445 tok->clear();
4446 tok->appendf("{0:.2f}", y);
4449 // write the DA string
4450 if (daToks) {
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)
4457 if (tmPos < 0) {
4458 appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
4461 // write a series of lines of text
4462 i = 0;
4463 xPrev = 0;
4464 while (i < text->getLength()) {
4465 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
4466 forceZapfDingbats);
4467 w *= fontSize;
4469 // compute text start position
4470 switch (quadding) {
4471 case quaddingLeftJustified:
4472 default:
4473 x = borderWidth + 2;
4474 break;
4475 case quaddingCentered:
4476 x = (dx - w) / 2;
4477 break;
4478 case quaddingRightJustified:
4479 x = dx - borderWidth - 2 - w;
4480 break;
4483 // draw the line
4484 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
4485 writeString(convertedText, appearBuf);
4486 appearBuf->append(" Tj\n");
4488 // next line
4489 xPrev = x;
4492 // single-line text
4493 } else {
4494 //~ replace newlines with spaces? - what does Acrobat do?
4496 // comb formatting
4497 if (comb > 0) {
4498 int charCount;
4500 // compute comb spacing
4501 w = (dx - 2 * borderWidth) / comb;
4503 // compute font autosize
4504 if (fontSize == 0) {
4505 fontSize = dy - 2 * borderWidth;
4506 if (w < fontSize) {
4507 fontSize = w;
4509 fontSize = floor(fontSize);
4510 if (tfPos >= 0) {
4511 tok = (GooString *)daToks->get(tfPos + 1);
4512 tok->clear();
4513 tok->appendf("{0:.2f}", fontSize);
4517 i = 0;
4518 layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
4519 forceZapfDingbats);
4520 if (charCount > comb)
4521 charCount = comb;
4523 // compute starting text cell
4524 switch (quadding) {
4525 case quaddingLeftJustified:
4526 default:
4527 x = borderWidth;
4528 break;
4529 case quaddingCentered:
4530 x = borderWidth + (comb - charCount) / 2 * w;
4531 break;
4532 case quaddingRightJustified:
4533 x = borderWidth + (comb - charCount) * w;
4534 break;
4536 y = 0.5 * dy - 0.4 * fontSize;
4538 // set the font matrix
4539 if (tmPos >= 0) {
4540 tok = (GooString *)daToks->get(tmPos + 4);
4541 tok->clear();
4542 tok->appendf("{0:.2f}", x);
4543 tok = (GooString *)daToks->get(tmPos + 5);
4544 tok->clear();
4545 tok->appendf("{0:.2f}", y);
4548 // write the DA string
4549 if (daToks) {
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)
4556 if (tmPos < 0) {
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();
4563 i = 0;
4564 xPrev = w; // so that first character is placed properly
4565 while (i < comb && len > 0) {
4566 CharCode code;
4567 Unicode *uAux;
4568 int uLen, n;
4569 double dx, dy, ox, oy;
4571 dx = 0.0;
4572 n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
4573 dx *= fontSize;
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
4578 x = 0.5 * (w - dx);
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");
4584 delete charBuf;
4586 i++;
4587 s += n;
4588 len -= n;
4589 xPrev = x;
4592 // regular (non-comb) formatting
4593 } else {
4594 i = 0;
4595 layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
4596 forceZapfDingbats);
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);
4606 if (tfPos >= 0) {
4607 tok = (GooString *)daToks->get(tfPos + 1);
4608 tok->clear();
4609 tok->appendf("{0:.2f}", fontSize);
4613 // compute text start position
4614 w *= fontSize;
4615 switch (quadding) {
4616 case quaddingLeftJustified:
4617 default:
4618 x = borderWidth + 2;
4619 break;
4620 case quaddingCentered:
4621 x = (dx - w) / 2;
4622 break;
4623 case quaddingRightJustified:
4624 x = dx - borderWidth - 2 - w;
4625 break;
4627 y = 0.5 * dy - 0.4 * fontSize;
4629 // set the font matrix
4630 if (tmPos >= 0) {
4631 tok = (GooString *)daToks->get(tmPos + 4);
4632 tok->clear();
4633 tok->appendf("{0:.2f}", x);
4634 tok = (GooString *)daToks->get(tmPos + 5);
4635 tok->clear();
4636 tok->appendf("{0:.2f}", y);
4639 // write the DA string
4640 if (daToks) {
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)
4647 if (tmPos < 0) {
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");
4656 // cleanup
4657 appearBuf->append("ET\n");
4658 appearBuf->append("Q\n");
4659 if (txField) {
4660 appearBuf->append("EMC\n");
4662 if (daToks) {
4663 deleteGooList(daToks, GooString);
4665 if (freeText) {
4666 delete text;
4668 delete convertedText;
4669 if (freeFont) {
4670 font->decRefCnt();
4674 // Draw the variable text or caption for a field.
4675 void AnnotWidget::drawListBox(FormFieldChoice *fieldChoice,
4676 GooString *da, GfxResources *resources, int quadding) {
4677 GooList *daToks;
4678 GooString *tok, *convertedText;
4679 GfxFont *font;
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
4688 tfPos = tmPos = -1;
4689 if (da) {
4690 daToks = new GooList();
4691 i = 0;
4692 while (i < da->getLength()) {
4693 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
4694 ++i;
4696 if (i < da->getLength()) {
4697 for (j = i + 1;
4698 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
4699 ++j) ;
4700 daToks->append(new GooString(da, i, j - i));
4701 i = j;
4704 for (i = 2; i < daToks->getLength(); ++i) {
4705 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
4706 tfPos = i - 2;
4707 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
4708 tmPos = i - 6;
4711 } else {
4712 daToks = NULL;
4715 // get the font and font size
4716 font = NULL;
4717 fontSize = 0;
4718 if (tfPos >= 0) {
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");
4724 } else {
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());
4729 } else {
4730 error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4732 if (!font) {
4733 if (daToks) {
4734 deleteGooList(daToks, GooString);
4736 return;
4739 convertedText = new GooString;
4741 // get the border width
4742 borderWidth = border ? border->getWidth() : 0;
4744 // compute font autosize
4745 if (fontSize == 0) {
4746 wMax = 0;
4747 for (i = 0; i < fieldChoice->getNumChoices(); ++i) {
4748 j = 0;
4749 if (fieldChoice->getChoice(i) == NULL) {
4750 error(errSyntaxError, -1, "Invalid annotation listbox");
4751 if (daToks) {
4752 deleteGooList(daToks, GooString);
4754 delete convertedText;
4755 return;
4757 layoutText(fieldChoice->getChoice(i), convertedText, &j, font, &w, 0.0, NULL, gFalse);
4758 if (w > wMax) {
4759 wMax = w;
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);
4768 if (tfPos >= 0) {
4769 tok = (GooString *)daToks->get(tfPos + 1);
4770 tok->clear();
4771 tok->appendf("{0:.2f}", fontSize);
4774 // draw the text
4775 y = rect->y2 - rect->y1 - 1.1 * fontSize;
4776 for (i = fieldChoice->getTopIndex(); i < fieldChoice->getNumChoices(); ++i) {
4777 // setup
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",
4784 borderWidth,
4785 y - 0.2 * fontSize,
4786 rect->x2 - rect->x1 - 2 * borderWidth,
4787 1.1 * fontSize);
4790 // setup
4791 appearBuf->append("BT\n");
4793 // compute text width and start position
4794 j = 0;
4795 layoutText(fieldChoice->getChoice(i), convertedText, &j, font, &w, 0.0, NULL, gFalse);
4796 w *= fontSize;
4797 switch (quadding) {
4798 case quaddingLeftJustified:
4799 default:
4800 x = borderWidth + 2;
4801 break;
4802 case quaddingCentered:
4803 x = (rect->x2 - rect->x1 - w) / 2;
4804 break;
4805 case quaddingRightJustified:
4806 x = rect->x2 - rect->x1 - borderWidth - 2 - w;
4807 break;
4810 // set the font matrix
4811 if (tmPos >= 0) {
4812 tok = (GooString *)daToks->get(tmPos + 4);
4813 tok->clear();
4814 tok->appendf("{0:.2f}", x);
4815 tok = (GooString *)daToks->get(tmPos + 5);
4816 tok->clear();
4817 tok->appendf("{0:.2f}", y);
4820 // write the DA string
4821 if (daToks) {
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)
4828 if (tmPos < 0) {
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");
4841 // cleanup
4842 appearBuf->append("ET\n");
4843 appearBuf->append("Q\n");
4845 // next line
4846 y -= 1.1 * fontSize;
4849 if (daToks) {
4850 deleteGooList(daToks, GooString);
4853 delete convertedText;
4856 void AnnotWidget::drawBorder() {
4857 int dashLength;
4858 double *dash;
4859 AnnotColor adjustedColor;
4860 double w = border->getWidth();
4862 AnnotColor *aColor = appearCharacs->getBorderColor();
4863 if (!aColor)
4864 aColor = appearCharacs->getBackColor();
4865 if (!aColor)
4866 return;
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);
4891 break;
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);
4905 break;
4907 } else {
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);
4923 break;
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");
4946 break;
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);
4951 break;
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;
4962 if (appearCharacs)
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())) {
4970 if (caption) {
4971 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter,
4972 gFalse, gTrue);
4973 } else if (appearCharacs) {
4974 AnnotColor *aColor = appearCharacs->getBorderColor();
4975 if (aColor) {
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);
4984 break;
4985 case formButtonPush:
4986 if (caption)
4987 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gFalse);
4988 break;
4989 case formButtonCheck:
4990 if (appearState && appearState->cmp("Off") != 0) {
4991 if (!caption) {
4992 GooString checkMark("3");
4993 drawText(&checkMark, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gTrue);
4994 } else {
4995 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gTrue);
4998 break;
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();
5008 if (contents) {
5009 quadding = field->hasTextQuadding() ? field->getTextQuadding() : form->getTextQuadding();
5011 int comb = 0;
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();
5029 if (selected) {
5030 drawText(selected, da, resources, gFalse, 0, quadding, gTrue, gFalse);
5031 //~ Acrobat draws a popup icon on the right side
5033 // list box
5034 } else {
5035 drawListBox(fieldChoice, da, resources, quadding);
5039 void AnnotWidget::generateFieldAppearance() {
5040 Object appearDict, obj1, obj2;
5041 GfxResources *resources;
5042 MemStream *appearStream;
5043 GooString *da;
5045 appearBuf = new GooString ();
5047 // draw the background
5048 if (appearCharacs) {
5049 AnnotColor *aColor = appearCharacs->getBackColor();
5050 if (aColor) {
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);
5057 // draw the border
5058 if (appearCharacs && border && border->getWidth() > 0)
5059 drawBorder();
5061 da = field->getDefaultAppearance();
5062 if (!da)
5063 da = form->getDefaultAppearance();
5065 resources = form->getDefaultResources();
5067 // draw the field contents
5068 switch (field->getType()) {
5069 case formButton:
5070 drawFormFieldButton(resources, da);
5071 break;
5072 case formText:
5073 drawFormFieldText(resources, da);
5074 break;
5075 case formChoice:
5076 drawFormFieldChoice(resources, da);
5077 break;
5078 case formSignature:
5079 //~unimp
5080 break;
5081 case formUndef:
5082 default:
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);
5107 appearance.free();
5108 appearance.initStream(appearStream);
5109 delete appearBuf;
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())
5125 return;
5127 // Create the new appearance
5128 generateFieldAppearance();
5130 // Fetch the appearance stream we've just created
5131 Object obj1;
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);
5140 obj1.free();
5142 // Write the AP dictionary
5143 Object obj2;
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);
5150 } else {
5151 // Replace the existing appearance stream
5152 xref->setModifiedObject(&obj1, updatedAppearanceStream);
5153 obj1.free();
5157 void AnnotWidget::draw(Gfx *gfx, GBool printing) {
5158 Object obj;
5160 if (!isVisible (printing))
5161 return;
5163 annotLocker();
5164 addDingbatsResource = gFalse;
5166 // Only construct the appearance stream when
5167 // - annot doesn't have an AP or
5168 // - NeedAppearances is true
5169 if (field) {
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");
5183 Object fontDictObj;
5184 Dict *fontDict = new Dict(gfx->getXRef());
5185 fontDict->decRef();
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);
5199 delete dict;
5201 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5202 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5203 if (addDingbatsResource) {
5204 gfx->popResources();
5206 obj.free();
5210 //------------------------------------------------------------------------
5211 // AnnotMovie
5212 //------------------------------------------------------------------------
5213 AnnotMovie::AnnotMovie(PDFDoc *docA, PDFRectangle *rect, Movie *movieA) :
5214 Annot(docA, rect) {
5215 Object obj1;
5217 type = typeMovie;
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) {
5228 type = typeMovie;
5229 initialize(docA, dict);
5232 AnnotMovie::~AnnotMovie() {
5233 if (title)
5234 delete title;
5235 delete movie;
5238 void AnnotMovie::initialize(PDFDoc *docA, Dict* dict) {
5239 Object obj1;
5241 if (dict->lookup("T", &obj1)->isString()) {
5242 title = obj1.getString()->copy();
5243 } else {
5244 title = NULL;
5246 obj1.free();
5248 Object movieDict;
5249 if (dict->lookup("Movie", &movieDict)->isDict()) {
5250 Object obj2;
5251 dict->lookup("A", &obj2);
5252 if (obj2.isDict())
5253 movie = new Movie (&movieDict, &obj2);
5254 else
5255 movie = new Movie (&movieDict);
5256 if (!movie->isOk()) {
5257 delete movie;
5258 movie = NULL;
5259 ok = gFalse;
5261 obj2.free();
5262 } else {
5263 error(errSyntaxError, -1, "Bad Annot Movie");
5264 movie = NULL;
5265 ok = gFalse;
5267 movieDict.free();
5270 void AnnotMovie::draw(Gfx *gfx, GBool printing) {
5271 Object obj;
5273 if (!isVisible (printing))
5274 return;
5276 annotLocker();
5277 if (appearance.isNull() && movie->getShowPoster()) {
5278 int width, height;
5279 Object poster;
5280 movie->getPoster(&poster);
5281 movie->getAspect(&width, &height);
5283 if (width != -1 && height != -1 && !poster.isNone()) {
5284 MemStream *mStream;
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");
5292 Object imgDict;
5293 imgDict.initDict(gfx->getXRef());
5294 imgDict.dictSet ("MImg", &poster);
5296 Object resDict;
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);
5321 Object aStream;
5322 mStream = new MemStream(copyString(appearBuf->getCString()), 0,
5323 appearBuf->getLength(), &formDict);
5324 mStream->setNeedFree(gTrue);
5325 aStream.initStream(mStream);
5326 delete appearBuf;
5328 Object objDict;
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");
5345 double bbox[4];
5346 bbox[0] = bbox[1] = 0;
5347 bbox[2] = width;
5348 bbox[3] = height;
5349 createForm(bbox, gFalse, &resDict, &appearance);
5350 delete appearBuf;
5352 poster.free();
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());
5359 obj.free();
5362 //------------------------------------------------------------------------
5363 // AnnotScreen
5364 //------------------------------------------------------------------------
5365 AnnotScreen::AnnotScreen(PDFDoc *docA, PDFRectangle *rect) :
5366 Annot(docA, rect) {
5367 Object obj1;
5369 type = typeScreen;
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) {
5377 type = typeScreen;
5378 initialize(docA, dict);
5381 AnnotScreen::~AnnotScreen() {
5382 if (title)
5383 delete title;
5384 if (appearCharacs)
5385 delete appearCharacs;
5386 if (action)
5387 delete action;
5389 additionalActions.free();
5392 void AnnotScreen::initialize(PDFDoc *docA, Dict* dict) {
5393 Object obj1;
5395 title = NULL;
5396 if (dict->lookup("T", &obj1)->isString()) {
5397 title = obj1.getString()->copy();
5399 obj1.free();
5401 action = NULL;
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");
5406 delete action;
5407 action = NULL;
5408 ok = gFalse;
5411 obj1.free();
5413 dict->lookupNF("AA", &additionalActions);
5415 appearCharacs = NULL;
5416 if(dict->lookup("MK", &obj1)->isDict()) {
5417 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
5419 obj1.free();
5422 LinkAction* AnnotScreen::getAdditionalAction(AdditionalActionsType type)
5424 if (type == actionFocusIn || type == actionFocusOut) // not defined for screen annotation
5425 return NULL;
5427 return ::getAdditionalAction(type, &additionalActions, doc);
5430 //------------------------------------------------------------------------
5431 // AnnotStamp
5432 //------------------------------------------------------------------------
5433 AnnotStamp::AnnotStamp(PDFDoc *docA, PDFRectangle *rect) :
5434 AnnotMarkup(docA, rect) {
5435 Object obj1;
5437 type = typeStamp;
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) {
5444 type = typeStamp;
5445 initialize(docA, dict);
5448 AnnotStamp::~AnnotStamp() {
5449 delete icon;
5452 void AnnotStamp::initialize(PDFDoc *docA, Dict* dict) {
5453 Object obj1;
5455 if (dict->lookup("Name", &obj1)->isName()) {
5456 icon = new GooString(obj1.getName());
5457 } else {
5458 icon = new GooString("Draft");
5460 obj1.free();
5464 void AnnotStamp::setIcon(GooString *new_icon) {
5465 delete icon;
5467 if (new_icon) {
5468 icon = new GooString (new_icon);
5469 } else {
5470 icon = new GooString();
5473 Object obj1;
5474 obj1.initName (icon->getCString());
5475 update("Name", &obj1);
5476 invalidateAppearance();
5479 //------------------------------------------------------------------------
5480 // AnnotGeometry
5481 //------------------------------------------------------------------------
5482 AnnotGeometry::AnnotGeometry(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
5483 AnnotMarkup(docA, rect) {
5484 Object obj1;
5486 switch (subType) {
5487 case typeSquare:
5488 annotObj.dictSet ("Subtype", obj1.initName ("Square"));
5489 break;
5490 case typeCircle:
5491 annotObj.dictSet ("Subtype", obj1.initName ("Circle"));
5492 break;
5493 default:
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()
5503 type = typeSquare;
5504 initialize(docA, dict);
5507 AnnotGeometry::~AnnotGeometry() {
5508 delete interiorColor;
5509 delete borderEffect;
5510 delete geometryRect;
5513 void AnnotGeometry::initialize(PDFDoc *docA, Dict* dict) {
5514 Object obj1;
5516 if (dict->lookup("Subtype", &obj1)->isName()) {
5517 GooString typeName(obj1.getName());
5518 if (!typeName.cmp("Square")) {
5519 type = typeSquare;
5520 } else if (!typeName.cmp("Circle")) {
5521 type = typeCircle;
5524 obj1.free();
5526 if (dict->lookup("IC", &obj1)->isArray()) {
5527 interiorColor = new AnnotColor(obj1.getArray());
5528 } else {
5529 interiorColor = NULL;
5531 obj1.free();
5533 if (dict->lookup("BS", &obj1)->isDict()) {
5534 delete border;
5535 border = new AnnotBorderBS(obj1.getDict());
5536 } else if (!border) {
5537 border = new AnnotBorderBS();
5539 obj1.free();
5541 if (dict->lookup("BE", &obj1)->isDict()) {
5542 borderEffect = new AnnotBorderEffect(obj1.getDict());
5543 } else {
5544 borderEffect = NULL;
5546 obj1.free();
5548 geometryRect = NULL;
5549 if (dict->lookup("RD", &obj1)->isArray()) {
5550 geometryRect = parseDiffRectangle(obj1.getArray(), rect);
5552 obj1.free();
5556 void AnnotGeometry::setType(AnnotSubtype new_type) {
5557 Object obj1;
5559 switch (new_type) {
5560 case typeSquare:
5561 obj1.initName("Square");
5562 break;
5563 case typeCircle:
5564 obj1.initName("Circle");
5565 break;
5566 default:
5567 assert(!"Invalid subtype");
5570 type = new_type;
5571 update("Subtype", &obj1);
5572 invalidateAppearance();
5575 void AnnotGeometry::setInteriorColor(AnnotColor *new_color) {
5576 delete interiorColor;
5578 if (new_color) {
5579 Object obj1;
5580 new_color->writeToObject(xref, &obj1);
5581 update ("IC", &obj1);
5582 interiorColor = new_color;
5583 } else {
5584 interiorColor = NULL;
5586 invalidateAppearance();
5589 void AnnotGeometry::draw(Gfx *gfx, GBool printing) {
5590 Object obj;
5591 double ca = 1;
5593 if (!isVisible (printing))
5594 return;
5596 annotLocker();
5597 if (appearance.isNull()) {
5598 ca = opacity;
5600 appearBuf = new GooString ();
5601 appearBuf->append ("q\n");
5602 if (color)
5603 setColor(color, gFalse);
5605 double borderWidth = border->getWidth();
5606 setLineStyleForBorder(border);
5608 if (interiorColor)
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);
5616 } else {
5617 double width, height;
5618 double b;
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;
5625 x1 = b;
5626 y1 = height / 2.0;
5627 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x1, y1);
5629 y1 += height / 4.0;
5630 x2 = width / 4.0;
5631 y2 = height - b;
5632 x3 = width / 2.0;
5633 y3 = y2;
5634 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5635 x1, y1, x2, y2, x3, y3);
5636 x2 = width - b;
5637 y2 = y1;
5638 x1 = x3 + (width / 4.0);
5639 y1 = y3;
5640 x3 = x2;
5641 y3 = height / 2.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);
5645 x2 = x1;
5646 y2 = b;
5647 x1 = x3;
5648 y1 = height / 4.0;
5649 x3 = width / 2.0;
5650 y3 = b;
5651 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5652 x1, y1, x2, y2, x3, y3);
5654 x2 = b;
5655 y2 = y1;
5656 x1 = width / 4.0;
5657 y1 = b;
5658 x3 = b;
5659 y3 = height / 2.0;
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");
5666 else
5667 appearBuf->append ("S\n");
5669 appearBuf->append ("Q\n");
5671 double bbox[4];
5672 bbox[0] = bbox[1] = 0;
5673 bbox[2] = rect->x2 - rect->x1;
5674 bbox[3] = rect->y2 - rect->y1;
5675 if (ca == 1) {
5676 createForm(bbox, gFalse, NULL, &appearance);
5677 } else {
5678 Object aStream;
5680 createForm(bbox, gTrue, NULL, &aStream);
5681 delete appearBuf;
5683 Object resDict;
5684 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
5685 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
5686 createForm(bbox, gFalse, &resDict, &appearance);
5688 delete appearBuf;
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());
5695 obj.free();
5698 //------------------------------------------------------------------------
5699 // AnnotPolygon
5700 //------------------------------------------------------------------------
5701 AnnotPolygon::AnnotPolygon(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
5702 AnnotMarkup(docA, rect) {
5703 Object obj1;
5705 switch (subType) {
5706 case typePolygon:
5707 annotObj.dictSet ("Subtype", obj1.initName ("Polygon"));
5708 break;
5709 case typePolyLine:
5710 annotObj.dictSet ("Subtype", obj1.initName ("PolyLine"));
5711 break;
5712 default:
5713 assert (0 && "Invalid subtype for AnnotGeometry\n");
5716 // Store dummy path with one null vertex only
5717 Object obj2, obj3;
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()
5729 type = typePolygon;
5730 initialize(docA, dict);
5733 AnnotPolygon::~AnnotPolygon() {
5734 delete vertices;
5736 if (interiorColor)
5737 delete interiorColor;
5739 if (borderEffect)
5740 delete borderEffect;
5743 void AnnotPolygon::initialize(PDFDoc *docA, Dict* dict) {
5744 Object obj1;
5746 if (dict->lookup("Subtype", &obj1)->isName()) {
5747 GooString typeName(obj1.getName());
5748 if (!typeName.cmp("Polygon")) {
5749 type = typePolygon;
5750 } else if (!typeName.cmp("PolyLine")) {
5751 type = typePolyLine;
5754 obj1.free();
5756 if (dict->lookup("Vertices", &obj1)->isArray()) {
5757 vertices = new AnnotPath(obj1.getArray());
5758 } else {
5759 vertices = new AnnotPath();
5760 error(errSyntaxError, -1, "Bad Annot Polygon Vertices");
5761 ok = gFalse;
5763 obj1.free();
5765 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
5766 Object obj2;
5768 if(obj1.arrayGet(0, &obj2)->isString())
5769 startStyle = parseAnnotLineEndingStyle(obj2.getString());
5770 else
5771 startStyle = annotLineEndingNone;
5772 obj2.free();
5774 if(obj1.arrayGet(1, &obj2)->isString())
5775 endStyle = parseAnnotLineEndingStyle(obj2.getString());
5776 else
5777 endStyle = annotLineEndingNone;
5778 obj2.free();
5780 } else {
5781 startStyle = endStyle = annotLineEndingNone;
5783 obj1.free();
5785 if (dict->lookup("IC", &obj1)->isArray()) {
5786 interiorColor = new AnnotColor(obj1.getArray());
5787 } else {
5788 interiorColor = NULL;
5790 obj1.free();
5792 if (dict->lookup("BS", &obj1)->isDict()) {
5793 delete border;
5794 border = new AnnotBorderBS(obj1.getDict());
5795 } else if (!border) {
5796 border = new AnnotBorderBS();
5798 obj1.free();
5800 if (dict->lookup("BE", &obj1)->isDict()) {
5801 borderEffect = new AnnotBorderEffect(obj1.getDict());
5802 } else {
5803 borderEffect = NULL;
5805 obj1.free();
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;
5814 } else {
5815 intent = polygonDimension;
5817 } else {
5818 intent = polygonCloud;
5820 obj1.free();
5823 void AnnotPolygon::setType(AnnotSubtype new_type) {
5824 Object obj1;
5826 switch (new_type) {
5827 case typePolygon:
5828 obj1.initName("Polygon");
5829 break;
5830 case typePolyLine:
5831 obj1.initName("PolyLine");
5832 break;
5833 default:
5834 assert(!"Invalid subtype");
5837 type = new_type;
5838 update("Subtype", &obj1);
5839 invalidateAppearance();
5842 void AnnotPolygon::setVertices(AnnotPath *path) {
5843 Object obj1, obj2;
5844 delete vertices;
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) {
5860 Object obj1, obj2;
5862 startStyle = start;
5863 endStyle = 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;
5876 if (new_color) {
5877 Object obj1;
5878 new_color->writeToObject(xref, &obj1);
5879 update ("IC", &obj1);
5880 interiorColor = new_color;
5881 } else {
5882 interiorColor = NULL;
5884 invalidateAppearance();
5887 void AnnotPolygon::setIntent(AnnotPolygonIntent new_intent) {
5888 Object obj1;
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) {
5901 Object obj;
5902 double ca = 1;
5904 if (!isVisible (printing))
5905 return;
5907 annotLocker();
5908 if (appearance.isNull()) {
5909 appearBBox = new AnnotAppearanceBBox(rect);
5910 ca = opacity;
5912 appearBuf = new GooString ();
5913 appearBuf->append ("q\n");
5915 if (color) {
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");
5938 } else {
5939 appearBuf->append ("s\n");
5941 } else {
5942 appearBuf->append ("S\n");
5946 appearBuf->append ("Q\n");
5948 double bbox[4];
5949 appearBBox->getBBoxRect(bbox);
5950 if (ca == 1) {
5951 createForm(bbox, gFalse, NULL, &appearance);
5952 } else {
5953 Object aStream, resDict;
5955 createForm(bbox, gTrue, NULL, &aStream);
5956 delete appearBuf;
5958 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
5959 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
5960 createForm(bbox, gFalse, &resDict, &appearance);
5962 delete appearBuf;
5965 // draw the appearance stream
5966 appearance.fetch(gfx->getXRef(), &obj);
5967 if (appearBBox) {
5968 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5969 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
5970 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
5971 getRotation());
5972 } else {
5973 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5974 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5976 obj.free();
5979 //------------------------------------------------------------------------
5980 // AnnotCaret
5981 //------------------------------------------------------------------------
5982 AnnotCaret::AnnotCaret(PDFDoc *docA, PDFRectangle *rect) :
5983 AnnotMarkup(docA, rect) {
5984 Object obj1;
5986 type = typeCaret;
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) {
5994 type = typeCaret;
5995 initialize(docA, dict);
5998 AnnotCaret::~AnnotCaret() {
5999 delete caretRect;
6002 void AnnotCaret::initialize(PDFDoc *docA, Dict* dict) {
6003 Object obj1;
6005 symbol = symbolNone;
6006 if (dict->lookup("Sy", &obj1)->isName()) {
6007 GooString typeName(obj1.getName());
6008 if (!typeName.cmp("P")) {
6009 symbol = symbolP;
6010 } else if (!typeName.cmp("None")) {
6011 symbol = symbolNone;
6014 obj1.free();
6016 if (dict->lookup("RD", &obj1)->isArray()) {
6017 caretRect = parseDiffRectangle(obj1.getArray(), rect);
6018 } else {
6019 caretRect = NULL;
6021 obj1.free();
6025 void AnnotCaret::setSymbol(AnnotCaretSymbol new_symbol) {
6026 Object obj1;
6027 obj1.initName( new_symbol == symbolP ? "P" : "None" );
6028 symbol = new_symbol;
6029 update("Sy", &obj1);
6030 invalidateAppearance();
6033 //------------------------------------------------------------------------
6034 // AnnotInk
6035 //------------------------------------------------------------------------
6036 AnnotInk::AnnotInk(PDFDoc *docA, PDFRectangle *rect) :
6037 AnnotMarkup(docA, rect) {
6038 Object obj1;
6040 type = typeInk;
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) {
6057 type = typeInk;
6058 initialize(docA, dict);
6061 AnnotInk::~AnnotInk() {
6062 freeInkList();
6065 void AnnotInk::initialize(PDFDoc *docA, Dict* dict) {
6066 Object obj1;
6068 if (dict->lookup("InkList", &obj1)->isArray()) {
6069 parseInkList(obj1.getArray());
6070 } else {
6071 inkListLength = 0;
6072 inkList = NULL;
6073 error(errSyntaxError, -1, "Bad Annot Ink List");
6074 ok = gFalse;
6076 obj1.free();
6078 if (dict->lookup("BS", &obj1)->isDict()) {
6079 delete border;
6080 border = new AnnotBorderBS(obj1.getDict());
6081 } else if (!border) {
6082 border = new AnnotBorderBS();
6084 obj1.free();
6087 void AnnotInk::writeInkList(AnnotPath **paths, int n_paths, Array *dest_array) {
6088 Object obj1, obj2;
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++) {
6105 Object obj2;
6106 if (array->get(i, &obj2)->isArray())
6107 inkList[i] = new AnnotPath(obj2.getArray());
6108 obj2.free();
6112 void AnnotInk::freeInkList() {
6113 if (inkList) {
6114 for (int i = 0; i < inkListLength; ++i)
6115 delete inkList[i];
6116 gfree(inkList);
6120 void AnnotInk::setInkList(AnnotPath **paths, int n_paths) {
6121 Object obj1;
6123 freeInkList();
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) {
6134 Object obj;
6135 double ca = 1;
6137 if (!isVisible (printing))
6138 return;
6140 annotLocker();
6141 if (appearance.isNull()) {
6142 appearBBox = new AnnotAppearanceBBox(rect);
6143 ca = opacity;
6145 appearBuf = new GooString ();
6146 appearBuf->append ("q\n");
6148 if (color) {
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");
6172 double bbox[4];
6173 appearBBox->getBBoxRect(bbox);
6174 if (ca == 1) {
6175 createForm(bbox, gFalse, NULL, &appearance);
6176 } else {
6177 Object aStream, resDict;
6179 createForm(bbox, gTrue, NULL, &aStream);
6180 delete appearBuf;
6182 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6183 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6184 createForm(bbox, gFalse, &resDict, &appearance);
6186 delete appearBuf;
6189 // draw the appearance stream
6190 appearance.fetch(gfx->getXRef(), &obj);
6191 if (appearBBox) {
6192 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6193 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
6194 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
6195 getRotation());
6196 } else {
6197 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6198 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6200 obj.free();
6203 //------------------------------------------------------------------------
6204 // AnnotFileAttachment
6205 //------------------------------------------------------------------------
6206 AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, PDFRectangle *rect, GooString *filename) :
6207 AnnotMarkup(docA, rect) {
6208 Object obj1;
6210 type = typeFileAttachment;
6212 annotObj.dictSet ("Subtype", obj1.initName ("FileAttachment"));
6214 Object obj2;
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() {
6228 file.free();
6230 if (name)
6231 delete name;
6234 void AnnotFileAttachment::initialize(PDFDoc *docA, Dict* dict) {
6235 Object obj1;
6237 if (dict->lookup("FS", &obj1)->isDict() || dict->lookup("FS", &obj1)->isString()) {
6238 obj1.copy(&file);
6239 } else {
6240 error(errSyntaxError, -1, "Bad Annot File Attachment");
6241 ok = gFalse;
6243 obj1.free();
6245 if (dict->lookup("Name", &obj1)->isName()) {
6246 name = new GooString(obj1.getName());
6247 } else {
6248 name = new GooString("PushPin");
6250 obj1.free();
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" \
6257 "4.301 23 m f\n" \
6258 "0.533333 0.541176 0.521569 RG 2 w\n" \
6259 "1 J\n" \
6260 "1 j\n" \
6261 "[] 0.0 d\n" \
6262 "4 M 5 4 m 6 5 l S\n" \
6263 "2 w\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" \
6266 "11 14 m S\n" \
6267 "3 w\n" \
6268 "6 5 m 9 8 l S\n" \
6269 "0.729412 0.741176 0.713725 RG 2 w\n" \
6270 "5 5 m 6 6 l S\n" \
6271 "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" \
6274 "11 15 m S\n" \
6275 "3 w\n" \
6276 "6 6 m 9 9 l S\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" \
6282 "4.301 23 m f\n" \
6283 "0.533333 0.541176 0.521569 RG 2 w\n" \
6284 "1 J\n" \
6285 "1 j\n" \
6286 "[] 0.0 d\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" \
6291 "17.887 l S\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" \
6297 "18.887 l S\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" \
6303 "4.301 23 m f\n" \
6304 "0.533333 0.541176 0.521569 RG 1 w\n" \
6305 "1 J\n" \
6306 "0 j\n" \
6307 "[] 0.0 d\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" \
6309 "18.5 15.5 m S\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" \
6313 "18.5 16.5 m S\n" \
6314 "0.533333 0.541176 0.521569 RG 2 w\n" \
6315 "1 j\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" \
6323 "4.301 23 m f\n" \
6324 "0.533333 0.541176 0.521569 RG 0.999781 w\n" \
6325 "1 J\n" \
6326 "1 j\n" \
6327 "[] 0.0 d\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" \
6333 "2 w\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" \
6346 "0 j\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" \
6354 "1 j\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" \
6360 "1 w\n" \
6361 "0 j\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) {
6366 Object obj;
6367 double ca = 1;
6369 if (!isVisible (printing))
6370 return;
6372 annotLocker();
6373 if (appearance.isNull()) {
6374 ca = opacity;
6376 appearBuf = new GooString ();
6378 appearBuf->append ("q\n");
6379 if (color)
6380 setColor(color, gTrue);
6381 else
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");
6393 double bbox[4];
6394 bbox[0] = bbox[1] = 0;
6395 bbox[2] = bbox[3] = 24;
6396 if (ca == 1) {
6397 createForm (bbox, gFalse, NULL, &appearance);
6398 } else {
6399 Object aStream;
6401 createForm (bbox, gTrue, NULL, &aStream);
6402 delete appearBuf;
6404 Object resDict;
6405 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6406 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6407 createForm(bbox, gFalse, &resDict, &appearance);
6409 delete appearBuf;
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());
6416 obj.free();
6419 //------------------------------------------------------------------------
6420 // AnnotSound
6421 //------------------------------------------------------------------------
6422 AnnotSound::AnnotSound(PDFDoc *docA, PDFRectangle *rect, Sound *soundA) :
6423 AnnotMarkup(docA, rect) {
6424 Object obj1;
6426 type = typeSound;
6428 annotObj.dictSet ("Subtype", obj1.initName ("Sound"));
6430 Object obj2;
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) {
6441 type = typeSound;
6442 initialize(docA, dict);
6445 AnnotSound::~AnnotSound() {
6446 delete sound;
6448 delete name;
6451 void AnnotSound::initialize(PDFDoc *docA, Dict* dict) {
6452 Object obj1;
6454 sound = Sound::parseSound(dict->lookup("Sound", &obj1));
6455 if (!sound) {
6456 error(errSyntaxError, -1, "Bad Annot Sound");
6457 ok = gFalse;
6459 obj1.free();
6461 if (dict->lookup("Name", &obj1)->isName()) {
6462 name = new GooString(obj1.getName());
6463 } else {
6464 name = new GooString("Speaker");
6466 obj1.free();
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" \
6473 "4.301 23 m f\n" \
6474 "0.533333 0.541176 0.521569 RG 2 w\n" \
6475 "0 J\n" \
6476 "1 j\n" \
6477 "[] 0.0 d\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" \
6479 "4 14 m S\n" \
6480 "1 w\n" \
6481 "1 J\n" \
6482 "0 j\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" \
6487 "0 J\n" \
6488 "1 j\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" \
6490 "4 15 m S\n" \
6491 "1 w\n" \
6492 "1 J\n" \
6493 "0 j\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" \
6502 "4.301 23 m f\n" \
6503 "0.533333 0.541176 0.521569 RG 2 w\n" \
6504 "1 J\n" \
6505 "0 j\n" \
6506 "[] 0.0 d\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" \
6509 "20 c h\n" \
6510 "12 20 m S\n" \
6511 "1 w\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" \
6514 "2 w\n" \
6515 "0 J\n" \
6516 "12 6.52 m 12 3 l S\n" \
6517 "1 J\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" \
6522 "12 21 m S\n" \
6523 "1 w\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" \
6526 "2 w\n" \
6527 "0 J\n" \
6528 "12 7.52 m 12 4 l S\n" \
6529 "1 J\n" \
6530 "8 4 m 16 4 l S\n"
6532 void AnnotSound::draw(Gfx *gfx, GBool printing) {
6533 Object obj;
6534 double ca = 1;
6536 if (!isVisible (printing))
6537 return;
6539 annotLocker();
6540 if (appearance.isNull()) {
6541 ca = opacity;
6543 appearBuf = new GooString ();
6545 appearBuf->append ("q\n");
6546 if (color)
6547 setColor(color, gTrue);
6548 else
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");
6556 double bbox[4];
6557 bbox[0] = bbox[1] = 0;
6558 bbox[2] = bbox[3] = 24;
6559 if (ca == 1) {
6560 createForm(bbox, gFalse, NULL, &appearance);
6561 } else {
6562 Object aStream, resDict;
6564 createForm(bbox, gTrue, NULL, &aStream);
6565 delete appearBuf;
6567 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6568 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6569 createForm(bbox, gFalse, &resDict, &appearance);
6571 delete appearBuf;
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());
6578 obj.free();
6581 //------------------------------------------------------------------------
6582 // Annot3D
6583 //------------------------------------------------------------------------
6584 Annot3D::Annot3D(PDFDoc *docA, PDFRectangle *rect) :
6585 Annot(docA, rect) {
6586 Object obj1;
6588 type = type3D;
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) {
6597 type = type3D;
6598 initialize(docA, dict);
6601 Annot3D::~Annot3D() {
6602 if (activation)
6603 delete activation;
6606 void Annot3D::initialize(PDFDoc *docA, Dict* dict) {
6607 Object obj1;
6609 if (dict->lookup("3DA", &obj1)->isDict()) {
6610 activation = new Activation(obj1.getDict());
6611 } else {
6612 activation = NULL;
6614 obj1.free();
6617 Annot3D::Activation::Activation(Dict *dict) {
6618 Object obj1;
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;
6629 } else {
6630 aTrigger = aTriggerUnknown;
6632 } else {
6633 aTrigger = aTriggerUnknown;
6635 obj1.free();
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;
6644 } else {
6645 aState = aStateUnknown;
6647 } else {
6648 aState = aStateUnknown;
6650 obj1.free();
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;
6661 } else {
6662 dTrigger = dTriggerUnknown;
6664 } else {
6665 dTrigger = dTriggerUnknown;
6667 obj1.free();
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;
6678 } else {
6679 dState = dStateUnknown;
6681 } else {
6682 dState = dStateUnknown;
6684 obj1.free();
6686 if (dict->lookup("TB", &obj1)->isBool()) {
6687 displayToolbar = obj1.getBool();
6688 } else {
6689 displayToolbar = gTrue;
6691 obj1.free();
6693 if (dict->lookup("NP", &obj1)->isBool()) {
6694 displayNavigation = obj1.getBool();
6695 } else {
6696 displayNavigation = gFalse;
6698 obj1.free();
6701 //------------------------------------------------------------------------
6702 // AnnotRichMedia
6703 //------------------------------------------------------------------------
6704 AnnotRichMedia::AnnotRichMedia(PDFDoc *docA, PDFRectangle *rect) :
6705 Annot(docA, rect) {
6706 Object obj1;
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() {
6722 delete content;
6723 delete settings;
6726 void AnnotRichMedia::initialize(PDFDoc *docA, Dict* dict) {
6727 Object obj1;
6729 if (dict->lookup("RichMediaContent", &obj1)->isDict()) {
6730 content = new AnnotRichMedia::Content(obj1.getDict());
6731 } else {
6732 content = NULL;
6734 obj1.free();
6736 if (dict->lookup("RichMediaSettings", &obj1)->isDict()) {
6737 settings = new AnnotRichMedia::Settings(obj1.getDict());
6738 } else {
6739 settings = NULL;
6741 obj1.free();
6744 AnnotRichMedia::Content* AnnotRichMedia::getContent() const {
6745 return content;
6748 AnnotRichMedia::Settings* AnnotRichMedia::getSettings() const {
6749 return settings;
6752 AnnotRichMedia::Settings::Settings(Dict *dict) {
6753 Object obj1;
6755 if (dict->lookup("Activation", &obj1)->isDict()) {
6756 activation = new AnnotRichMedia::Activation(obj1.getDict());
6757 } else {
6758 activation = NULL;
6760 obj1.free();
6762 if (dict->lookup("Deactivation", &obj1)->isDict()) {
6763 deactivation = new AnnotRichMedia::Deactivation(obj1.getDict());
6764 } else {
6765 deactivation = NULL;
6767 obj1.free();
6770 AnnotRichMedia::Settings::~Settings() {
6771 delete activation;
6772 delete deactivation;
6775 AnnotRichMedia::Activation* AnnotRichMedia::Settings::getActivation() const {
6776 return activation;
6779 AnnotRichMedia::Deactivation* AnnotRichMedia::Settings::getDeactivation() const {
6780 return deactivation;
6783 AnnotRichMedia::Activation::Activation(Dict *dict) {
6784 Object obj1;
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;
6795 } else {
6796 condition = conditionUserAction;
6798 } else {
6799 condition = conditionUserAction;
6801 obj1.free();
6804 AnnotRichMedia::Activation::Condition AnnotRichMedia::Activation::getCondition() const {
6805 return condition;
6808 AnnotRichMedia::Deactivation::Deactivation(Dict *dict) {
6809 Object obj1;
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;
6820 } else {
6821 condition = conditionUserAction;
6823 } else {
6824 condition = conditionUserAction;
6826 obj1.free();
6829 AnnotRichMedia::Deactivation::Condition AnnotRichMedia::Deactivation::getCondition() const {
6830 return condition;
6833 AnnotRichMedia::Content::Content(Dict *dict) {
6834 Object obj1;
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) {
6842 Object obj2;
6844 if (obj1.arrayGet(i, &obj2)->isDict()) {
6845 configurations[i] = new AnnotRichMedia::Configuration(obj2.getDict());
6846 } else {
6847 configurations[i] = NULL;
6849 obj2.free();
6851 } else {
6852 configurations = NULL;
6854 obj1.free();
6856 if (dict->lookup("Assets", &obj1)->isDict()) {
6857 Object obj2;
6859 if (obj1.getDict()->lookup("Names", &obj2)->isArray()) {
6860 nAssets = obj2.arrayGetLength() / 2;
6862 assets = (Asset **)gmallocn(nAssets, sizeof(Asset *));
6864 int counter = 0;
6865 for (int i = 0; i < obj2.arrayGetLength(); i += 2) {
6866 Object objKey;
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() );
6874 ++counter;
6876 objKey.free();
6879 obj2.free();
6881 } else {
6882 assets = NULL;
6884 obj1.free();
6887 AnnotRichMedia::Content::~Content() {
6888 if (configurations) {
6889 for (int i = 0; i < nConfigurations; ++i)
6890 delete configurations[i];
6891 gfree(configurations);
6894 if (assets) {
6895 for (int i = 0; i < nAssets; ++i)
6896 delete assets[i];
6897 gfree(assets);
6901 int AnnotRichMedia::Content::getConfigurationsCount() const {
6902 return nConfigurations;
6905 AnnotRichMedia::Configuration* AnnotRichMedia::Content::getConfiguration(int index) const {
6906 if (index < 0 || index >= nConfigurations)
6907 return NULL;
6909 return configurations[index];
6912 int AnnotRichMedia::Content::getAssetsCount() const {
6913 return nAssets;
6916 AnnotRichMedia::Asset* AnnotRichMedia::Content::getAsset(int index) const {
6917 if (index < 0 || index >= nAssets)
6918 return NULL;
6920 return assets[index];
6923 AnnotRichMedia::Asset::Asset()
6924 : name(NULL)
6928 AnnotRichMedia::Asset::~Asset()
6930 delete name;
6931 fileSpec.free();
6934 GooString* AnnotRichMedia::Asset::getName() const {
6935 return name;
6938 Object* AnnotRichMedia::Asset::getFileSpec() const {
6939 return const_cast<Object*>(&fileSpec);
6942 AnnotRichMedia::Configuration::Configuration(Dict *dict)
6944 Object obj1;
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) {
6952 Object obj2;
6954 if (obj1.arrayGet(i, &obj2)->isDict()) {
6955 instances[i] = new AnnotRichMedia::Instance(obj2.getDict());
6956 } else {
6957 instances[i] = NULL;
6959 obj2.free();
6961 } else {
6962 instances = NULL;
6964 obj1.free();
6966 if (dict->lookup("Name", &obj1)->isString()) {
6967 name = new GooString(obj1.getString());
6968 } else {
6969 name = NULL;
6971 obj1.free();
6973 if (dict->lookup("Subtype", &obj1)->isName()) {
6974 const char *name = obj1.getName();
6976 if (!strcmp(name, "3D")) {
6977 type = type3D;
6978 } else if (!strcmp(name, "Flash")) {
6979 type = typeFlash;
6980 } else if (!strcmp(name, "Sound")) {
6981 type = typeSound;
6982 } else if (!strcmp(name, "Video")) {
6983 type = typeVideo;
6984 } else {
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:
6990 type = type3D;
6991 break;
6992 case AnnotRichMedia::Instance::typeFlash:
6993 type = typeFlash;
6994 break;
6995 case AnnotRichMedia::Instance::typeSound:
6996 type = typeSound;
6997 break;
6998 case AnnotRichMedia::Instance::typeVideo:
6999 type = typeVideo;
7000 break;
7001 default:
7002 type = typeFlash;
7003 break;
7008 obj1.free();
7011 AnnotRichMedia::Configuration::~Configuration()
7013 if (instances) {
7014 for (int i = 0; i < nInstances; ++i)
7015 delete instances[i];
7016 gfree(instances);
7019 delete name;
7022 int AnnotRichMedia::Configuration::getInstancesCount() const {
7023 return nInstances;
7026 AnnotRichMedia::Instance* AnnotRichMedia::Configuration::getInstance(int index) const {
7027 if (index < 0 || index >= nInstances)
7028 return NULL;
7030 return instances[index];
7033 GooString* AnnotRichMedia::Configuration::getName() const {
7034 return name;
7037 AnnotRichMedia::Configuration::Type AnnotRichMedia::Configuration::getType() const {
7038 return type;
7041 AnnotRichMedia::Instance::Instance(Dict *dict)
7043 Object obj1;
7045 if (dict->lookup("Subtype", &obj1)->isName()) {
7046 const char *name = obj1.getName();
7048 if (!strcmp(name, "3D")) {
7049 type = type3D;
7050 } else if (!strcmp(name, "Flash")) {
7051 type = typeFlash;
7052 } else if (!strcmp(name, "Sound")) {
7053 type = typeSound;
7054 } else if (!strcmp(name, "Video")) {
7055 type = typeVideo;
7056 } else {
7057 type = typeFlash;
7060 obj1.free();
7062 if (dict->lookup("Params", &obj1)->isDict()) {
7063 params = new AnnotRichMedia::Params(obj1.getDict());
7064 } else {
7065 params = NULL;
7069 AnnotRichMedia::Instance::~Instance()
7071 delete params;
7074 AnnotRichMedia::Instance::Type AnnotRichMedia::Instance::getType() const {
7075 return type;
7078 AnnotRichMedia::Params* AnnotRichMedia::Instance::getParams() const {
7079 return params;
7082 AnnotRichMedia::Params::Params(Dict *dict)
7084 Object obj1;
7086 if (dict->lookup("FlashVars", &obj1)->isString()) {
7087 flashVars = new GooString(obj1.getString());
7088 } else {
7089 flashVars = NULL;
7091 obj1.free();
7094 AnnotRichMedia::Params::~Params()
7096 delete flashVars;
7099 GooString* AnnotRichMedia::Params::getFlashVars() const {
7100 return flashVars;
7103 //------------------------------------------------------------------------
7104 // Annots
7105 //------------------------------------------------------------------------
7107 Annots::Annots(PDFDoc *docA, int page, Object *annotsObj) {
7108 Annot *annot;
7109 Object obj1;
7110 int i;
7112 doc = docA;
7113 annots = NULL;
7114 size = 0;
7115 nAnnots = 0;
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
7121 //form widget
7122 Object obj2;
7123 if (annotsObj->arrayGet(i, &obj1)->isDict()) {
7124 annotsObj->arrayGetNF(i, &obj2);
7125 annot = createAnnot (obj1.getDict(), &obj2);
7126 if (annot) {
7127 if (annot->isOk()) {
7128 annot->setPage(page, gFalse); // Don't change /P
7129 appendAnnot(annot);
7131 annot->decRefCnt();
7134 obj2.free();
7135 obj1.free();
7140 void Annots::appendAnnot(Annot *annot) {
7141 if (annot && annot->isOk()) {
7142 if (nAnnots >= size) {
7143 size += 16;
7144 annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
7146 annots[nAnnots++] = annot;
7147 annot->incRefCnt();
7151 GBool Annots::removeAnnot(Annot *annot) {
7152 int idx = -1;
7153 // Search annot and determine its index
7154 for (int i = 0; idx == -1 && i < nAnnots; i++) {
7155 if (annots[i] == annot) {
7156 idx = i;
7159 if (idx == -1) {
7160 return gFalse;
7161 } else {
7162 --nAnnots;
7163 memmove( annots + idx, annots + idx + 1, sizeof(annots[0]) * (nAnnots - idx) );
7164 annot->decRefCnt();
7165 return gTrue;
7169 Annot *Annots::createAnnot(Dict* dict, Object *obj) {
7170 Annot *annot = NULL;
7171 Object obj1;
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
7214 if (obj->isRef()) {
7215 Form *form = doc->getCatalog()->getForm();
7216 if (form) {
7217 FormWidget *widget = form->findWidgetByRef(obj->getRef());
7218 if (widget) {
7219 annot = widget->getWidgetAnnotation();
7220 annot->incRefCnt();
7224 if (!annot)
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
7243 Object obj2;
7245 if (dict->lookup("Parent", &obj2)->isNull())
7246 annot = new AnnotPopup(doc, dict, obj);
7247 else
7248 annot = NULL;
7250 obj2.free();
7251 } else {
7252 annot = new Annot(doc, dict, obj);
7255 obj1.free();
7257 return annot;
7260 Annot *Annots::findAnnot(Ref *ref) {
7261 int i;
7263 for (i = 0; i < nAnnots; ++i) {
7264 if (annots[i]->match(ref)) {
7265 return annots[i];
7268 return NULL;
7272 Annots::~Annots() {
7273 int i;
7275 for (i = 0; i < nAnnots; ++i) {
7276 annots[i]->decRefCnt();
7278 gfree(annots);