1 //========================================================================
2 //
3 // Annot.cc
4 //
5 // Copyright 2000-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 //========================================================================
10 //
11 // Modified under the Poppler project - http://poppler.freedesktop.org
12 //
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
15 //
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 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 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 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 //
37 // To see a description of the changes please see the Changelog file that
38 // came with your tarball or type make ChangeLog if you are building from git
39 //
40 //========================================================================
41
42 #include <config.h>
43
44 #ifdef USE_GCC_PRAGMAS
45 #pragma implementation
46 #endif
47
48 #include <stdlib.h>
49 #include <math.h>
50 #include <assert.h>
51 #include "goo/gmem.h"
52 #include "goo/gstrtod.h"
53 #include "GooList.h"
54 #include "Error.h"
55 #include "Object.h"
56 #include "Catalog.h"
57 #include "Gfx.h"
58 #include "Lexer.h"
59 #include "PDFDoc.h"
60 #include "Page.h"
61 #include "Annot.h"
62 #include "GfxFont.h"
63 #include "CharCodeToUnicode.h"
64 #include "PDFDocEncoding.h"
65 #include "Form.h"
66 #include "Error.h"
67 #include "XRef.h"
68 #include "Movie.h"
69 #include "OptionalContent.h"
70 #include "Sound.h"
71 #include "FileSpec.h"
72 #include "DateInfo.h"
73 #include "Link.h"
74 #include <string.h>
75
76 #if MULTITHREADED
77 # define annotLocker() MutexLocker locker(&mutex)
78 # define annotCondLocker(X) MutexLocker locker(&mutex, (X))
79 #else
80 # define annotLocker()
81 # define annotCondLocker(X)
82 #endif
83
84 #define fieldFlagReadOnly 0x00000001
85 #define fieldFlagRequired 0x00000002
86 #define fieldFlagNoExport 0x00000004
87 #define fieldFlagMultiline 0x00001000
88 #define fieldFlagPassword 0x00002000
89 #define fieldFlagNoToggleToOff 0x00004000
90 #define fieldFlagRadio 0x00008000
91 #define fieldFlagPushbutton 0x00010000
92 #define fieldFlagCombo 0x00020000
93 #define fieldFlagEdit 0x00040000
94 #define fieldFlagSort 0x00080000
95 #define fieldFlagFileSelect 0x00100000
96 #define fieldFlagMultiSelect 0x00200000
97 #define fieldFlagDoNotSpellCheck 0x00400000
98 #define fieldFlagDoNotScroll 0x00800000
99 #define fieldFlagComb 0x01000000
100 #define fieldFlagRichText 0x02000000
101 #define fieldFlagRadiosInUnison 0x02000000
102 #define fieldFlagCommitOnSelChange 0x04000000
103
104 #define fieldQuadLeft 0
105 #define fieldQuadCenter 1
106 #define fieldQuadRight 2
107
108 // distance of Bezier control point from center for circle approximation
109 // = (4 * (sqrt(2) - 1) / 3) * r
110 #define bezierCircle 0.55228475
111
parseAnnotLineEndingStyle(GooString * string)112 AnnotLineEndingStyle parseAnnotLineEndingStyle(GooString *string) {
113 if (string != NULL) {
114 if (!string->cmp("Square")) {
115 return annotLineEndingSquare;
116 } else if (!string->cmp("Circle")) {
117 return annotLineEndingCircle;
118 } else if (!string->cmp("Diamond")) {
119 return annotLineEndingDiamond;
120 } else if (!string->cmp("OpenArrow")) {
121 return annotLineEndingOpenArrow;
122 } else if (!string->cmp("ClosedArrow")) {
123 return annotLineEndingClosedArrow;
124 } else if (!string->cmp("Butt")) {
125 return annotLineEndingButt;
126 } else if (!string->cmp("ROpenArrow")) {
127 return annotLineEndingROpenArrow;
128 } else if (!string->cmp("RClosedArrow")) {
129 return annotLineEndingRClosedArrow;
130 } else if (!string->cmp("Slash")) {
131 return annotLineEndingSlash;
132 } else {
133 return annotLineEndingNone;
134 }
135 } else {
136 return annotLineEndingNone;
137 }
138 }
139
convertAnnotLineEndingStyle(AnnotLineEndingStyle style)140 const char* convertAnnotLineEndingStyle(AnnotLineEndingStyle style) {
141 switch (style) {
142 case annotLineEndingSquare:
143 return "Square";
144 case annotLineEndingCircle:
145 return "Circle";
146 case annotLineEndingDiamond:
147 return "Diamond";
148 case annotLineEndingOpenArrow:
149 return "OpenArrow";
150 case annotLineEndingClosedArrow:
151 return "ClosedArrow";
152 case annotLineEndingButt:
153 return "Butt";
154 case annotLineEndingROpenArrow:
155 return "ROpenArrow";
156 case annotLineEndingRClosedArrow:
157 return "RClosedArrow";
158 case annotLineEndingSlash:
159 return "Slash";
160 default:
161 return "None";
162 }
163 }
164
parseAnnotExternalData(Dict * dict)165 static AnnotExternalDataType parseAnnotExternalData(Dict* dict) {
166 Object obj1;
167 AnnotExternalDataType type;
168
169 if (dict->lookup("Subtype", &obj1)->isName()) {
170 const char *typeName = obj1.getName();
171
172 if (!strcmp(typeName, "Markup3D")) {
173 type = annotExternalDataMarkup3D;
174 } else {
175 type = annotExternalDataMarkupUnknown;
176 }
177 } else {
178 type = annotExternalDataMarkupUnknown;
179 }
180 obj1.free();
181
182 return type;
183 }
184
parseDiffRectangle(Array * array,PDFRectangle * rect)185 PDFRectangle *parseDiffRectangle(Array *array, PDFRectangle *rect) {
186 PDFRectangle *newRect = NULL;
187 if (array->getLength() == 4) {
188 // deltas
189 Object obj1;
190 double dx1 = (array->get(0, &obj1)->isNum() ? obj1.getNum() : 0);
191 obj1.free();
192 double dy1 = (array->get(1, &obj1)->isNum() ? obj1.getNum() : 0);
193 obj1.free();
194 double dx2 = (array->get(2, &obj1)->isNum() ? obj1.getNum() : 0);
195 obj1.free();
196 double dy2 = (array->get(3, &obj1)->isNum() ? obj1.getNum() : 0);
197 obj1.free();
198
199 // checking that the numbers are valid (i.e. >= 0),
200 // and that applying the differences still give us a valid rect
201 if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2
202 && (rect->x2 - rect->x1 - dx1 - dx2) >= 0
203 && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
204 newRect = new PDFRectangle();
205 newRect->x1 = rect->x1 + dx1;
206 newRect->y1 = rect->y1 + dy1;
207 newRect->x2 = rect->x2 - dx2;
208 newRect->y2 = rect->y2 - dy2;
209 }
210 }
211 return newRect;
212 }
213
getAdditionalAction(Annot::AdditionalActionsType type,Object * additionalActions,PDFDoc * doc)214 static LinkAction* getAdditionalAction(Annot::AdditionalActionsType type, Object *additionalActions, PDFDoc *doc) {
215 Object additionalActionsObject;
216 LinkAction *linkAction = NULL;
217
218 if (additionalActions->fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
219 const char *key = (type == Annot::actionCursorEntering ? "E" :
220 type == Annot::actionCursorLeaving ? "X" :
221 type == Annot::actionMousePressed ? "D" :
222 type == Annot::actionMouseReleased ? "U" :
223 type == Annot::actionFocusIn ? "Fo" :
224 type == Annot::actionFocusOut ? "BI" :
225 type == Annot::actionPageOpening ? "PO" :
226 type == Annot::actionPageClosing ? "PC" :
227 type == Annot::actionPageVisible ? "PV" :
228 type == Annot::actionPageInvisible ? "PI" : NULL);
229
230 Object actionObject;
231
232 if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
233 linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
234 actionObject.free();
235 }
236
237 additionalActionsObject.free();
238
239 return linkAction;
240 }
241
getFormAdditionalAction(Annot::FormAdditionalActionsType type,Object * additionalActions,PDFDoc * doc)242 static LinkAction* getFormAdditionalAction(Annot::FormAdditionalActionsType type, Object *additionalActions, PDFDoc *doc) {
243 Object additionalActionsObject;
244 LinkAction *linkAction = NULL;
245
246 if (additionalActions->fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
247 const char *key = (type == Annot::actionFieldModified ? "K" :
248 type == Annot::actionFormatField ? "F" :
249 type == Annot::actionValidateField ? "V" :
250 type == Annot::actionCalculateField ? "C" : NULL);
251
252 Object actionObject;
253
254 if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
255 linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
256 actionObject.free();
257 }
258
259 additionalActionsObject.free();
260
261 return linkAction;
262 }
263
264 //------------------------------------------------------------------------
265 // AnnotBorderEffect
266 //------------------------------------------------------------------------
267
AnnotBorderEffect(Dict * dict)268 AnnotBorderEffect::AnnotBorderEffect(Dict *dict) {
269 Object obj1;
270
271 if (dict->lookup("S", &obj1)->isName()) {
272 const char *effectName = obj1.getName();
273
274 if (!strcmp(effectName, "C"))
275 effectType = borderEffectCloudy;
276 else
277 effectType = borderEffectNoEffect;
278 } else {
279 effectType = borderEffectNoEffect;
280 }
281 obj1.free();
282
283 if ((dict->lookup("I", &obj1)->isNum()) && effectType == borderEffectCloudy) {
284 intensity = obj1.getNum();
285 } else {
286 intensity = 0;
287 }
288 obj1.free();
289 }
290
291 //------------------------------------------------------------------------
292 // AnnotPath
293 //------------------------------------------------------------------------
294
AnnotPath()295 AnnotPath::AnnotPath() {
296 coords = NULL;
297 coordsLength = 0;
298 }
299
AnnotPath(Array * array)300 AnnotPath::AnnotPath(Array *array) {
301 coords = NULL;
302 coordsLength = 0;
303 parsePathArray(array);
304 }
305
AnnotPath(AnnotCoord ** coords,int coordsLength)306 AnnotPath::AnnotPath(AnnotCoord **coords, int coordsLength) {
307 this->coords = coords;
308 this->coordsLength = coordsLength;
309 }
310
~AnnotPath()311 AnnotPath::~AnnotPath() {
312 if (coords) {
313 for (int i = 0; i < coordsLength; ++i)
314 delete coords[i];
315 gfree(coords);
316 }
317 }
318
getX(int coord) const319 double AnnotPath::getX(int coord) const {
320 if (coord >= 0 && coord < coordsLength)
321 return coords[coord]->getX();
322 return 0;
323 }
324
getY(int coord) const325 double AnnotPath::getY(int coord) const {
326 if (coord >= 0 && coord < coordsLength)
327 return coords[coord]->getY();
328 return 0;
329 }
330
getCoord(int coord) const331 AnnotCoord *AnnotPath::getCoord(int coord) const {
332 if (coord >= 0 && coord < coordsLength)
333 return coords[coord];
334 return NULL;
335 }
336
parsePathArray(Array * array)337 void AnnotPath::parsePathArray(Array *array) {
338 int tempLength;
339 AnnotCoord **tempCoords;
340 GBool correct = gTrue;
341
342 if (array->getLength() % 2) {
343 error(errSyntaxError, -1, "Bad Annot Path");
344 return;
345 }
346
347 tempLength = array->getLength() / 2;
348 tempCoords = (AnnotCoord **) gmallocn (tempLength, sizeof(AnnotCoord *));
349 memset(tempCoords, 0, tempLength * sizeof(AnnotCoord *));
350 for (int i = 0; i < tempLength && correct; i++) {
351 Object obj1;
352 double x = 0, y = 0;
353
354 if (array->get(i * 2, &obj1)->isNum()) {
355 x = obj1.getNum();
356 } else {
357 correct = gFalse;
358 }
359 obj1.free();
360
361 if (array->get((i * 2) + 1, &obj1)->isNum()) {
362 y = obj1.getNum();
363 } else {
364 correct = gFalse;
365 }
366 obj1.free();
367
368 if (!correct) {
369 for (int j = i - 1; j >= 0; j--)
370 delete tempCoords[j];
371 gfree (tempCoords);
372 return;
373 }
374
375 tempCoords[i] = new AnnotCoord(x, y);
376 }
377
378 coords = tempCoords;
379 coordsLength = tempLength;
380 }
381
382 //------------------------------------------------------------------------
383 // AnnotCalloutLine
384 //------------------------------------------------------------------------
385
AnnotCalloutLine(double x1,double y1,double x2,double y2)386 AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2)
387 : coord1(x1, y1), coord2(x2, y2) {
388 }
389
390 //------------------------------------------------------------------------
391 // AnnotCalloutMultiLine
392 //------------------------------------------------------------------------
393
AnnotCalloutMultiLine(double x1,double y1,double x2,double y2,double x3,double y3)394 AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2,
395 double y2, double x3, double y3)
396 : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) {
397 }
398
399 //------------------------------------------------------------------------
400 // AnnotQuadrilateral
401 //------------------------------------------------------------------------
402
AnnotQuadrilaterals(Array * array,PDFRectangle * rect)403 AnnotQuadrilaterals::AnnotQuadrilaterals(Array *array, PDFRectangle *rect) {
404 int arrayLength = array->getLength();
405 GBool correct = gTrue;
406 int quadsLength = 0;
407 AnnotQuadrilateral **quads;
408 double quadArray[8];
409
410 // default values
411 quadrilaterals = NULL;
412 quadrilateralsLength = 0;
413
414 if ((arrayLength % 8) == 0) {
415 int i;
416
417 quadsLength = arrayLength / 8;
418 quads = (AnnotQuadrilateral **) gmallocn
419 ((quadsLength), sizeof(AnnotQuadrilateral *));
420 memset(quads, 0, quadsLength * sizeof(AnnotQuadrilateral *));
421
422 for (i = 0; i < quadsLength; i++) {
423 for (int j = 0; j < 8; j++) {
424 Object obj;
425 if (array->get(i * 8 + j, &obj)->isNum()) {
426 quadArray[j] = obj.getNum();
427 } else {
428 correct = gFalse;
429 obj.free();
430 error (errSyntaxError, -1, "Invalid QuadPoint in annot");
431 break;
432 }
433 obj.free();
434 }
435
436 if (!correct)
437 break;
438
439 quads[i] = new AnnotQuadrilateral(quadArray[0], quadArray[1],
440 quadArray[2], quadArray[3],
441 quadArray[4], quadArray[5],
442 quadArray[6], quadArray[7]);
443 }
444
445 if (correct) {
446 quadrilateralsLength = quadsLength;
447 quadrilaterals = quads;
448 } else {
449 for (int j = 0; j < i; j++)
450 delete quads[j];
451 gfree (quads);
452 }
453 }
454 }
455
AnnotQuadrilaterals(AnnotQuadrilaterals::AnnotQuadrilateral ** quads,int quadsLength)456 AnnotQuadrilaterals::AnnotQuadrilaterals(AnnotQuadrilaterals::AnnotQuadrilateral **quads, int quadsLength) {
457 quadrilaterals = quads;
458 quadrilateralsLength = quadsLength;
459 }
460
~AnnotQuadrilaterals()461 AnnotQuadrilaterals::~AnnotQuadrilaterals() {
462 if (quadrilaterals) {
463 for(int i = 0; i < quadrilateralsLength; i++)
464 delete quadrilaterals[i];
465
466 gfree (quadrilaterals);
467 }
468 }
469
getX1(int quadrilateral)470 double AnnotQuadrilaterals::getX1(int quadrilateral) {
471 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
472 return quadrilaterals[quadrilateral]->coord1.getX();
473 return 0;
474 }
475
getY1(int quadrilateral)476 double AnnotQuadrilaterals::getY1(int quadrilateral) {
477 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
478 return quadrilaterals[quadrilateral]->coord1.getY();
479 return 0;
480 }
481
getX2(int quadrilateral)482 double AnnotQuadrilaterals::getX2(int quadrilateral) {
483 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
484 return quadrilaterals[quadrilateral]->coord2.getX();
485 return 0;
486 }
487
getY2(int quadrilateral)488 double AnnotQuadrilaterals::getY2(int quadrilateral) {
489 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
490 return quadrilaterals[quadrilateral]->coord2.getY();
491 return 0;
492 }
493
getX3(int quadrilateral)494 double AnnotQuadrilaterals::getX3(int quadrilateral) {
495 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
496 return quadrilaterals[quadrilateral]->coord3.getX();
497 return 0;
498 }
499
getY3(int quadrilateral)500 double AnnotQuadrilaterals::getY3(int quadrilateral) {
501 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
502 return quadrilaterals[quadrilateral]->coord3.getY();
503 return 0;
504 }
505
getX4(int quadrilateral)506 double AnnotQuadrilaterals::getX4(int quadrilateral) {
507 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
508 return quadrilaterals[quadrilateral]->coord4.getX();
509 return 0;
510 }
511
getY4(int quadrilateral)512 double AnnotQuadrilaterals::getY4(int quadrilateral) {
513 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
514 return quadrilaterals[quadrilateral]->coord4.getY();
515 return 0;
516 }
517
AnnotQuadrilateral(double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4)518 AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1,
519 double x2, double y2, double x3, double y3, double x4, double y4)
520 : coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) {
521 }
522
523 //------------------------------------------------------------------------
524 // AnnotBorder
525 //------------------------------------------------------------------------
AnnotBorder()526 AnnotBorder::AnnotBorder() {
527 width = 1;
528 dashLength = 0;
529 dash = NULL;
530 style = borderSolid;
531 }
532
parseDashArray(Object * dashObj)533 GBool AnnotBorder::parseDashArray(Object *dashObj) {
534 GBool correct = gTrue;
535 int tempLength = dashObj->arrayGetLength();
536 double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
537
538 // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
539 for (int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
540 Object obj1;
541
542 if (dashObj->arrayGet(i, &obj1)->isNum()) {
543 tempDash[i] = obj1.getNum();
544
545 correct = tempDash[i] >= 0;
546 obj1.free();
547 }
548 }
549
550 if (correct) {
551 dashLength = tempLength;
552 dash = tempDash;
553 style = borderDashed;
554 } else {
555 gfree (tempDash);
556 }
557
558 return correct;
559 }
560
~AnnotBorder()561 AnnotBorder::~AnnotBorder() {
562 if (dash)
563 gfree (dash);
564 }
565
566 //------------------------------------------------------------------------
567 // AnnotBorderArray
568 //------------------------------------------------------------------------
569
AnnotBorderArray()570 AnnotBorderArray::AnnotBorderArray() {
571 horizontalCorner = 0;
572 verticalCorner = 0;
573 }
574
AnnotBorderArray(Array * array)575 AnnotBorderArray::AnnotBorderArray(Array *array) {
576 Object obj1;
577 int arrayLength = array->getLength();
578
579 GBool correct = gTrue;
580 if (arrayLength == 3 || arrayLength == 4) {
581 // implementation note 81 in Appendix H.
582
583 if (array->get(0, &obj1)->isNum())
584 horizontalCorner = obj1.getNum();
585 else
586 correct = gFalse;
587 obj1.free();
588
589 if (array->get(1, &obj1)->isNum())
590 verticalCorner = obj1.getNum();
591 else
592 correct = gFalse;
593 obj1.free();
594
595 if (array->get(2, &obj1)->isNum())
596 width = obj1.getNum();
597 else
598 correct = gFalse;
599 obj1.free();
600
601 if (arrayLength == 4) {
602 if (array->get(3, &obj1)->isArray())
603 correct = parseDashArray(&obj1);
604 else
605 correct = gFalse;
606 obj1.free();
607 }
608 } else {
609 correct = gFalse;
610 }
611
612 if (!correct) {
613 width = 0;
614 }
615 }
616
writeToObject(XRef * xref,Object * obj1) const617 void AnnotBorderArray::writeToObject(XRef *xref, Object *obj1) const {
618 Object obj2;
619
620 obj1->initArray(xref);
621 obj1->arrayAdd(obj2.initReal(horizontalCorner));
622 obj1->arrayAdd(obj2.initReal(verticalCorner));
623 obj1->arrayAdd(obj2.initReal(width));
624
625 if (dashLength > 0) {
626 Object obj3;
627
628 obj1->arrayAdd(obj3.initArray(xref));
629 for (int i = 0; i < dashLength; i++)
630 obj3.arrayAdd(obj2.initReal(dash[i]));
631 }
632 }
633
634 //------------------------------------------------------------------------
635 // AnnotBorderBS
636 //------------------------------------------------------------------------
637
AnnotBorderBS()638 AnnotBorderBS::AnnotBorderBS() {
639 }
640
AnnotBorderBS(Dict * dict)641 AnnotBorderBS::AnnotBorderBS(Dict *dict) {
642 Object obj1, obj2;
643
644 // acroread 8 seems to need both W and S entries for
645 // any border to be drawn, even though the spec
646 // doesn't claim anything of that sort. We follow
647 // that behaviour by veryifying both entries exist
648 // otherwise we set the borderWidth to 0
649 // --jrmuizel
650 dict->lookup("W", &obj1);
651 dict->lookup("S", &obj2);
652 if (obj1.isNum() && obj2.isName()) {
653 const char *styleName = obj2.getName();
654
655 width = obj1.getNum();
656
657 if (!strcmp(styleName, "S")) {
658 style = borderSolid;
659 } else if (!strcmp(styleName, "D")) {
660 style = borderDashed;
661 } else if (!strcmp(styleName, "B")) {
662 style = borderBeveled;
663 } else if (!strcmp(styleName, "I")) {
664 style = borderInset;
665 } else if (!strcmp(styleName, "U")) {
666 style = borderUnderlined;
667 } else {
668 style = borderSolid;
669 }
670 } else {
671 width = 0;
672 }
673 obj2.free();
674 obj1.free();
675
676 if (style == borderDashed) {
677 if (dict->lookup("D", &obj1)->isArray())
678 parseDashArray(&obj1);
679 obj1.free();
680
681 if (!dash) {
682 dashLength = 1;
683 dash = (double *) gmallocn (dashLength, sizeof (double));
684 dash[0] = 3;
685 }
686 }
687 }
688
getStyleName() const689 const char *AnnotBorderBS::getStyleName() const {
690 switch (style) {
691 case borderSolid:
692 return "S";
693 case borderDashed:
694 return "D";
695 case borderBeveled:
696 return "B";
697 case borderInset:
698 return "I";
699 case borderUnderlined:
700 return "U";
701 }
702
703 return "S";
704 }
705
writeToObject(XRef * xref,Object * obj1) const706 void AnnotBorderBS::writeToObject(XRef *xref, Object *obj1) const {
707 Object obj2;
708
709 obj1->initDict(xref);
710 obj1->dictSet("W", obj2.initReal(width));
711 obj1->dictSet("S", obj2.initName(getStyleName()));
712 if (style == borderDashed && dashLength > 0) {
713 Object obj3;
714
715 obj1->dictSet("D", obj3.initArray(xref));
716 for (int i = 0; i < dashLength; i++)
717 obj3.arrayAdd(obj2.initReal(dash[i]));
718 }
719 }
720
721 //------------------------------------------------------------------------
722 // AnnotColor
723 //------------------------------------------------------------------------
724
AnnotColor()725 AnnotColor::AnnotColor() {
726 length = 0;
727 }
728
AnnotColor(double gray)729 AnnotColor::AnnotColor(double gray) {
730 length = 1;
731
732 values[0] = gray;
733 }
734
AnnotColor(double r,double g,double b)735 AnnotColor::AnnotColor(double r, double g, double b) {
736 length = 3;
737
738 values[0] = r;
739 values[1] = g;
740 values[2] = b;
741 }
742
AnnotColor(double c,double m,double y,double k)743 AnnotColor::AnnotColor(double c, double m, double y, double k) {
744 length = 4;
745
746 values[0] = c;
747 values[1] = m;
748 values[2] = y;
749 values[3] = k;
750 }
751
752 // If <adjust> is +1, color is brightened;
753 // if <adjust> is -1, color is darkened;
754 // otherwise color is not modified.
AnnotColor(Array * array,int adjust)755 AnnotColor::AnnotColor(Array *array, int adjust) {
756 int i;
757
758 length = array->getLength();
759 if (length > 4)
760 length = 4;
761
762 for (i = 0; i < length; i++) {
763 Object obj1;
764
765 if (array->get(i, &obj1)->isNum()) {
766 values[i] = obj1.getNum();
767
768 if (values[i] < 0 || values[i] > 1)
769 values[i] = 0;
770 } else {
771 values[i] = 0;
772 }
773 obj1.free();
774 }
775
776 if (adjust != 0)
777 adjustColor(adjust);
778 }
779
adjustColor(int adjust)780 void AnnotColor::adjustColor(int adjust) {
781 int i;
782
783 if (length == 4) {
784 adjust = -adjust;
785 }
786 if (adjust > 0) {
787 for (i = 0; i < length; ++i) {
788 values[i] = 0.5 * values[i] + 0.5;
789 }
790 } else if (adjust < 0) {
791 for (i = 0; i < length; ++i) {
792 values[i] = 0.5 * values[i];
793 }
794 }
795 }
796
writeToObject(XRef * xref,Object * obj1) const797 void AnnotColor::writeToObject(XRef *xref, Object *obj1) const {
798 Object obj2;
799 int i;
800
801 if (length == 0) {
802 obj1->initNull(); // Transparent (no color)
803 } else {
804 obj1->initArray(xref);
805 for (i = 0; i < length; ++i)
806 obj1->arrayAdd( obj2.initReal( values[i] ) );
807 }
808 }
809
810 //------------------------------------------------------------------------
811 // AnnotIconFit
812 //------------------------------------------------------------------------
813
AnnotIconFit(Dict * dict)814 AnnotIconFit::AnnotIconFit(Dict* dict) {
815 Object obj1;
816
817 if (dict->lookup("SW", &obj1)->isName()) {
818 const char *scaleName = obj1.getName();
819
820 if(!strcmp(scaleName, "B")) {
821 scaleWhen = scaleBigger;
822 } else if(!strcmp(scaleName, "S")) {
823 scaleWhen = scaleSmaller;
824 } else if(!strcmp(scaleName, "N")) {
825 scaleWhen = scaleNever;
826 } else {
827 scaleWhen = scaleAlways;
828 }
829 } else {
830 scaleWhen = scaleAlways;
831 }
832 obj1.free();
833
834 if (dict->lookup("S", &obj1)->isName()) {
835 const char *scaleName = obj1.getName();
836
837 if(!strcmp(scaleName, "A")) {
838 scale = scaleAnamorphic;
839 } else {
840 scale = scaleProportional;
841 }
842 } else {
843 scale = scaleProportional;
844 }
845 obj1.free();
846
847 if (dict->lookup("A", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
848 Object obj2;
849 (obj1.arrayGet(0, &obj2)->isNum() ? left = obj2.getNum() : left = 0);
850 obj2.free();
851 (obj1.arrayGet(1, &obj2)->isNum() ? bottom = obj2.getNum() : bottom = 0);
852 obj2.free();
853
854 if (left < 0 || left > 1)
855 left = 0.5;
856
857 if (bottom < 0 || bottom > 1)
858 bottom = 0.5;
859
860 } else {
861 left = bottom = 0.5;
862 }
863 obj1.free();
864
865 if (dict->lookup("FB", &obj1)->isBool()) {
866 fullyBounds = obj1.getBool();
867 } else {
868 fullyBounds = gFalse;
869 }
870 obj1.free();
871 }
872
873 //------------------------------------------------------------------------
874 // AnnotAppearance
875 //------------------------------------------------------------------------
876
AnnotAppearance(PDFDoc * docA,Object * dict)877 AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict) {
878 assert(dict->isDict());
879 doc = docA;
880 xref = docA->getXRef();
881 dict->copy(&appearDict);
882 }
883
~AnnotAppearance()884 AnnotAppearance::~AnnotAppearance() {
885 appearDict.free();
886 }
887
getAppearanceStream(AnnotAppearanceType type,const char * state,Object * dest)888 void AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, const char *state, Object *dest) {
889 Object apData, stream;
890 apData.initNull();
891
892 // Obtain dictionary or stream associated to appearance type
893 switch (type) {
894 case appearRollover:
895 if (appearDict.dictLookupNF("R", &apData)->isNull())
896 appearDict.dictLookupNF("N", &apData);
897 break;
898 case appearDown:
899 if (appearDict.dictLookupNF("D", &apData)->isNull())
900 appearDict.dictLookupNF("N", &apData);
901 break;
902 case appearNormal:
903 appearDict.dictLookupNF("N", &apData);
904 break;
905 }
906
907 dest->initNull();
908 if (apData.isDict() && state)
909 apData.dictLookupNF(state, dest);
910 else if (apData.isRef())
911 apData.copy(dest);
912 apData.free();
913 }
914
getStateKey(int i)915 GooString * AnnotAppearance::getStateKey(int i) {
916 Object obj1;
917 GooString * res = NULL;
918 if (appearDict.dictLookupNF("N", &obj1)->isDict())
919 res = new GooString(obj1.dictGetKey(i));
920 obj1.free();
921 return res;
922 }
923
getNumStates()924 int AnnotAppearance::getNumStates() {
925 Object obj1;
926 int res = 0;
927 if (appearDict.dictLookupNF("N", &obj1)->isDict())
928 res = obj1.dictGetLength();
929 obj1.free();
930 return res;
931 }
932
933 // Test if stateObj (a Ref or a Dict) points to the specified stream
referencesStream(Object * stateObj,Ref refToStream)934 GBool AnnotAppearance::referencesStream(Object *stateObj, Ref refToStream) {
935 if (stateObj->isRef()) {
936 Ref r = stateObj->getRef();
937 if (r.num == refToStream.num && r.gen == refToStream.gen) {
938 return gTrue;
939 }
940 } else if (stateObj->isDict()) { // Test each value
941 const int size = stateObj->dictGetLength();
942 for (int i = 0; i < size; ++i) {
943 Object obj1;
944 stateObj->dictGetValNF(i, &obj1);
945 if (obj1.isRef()) {
946 Ref r = obj1.getRef();
947 if (r.num == refToStream.num && r.gen == refToStream.gen) {
948 return gTrue;
949 }
950 }
951 obj1.free();
952 }
953 }
954 return gFalse; // Not found
955 }
956
957 // Test if this AnnotAppearance references the specified stream
referencesStream(Ref refToStream)958 GBool AnnotAppearance::referencesStream(Ref refToStream) {
959 Object obj1;
960 GBool found;
961
962 // Scan each state's ref/subdictionary
963 appearDict.dictLookupNF("N", &obj1);
964 found = referencesStream(&obj1, refToStream);
965 obj1.free();
966 if (found)
967 return gTrue;
968
969 appearDict.dictLookupNF("R", &obj1);
970 found = referencesStream(&obj1, refToStream);
971 obj1.free();
972 if (found)
973 return gTrue;
974
975 appearDict.dictLookupNF("D", &obj1);
976 found = referencesStream(&obj1, refToStream);
977 obj1.free();
978 return found;
979 }
980
981 // If this is the only annotation in the document that references the
982 // specified appearance stream, remove the appearance stream
removeStream(Ref refToStream)983 void AnnotAppearance::removeStream(Ref refToStream) {
984 const int lastpage = doc->getNumPages();
985 for (int pg = 1; pg <= lastpage; ++pg) { // Scan all annotations in the document
986 Page *page = doc->getPage(pg);
987 if (!page) {
988 error(errSyntaxError, -1, "Failed check for shared annotation stream at page {0:d}", pg);
989 continue;
990 }
991 Annots *annots = page->getAnnots();
992 for (int i = 0; i < annots->getNumAnnots(); ++i) {
993 AnnotAppearance *annotAp = annots->getAnnot(i)->getAppearStreams();
994 if (annotAp && annotAp != this && annotAp->referencesStream(refToStream)) {
995 return; // Another annotation points to the stream -> Don't delete it
996 }
997 }
998 }
999
1000 // TODO: stream resources (e.g. font), AP name tree
1001 xref->removeIndirectObject(refToStream);
1002 }
1003
1004 // Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict
removeStateStreams(Object * obj1)1005 void AnnotAppearance::removeStateStreams(Object *obj1) {
1006 if (obj1->isRef()) {
1007 removeStream(obj1->getRef());
1008 } else if (obj1->isDict()) {
1009 const int size = obj1->dictGetLength();
1010 for (int i = 0; i < size; ++i) {
1011 Object obj2;
1012 obj1->dictGetValNF(i, &obj2);
1013 if (obj2.isRef()) {
1014 removeStream(obj2.getRef());
1015 }
1016 obj2.free();
1017 }
1018 }
1019 }
1020
removeAllStreams()1021 void AnnotAppearance::removeAllStreams() {
1022 Object obj1;
1023 appearDict.dictLookupNF("N", &obj1);
1024 removeStateStreams(&obj1);
1025 obj1.free();
1026 appearDict.dictLookupNF("R", &obj1);
1027 removeStateStreams(&obj1);
1028 obj1.free();
1029 appearDict.dictLookupNF("D", &obj1);
1030 removeStateStreams(&obj1);
1031 obj1.free();
1032 }
1033
1034 //------------------------------------------------------------------------
1035 // AnnotAppearanceCharacs
1036 //------------------------------------------------------------------------
1037
AnnotAppearanceCharacs(Dict * dict)1038 AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) {
1039 Object obj1;
1040
1041 if (dict->lookup("R", &obj1)->isInt()) {
1042 rotation = obj1.getInt();
1043 } else {
1044 rotation = 0;
1045 }
1046 obj1.free();
1047
1048 if (dict->lookup("BC", &obj1)->isArray()) {
1049 Array *colorComponents = obj1.getArray();
1050 if (colorComponents->getLength() > 0) {
1051 borderColor = new AnnotColor(colorComponents);
1052 } else {
1053 borderColor = NULL;
1054 }
1055 } else {
1056 borderColor = NULL;
1057 }
1058 obj1.free();
1059
1060 if (dict->lookup("BG", &obj1)->isArray()) {
1061 Array *colorComponents = obj1.getArray();
1062 if (colorComponents->getLength() > 0) {
1063 backColor = new AnnotColor(colorComponents);
1064 } else {
1065 backColor = NULL;
1066 }
1067 } else {
1068 backColor = NULL;
1069 }
1070 obj1.free();
1071
1072 if (dict->lookup("CA", &obj1)->isString()) {
1073 normalCaption = new GooString(obj1.getString());
1074 } else {
1075 normalCaption = NULL;
1076 }
1077 obj1.free();
1078
1079 if (dict->lookup("RC", &obj1)->isString()) {
1080 rolloverCaption = new GooString(obj1.getString());
1081 } else {
1082 rolloverCaption = NULL;
1083 }
1084 obj1.free();
1085
1086 if (dict->lookup("AC", &obj1)->isString()) {
1087 alternateCaption = new GooString(obj1.getString());
1088 } else {
1089 alternateCaption = NULL;
1090 }
1091 obj1.free();
1092
1093 if (dict->lookup("IF", &obj1)->isDict()) {
1094 iconFit = new AnnotIconFit(obj1.getDict());
1095 } else {
1096 iconFit = NULL;
1097 }
1098 obj1.free();
1099
1100 if (dict->lookup("TP", &obj1)->isInt()) {
1101 position = (AnnotAppearanceCharacsTextPos) obj1.getInt();
1102 } else {
1103 position = captionNoIcon;
1104 }
1105 obj1.free();
1106 }
1107
~AnnotAppearanceCharacs()1108 AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
1109 if (borderColor)
1110 delete borderColor;
1111
1112 if (backColor)
1113 delete backColor;
1114
1115 if (normalCaption)
1116 delete normalCaption;
1117
1118 if (rolloverCaption)
1119 delete rolloverCaption;
1120
1121 if (alternateCaption)
1122 delete alternateCaption;
1123
1124 if (iconFit)
1125 delete iconFit;
1126 }
1127
1128 //------------------------------------------------------------------------
1129 // AnnotAppearanceBBox
1130 //------------------------------------------------------------------------
1131
AnnotAppearanceBBox(PDFRectangle * rect)1132 AnnotAppearanceBBox::AnnotAppearanceBBox(PDFRectangle *rect) {
1133 origX = rect->x1;
1134 origY = rect->y1;
1135 borderWidth = 0;
1136
1137 // Initially set the same size as rect
1138 minX = 0;
1139 minY = 0;
1140 maxX = rect->x2 - rect->x1;
1141 maxY = rect->y2 - rect->y1;
1142 }
1143
extendTo(double x,double y)1144 void AnnotAppearanceBBox::extendTo(double x, double y) {
1145 if (x < minX) {
1146 minX = x;
1147 } else if (x > maxX) {
1148 maxX = x;
1149 }
1150 if (y < minY) {
1151 minY = y;
1152 } else if (y > maxY) {
1153 maxY = y;
1154 }
1155 }
1156
getBBoxRect(double bbox[4]) const1157 void AnnotAppearanceBBox::getBBoxRect(double bbox[4]) const {
1158 bbox[0] = minX - borderWidth;
1159 bbox[1] = minY - borderWidth;
1160 bbox[2] = maxX + borderWidth;
1161 bbox[3] = maxY + borderWidth;
1162 }
1163
getPageXMin() const1164 double AnnotAppearanceBBox::getPageXMin() const {
1165 return origX + minX - borderWidth;
1166 }
1167
getPageYMin() const1168 double AnnotAppearanceBBox::getPageYMin() const {
1169 return origY + minY - borderWidth;
1170 }
1171
getPageXMax() const1172 double AnnotAppearanceBBox::getPageXMax() const {
1173 return origX + maxX + borderWidth;
1174 }
1175
getPageYMax() const1176 double AnnotAppearanceBBox::getPageYMax() const {
1177 return origY + maxY + borderWidth;
1178 }
1179
1180 //------------------------------------------------------------------------
1181 // Annot
1182 //------------------------------------------------------------------------
1183
Annot(PDFDoc * docA,PDFRectangle * rectA)1184 Annot::Annot(PDFDoc *docA, PDFRectangle *rectA) {
1185 Object obj1;
1186
1187 refCnt = 1;
1188 flags = flagUnknown;
1189 type = typeUnknown;
1190
1191 obj1.initArray (docA->getXRef());
1192 Object obj2;
1193 obj1.arrayAdd (obj2.initReal (rectA->x1));
1194 obj1.arrayAdd (obj2.initReal (rectA->y1));
1195 obj1.arrayAdd (obj2.initReal (rectA->x2));
1196 obj1.arrayAdd (obj2.initReal (rectA->y2));
1197 obj2.free ();
1198
1199 annotObj.initDict (docA->getXRef());
1200 annotObj.dictSet ("Type", obj2.initName ("Annot"));
1201 annotObj.dictSet ("Rect", &obj1);
1202 // obj1 is owned by the dict
1203
1204 ref = docA->getXRef()->addIndirectObject (&annotObj);
1205
1206 initialize (docA, annotObj.getDict());
1207 }
1208
Annot(PDFDoc * docA,Dict * dict)1209 Annot::Annot(PDFDoc *docA, Dict *dict) {
1210 refCnt = 1;
1211 hasRef = false;
1212 flags = flagUnknown;
1213 type = typeUnknown;
1214 annotObj.initDict (dict);
1215 initialize (docA, dict);
1216 }
1217
Annot(PDFDoc * docA,Dict * dict,Object * obj)1218 Annot::Annot(PDFDoc *docA, Dict *dict, Object *obj) {
1219 refCnt = 1;
1220 if (obj->isRef()) {
1221 hasRef = gTrue;
1222 ref = obj->getRef();
1223 } else {
1224 hasRef = gFalse;
1225 }
1226 flags = flagUnknown;
1227 type = typeUnknown;
1228 annotObj.initDict (dict);
1229 initialize (docA, dict);
1230 }
1231
initialize(PDFDoc * docA,Dict * dict)1232 void Annot::initialize(PDFDoc *docA, Dict *dict) {
1233 Object apObj, asObj, obj1, obj2;
1234
1235 ok = gTrue;
1236 doc = docA;
1237 xref = doc->getXRef();
1238 appearStreams = NULL;
1239 appearBBox = NULL;
1240 appearState = NULL;
1241 appearBuf = NULL;
1242 fontSize = 0;
1243
1244 appearance.initNull();
1245
1246 //----- parse the rectangle
1247 rect = new PDFRectangle();
1248 if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
1249 Object obj2;
1250 (obj1.arrayGet(0, &obj2)->isNum() ? rect->x1 = obj2.getNum() : rect->x1 = 0);
1251 obj2.free();
1252 (obj1.arrayGet(1, &obj2)->isNum() ? rect->y1 = obj2.getNum() : rect->y1 = 0);
1253 obj2.free();
1254 (obj1.arrayGet(2, &obj2)->isNum() ? rect->x2 = obj2.getNum() : rect->x2 = 1);
1255 obj2.free();
1256 (obj1.arrayGet(3, &obj2)->isNum() ? rect->y2 = obj2.getNum() : rect->y2 = 1);
1257 obj2.free();
1258
1259 if (rect->x1 > rect->x2) {
1260 double t = rect->x1;
1261 rect->x1 = rect->x2;
1262 rect->x2 = t;
1263 }
1264
1265 if (rect->y1 > rect->y2) {
1266 double t = rect->y1;
1267 rect->y1 = rect->y2;
1268 rect->y2 = t;
1269 }
1270 } else {
1271 rect->x1 = rect->y1 = 0;
1272 rect->x2 = rect->y2 = 1;
1273 error(errSyntaxError, -1, "Bad bounding box for annotation");
1274 ok = gFalse;
1275 }
1276 obj1.free();
1277
1278 if (dict->lookup("Contents", &obj1)->isString()) {
1279 contents = obj1.getString()->copy();
1280 } else {
1281 contents = new GooString();
1282 }
1283 obj1.free();
1284
1285 // Note: This value is overwritten by Annots ctor
1286 if (dict->lookupNF("P", &obj1)->isRef()) {
1287 Ref ref = obj1.getRef();
1288
1289 page = doc->getCatalog()->findPage (ref.num, ref.gen);
1290 } else {
1291 page = 0;
1292 }
1293 obj1.free();
1294
1295 if (dict->lookup("NM", &obj1)->isString()) {
1296 name = obj1.getString()->copy();
1297 } else {
1298 name = NULL;
1299 }
1300 obj1.free();
1301
1302 if (dict->lookup("M", &obj1)->isString()) {
1303 modified = obj1.getString()->copy();
1304 } else {
1305 modified = NULL;
1306 }
1307 obj1.free();
1308
1309 //----- get the flags
1310 if (dict->lookup("F", &obj1)->isInt()) {
1311 flags |= obj1.getInt();
1312 } else {
1313 flags = flagUnknown;
1314 }
1315 obj1.free();
1316
1317 //----- get the annotation appearance dictionary
1318 dict->lookup("AP", &apObj);
1319 if (apObj.isDict()) {
1320 appearStreams = new AnnotAppearance(doc, &apObj);
1321 }
1322 apObj.free();
1323
1324 //----- get the appearance state
1325 dict->lookup("AS", &asObj);
1326 if (asObj.isName()) {
1327 appearState = new GooString(asObj.getName());
1328 } else if (appearStreams && appearStreams->getNumStates() != 0) {
1329 error (errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries");
1330 // AS value is required in this case, but if the
1331 // N dictionary contains only one entry
1332 // take it as default appearance.
1333 if (appearStreams->getNumStates() == 1) {
1334 appearState = appearStreams->getStateKey(0);
1335 }
1336 }
1337 if (!appearState) {
1338 appearState = new GooString("Off");
1339 }
1340 asObj.free();
1341
1342 //----- get the annotation appearance
1343 if (appearStreams) {
1344 appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->getCString(), &appearance);
1345 }
1346
1347 //----- parse the border style
1348 // According to the spec if neither the Border nor the BS entry is present,
1349 // the border shall be drawn as a solid line with a width of 1 point. But acroread
1350 // seems to ignore the Border entry for annots that can't have a BS entry. So, we only
1351 // follow this rule for annots tha can have a BS entry.
1352 if (dict->lookup("Border", &obj1)->isArray())
1353 border = new AnnotBorderArray(obj1.getArray());
1354 else
1355 border = NULL;
1356 obj1.free();
1357
1358 if (dict->lookup("C", &obj1)->isArray()) {
1359 color = new AnnotColor(obj1.getArray());
1360 } else {
1361 color = NULL;
1362 }
1363 obj1.free();
1364
1365 if (dict->lookup("StructParent", &obj1)->isInt()) {
1366 treeKey = obj1.getInt();
1367 } else {
1368 treeKey = 0;
1369 }
1370 obj1.free();
1371
1372 dict->lookupNF("OC", &oc);
1373
1374 #if MULTITHREADED
1375 gInitMutex(&mutex);
1376 #endif
1377 }
1378
getRect(double * x1,double * y1,double * x2,double * y2) const1379 void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const {
1380 *x1 = rect->x1;
1381 *y1 = rect->y1;
1382 *x2 = rect->x2;
1383 *y2 = rect->y2;
1384 }
1385
setRect(PDFRectangle * rect)1386 void Annot::setRect(PDFRectangle *rect) {
1387 setRect(rect->x1, rect->y1, rect->x2, rect->y2);
1388 }
1389
setRect(double x1,double y1,double x2,double y2)1390 void Annot::setRect(double x1, double y1, double x2, double y2) {
1391 Object obj1, obj2;
1392
1393 if (x1 < x2) {
1394 rect->x1 = x1;
1395 rect->x2 = x2;
1396 } else {
1397 rect->x1 = x2;
1398 rect->x2 = x1;
1399 }
1400
1401 if (y1 < y2) {
1402 rect->y1 = y1;
1403 rect->y2 = y2;
1404 } else {
1405 rect->y1 = y2;
1406 rect->y2 = y1;
1407 }
1408
1409 obj1.initArray (xref);
1410 obj1.arrayAdd (obj2.initReal (rect->x1));
1411 obj1.arrayAdd (obj2.initReal (rect->y1));
1412 obj1.arrayAdd (obj2.initReal (rect->x2));
1413 obj1.arrayAdd (obj2.initReal (rect->y2));
1414
1415 update("Rect", &obj1);
1416 invalidateAppearance();
1417 }
1418
inRect(double x,double y) const1419 GBool Annot::inRect(double x, double y) const {
1420 return rect->contains(x, y);
1421 }
1422
update(const char * key,Object * value)1423 void Annot::update(const char *key, Object *value) {
1424 annotLocker();
1425 /* Set M to current time, unless we are updating M itself */
1426 if (strcmp(key, "M") != 0) {
1427 delete modified;
1428 modified = timeToDateString(NULL);
1429
1430 Object obj1;
1431 obj1.initString (modified->copy());
1432 annotObj.dictSet("M", &obj1);
1433 }
1434
1435 annotObj.dictSet(const_cast<char*>(key), value);
1436
1437 xref->setModifiedObject(&annotObj, ref);
1438 }
1439
setContents(GooString * new_content)1440 void Annot::setContents(GooString *new_content) {
1441 annotLocker();
1442 delete contents;
1443
1444 if (new_content) {
1445 contents = new GooString(new_content);
1446 //append the unicode marker <FE FF> if needed
1447 if (!contents->hasUnicodeMarker()) {
1448 contents->insert(0, 0xff);
1449 contents->insert(0, 0xfe);
1450 }
1451 } else {
1452 contents = new GooString();
1453 }
1454
1455 Object obj1;
1456 obj1.initString(contents->copy());
1457 update ("Contents", &obj1);
1458 }
1459
setName(GooString * new_name)1460 void Annot::setName(GooString *new_name) {
1461 annotLocker();
1462 delete name;
1463
1464 if (new_name) {
1465 name = new GooString(new_name);
1466 } else {
1467 name = new GooString();
1468 }
1469
1470 Object obj1;
1471 obj1.initString(name->copy());
1472 update ("NM", &obj1);
1473 }
1474
setModified(GooString * new_modified)1475 void Annot::setModified(GooString *new_modified) {
1476 annotLocker();
1477 delete modified;
1478
1479 if (new_modified)
1480 modified = new GooString(new_modified);
1481 else
1482 modified = new GooString();
1483
1484 Object obj1;
1485 obj1.initString(modified->copy());
1486 update ("M", &obj1);
1487 }
1488
setFlags(Guint new_flags)1489 void Annot::setFlags(Guint new_flags) {
1490 annotLocker();
1491 Object obj1;
1492 flags = new_flags;
1493 obj1.initInt(flags);
1494 update ("F", &obj1);
1495 }
1496
setBorder(AnnotBorder * new_border)1497 void Annot::setBorder(AnnotBorder *new_border) {
1498 annotLocker();
1499 delete border;
1500
1501 if (new_border) {
1502 Object obj1;
1503 new_border->writeToObject(xref, &obj1);
1504 update(new_border->getType() == AnnotBorder::typeArray ? "Border" : "BS", &obj1);
1505 border = new_border;
1506 } else {
1507 border = NULL;
1508 }
1509 invalidateAppearance();
1510 }
1511
setColor(AnnotColor * new_color)1512 void Annot::setColor(AnnotColor *new_color) {
1513 annotLocker();
1514 delete color;
1515
1516 if (new_color) {
1517 Object obj1;
1518 new_color->writeToObject(xref, &obj1);
1519 update ("C", &obj1);
1520 color = new_color;
1521 } else {
1522 color = NULL;
1523 }
1524 invalidateAppearance();
1525 }
1526
setPage(int pageIndex,GBool updateP)1527 void Annot::setPage(int pageIndex, GBool updateP) {
1528 annotLocker();
1529 Page *pageobj = doc->getPage(pageIndex);
1530 Object obj1;
1531
1532 if (pageobj) {
1533 Ref pageRef = pageobj->getRef();
1534 obj1.initRef(pageRef.num, pageRef.gen);
1535 page = pageIndex;
1536 } else {
1537 obj1.initNull();
1538 page = 0;
1539 }
1540
1541 if (updateP) {
1542 update("P", &obj1);
1543 }
1544 }
1545
setAppearanceState(const char * state)1546 void Annot::setAppearanceState(const char *state) {
1547 annotLocker();
1548 if (!state)
1549 return;
1550
1551 delete appearState;
1552 appearState = new GooString(state);
1553
1554 delete appearBBox;
1555 appearBBox = NULL;
1556
1557 Object obj1;
1558 obj1.initName(state);
1559 update ("AS", &obj1);
1560
1561 // The appearance state determines the current appearance stream
1562 appearance.free();
1563 if (appearStreams) {
1564 appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->getCString(), &appearance);
1565 } else {
1566 appearance.initNull();
1567 }
1568 }
1569
invalidateAppearance()1570 void Annot::invalidateAppearance() {
1571 annotLocker();
1572 if (appearStreams) { // Remove existing appearance streams
1573 appearStreams->removeAllStreams();
1574 }
1575 delete appearStreams;
1576 appearStreams = NULL;
1577
1578 delete appearState;
1579 appearState = NULL;
1580
1581 delete appearBBox;
1582 appearBBox = NULL;
1583
1584 appearance.free();
1585 appearance.initNull();
1586
1587 Object obj1, obj2;
1588 obj1.initNull();
1589 if (!annotObj.dictLookup("AP", &obj2)->isNull())
1590 update ("AP", &obj1); // Remove AP
1591 obj2.free();
1592
1593 if (!annotObj.dictLookup("AS", &obj2)->isNull())
1594 update ("AS", &obj1); // Remove AS
1595 obj2.free();
1596 }
1597
getXMin()1598 double Annot::getXMin() {
1599 return rect->x1;
1600 }
1601
getYMin()1602 double Annot::getYMin() {
1603 return rect->y1;
1604 }
1605
getXMax()1606 double Annot::getXMax() {
1607 return rect->x2;
1608 }
1609
getYMax()1610 double Annot::getYMax() {
1611 return rect->y2;
1612 }
1613
readArrayNum(Object * pdfArray,int key,double * value)1614 void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
1615 Object valueObject;
1616
1617 pdfArray->arrayGet(key, &valueObject);
1618 if (valueObject.isNum()) {
1619 *value = valueObject.getNum();
1620 } else {
1621 *value = 0;
1622 ok = gFalse;
1623 }
1624 valueObject.free();
1625 }
1626
removeReferencedObjects()1627 void Annot::removeReferencedObjects() {
1628 // Remove appearance streams (if any)
1629 invalidateAppearance();
1630 }
1631
incRefCnt()1632 void Annot::incRefCnt() {
1633 annotLocker();
1634 refCnt++;
1635 }
1636
decRefCnt()1637 void Annot::decRefCnt() {
1638 #if MULTITHREADED
1639 gLockMutex(&mutex);
1640 #endif
1641 if (--refCnt == 0) {
1642 #if MULTITHREADED
1643 gUnlockMutex(&mutex);
1644 #endif
1645 delete this;
1646 return;
1647 }
1648 #if MULTITHREADED
1649 gUnlockMutex(&mutex);
1650 #endif
1651 }
1652
~Annot()1653 Annot::~Annot() {
1654 annotObj.free();
1655
1656 delete rect;
1657 delete contents;
1658
1659 if (name)
1660 delete name;
1661
1662 if (modified)
1663 delete modified;
1664
1665 delete appearStreams;
1666 delete appearBBox;
1667 appearance.free();
1668
1669 if (appearState)
1670 delete appearState;
1671
1672 if (border)
1673 delete border;
1674
1675 if (color)
1676 delete color;
1677
1678 oc.free();
1679
1680 #if MULTITHREADED
1681 gDestroyMutex(&mutex);
1682 #endif
1683 }
1684
setColor(AnnotColor * color,GBool fill)1685 void Annot::setColor(AnnotColor *color, GBool fill) {
1686 const double *values = color->getValues();
1687
1688 switch (color->getSpace()) {
1689 case AnnotColor::colorCMYK:
1690 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1691 values[0], values[1], values[2], values[3],
1692 fill ? 'k' : 'K');
1693 break;
1694 case AnnotColor::colorRGB:
1695 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1696 values[0], values[1], values[2],
1697 fill ? "rg" : "RG");
1698 break;
1699 case AnnotColor::colorGray:
1700 appearBuf->appendf("{0:.2f} {1:c}\n",
1701 values[0],
1702 fill ? 'g' : 'G');
1703 break;
1704 case AnnotColor::colorTransparent:
1705 default:
1706 break;
1707 }
1708 }
1709
setLineStyleForBorder(AnnotBorder * border)1710 void Annot::setLineStyleForBorder(AnnotBorder *border) {
1711 int i, dashLength;
1712 double *dash;
1713
1714 switch (border->getStyle()) {
1715 case AnnotBorder::borderDashed:
1716 appearBuf->append("[");
1717 dashLength = border->getDashLength();
1718 dash = border->getDash();
1719 for (i = 0; i < dashLength; ++i)
1720 appearBuf->appendf(" {0:.2f}", dash[i]);
1721 appearBuf->append(" ] 0 d\n");
1722 break;
1723 default:
1724 appearBuf->append("[] 0 d\n");
1725 break;
1726 }
1727 appearBuf->appendf("{0:.2f} w\n", border->getWidth());
1728 }
1729
1730 // Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1731 // If <fill> is true, the circle is filled; otherwise it is stroked.
drawCircle(double cx,double cy,double r,GBool fill)1732 void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
1733 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1734 cx + r, cy);
1735 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1736 cx + r, cy + bezierCircle * r,
1737 cx + bezierCircle * r, cy + r,
1738 cx, cy + r);
1739 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1740 cx - bezierCircle * r, cy + r,
1741 cx - r, cy + bezierCircle * r,
1742 cx - r, cy);
1743 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1744 cx - r, cy - bezierCircle * r,
1745 cx - bezierCircle * r, cy - r,
1746 cx, cy - r);
1747 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1748 cx + bezierCircle * r, cy - r,
1749 cx + r, cy - bezierCircle * r,
1750 cx + r, cy);
1751 appearBuf->append(fill ? "f\n" : "s\n");
1752 }
1753
1754 // Draw the top-left half of an (approximate) circle of radius <r>
1755 // centered at (<cx>, <cy>).
drawCircleTopLeft(double cx,double cy,double r)1756 void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1757 double r2;
1758
1759 r2 = r / sqrt(2.0);
1760 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1761 cx + r2, cy + r2);
1762 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1763 cx + (1 - bezierCircle) * r2,
1764 cy + (1 + bezierCircle) * r2,
1765 cx - (1 - bezierCircle) * r2,
1766 cy + (1 + bezierCircle) * r2,
1767 cx - r2,
1768 cy + r2);
1769 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1770 cx - (1 + bezierCircle) * r2,
1771 cy + (1 - bezierCircle) * r2,
1772 cx - (1 + bezierCircle) * r2,
1773 cy - (1 - bezierCircle) * r2,
1774 cx - r2,
1775 cy - r2);
1776 appearBuf->append("S\n");
1777 }
1778
1779 // Draw the bottom-right half of an (approximate) circle of radius <r>
1780 // centered at (<cx>, <cy>).
drawCircleBottomRight(double cx,double cy,double r)1781 void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1782 double r2;
1783
1784 r2 = r / sqrt(2.0);
1785 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1786 cx - r2, cy - r2);
1787 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1788 cx - (1 - bezierCircle) * r2,
1789 cy - (1 + bezierCircle) * r2,
1790 cx + (1 - bezierCircle) * r2,
1791 cy - (1 + bezierCircle) * r2,
1792 cx + r2,
1793 cy - r2);
1794 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1795 cx + (1 + bezierCircle) * r2,
1796 cy - (1 - bezierCircle) * r2,
1797 cx + (1 + bezierCircle) * r2,
1798 cy + (1 - bezierCircle) * r2,
1799 cx + r2,
1800 cy + r2);
1801 appearBuf->append("S\n");
1802 }
1803
createForm(double * bbox,GBool transparencyGroup,Object * resDict,Object * aStream)1804 void Annot::createForm(double *bbox, GBool transparencyGroup, Object *resDict, Object *aStream) {
1805 Object obj1, obj2;
1806 Object appearDict;
1807
1808 appearDict.initDict(xref);
1809 appearDict.dictSet("Length", obj1.initInt(appearBuf->getLength()));
1810 appearDict.dictSet("Subtype", obj1.initName("Form"));
1811 obj1.initArray(xref);
1812 obj1.arrayAdd(obj2.initReal(bbox[0]));
1813 obj1.arrayAdd(obj2.initReal(bbox[1]));
1814 obj1.arrayAdd(obj2.initReal(bbox[2]));
1815 obj1.arrayAdd(obj2.initReal(bbox[3]));
1816 appearDict.dictSet("BBox", &obj1);
1817 if (transparencyGroup) {
1818 Object transDict;
1819 transDict.initDict(xref);
1820 transDict.dictSet("S", obj1.initName("Transparency"));
1821 appearDict.dictSet("Group", &transDict);
1822 }
1823 if (resDict)
1824 appearDict.dictSet("Resources", resDict);
1825
1826 MemStream *mStream = new MemStream(copyString(appearBuf->getCString()), 0,
1827 appearBuf->getLength(), &appearDict);
1828 mStream->setNeedFree(gTrue);
1829 aStream->initStream(mStream);
1830 }
1831
createResourcesDict(const char * formName,Object * formStream,const char * stateName,double opacity,const char * blendMode,Object * resDict)1832 void Annot::createResourcesDict(const char *formName, Object *formStream,
1833 const char *stateName,
1834 double opacity, const char *blendMode,
1835 Object *resDict) {
1836 Object gsDict, stateDict, formDict, obj1;
1837
1838 gsDict.initDict(xref);
1839 if (opacity != 1) {
1840 gsDict.dictSet("CA", obj1.initReal(opacity));
1841 gsDict.dictSet("ca", obj1.initReal(opacity));
1842 }
1843 if (blendMode)
1844 gsDict.dictSet("BM", obj1.initName(blendMode));
1845 stateDict.initDict(xref);
1846 stateDict.dictSet(stateName, &gsDict);
1847 formDict.initDict(xref);
1848 formDict.dictSet(formName, formStream);
1849
1850 resDict->initDict(xref);
1851 resDict->dictSet("ExtGState", &stateDict);
1852 resDict->dictSet("XObject", &formDict);
1853 }
1854
getAppearanceResDict(Object * dest)1855 Object *Annot::getAppearanceResDict(Object *dest) {
1856 Object obj1, obj2;
1857
1858 dest->initNull(); // Default value
1859
1860 // Fetch appearance's resource dict (if any)
1861 appearance.fetch(xref, &obj1);
1862 if (obj1.isStream()) {
1863 obj1.streamGetDict()->lookup("Resources", &obj2);
1864 if (obj2.isDict()) {
1865 obj2.copy(dest);
1866 }
1867 obj2.free();
1868 }
1869 obj1.free();
1870
1871 return dest;
1872 }
1873
isVisible(GBool printing)1874 GBool Annot::isVisible(GBool printing) {
1875 // check the flags
1876 if ((flags & flagHidden) ||
1877 (printing && !(flags & flagPrint)) ||
1878 (!printing && (flags & flagNoView))) {
1879 return gFalse;
1880 }
1881
1882 // check the OC
1883 OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig();
1884 if (optContentConfig) {
1885 if (! optContentConfig->optContentIsVisible(&oc))
1886 return gFalse;
1887 }
1888
1889 return gTrue;
1890 }
1891
getRotation() const1892 int Annot::getRotation() const
1893 {
1894 Page *pageobj = doc->getPage(page);
1895 assert(pageobj != NULL);
1896
1897 if (flags & flagNoRotate) {
1898 return (360 - pageobj->getRotate()) % 360;
1899 } else {
1900 return 0;
1901 }
1902 }
1903
draw(Gfx * gfx,GBool printing)1904 void Annot::draw(Gfx *gfx, GBool printing) {
1905 Object obj;
1906
1907 annotLocker();
1908 if (!isVisible (printing))
1909 return;
1910
1911 // draw the appearance stream
1912 appearance.fetch(gfx->getXRef(), &obj);
1913 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
1914 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
1915 obj.free();
1916 }
1917
1918 //------------------------------------------------------------------------
1919 // AnnotPopup
1920 //------------------------------------------------------------------------
1921
AnnotPopup(PDFDoc * docA,PDFRectangle * rect)1922 AnnotPopup::AnnotPopup(PDFDoc *docA, PDFRectangle *rect) :
1923 Annot(docA, rect) {
1924 Object obj1;
1925
1926 type = typePopup;
1927
1928 annotObj.dictSet ("Subtype", obj1.initName ("Popup"));
1929 initialize (docA, annotObj.getDict());
1930 }
1931
AnnotPopup(PDFDoc * docA,Dict * dict,Object * obj)1932 AnnotPopup::AnnotPopup(PDFDoc *docA, Dict *dict, Object *obj) :
1933 Annot(docA, dict, obj) {
1934 type = typePopup;
1935 initialize(docA, dict);
1936 }
1937
~AnnotPopup()1938 AnnotPopup::~AnnotPopup() {
1939 parent.free();
1940 }
1941
initialize(PDFDoc * docA,Dict * dict)1942 void AnnotPopup::initialize(PDFDoc *docA, Dict *dict) {
1943 Object obj1;
1944
1945 if (!dict->lookupNF("Parent", &parent)->isRef()) {
1946 parent.initNull();
1947 }
1948
1949 if (dict->lookup("Open", &obj1)->isBool()) {
1950 open = obj1.getBool();
1951 } else {
1952 open = gFalse;
1953 }
1954 obj1.free();
1955 }
1956
setParent(Object * parentA)1957 void AnnotPopup::setParent(Object *parentA) {
1958 parentA->copy(&parent);
1959 update ("Parent", &parent);
1960 }
1961
setParent(Annot * parentA)1962 void AnnotPopup::setParent(Annot *parentA) {
1963 Ref parentRef = parentA->getRef();
1964 parent.initRef(parentRef.num, parentRef.gen);
1965 update ("Parent", &parent);
1966 }
1967
setOpen(GBool openA)1968 void AnnotPopup::setOpen(GBool openA) {
1969 Object obj1;
1970
1971 open = openA;
1972 obj1.initBool(open);
1973 update ("Open", &obj1);
1974 }
1975
1976 //------------------------------------------------------------------------
1977 // AnnotMarkup
1978 //------------------------------------------------------------------------
AnnotMarkup(PDFDoc * docA,PDFRectangle * rect)1979 AnnotMarkup::AnnotMarkup(PDFDoc *docA, PDFRectangle *rect) :
1980 Annot(docA, rect) {
1981 initialize(docA, annotObj.getDict(), &annotObj);
1982 }
1983
AnnotMarkup(PDFDoc * docA,Dict * dict,Object * obj)1984 AnnotMarkup::AnnotMarkup(PDFDoc *docA, Dict *dict, Object *obj) :
1985 Annot(docA, dict, obj) {
1986 initialize(docA, dict, obj);
1987 }
1988
~AnnotMarkup()1989 AnnotMarkup::~AnnotMarkup() {
1990 if (label)
1991 delete label;
1992
1993 if (popup)
1994 delete popup;
1995
1996 if (date)
1997 delete date;
1998
1999 if (subject)
2000 delete subject;
2001 }
2002
initialize(PDFDoc * docA,Dict * dict,Object * obj)2003 void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict, Object *obj) {
2004 Object obj1, obj2;
2005
2006 if (dict->lookup("T", &obj1)->isString()) {
2007 label = obj1.getString()->copy();
2008 } else {
2009 label = NULL;
2010 }
2011 obj1.free();
2012
2013 if (dict->lookup("Popup", &obj1)->isDict() && dict->lookupNF("Popup", &obj2)->isRef()) {
2014 popup = new AnnotPopup(docA, obj1.getDict(), &obj2);
2015 } else {
2016 popup = NULL;
2017 }
2018 obj1.free();
2019
2020 if (dict->lookup("CA", &obj1)->isNum()) {
2021 opacity = obj1.getNum();
2022 } else {
2023 opacity = 1.0;
2024 }
2025 obj1.free();
2026
2027 if (dict->lookup("CreationDate", &obj1)->isString()) {
2028 date = obj1.getString()->copy();
2029 } else {
2030 date = NULL;
2031 }
2032 obj1.free();
2033
2034 if (dict->lookupNF("IRT", &obj1)->isRef()) {
2035 inReplyTo = obj1.getRef();
2036 } else {
2037 inReplyTo.num = 0;
2038 inReplyTo.gen = 0;
2039 }
2040 obj1.free();
2041
2042 if (dict->lookup("Subj", &obj1)->isString()) {
2043 subject = obj1.getString()->copy();
2044 } else {
2045 subject = NULL;
2046 }
2047 obj1.free();
2048
2049 if (dict->lookup("RT", &obj1)->isName()) {
2050 const char *replyName = obj1.getName();
2051
2052 if (!strcmp(replyName, "R")) {
2053 replyTo = replyTypeR;
2054 } else if (!strcmp(replyName, "Group")) {
2055 replyTo = replyTypeGroup;
2056 } else {
2057 replyTo = replyTypeR;
2058 }
2059 } else {
2060 replyTo = replyTypeR;
2061 }
2062 obj1.free();
2063
2064 if (dict->lookup("ExData", &obj1)->isDict()) {
2065 exData = parseAnnotExternalData(obj1.getDict());
2066 } else {
2067 exData = annotExternalDataMarkupUnknown;
2068 }
2069 obj1.free();
2070 }
2071
setLabel(GooString * new_label)2072 void AnnotMarkup::setLabel(GooString *new_label) {
2073 delete label;
2074
2075 if (new_label) {
2076 label = new GooString(new_label);
2077 //append the unicode marker <FE FF> if needed
2078 if (!label->hasUnicodeMarker()) {
2079 label->insert(0, 0xff);
2080 label->insert(0, 0xfe);
2081 }
2082 } else {
2083 label = new GooString();
2084 }
2085
2086 Object obj1;
2087 obj1.initString(label->copy());
2088 update ("T", &obj1);
2089 }
2090
setPopup(AnnotPopup * new_popup)2091 void AnnotMarkup::setPopup(AnnotPopup *new_popup) {
2092 delete popup;
2093
2094 if (new_popup) {
2095 Object obj1;
2096 Ref popupRef = new_popup->getRef();
2097
2098 obj1.initRef (popupRef.num, popupRef.gen);
2099 update ("Popup", &obj1);
2100
2101 new_popup->setParent(this);
2102 popup = new_popup;
2103 } else {
2104 popup = NULL;
2105 }
2106 }
2107
setOpacity(double opacityA)2108 void AnnotMarkup::setOpacity(double opacityA) {
2109 Object obj1;
2110
2111 opacity = opacityA;
2112 obj1.initReal(opacity);
2113 update ("CA", &obj1);
2114 invalidateAppearance();
2115 }
2116
setDate(GooString * new_date)2117 void AnnotMarkup::setDate(GooString *new_date) {
2118 delete date;
2119
2120 if (new_date)
2121 date = new GooString(new_date);
2122 else
2123 date = new GooString();
2124
2125 Object obj1;
2126 obj1.initString(date->copy());
2127 update ("CreationDate", &obj1);
2128 }
2129
removeReferencedObjects()2130 void AnnotMarkup::removeReferencedObjects() {
2131 Page *pageobj = doc->getPage(page);
2132 assert(pageobj != NULL); // We're called when removing an annot from a page
2133
2134 // Remove popup
2135 if (popup) {
2136 pageobj->removeAnnot(popup);
2137 }
2138
2139 Annot::removeReferencedObjects();
2140 }
2141
2142 //------------------------------------------------------------------------
2143 // AnnotText
2144 //------------------------------------------------------------------------
2145
AnnotText(PDFDoc * docA,PDFRectangle * rect)2146 AnnotText::AnnotText(PDFDoc *docA, PDFRectangle *rect) :
2147 AnnotMarkup(docA, rect) {
2148 Object obj1;
2149
2150 type = typeText;
2151 flags |= flagNoZoom | flagNoRotate;
2152
2153 annotObj.dictSet ("Subtype", obj1.initName ("Text"));
2154 initialize (docA, annotObj.getDict());
2155 }
2156
AnnotText(PDFDoc * docA,Dict * dict,Object * obj)2157 AnnotText::AnnotText(PDFDoc *docA, Dict *dict, Object *obj) :
2158 AnnotMarkup(docA, dict, obj) {
2159
2160 type = typeText;
2161 flags |= flagNoZoom | flagNoRotate;
2162 initialize (docA, dict);
2163 }
2164
~AnnotText()2165 AnnotText::~AnnotText() {
2166 delete icon;
2167 }
2168
initialize(PDFDoc * docA,Dict * dict)2169 void AnnotText::initialize(PDFDoc *docA, Dict *dict) {
2170 Object obj1;
2171
2172 if (dict->lookup("Open", &obj1)->isBool())
2173 open = obj1.getBool();
2174 else
2175 open = gFalse;
2176 obj1.free();
2177
2178 if (dict->lookup("Name", &obj1)->isName()) {
2179 icon = new GooString(obj1.getName());
2180 } else {
2181 icon = new GooString("Note");
2182 }
2183 obj1.free();
2184
2185 if (dict->lookup("StateModel", &obj1)->isString()) {
2186 Object obj2;
2187 GooString *modelName = obj1.getString();
2188
2189 if (dict->lookup("State", &obj2)->isString()) {
2190 GooString *stateName = obj2.getString();
2191
2192 if (!stateName->cmp("Marked")) {
2193 state = stateMarked;
2194 } else if (!stateName->cmp("Unmarked")) {
2195 state = stateUnmarked;
2196 } else if (!stateName->cmp("Accepted")) {
2197 state = stateAccepted;
2198 } else if (!stateName->cmp("Rejected")) {
2199 state = stateRejected;
2200 } else if (!stateName->cmp("Cancelled")) {
2201 state = stateCancelled;
2202 } else if (!stateName->cmp("Completed")) {
2203 state = stateCompleted;
2204 } else if (!stateName->cmp("None")) {
2205 state = stateNone;
2206 } else {
2207 state = stateUnknown;
2208 }
2209 } else {
2210 state = stateUnknown;
2211 }
2212 obj2.free();
2213
2214 if (!modelName->cmp("Marked")) {
2215 switch (state) {
2216 case stateUnknown:
2217 state = stateMarked;
2218 break;
2219 case stateAccepted:
2220 case stateRejected:
2221 case stateCancelled:
2222 case stateCompleted:
2223 case stateNone:
2224 state = stateUnknown;
2225 break;
2226 default:
2227 break;
2228 }
2229 } else if (!modelName->cmp("Review")) {
2230 switch (state) {
2231 case stateUnknown:
2232 state = stateNone;
2233 break;
2234 case stateMarked:
2235 case stateUnmarked:
2236 state = stateUnknown;
2237 break;
2238 default:
2239 break;
2240 }
2241 } else {
2242 state = stateUnknown;
2243 }
2244 } else {
2245 state = stateUnknown;
2246 }
2247 obj1.free();
2248 }
2249
setOpen(GBool openA)2250 void AnnotText::setOpen(GBool openA) {
2251 Object obj1;
2252
2253 open = openA;
2254 obj1.initBool(open);
2255 update ("Open", &obj1);
2256 }
2257
setIcon(GooString * new_icon)2258 void AnnotText::setIcon(GooString *new_icon) {
2259 if (new_icon && icon->cmp(new_icon) == 0)
2260 return;
2261
2262 delete icon;
2263
2264 if (new_icon) {
2265 icon = new GooString (new_icon);
2266 } else {
2267 icon = new GooString("Note");
2268 }
2269
2270 Object obj1;
2271 obj1.initName (icon->getCString());
2272 update("Name", &obj1);
2273 invalidateAppearance();
2274 }
2275
2276 #define ANNOT_TEXT_AP_NOTE \
2277 "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \
2278 "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" \
2279 "l 0 22.387 1.613 24 3.602 24 c h\n" \
2280 "3.602 24 m f\n" \
2281 "0.533333 0.541176 0.521569 RG 2 w\n" \
2282 "1 J\n" \
2283 "1 j\n" \
2284 "[] 0.0 d\n" \
2285 "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" \
2286 "1.5 w\n" \
2287 "0 j\n" \
2288 "10 16 m 14 21 l S\n" \
2289 "1.85625 w\n" \
2290 "1 j\n" \
2291 "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \
2292 "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \
2293 "14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \
2294 "15.07 20.523 m S\n" \
2295 "1 w\n" \
2296 "0 j\n" \
2297 "6.5 13.5 m 15.5 13.5 l S\n" \
2298 "6.5 10.5 m 13.5 10.5 l S\n" \
2299 "6.801 7.5 m 15.5 7.5 l S\n" \
2300 "0.729412 0.741176 0.713725 RG 2 w\n" \
2301 "1 j\n" \
2302 "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" \
2303 "1.5 w\n" \
2304 "0 j\n" \
2305 "10 17 m 14 22 l S\n" \
2306 "1.85625 w\n" \
2307 "1 j\n" \
2308 "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \
2309 "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \
2310 "14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \
2311 "15.07 21.523 m S\n" \
2312 "1 w\n" \
2313 "0 j\n" \
2314 "6.5 14.5 m 15.5 14.5 l S\n" \
2315 "6.5 11.5 m 13.5 11.5 l S\n" \
2316 "6.801 8.5 m 15.5 8.5 l S\n"
2317
2318 #define ANNOT_TEXT_AP_COMMENT \
2319 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2320 "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" \
2321 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2322 "4.301 23 m f\n" \
2323 "0.533333 0.541176 0.521569 RG 2 w\n" \
2324 "0 J\n" \
2325 "1 j\n" \
2326 "[] 0.0 d\n" \
2327 "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" \
2328 "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" \
2329 "5.637 20 8 20 c h\n" \
2330 "8 20 m S\n" \
2331 "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \
2332 "c 20 14 l 20 11.785 18.363 10\n" \
2333 "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" \
2334 "19.215 5.637 21 8 21 c h\n" \
2335 "8 21 m S\n"
2336
2337 #define ANNOT_TEXT_AP_KEY \
2338 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2339 "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" \
2340 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2341 "4.301 23 m f\n" \
2342 "0.533333 0.541176 0.521569 RG 2 w\n" \
2343 "1 J\n" \
2344 "0 j\n" \
2345 "[] 0.0 d\n" \
2346 "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \
2347 "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \
2348 "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \
2349 "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" \
2350 "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \
2351 "11.895 18.754 m S\n" \
2352 "1.5 w\n" \
2353 "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \
2354 "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \
2355 "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \
2356 "16.059 15.586 m S\n" \
2357 "0.729412 0.741176 0.713725 RG 2 w\n" \
2358 "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \
2359 "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \
2360 "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \
2361 "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" \
2362 "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \
2363 "11.895 19.754 m S\n" \
2364 "1.5 w\n" \
2365 "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \
2366 "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \
2367 "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \
2368 "16.059 16.586 m S\n"
2369
2370 #define ANNOT_TEXT_AP_HELP \
2371 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2372 "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" \
2373 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2374 "4.301 23 m f\n" \
2375 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2376 "1 J\n" \
2377 "1 j\n" \
2378 "[] 0.0 d\n" \
2379 "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" \
2380 "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \
2381 "c S\n" \
2382 "0 j\n" \
2383 "12 8 m 12 12 16 11 16 15 c S\n" \
2384 "1.539286 w\n" \
2385 "1 j\n" \
2386 "q 1 0 0 -0.999991 0 24 cm\n" \
2387 "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \
2388 "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \
2389 "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \
2390 "0.729412 0.741176 0.713725 RG 2.5 w\n" \
2391 "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \
2392 "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \
2393 "c S\n" \
2394 "0 j\n" \
2395 "12 9 m 12 13 16 12 16 16 c S\n" \
2396 "1.539286 w\n" \
2397 "1 j\n" \
2398 "q 1 0 0 -0.999991 0 24 cm\n" \
2399 "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \
2400 "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \
2401 "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
2402
2403 #define ANNOT_TEXT_AP_NEW_PARAGRAPH \
2404 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2405 "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" \
2406 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2407 "4.301 23 m f\n" \
2408 "0.533333 0.541176 0.521569 RG 4 w\n" \
2409 "0 J\n" \
2410 "2 j\n" \
2411 "[] 0.0 d\n" \
2412 "4 M q 1 0 0 -1 0 24 cm\n" \
2413 "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \
2414 "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" \
2415 "1.004413 w\n" \
2416 "1 J\n" \
2417 "1 j\n" \
2418 "q 1 0 0 -0.991232 0 24 cm\n" \
2419 "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \
2420 "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \
2421 "17.461 8.252 l 18.07 11.511 l h\n" \
2422 "18.07 11.511 m S Q\n" \
2423 "2 w\n" \
2424 "0 j\n" \
2425 "11 17 m 10 17 l 10 3 l S\n" \
2426 "14 3 m 14 13 l S\n" \
2427 "0.729412 0.741176 0.713725 RG 4 w\n" \
2428 "0 J\n" \
2429 "2 j\n" \
2430 "q 1 0 0 -1 0 24 cm\n" \
2431 "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \
2432 "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" \
2433 "1.004413 w\n" \
2434 "1 J\n" \
2435 "1 j\n" \
2436 "q 1 0 0 -0.991232 0 24 cm\n" \
2437 "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \
2438 "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \
2439 "17.461 7.243 l 18.07 10.502 l h\n" \
2440 "18.07 10.502 m S Q\n" \
2441 "2 w\n" \
2442 "0 j\n" \
2443 "11 18 m 10 18 l 10 4 l S\n" \
2444 "14 4 m 14 14 l S\n"
2445
2446 #define ANNOT_TEXT_AP_PARAGRAPH \
2447 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2448 "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" \
2449 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2450 "4.301 23 m f\n" \
2451 "0.533333 0.541176 0.521569 RG 2 w\n" \
2452 "1 J\n" \
2453 "1 j\n" \
2454 "[] 0.0 d\n" \
2455 "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
2456 "4 w\n" \
2457 "q 1 0 0 -1 0 24 cm\n" \
2458 "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \
2459 "7.066 9.879 7.004 c S Q\n" \
2460 "0.729412 0.741176 0.713725 RG 2 w\n" \
2461 "15 4 m 15 19 l 11 19 l 11 4 l S\n" \
2462 "4 w\n" \
2463 "q 1 0 0 -1 0 24 cm\n" \
2464 "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" \
2465 "9.879 6.004 c S Q\n"
2466
2467 #define ANNOT_TEXT_AP_INSERT \
2468 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2469 "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" \
2470 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2471 "4.301 23 m f\n" \
2472 "0.533333 0.541176 0.521569 RG 2 w\n" \
2473 "1 J\n" \
2474 "0 j\n" \
2475 "[] 0.0 d\n" \
2476 "4 M 12 18.012 m 20 18 l S\n" \
2477 "9 10 m 17 10 l S\n" \
2478 "12 14.012 m 20 14 l S\n" \
2479 "12 6.012 m 20 6.012 l S\n" \
2480 "4 12 m 6 10 l 4 8 l S\n" \
2481 "4 12 m 4 8 l S\n" \
2482 "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \
2483 "9 11 m 17 11 l S\n" \
2484 "12 15.012 m 20 15 l S\n" \
2485 "12 7.012 m 20 7.012 l S\n" \
2486 "4 13 m 6 11 l 4 9 l S\n" \
2487 "4 13 m 4 9 l S\n"
2488
2489 #define ANNOT_TEXT_AP_CROSS \
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.5 w\n" \
2495 "1 J\n" \
2496 "0 j\n" \
2497 "[] 0.0 d\n" \
2498 "4 M 18 5 m 6 17 l S\n" \
2499 "6 5 m 18 17 l S\n" \
2500 "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \
2501 "6 6 m 18 18 l S\n"
2502
2503 #define ANNOT_TEXT_AP_CIRCLE \
2504 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2505 "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" \
2506 "l 1 21.523 2.477 23 4.301 23 c h\n" \
2507 "4.301 23 m f\n" \
2508 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2509 "1 J\n" \
2510 "1 j\n" \
2511 "[] 0.0 d\n" \
2512 "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" \
2513 "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \
2514 "19.5 11.5 m S\n" \
2515 "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \
2516 "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \
2517 "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \
2518 "19.5 12.5 m S\n"
2519
draw(Gfx * gfx,GBool printing)2520 void AnnotText::draw(Gfx *gfx, GBool printing) {
2521 Object obj;
2522 double ca = 1;
2523
2524 if (!isVisible (printing))
2525 return;
2526
2527 annotLocker();
2528 if (appearance.isNull()) {
2529 ca = opacity;
2530
2531 appearBuf = new GooString ();
2532
2533 appearBuf->append ("q\n");
2534 if (color)
2535 setColor(color, gTrue);
2536 else
2537 appearBuf->append ("1 1 1 rg\n");
2538 if (!icon->cmp("Note"))
2539 appearBuf->append (ANNOT_TEXT_AP_NOTE);
2540 else if (!icon->cmp("Comment"))
2541 appearBuf->append (ANNOT_TEXT_AP_COMMENT);
2542 else if (!icon->cmp("Key"))
2543 appearBuf->append (ANNOT_TEXT_AP_KEY);
2544 else if (!icon->cmp("Help"))
2545 appearBuf->append (ANNOT_TEXT_AP_HELP);
2546 else if (!icon->cmp("NewParagraph"))
2547 appearBuf->append (ANNOT_TEXT_AP_NEW_PARAGRAPH);
2548 else if (!icon->cmp("Paragraph"))
2549 appearBuf->append (ANNOT_TEXT_AP_PARAGRAPH);
2550 else if (!icon->cmp("Insert"))
2551 appearBuf->append (ANNOT_TEXT_AP_INSERT);
2552 else if (!icon->cmp("Cross"))
2553 appearBuf->append (ANNOT_TEXT_AP_CROSS);
2554 else if (!icon->cmp("Circle"))
2555 appearBuf->append (ANNOT_TEXT_AP_CIRCLE);
2556 appearBuf->append ("Q\n");
2557
2558 // Force 24x24 rectangle
2559 PDFRectangle fixedRect(rect->x1, rect->y2 - 24, rect->x1 + 24, rect->y2);
2560 appearBBox = new AnnotAppearanceBBox(&fixedRect);
2561 double bbox[4];
2562 appearBBox->getBBoxRect(bbox);
2563 if (ca == 1) {
2564 createForm(bbox, gFalse, NULL, &appearance);
2565 } else {
2566 Object aStream, resDict;
2567
2568 createForm(bbox, gTrue, NULL, &aStream);
2569 delete appearBuf;
2570
2571 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
2572 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
2573 createForm(bbox, gFalse, &resDict, &appearance);
2574 }
2575 delete appearBuf;
2576 }
2577
2578 // draw the appearance stream
2579 appearance.fetch(gfx->getXRef(), &obj);
2580 if (appearBBox) {
2581 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2582 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
2583 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
2584 getRotation());
2585 } else {
2586 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2587 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2588 }
2589 obj.free();
2590 }
2591
2592 //------------------------------------------------------------------------
2593 // AnnotLink
2594 //------------------------------------------------------------------------
AnnotLink(PDFDoc * docA,PDFRectangle * rect)2595 AnnotLink::AnnotLink(PDFDoc *docA, PDFRectangle *rect) :
2596 Annot(docA, rect) {
2597 Object obj1;
2598
2599 type = typeLink;
2600 annotObj.dictSet ("Subtype", obj1.initName ("Link"));
2601 initialize (docA, annotObj.getDict());
2602 }
2603
AnnotLink(PDFDoc * docA,Dict * dict,Object * obj)2604 AnnotLink::AnnotLink(PDFDoc *docA, Dict *dict, Object *obj) :
2605 Annot(docA, dict, obj) {
2606
2607 type = typeLink;
2608 initialize (docA, dict);
2609 }
2610
~AnnotLink()2611 AnnotLink::~AnnotLink() {
2612 delete action;
2613 /*
2614 if (uriAction)
2615 delete uriAction;
2616 */
2617 if (quadrilaterals)
2618 delete quadrilaterals;
2619 }
2620
initialize(PDFDoc * docA,Dict * dict)2621 void AnnotLink::initialize(PDFDoc *docA, Dict *dict) {
2622 Object obj1;
2623
2624 action = NULL;
2625
2626 // look for destination
2627 if (!dict->lookup("Dest", &obj1)->isNull()) {
2628 action = LinkAction::parseDest(&obj1);
2629 // look for action
2630 } else {
2631 obj1.free();
2632 if (dict->lookup("A", &obj1)->isDict()) {
2633 action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
2634 }
2635 }
2636 obj1.free();
2637
2638 if (dict->lookup("H", &obj1)->isName()) {
2639 const char *effect = obj1.getName();
2640
2641 if (!strcmp(effect, "N")) {
2642 linkEffect = effectNone;
2643 } else if (!strcmp(effect, "I")) {
2644 linkEffect = effectInvert;
2645 } else if (!strcmp(effect, "O")) {
2646 linkEffect = effectOutline;
2647 } else if (!strcmp(effect, "P")) {
2648 linkEffect = effectPush;
2649 } else {
2650 linkEffect = effectInvert;
2651 }
2652 } else {
2653 linkEffect = effectInvert;
2654 }
2655 obj1.free();
2656 /*
2657 if (dict->lookup("PA", &obj1)->isDict()) {
2658 uriAction = NULL;
2659 } else {
2660 uriAction = NULL;
2661 }
2662 obj1.free();
2663 */
2664 if (dict->lookup("QuadPoints", &obj1)->isArray()) {
2665 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
2666 } else {
2667 quadrilaterals = NULL;
2668 }
2669 obj1.free();
2670
2671 if (dict->lookup("BS", &obj1)->isDict()) {
2672 delete border;
2673 border = new AnnotBorderBS(obj1.getDict());
2674 } else if (!border) {
2675 border = new AnnotBorderBS();
2676 }
2677 obj1.free();
2678 }
2679
draw(Gfx * gfx,GBool printing)2680 void AnnotLink::draw(Gfx *gfx, GBool printing) {
2681 Object obj;
2682
2683 if (!isVisible (printing))
2684 return;
2685
2686 annotLocker();
2687 // draw the appearance stream
2688 appearance.fetch(gfx->getXRef(), &obj);
2689 gfx->drawAnnot(&obj, border, color,
2690 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2691 obj.free();
2692 }
2693
2694 //------------------------------------------------------------------------
2695 // AnnotFreeText
2696 //------------------------------------------------------------------------
AnnotFreeText(PDFDoc * docA,PDFRectangle * rect,GooString * da)2697 AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rect, GooString *da) :
2698 AnnotMarkup(docA, rect) {
2699 Object obj1;
2700
2701 type = typeFreeText;
2702
2703 annotObj.dictSet ("Subtype", obj1.initName ("FreeText"));
2704
2705 Object obj2;
2706 obj2.initString (da->copy());
2707 annotObj.dictSet("DA", &obj2);
2708
2709 initialize (docA, annotObj.getDict());
2710 }
2711
AnnotFreeText(PDFDoc * docA,Dict * dict,Object * obj)2712 AnnotFreeText::AnnotFreeText(PDFDoc *docA, Dict *dict, Object *obj) :
2713 AnnotMarkup(docA, dict, obj) {
2714 type = typeFreeText;
2715 initialize(docA, dict);
2716 }
2717
~AnnotFreeText()2718 AnnotFreeText::~AnnotFreeText() {
2719 delete appearanceString;
2720
2721 if (styleString)
2722 delete styleString;
2723
2724 if (calloutLine)
2725 delete calloutLine;
2726
2727 if (borderEffect)
2728 delete borderEffect;
2729
2730 if (rectangle)
2731 delete rectangle;
2732 }
2733
initialize(PDFDoc * docA,Dict * dict)2734 void AnnotFreeText::initialize(PDFDoc *docA, Dict *dict) {
2735 Object obj1;
2736
2737 if (dict->lookup("DA", &obj1)->isString()) {
2738 appearanceString = obj1.getString()->copy();
2739 } else {
2740 appearanceString = new GooString();
2741 error(errSyntaxError, -1, "Bad appearance for annotation");
2742 ok = gFalse;
2743 }
2744 obj1.free();
2745
2746 if (dict->lookup("Q", &obj1)->isInt()) {
2747 quadding = (AnnotFreeTextQuadding) obj1.getInt();
2748 } else {
2749 quadding = quaddingLeftJustified;
2750 }
2751 obj1.free();
2752
2753 if (dict->lookup("DS", &obj1)->isString()) {
2754 styleString = obj1.getString()->copy();
2755 } else {
2756 styleString = NULL;
2757 }
2758 obj1.free();
2759
2760 if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
2761 double x1, y1, x2, y2;
2762 Object obj2;
2763
2764 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
2765 obj2.free();
2766 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
2767 obj2.free();
2768 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
2769 obj2.free();
2770 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
2771 obj2.free();
2772
2773 if (obj1.arrayGetLength() == 6) {
2774 double x3, y3;
2775 (obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
2776 obj2.free();
2777 (obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
2778 obj2.free();
2779 calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
2780 } else {
2781 calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
2782 }
2783 } else {
2784 calloutLine = NULL;
2785 }
2786 obj1.free();
2787
2788 if (dict->lookup("IT", &obj1)->isName()) {
2789 const char *intentName = obj1.getName();
2790
2791 if (!strcmp(intentName, "FreeText")) {
2792 intent = intentFreeText;
2793 } else if (!strcmp(intentName, "FreeTextCallout")) {
2794 intent = intentFreeTextCallout;
2795 } else if (!strcmp(intentName, "FreeTextTypeWriter")) {
2796 intent = intentFreeTextTypeWriter;
2797 } else {
2798 intent = intentFreeText;
2799 }
2800 } else {
2801 intent = intentFreeText;
2802 }
2803 obj1.free();
2804
2805 if (dict->lookup("BS", &obj1)->isDict()) {
2806 delete border;
2807 border = new AnnotBorderBS(obj1.getDict());
2808 } else if (!border) {
2809 border = new AnnotBorderBS();
2810 }
2811 obj1.free();
2812
2813 if (dict->lookup("BE", &obj1)->isDict()) {
2814 borderEffect = new AnnotBorderEffect(obj1.getDict());
2815 } else {
2816 borderEffect = NULL;
2817 }
2818 obj1.free();
2819
2820 if (dict->lookup("RD", &obj1)->isArray()) {
2821 rectangle = parseDiffRectangle(obj1.getArray(), rect);
2822 } else {
2823 rectangle = NULL;
2824 }
2825 obj1.free();
2826
2827 if (dict->lookup("LE", &obj1)->isName()) {
2828 GooString styleName(obj1.getName());
2829 endStyle = parseAnnotLineEndingStyle(&styleName);
2830 } else {
2831 endStyle = annotLineEndingNone;
2832 }
2833 obj1.free();
2834 }
2835
setContents(GooString * new_content)2836 void AnnotFreeText::setContents(GooString *new_content) {
2837 Annot::setContents(new_content);
2838 invalidateAppearance();
2839 }
2840
setAppearanceString(GooString * new_string)2841 void AnnotFreeText::setAppearanceString(GooString *new_string) {
2842 delete appearanceString;
2843
2844 if (new_string) {
2845 appearanceString = new GooString(new_string);
2846 } else {
2847 appearanceString = new GooString();
2848 }
2849
2850 Object obj1;
2851 obj1.initString(appearanceString->copy());
2852 update ("DA", &obj1);
2853 invalidateAppearance();
2854 }
2855
setQuadding(AnnotFreeTextQuadding new_quadding)2856 void AnnotFreeText::setQuadding(AnnotFreeTextQuadding new_quadding) {
2857 Object obj1;
2858 quadding = new_quadding;
2859 obj1.initInt((int)quadding);
2860 update ("Q", &obj1);
2861 invalidateAppearance();
2862 }
2863
setStyleString(GooString * new_string)2864 void AnnotFreeText::setStyleString(GooString *new_string) {
2865 delete styleString;
2866
2867 if (new_string) {
2868 styleString = new GooString(new_string);
2869 //append the unicode marker <FE FF> if needed
2870 if (!styleString->hasUnicodeMarker()) {
2871 styleString->insert(0, 0xff);
2872 styleString->insert(0, 0xfe);
2873 }
2874 } else {
2875 styleString = new GooString();
2876 }
2877
2878 Object obj1;
2879 obj1.initString(styleString->copy());
2880 update ("DS", &obj1);
2881 }
2882
setCalloutLine(AnnotCalloutLine * line)2883 void AnnotFreeText::setCalloutLine(AnnotCalloutLine *line) {
2884 delete calloutLine;
2885
2886 Object obj1;
2887 if (line == NULL) {
2888 obj1.initNull();
2889 calloutLine = NULL;
2890 } else {
2891 double x1 = line->getX1(), y1 = line->getY1();
2892 double x2 = line->getX2(), y2 = line->getY2();
2893 Object obj2;
2894 obj1.initArray(xref);
2895 obj1.arrayAdd( obj2.initReal(x1) );
2896 obj1.arrayAdd( obj2.initReal(y1) );
2897 obj1.arrayAdd( obj2.initReal(x2) );
2898 obj1.arrayAdd( obj2.initReal(y2) );
2899
2900 AnnotCalloutMultiLine *mline = dynamic_cast<AnnotCalloutMultiLine*>(line);
2901 if (mline) {
2902 double x3 = mline->getX3(), y3 = mline->getY3();
2903 obj1.arrayAdd( obj2.initReal(x3) );
2904 obj1.arrayAdd( obj2.initReal(y3) );
2905 calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
2906 } else {
2907 calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
2908 }
2909 }
2910
2911 update("CL", &obj1);
2912 invalidateAppearance();
2913 }
2914
setIntent(AnnotFreeTextIntent new_intent)2915 void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent) {
2916 Object obj1;
2917
2918 intent = new_intent;
2919 if (new_intent == intentFreeText)
2920 obj1.initName("FreeText");
2921 else if (new_intent == intentFreeTextCallout)
2922 obj1.initName("FreeTextCallout");
2923 else // intentFreeTextTypeWriter
2924 obj1.initName("FreeTextTypeWriter");
2925 update ("IT", &obj1);
2926 }
2927
createAnnotDrawFont(XRef * xref,Object * fontResDict)2928 static GfxFont * createAnnotDrawFont(XRef * xref, Object *fontResDict)
2929 {
2930 Ref dummyRef = { -1, -1 };
2931
2932 Object baseFontObj, subtypeObj, encodingObj;
2933 baseFontObj.initName("Helvetica");
2934 subtypeObj.initName("Type0");
2935 encodingObj.initName("WinAnsiEncoding");
2936
2937 Object fontDictObj;
2938 Dict *fontDict = new Dict(xref);
2939 fontDict->decRef();
2940 fontDict->add(copyString("BaseFont"), &baseFontObj);
2941 fontDict->add(copyString("Subtype"), &subtypeObj);
2942 fontDict->add(copyString("Encoding"), &encodingObj);
2943 fontDictObj.initDict(fontDict);
2944
2945 Object fontsDictObj;
2946 Dict *fontsDict = new Dict(xref);
2947 fontsDict->decRef();
2948 fontsDict->add(copyString("AnnotDrawFont"), &fontDictObj);
2949 fontsDictObj.initDict(fontsDict);
2950
2951 Dict *dict = new Dict(xref);
2952 dict->add(copyString("Font"), &fontsDictObj);
2953
2954 fontResDict->initDict(dict);
2955 return GfxFont::makeFont(xref, "AnnotDrawFont", dummyRef, fontDict);
2956 }
2957
parseAppearanceString(GooString * da,double & fontsize,AnnotColor * & fontcolor)2958 void AnnotFreeText::parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor) {
2959 fontsize = -1;
2960 fontcolor = NULL;
2961 if (da) {
2962 GooList * daToks = new GooList();
2963 int j, i = 0;
2964
2965 // Tokenize
2966 while (i < da->getLength()) {
2967 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2968 ++i;
2969 }
2970 if (i < da->getLength()) {
2971 for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j) {
2972 }
2973 daToks->append(new GooString(da, i, j - i));
2974 i = j;
2975 }
2976 }
2977
2978 // Scan backwards: we are looking for the last set value
2979 for (i = daToks->getLength()-1; i >= 0; --i) {
2980 if (fontsize == -1) {
2981 if (!((GooString *)daToks->get(i))->cmp("Tf") && i >= 2) {
2982 // TODO: Font name
2983 fontsize = gatof(( (GooString *)daToks->get(i-1) )->getCString());
2984 }
2985 }
2986 if (fontcolor == NULL) {
2987 if (!((GooString *)daToks->get(i))->cmp("g") && i >= 1) {
2988 fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-1) )->getCString()));
2989 } else if (!((GooString *)daToks->get(i))->cmp("rg") && i >= 3) {
2990 fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-3) )->getCString()),
2991 gatof(( (GooString *)daToks->get(i-2) )->getCString()),
2992 gatof(( (GooString *)daToks->get(i-1) )->getCString()));
2993 } else if (!((GooString *)daToks->get(i))->cmp("k") && i >= 4) {
2994 fontcolor = new AnnotColor(gatof(( (GooString *)daToks->get(i-4) )->getCString()),
2995 gatof(( (GooString *)daToks->get(i-3) )->getCString()),
2996 gatof(( (GooString *)daToks->get(i-2) )->getCString()),
2997 gatof(( (GooString *)daToks->get(i-1) )->getCString()));
2998 }
2999 }
3000 }
3001 deleteGooList(daToks, GooString);
3002 }
3003 }
3004
generateFreeTextAppearance()3005 void AnnotFreeText::generateFreeTextAppearance()
3006 {
3007 double borderWidth, ca = opacity;
3008
3009 appearBuf = new GooString ();
3010 appearBuf->append ("q\n");
3011
3012 borderWidth = border->getWidth();
3013 if (borderWidth > 0)
3014 setLineStyleForBorder(border);
3015
3016 // Box size
3017 const double width = rect->x2 - rect->x1;
3018 const double height = rect->y2 - rect->y1;
3019
3020 // Parse some properties from the appearance string
3021 double fontsize;
3022 AnnotColor *fontcolor;
3023 parseAppearanceString(appearanceString, fontsize, fontcolor);
3024 // Default values
3025 if (fontsize <= 0)
3026 fontsize = 10;
3027 if (fontcolor == NULL)
3028 fontcolor = new AnnotColor(0, 0, 0); // Black
3029 if (!contents)
3030 contents = new GooString ();
3031
3032 // Draw box
3033 GBool doFill = (color && color->getSpace() != AnnotColor::colorTransparent);
3034 GBool doStroke = (borderWidth != 0);
3035 if (doFill || doStroke) {
3036 if (doStroke) {
3037 setColor(fontcolor, gFalse); // Border color: same as font color
3038 }
3039 appearBuf->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth/2, width-borderWidth, height-borderWidth);
3040 if (doFill) {
3041 setColor(color, gTrue);
3042 appearBuf->append(doStroke ? "B\n" : "f\n");
3043 } else {
3044 appearBuf->append("S\n");
3045 }
3046 }
3047
3048 // Setup text clipping
3049 const double textmargin = borderWidth * 2;
3050 const double textwidth = width - 2*textmargin;
3051 appearBuf->appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2*textmargin);
3052
3053 Object fontResDict;
3054 GfxFont *font = createAnnotDrawFont(xref, &fontResDict);
3055
3056 // Set font state
3057 setColor(fontcolor, gTrue);
3058 appearBuf->appendf ("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - fontsize * font->getDescent());
3059 appearBuf->appendf ("/AnnotDrawFont {0:.2f} Tf\n", fontsize);
3060
3061 int i = 0;
3062 double xposPrev = 0;
3063 while (i < contents->getLength()) {
3064 GooString out;
3065 double linewidth, xpos;
3066 layoutText(contents, &out, &i, font, &linewidth, textwidth/fontsize, NULL, gFalse);
3067 linewidth *= fontsize;
3068 switch (quadding) {
3069 case quaddingCentered:
3070 xpos = (textwidth - linewidth) / 2;
3071 break;
3072 case quaddingRightJustified:
3073 xpos = textwidth - linewidth;
3074 break;
3075 default: // quaddingLeftJustified:
3076 xpos = 0;
3077 break;
3078 }
3079 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize);
3080 writeString(&out, appearBuf);
3081 appearBuf->append("Tj\n");
3082 xposPrev = xpos;
3083 }
3084
3085 font->decRefCnt();
3086 delete fontcolor;
3087 appearBuf->append ("ET Q\n");
3088
3089 double bbox[4];
3090 bbox[0] = bbox[1] = 0;
3091 bbox[2] = rect->x2 - rect->x1;
3092 bbox[3] = rect->y2 - rect->y1;
3093
3094 if (ca == 1) {
3095 createForm(bbox, gFalse, &fontResDict, &appearance);
3096 } else {
3097 Object aStream, resDict;
3098
3099 createForm(bbox, gTrue, &fontResDict, &aStream);
3100 delete appearBuf;
3101
3102 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3103 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3104 createForm(bbox, gFalse, &resDict, &appearance);
3105 }
3106 delete appearBuf;
3107 }
3108
draw(Gfx * gfx,GBool printing)3109 void AnnotFreeText::draw(Gfx *gfx, GBool printing) {
3110 Object obj;
3111
3112 if (!isVisible (printing))
3113 return;
3114
3115 annotLocker();
3116 if (appearance.isNull()) {
3117 generateFreeTextAppearance();
3118 }
3119
3120 // draw the appearance stream
3121 appearance.fetch(gfx->getXRef(), &obj);
3122 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3123 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3124 obj.free();
3125 }
3126
3127 // Before retrieving the res dict, regenerate the appearance stream if needed,
3128 // because AnnotFreeText::draw needs to store font info in the res dict
getAppearanceResDict(Object * dest)3129 Object *AnnotFreeText::getAppearanceResDict(Object *dest) {
3130 if (appearance.isNull()) {
3131 generateFreeTextAppearance();
3132 }
3133 return Annot::getAppearanceResDict(dest);
3134 }
3135
3136 //------------------------------------------------------------------------
3137 // AnnotLine
3138 //------------------------------------------------------------------------
3139
AnnotLine(PDFDoc * docA,PDFRectangle * rect)3140 AnnotLine::AnnotLine(PDFDoc *docA, PDFRectangle *rect) :
3141 AnnotMarkup(docA, rect) {
3142 Object obj1;
3143
3144 type = typeLine;
3145 annotObj.dictSet ("Subtype", obj1.initName ("Line"));
3146
3147 initialize (docA, annotObj.getDict());
3148 }
3149
AnnotLine(PDFDoc * docA,Dict * dict,Object * obj)3150 AnnotLine::AnnotLine(PDFDoc *docA, Dict *dict, Object *obj) :
3151 AnnotMarkup(docA, dict, obj) {
3152 type = typeLine;
3153 initialize(docA, dict);
3154 }
3155
~AnnotLine()3156 AnnotLine::~AnnotLine() {
3157 delete coord1;
3158 delete coord2;
3159
3160 if (interiorColor)
3161 delete interiorColor;
3162
3163 if (measure)
3164 delete measure;
3165 }
3166
initialize(PDFDoc * docA,Dict * dict)3167 void AnnotLine::initialize(PDFDoc *docA, Dict *dict) {
3168 Object obj1;
3169
3170 if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
3171 Object obj2;
3172 double x1, y1, x2, y2;
3173
3174 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
3175 obj2.free();
3176 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
3177 obj2.free();
3178 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
3179 obj2.free();
3180 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
3181 obj2.free();
3182
3183 coord1 = new AnnotCoord(x1, y1);
3184 coord2 = new AnnotCoord(x2, y2);
3185 } else {
3186 coord1 = new AnnotCoord();
3187 coord2 = new AnnotCoord();
3188 }
3189 obj1.free();
3190
3191 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
3192 Object obj2;
3193
3194 if(obj1.arrayGet(0, &obj2)->isString())
3195 startStyle = parseAnnotLineEndingStyle(obj2.getString());
3196 else
3197 startStyle = annotLineEndingNone;
3198 obj2.free();
3199
3200 if(obj1.arrayGet(1, &obj2)->isString())
3201 endStyle = parseAnnotLineEndingStyle(obj2.getString());
3202 else
3203 endStyle = annotLineEndingNone;
3204 obj2.free();
3205
3206 } else {
3207 startStyle = endStyle = annotLineEndingNone;
3208 }
3209 obj1.free();
3210
3211 if (dict->lookup("IC", &obj1)->isArray()) {
3212 interiorColor = new AnnotColor(obj1.getArray());
3213 } else {
3214 interiorColor = NULL;
3215 }
3216 obj1.free();
3217
3218 if (dict->lookup("LL", &obj1)->isNum()) {
3219 leaderLineLength = obj1.getNum();
3220 } else {
3221 leaderLineLength = 0;
3222 }
3223 obj1.free();
3224
3225 if (dict->lookup("LLE", &obj1)->isNum()) {
3226 leaderLineExtension = obj1.getNum();
3227
3228 if (leaderLineExtension < 0)
3229 leaderLineExtension = 0;
3230 } else {
3231 leaderLineExtension = 0;
3232 }
3233 obj1.free();
3234
3235 if (dict->lookup("Cap", &obj1)->isBool()) {
3236 caption = obj1.getBool();
3237 } else {
3238 caption = gFalse;
3239 }
3240 obj1.free();
3241
3242 if (dict->lookup("IT", &obj1)->isName()) {
3243 const char *intentName = obj1.getName();
3244
3245 if(!strcmp(intentName, "LineArrow")) {
3246 intent = intentLineArrow;
3247 } else if(!strcmp(intentName, "LineDimension")) {
3248 intent = intentLineDimension;
3249 } else {
3250 intent = intentLineArrow;
3251 }
3252 } else {
3253 intent = intentLineArrow;
3254 }
3255 obj1.free();
3256
3257 if (dict->lookup("LLO", &obj1)->isNum()) {
3258 leaderLineOffset = obj1.getNum();
3259
3260 if (leaderLineOffset < 0)
3261 leaderLineOffset = 0;
3262 } else {
3263 leaderLineOffset = 0;
3264 }
3265 obj1.free();
3266
3267 if (dict->lookup("CP", &obj1)->isName()) {
3268 const char *captionName = obj1.getName();
3269
3270 if(!strcmp(captionName, "Inline")) {
3271 captionPos = captionPosInline;
3272 } else if(!strcmp(captionName, "Top")) {
3273 captionPos = captionPosTop;
3274 } else {
3275 captionPos = captionPosInline;
3276 }
3277 } else {
3278 captionPos = captionPosInline;
3279 }
3280 obj1.free();
3281
3282 if (dict->lookup("Measure", &obj1)->isDict()) {
3283 measure = NULL;
3284 } else {
3285 measure = NULL;
3286 }
3287 obj1.free();
3288
3289 if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
3290 Object obj2;
3291
3292 (obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
3293 captionTextHorizontal = 0);
3294 obj2.free();
3295 (obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
3296 captionTextVertical = 0);
3297 obj2.free();
3298 } else {
3299 captionTextHorizontal = captionTextVertical = 0;
3300 }
3301 obj1.free();
3302
3303 if (dict->lookup("BS", &obj1)->isDict()) {
3304 delete border;
3305 border = new AnnotBorderBS(obj1.getDict());
3306 } else if (!border) {
3307 border = new AnnotBorderBS();
3308 }
3309 obj1.free();
3310 }
3311
setContents(GooString * new_content)3312 void AnnotLine::setContents(GooString *new_content) {
3313 Annot::setContents(new_content);
3314 if (caption)
3315 invalidateAppearance();
3316 }
3317
setVertices(double x1,double y1,double x2,double y2)3318 void AnnotLine::setVertices(double x1, double y1, double x2, double y2) {
3319 Object obj1, obj2;
3320
3321 delete coord1;
3322 coord1 = new AnnotCoord(x1, y1);
3323 delete coord2;
3324 coord2 = new AnnotCoord(x2, y2);
3325
3326 obj1.initArray(xref);
3327 obj1.arrayAdd( obj2.initReal(x1) );
3328 obj1.arrayAdd( obj2.initReal(y1) );
3329 obj1.arrayAdd( obj2.initReal(x2) );
3330 obj1.arrayAdd( obj2.initReal(y2) );
3331
3332 update("L", &obj1);
3333 invalidateAppearance();
3334 }
3335
setStartEndStyle(AnnotLineEndingStyle start,AnnotLineEndingStyle end)3336 void AnnotLine::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end) {
3337 Object obj1, obj2;
3338
3339 startStyle = start;
3340 endStyle = end;
3341
3342 obj1.initArray(xref);
3343 obj1.arrayAdd( obj2.initName(convertAnnotLineEndingStyle( startStyle )) );
3344 obj1.arrayAdd( obj2.initName(convertAnnotLineEndingStyle( endStyle )) );
3345
3346 update("LE", &obj1);
3347 invalidateAppearance();
3348 }
3349
setInteriorColor(AnnotColor * new_color)3350 void AnnotLine::setInteriorColor(AnnotColor *new_color) {
3351 delete interiorColor;
3352
3353 if (new_color) {
3354 Object obj1;
3355 new_color->writeToObject(xref, &obj1);
3356 update ("IC", &obj1);
3357 interiorColor = new_color;
3358 } else {
3359 interiorColor = NULL;
3360 }
3361 invalidateAppearance();
3362 }
3363
setLeaderLineLength(double len)3364 void AnnotLine::setLeaderLineLength(double len) {
3365 Object obj1;
3366
3367 leaderLineLength = len;
3368 obj1.initReal(len);
3369 update ("LL", &obj1);
3370 invalidateAppearance();
3371 }
3372
setLeaderLineExtension(double len)3373 void AnnotLine::setLeaderLineExtension(double len) {
3374 Object obj1;
3375
3376 leaderLineExtension = len;
3377 obj1.initReal(len);
3378 update ("LLE", &obj1);
3379
3380 // LL is required if LLE is present
3381 obj1.initReal(leaderLineLength);
3382 update ("LL", &obj1);
3383 invalidateAppearance();
3384 }
3385
setCaption(bool new_cap)3386 void AnnotLine::setCaption(bool new_cap) {
3387 Object obj1;
3388
3389 caption = new_cap;
3390 obj1.initBool(new_cap);
3391 update ("Cap", &obj1);
3392 invalidateAppearance();
3393 }
3394
setIntent(AnnotLineIntent new_intent)3395 void AnnotLine::setIntent(AnnotLineIntent new_intent) {
3396 Object obj1;
3397
3398 intent = new_intent;
3399 if (new_intent == intentLineArrow)
3400 obj1.initName("LineArrow");
3401 else // intentLineDimension
3402 obj1.initName("LineDimension");
3403 update ("IT", &obj1);
3404 }
3405
generateLineAppearance()3406 void AnnotLine::generateLineAppearance()
3407 {
3408 double borderWidth, ca = opacity;
3409
3410 appearBBox = new AnnotAppearanceBBox(rect);
3411 appearBuf = new GooString ();
3412 appearBuf->append ("q\n");
3413 if (color) {
3414 setColor(color, gFalse);
3415 }
3416
3417 setLineStyleForBorder(border);
3418 borderWidth = border->getWidth();
3419 appearBBox->setBorderWidth(std::max(1., borderWidth));
3420
3421 const double x1 = coord1->getX();
3422 const double y1 = coord1->getY();
3423 const double x2 = coord2->getX();
3424 const double y2 = coord2->getY();
3425
3426 // Main segment length
3427 const double main_len = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
3428
3429 // Main segment becomes positive x direction, coord1 becomes (0,0)
3430 Matrix matr;
3431 const double angle = atan2(y2 - y1, x2 - x1);
3432 matr.m[0] = matr.m[3] = cos(angle);
3433 matr.m[1] = sin(angle);
3434 matr.m[2] = -matr.m[1];
3435 matr.m[4] = x1-rect->x1;
3436 matr.m[5] = y1-rect->y1;
3437
3438 double tx, ty, captionwidth = 0, captionheight = 0;
3439 AnnotLineCaptionPos actualCaptionPos = captionPos;
3440 const double fontsize = 9;
3441 const double captionhmargin = 2; // Left and right margin (inline caption only)
3442 const double captionmaxwidth = main_len - 2 * captionhmargin;
3443
3444 Object fontResDict;
3445 GfxFont *font;
3446
3447 // Calculate caption width and height
3448 if (caption) {
3449 font = createAnnotDrawFont(xref, &fontResDict);
3450 int lines = 0;
3451 int i = 0;
3452 while (i < contents->getLength()) {
3453 GooString out;
3454 double linewidth;
3455 layoutText(contents, &out, &i, font, &linewidth, 0, NULL, gFalse);
3456 linewidth *= fontsize;
3457 if (linewidth > captionwidth) {
3458 captionwidth = linewidth;
3459 }
3460 ++lines;
3461 }
3462 captionheight = lines * fontsize;
3463 // If text is longer than available space, turn into captionPosTop
3464 if (captionwidth > captionmaxwidth) {
3465 actualCaptionPos = captionPosTop;
3466 }
3467 } else {
3468 fontResDict.initNull();
3469 font = NULL;
3470 }
3471
3472 // Draw main segment
3473 matr.transform (0, leaderLineLength, &tx, &ty);
3474 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty);
3475 appearBBox->extendTo (tx, ty);
3476
3477 if (captionwidth != 0 && actualCaptionPos == captionPosInline) { // Break in the middle
3478 matr.transform ((main_len-captionwidth)/2 - captionhmargin, leaderLineLength, &tx, &ty);
3479 appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty);
3480
3481 matr.transform ((main_len+captionwidth)/2 + captionhmargin, leaderLineLength, &tx, &ty);
3482 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty);
3483 }
3484
3485 matr.transform (main_len, leaderLineLength, &tx, &ty);
3486 appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty);
3487 appearBBox->extendTo (tx, ty);
3488
3489 // TODO: Line endings
3490
3491 // Draw caption text
3492 if (caption) {
3493 double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords
3494 if (actualCaptionPos == captionPosInline) {
3495 tly = leaderLineLength + captionheight / 2;
3496 } else {
3497 tly = leaderLineLength + captionheight + 2*borderWidth;
3498 }
3499
3500 tlx += captionTextHorizontal;
3501 tly += captionTextVertical;
3502
3503 // Adjust bounding box
3504 matr.transform (tlx, tly-captionheight, &tx, &ty);
3505 appearBBox->extendTo (tx, ty);
3506 matr.transform (tlx+captionwidth, tly-captionheight, &tx, &ty);
3507 appearBBox->extendTo (tx, ty);
3508 matr.transform (tlx+captionwidth, tly, &tx, &ty);
3509 appearBBox->extendTo (tx, ty);
3510 matr.transform (tlx, tly, &tx, &ty);
3511 appearBBox->extendTo (tx, ty);
3512
3513 // Setup text state (reusing transformed top-left coord)
3514 appearBuf->appendf ("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize); // Font color: black
3515 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} Tm\n",
3516 matr.m[0], matr.m[1], matr.m[2], matr.m[3], tx, ty);
3517 appearBuf->appendf ("0 {0:.2f} Td\n", -fontsize * font->getDescent());
3518 // Draw text
3519 int i = 0;
3520 double xposPrev = 0;
3521 while (i < contents->getLength()) {
3522 GooString out;
3523 double linewidth, xpos;
3524 layoutText(contents, &out, &i, font, &linewidth, 0, NULL, gFalse);
3525 linewidth *= fontsize;
3526 xpos = (captionwidth - linewidth) / 2;
3527 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize);
3528 writeString(&out, appearBuf);
3529 appearBuf->append ("Tj\n");
3530 xposPrev = xpos;
3531 }
3532 appearBuf->append ("ET\n");
3533 font->decRefCnt();
3534 }
3535
3536 // Draw leader lines
3537 double ll_len = fabs(leaderLineLength) + leaderLineExtension;
3538 double sign = leaderLineLength >= 0 ? 1 : -1;
3539 if (ll_len != 0) {
3540 matr.transform (0, 0, &tx, &ty);
3541 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty);
3542 appearBBox->extendTo (tx, ty);
3543 matr.transform (0, sign*ll_len, &tx, &ty);
3544 appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty);
3545 appearBBox->extendTo (tx, ty);
3546
3547 matr.transform (main_len, 0, &tx, &ty);
3548 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", tx, ty);
3549 appearBBox->extendTo (tx, ty);
3550 matr.transform (main_len, sign*ll_len, &tx, &ty);
3551 appearBuf->appendf ("{0:.2f} {1:.2f} l S\n", tx, ty);
3552 appearBBox->extendTo (tx, ty);
3553 }
3554
3555 appearBuf->append ("Q\n");
3556
3557 double bbox[4];
3558 appearBBox->getBBoxRect(bbox);
3559 if (ca == 1) {
3560 createForm(bbox, gFalse, &fontResDict, &appearance);
3561 } else {
3562 Object aStream, resDict;
3563
3564 createForm(bbox, gTrue, &fontResDict, &aStream);
3565 delete appearBuf;
3566
3567 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3568 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3569 createForm(bbox, gFalse, &resDict, &appearance);
3570 }
3571 delete appearBuf;
3572 }
3573
draw(Gfx * gfx,GBool printing)3574 void AnnotLine::draw(Gfx *gfx, GBool printing) {
3575 Object obj;
3576
3577 if (!isVisible (printing))
3578 return;
3579
3580 annotLocker();
3581 if (appearance.isNull()) {
3582 generateLineAppearance();
3583 }
3584
3585 // draw the appearance stream
3586 appearance.fetch(gfx->getXRef(), &obj);
3587 if (appearBBox) {
3588 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3589 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
3590 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
3591 getRotation());
3592 } else {
3593 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3594 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3595 }
3596 obj.free();
3597 }
3598
3599 // Before retrieving the res dict, regenerate the appearance stream if needed,
3600 // because AnnotLine::draw may need to store font info in the res dict
getAppearanceResDict(Object * dest)3601 Object *AnnotLine::getAppearanceResDict(Object *dest) {
3602 if (appearance.isNull()) {
3603 generateLineAppearance();
3604 }
3605 return Annot::getAppearanceResDict(dest);
3606 }
3607
3608 //------------------------------------------------------------------------
3609 // AnnotTextMarkup
3610 //------------------------------------------------------------------------
AnnotTextMarkup(PDFDoc * docA,PDFRectangle * rect,AnnotSubtype subType)3611 AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
3612 AnnotMarkup(docA, rect) {
3613 Object obj1;
3614
3615 switch (subType) {
3616 case typeHighlight:
3617 annotObj.dictSet ("Subtype", obj1.initName ("Highlight"));
3618 break;
3619 case typeUnderline:
3620 annotObj.dictSet ("Subtype", obj1.initName ("Underline"));
3621 break;
3622 case typeSquiggly:
3623 annotObj.dictSet ("Subtype", obj1.initName ("Squiggly"));
3624 break;
3625 case typeStrikeOut:
3626 annotObj.dictSet ("Subtype", obj1.initName ("StrikeOut"));
3627 break;
3628 default:
3629 assert (0 && "Invalid subtype for AnnotTextMarkup\n");
3630 }
3631
3632 // Store dummy quadrilateral with null coordinates
3633 Object obj2, obj3;
3634 obj2.initArray (doc->getXRef());
3635 for (int i = 0; i < 4*2; ++i) {
3636 obj2.arrayAdd (obj3.initReal (0));
3637 }
3638 annotObj.dictSet ("QuadPoints", &obj2);
3639
3640 initialize(docA, annotObj.getDict());
3641 }
3642
AnnotTextMarkup(PDFDoc * docA,Dict * dict,Object * obj)3643 AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, Dict *dict, Object *obj) :
3644 AnnotMarkup(docA, dict, obj) {
3645 // the real type will be read in initialize()
3646 type = typeHighlight;
3647 initialize(docA, dict);
3648 }
3649
initialize(PDFDoc * docA,Dict * dict)3650 void AnnotTextMarkup::initialize(PDFDoc *docA, Dict *dict) {
3651 Object obj1;
3652
3653 if (dict->lookup("Subtype", &obj1)->isName()) {
3654 GooString typeName(obj1.getName());
3655 if (!typeName.cmp("Highlight")) {
3656 type = typeHighlight;
3657 } else if (!typeName.cmp("Underline")) {
3658 type = typeUnderline;
3659 } else if (!typeName.cmp("Squiggly")) {
3660 type = typeSquiggly;
3661 } else if (!typeName.cmp("StrikeOut")) {
3662 type = typeStrikeOut;
3663 }
3664 }
3665 obj1.free();
3666
3667 if(dict->lookup("QuadPoints", &obj1)->isArray()) {
3668 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
3669 } else {
3670 error(errSyntaxError, -1, "Bad Annot Text Markup QuadPoints");
3671 quadrilaterals = NULL;
3672 ok = gFalse;
3673 }
3674 obj1.free();
3675 }
3676
~AnnotTextMarkup()3677 AnnotTextMarkup::~AnnotTextMarkup() {
3678 if(quadrilaterals) {
3679 delete quadrilaterals;
3680 }
3681 }
3682
setType(AnnotSubtype new_type)3683 void AnnotTextMarkup::setType(AnnotSubtype new_type) {
3684 Object obj1;
3685
3686 switch (new_type) {
3687 case typeHighlight:
3688 obj1.initName("Highlight");
3689 break;
3690 case typeUnderline:
3691 obj1.initName("Underline");
3692 break;
3693 case typeSquiggly:
3694 obj1.initName("Squiggly");
3695 break;
3696 case typeStrikeOut:
3697 obj1.initName("StrikeOut");
3698 break;
3699 default:
3700 assert(!"Invalid subtype");
3701 }
3702
3703 type = new_type;
3704 update("Subtype", &obj1);
3705 invalidateAppearance();
3706 }
3707
setQuadrilaterals(AnnotQuadrilaterals * quadPoints)3708 void AnnotTextMarkup::setQuadrilaterals(AnnotQuadrilaterals *quadPoints) {
3709 Object obj1, obj2;
3710 obj1.initArray (xref);
3711
3712 for (int i = 0; i < quadPoints->getQuadrilateralsLength(); ++i) {
3713 obj1.arrayAdd (obj2.initReal (quadPoints->getX1(i)));
3714 obj1.arrayAdd (obj2.initReal (quadPoints->getY1(i)));
3715 obj1.arrayAdd (obj2.initReal (quadPoints->getX2(i)));
3716 obj1.arrayAdd (obj2.initReal (quadPoints->getY2(i)));
3717 obj1.arrayAdd (obj2.initReal (quadPoints->getX3(i)));
3718 obj1.arrayAdd (obj2.initReal (quadPoints->getY3(i)));
3719 obj1.arrayAdd (obj2.initReal (quadPoints->getX4(i)));
3720 obj1.arrayAdd (obj2.initReal (quadPoints->getY4(i)));
3721 }
3722
3723 delete quadrilaterals;
3724 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
3725
3726 annotObj.dictSet ("QuadPoints", &obj1);
3727 invalidateAppearance();
3728 }
3729
draw(Gfx * gfx,GBool printing)3730 void AnnotTextMarkup::draw(Gfx *gfx, GBool printing) {
3731 Object obj;
3732 double ca = 1;
3733 int i;
3734 Object obj1, obj2;
3735
3736 if (!isVisible (printing))
3737 return;
3738
3739 annotLocker();
3740 if (appearance.isNull() || type == typeHighlight) {
3741 GBool blendMultiply = gTrue;
3742 ca = opacity;
3743
3744 appearBuf = new GooString ();
3745 appearBuf->append ("q\n");
3746
3747 /* Adjust BBox */
3748 delete appearBBox;
3749 appearBBox = new AnnotAppearanceBBox(rect);
3750 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3751 appearBBox->extendTo (quadrilaterals->getX1(i) - rect->x1, quadrilaterals->getY1(i) - rect->y1);
3752 appearBBox->extendTo (quadrilaterals->getX2(i) - rect->x1, quadrilaterals->getY2(i) - rect->y1);
3753 appearBBox->extendTo (quadrilaterals->getX3(i) - rect->x1, quadrilaterals->getY3(i) - rect->y1);
3754 appearBBox->extendTo (quadrilaterals->getX4(i) - rect->x1, quadrilaterals->getY4(i) - rect->y1);
3755 }
3756
3757 switch (type) {
3758 case typeUnderline:
3759 if (color) {
3760 setColor(color, gFalse);
3761 }
3762 appearBuf->append ("[] 0 d 1 w\n");
3763
3764 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3765 double x3, y3, x4, y4;
3766
3767 x3 = quadrilaterals->getX3(i);
3768 y3 = quadrilaterals->getY3(i);
3769 x4 = quadrilaterals->getX4(i);
3770 y4 = quadrilaterals->getY4(i);
3771
3772 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x3, y3);
3773 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x4, y4);
3774 appearBuf->append ("S\n");
3775 }
3776 break;
3777 case typeStrikeOut:
3778 if (color) {
3779 setColor(color, gFalse);
3780 }
3781 blendMultiply = gFalse;
3782 appearBuf->append ("[] 0 d 1 w\n");
3783
3784 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3785 double x1, y1, x2, y2;
3786 double x3, y3, x4, y4;
3787
3788 x1 = quadrilaterals->getX1(i);
3789 y1 = quadrilaterals->getY1(i);
3790 x2 = quadrilaterals->getX2(i);
3791 y2 = quadrilaterals->getY2(i);
3792
3793 x3 = quadrilaterals->getX3(i);
3794 y3 = quadrilaterals->getY3(i);
3795 x4 = quadrilaterals->getX4(i);
3796 y4 = quadrilaterals->getY4(i);
3797
3798 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", (x1+x3)/2., (y1+y3)/2.);
3799 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", (x2+x4)/2., (y2+y4)/2.);
3800 appearBuf->append ("S\n");
3801 }
3802 break;
3803 case typeSquiggly:
3804 if (color) {
3805 setColor(color, gFalse);
3806 }
3807 appearBuf->append ("[] 0 d 1 w\n");
3808
3809 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3810 double x1, y1, x2, y3;
3811 double h6;
3812
3813 x1 = quadrilaterals->getX1(i);
3814 y1 = quadrilaterals->getY1(i);
3815 x2 = quadrilaterals->getX2(i);
3816 y3 = quadrilaterals->getY3(i);
3817 h6 = (y1 - y3) / 6.0;
3818
3819 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x1, y3+h6);
3820 bool down = false;
3821 do {
3822 down = !down; // Zigzag line
3823 x1 += 2;
3824 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x1, y3 + (down ? 0 : h6));
3825 } while (x1 < x2);
3826 appearBuf->append ("S\n");
3827 }
3828 break;
3829 default:
3830 case typeHighlight:
3831 appearance.free();
3832
3833 if (color)
3834 setColor(color, gTrue);
3835
3836 double biggestBorder = 0;
3837 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3838 double x1, y1, x2, y2, x3, y3, x4, y4;
3839 double h4;
3840
3841 x1 = quadrilaterals->getX1(i);
3842 y1 = quadrilaterals->getY1(i);
3843 x2 = quadrilaterals->getX2(i);
3844 y2 = quadrilaterals->getY2(i);
3845 x3 = quadrilaterals->getX3(i);
3846 y3 = quadrilaterals->getY3(i);
3847 x4 = quadrilaterals->getX4(i);
3848 y4 = quadrilaterals->getY4(i);
3849 h4 = fabs(y1 - y3) / 4.0;
3850
3851 if (h4 > biggestBorder) {
3852 biggestBorder = h4;
3853 }
3854
3855 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x3, y3);
3856 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
3857 x3 - h4, y3 + h4, x1 - h4, y1 - h4, x1, y1);
3858 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x2, y2);
3859 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
3860 x2 + h4, y2 - h4, x4 + h4, y4 + h4, x4, y4);
3861 appearBuf->append ("f\n");
3862 }
3863 appearBBox->setBorderWidth(biggestBorder);
3864 break;
3865 }
3866 appearBuf->append ("Q\n");
3867
3868 Object aStream, resDict;
3869 double bbox[4];
3870 bbox[0] = appearBBox->getPageXMin();
3871 bbox[1] = appearBBox->getPageYMin();
3872 bbox[2] = appearBBox->getPageXMax();
3873 bbox[3] = appearBBox->getPageYMax();
3874 createForm(bbox, gTrue, NULL, &aStream);
3875 delete appearBuf;
3876
3877 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3878 createResourcesDict("Fm0", &aStream, "GS0", 1, blendMultiply ? "Multiply" : NULL, &resDict);
3879 if (ca == 1) {
3880 createForm(bbox, gFalse, &resDict, &appearance);
3881 } else {
3882 createForm(bbox, gTrue, &resDict, &aStream);
3883 delete appearBuf;
3884
3885 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
3886 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
3887 createForm(bbox, gFalse, &resDict, &appearance);
3888 }
3889 delete appearBuf;
3890 }
3891
3892 // draw the appearance stream
3893 appearance.fetch(gfx->getXRef(), &obj);
3894 if (appearBBox) {
3895 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3896 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
3897 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
3898 getRotation());
3899 } else {
3900 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3901 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3902 }
3903 obj.free();
3904 }
3905
3906 //------------------------------------------------------------------------
3907 // AnnotWidget
3908 //------------------------------------------------------------------------
3909
AnnotWidget(PDFDoc * docA,Dict * dict,Object * obj)3910 AnnotWidget::AnnotWidget(PDFDoc *docA, Dict *dict, Object *obj) :
3911 Annot(docA, dict, obj) {
3912 type = typeWidget;
3913 field = NULL;
3914 initialize(docA, dict);
3915 }
3916
AnnotWidget(PDFDoc * docA,Dict * dict,Object * obj,FormField * fieldA)3917 AnnotWidget::AnnotWidget(PDFDoc *docA, Dict *dict, Object *obj, FormField *fieldA) :
3918 Annot(docA, dict, obj) {
3919 type = typeWidget;
3920 field = fieldA;
3921 initialize(docA, dict);
3922 }
3923
~AnnotWidget()3924 AnnotWidget::~AnnotWidget() {
3925 if (appearCharacs)
3926 delete appearCharacs;
3927
3928 if (action)
3929 delete action;
3930
3931 additionalActions.free();
3932
3933 if (parent)
3934 delete parent;
3935 }
3936
initialize(PDFDoc * docA,Dict * dict)3937 void AnnotWidget::initialize(PDFDoc *docA, Dict *dict) {
3938 Object obj1;
3939
3940 form = doc->getCatalog()->getForm();
3941
3942 if(dict->lookup("H", &obj1)->isName()) {
3943 const char *modeName = obj1.getName();
3944
3945 if(!strcmp(modeName, "N")) {
3946 mode = highlightModeNone;
3947 } else if(!strcmp(modeName, "O")) {
3948 mode = highlightModeOutline;
3949 } else if(!strcmp(modeName, "P") || !strcmp(modeName, "T")) {
3950 mode = highlightModePush;
3951 } else {
3952 mode = highlightModeInvert;
3953 }
3954 } else {
3955 mode = highlightModeInvert;
3956 }
3957 obj1.free();
3958
3959 if(dict->lookup("MK", &obj1)->isDict()) {
3960 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
3961 } else {
3962 appearCharacs = NULL;
3963 }
3964 obj1.free();
3965
3966 action = NULL;
3967 if(dict->lookup("A", &obj1)->isDict()) {
3968 action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
3969 }
3970 obj1.free();
3971
3972 dict->lookupNF("AA", &additionalActions);
3973
3974 if(dict->lookup("Parent", &obj1)->isDict()) {
3975 parent = NULL;
3976 } else {
3977 parent = NULL;
3978 }
3979 obj1.free();
3980
3981 if (dict->lookup("BS", &obj1)->isDict()) {
3982 delete border;
3983 border = new AnnotBorderBS(obj1.getDict());
3984 }
3985 obj1.free();
3986
3987 updatedAppearanceStream.num = updatedAppearanceStream.gen = -1;
3988 }
3989
getAdditionalAction(AdditionalActionsType type)3990 LinkAction* AnnotWidget::getAdditionalAction(AdditionalActionsType type)
3991 {
3992 return ::getAdditionalAction(type, &additionalActions, doc);
3993 }
3994
getFormAdditionalAction(FormAdditionalActionsType type)3995 LinkAction* AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType type)
3996 {
3997 return ::getFormAdditionalAction(type, &additionalActions, doc);
3998 }
3999
4000 // Grand unified handler for preparing text strings to be drawn into form
4001 // fields. Takes as input a text string (in PDFDocEncoding or UTF-16).
4002 // Converts some or all of this string to the appropriate encoding for the
4003 // specified font, and computes the width of the text. Can optionally stop
4004 // converting when a specified width has been reached, to perform line-breaking
4005 // for multi-line fields.
4006 //
4007 // Parameters:
4008 // text: input text string to convert
4009 // outBuf: buffer for writing re-encoded string
4010 // i: index at which to start converting; will be updated to point just after
4011 // last character processed
4012 // font: the font which will be used for display
4013 // width: computed width (unscaled by font size) will be stored here
4014 // widthLimit: if non-zero, stop converting to keep width under this value
4015 // (should be scaled down by font size)
4016 // charCount: count of number of characters will be stored here
4017 // noReencode: if set, do not try to translate the character encoding
4018 // (useful for Zapf Dingbats or other unusual encodings)
4019 // can only be used with simple fonts, not CID-keyed fonts
4020 //
4021 // TODO: Handle surrogate pairs in UTF-16.
4022 // Should be able to generate output for any CID-keyed font.
4023 // Doesn't handle vertical fonts--should it?
layoutText(GooString * text,GooString * outBuf,int * i,GfxFont * font,double * width,double widthLimit,int * charCount,GBool noReencode)4024 void Annot::layoutText(GooString *text, GooString *outBuf, int *i,
4025 GfxFont *font, double *width, double widthLimit,
4026 int *charCount, GBool noReencode)
4027 {
4028 CharCode c;
4029 Unicode uChar, *uAux;
4030 double w = 0.0;
4031 int uLen, n;
4032 double dx, dy, ox, oy;
4033 GBool unicode = text->hasUnicodeMarker();
4034 GBool spacePrev; // previous character was a space
4035
4036 // State for backtracking when more text has been processed than fits within
4037 // widthLimit. We track for both input (text) and output (outBuf) the offset
4038 // to the first character to discard.
4039 //
4040 // We keep track of several points:
4041 // 1 - end of previous completed word which fits
4042 // 2 - previous character which fit
4043 int last_i1, last_i2, last_o1, last_o2;
4044
4045 if (unicode && text->getLength() % 2 != 0) {
4046 error(errSyntaxError, -1, "AnnotWidget::layoutText, bad unicode string");
4047 return;
4048 }
4049
4050 // skip Unicode marker on string if needed
4051 if (unicode && *i == 0)
4052 *i = 2;
4053
4054 // Start decoding and copying characters, until either:
4055 // we reach the end of the string
4056 // we reach the maximum width
4057 // we reach a newline character
4058 // As we copy characters, keep track of the last full word to fit, so that we
4059 // can backtrack if we exceed the maximum width.
4060 last_i1 = last_i2 = *i;
4061 last_o1 = last_o2 = 0;
4062 spacePrev = gFalse;
4063 outBuf->clear();
4064
4065 while (*i < text->getLength()) {
4066 last_i2 = *i;
4067 last_o2 = outBuf->getLength();
4068
4069 if (unicode) {
4070 uChar = (unsigned char)(text->getChar(*i)) << 8;
4071 uChar += (unsigned char)(text->getChar(*i + 1));
4072 *i += 2;
4073 } else {
4074 if (noReencode)
4075 uChar = text->getChar(*i) & 0xff;
4076 else
4077 uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
4078 *i += 1;
4079 }
4080
4081 // Explicit line break?
4082 if (uChar == '\r' || uChar == '\n') {
4083 // Treat a <CR><LF> sequence as a single line break
4084 if (uChar == '\r' && *i < text->getLength()) {
4085 if (unicode && text->getChar(*i) == '\0'
4086 && text->getChar(*i + 1) == '\n')
4087 *i += 2;
4088 else if (!unicode && text->getChar(*i) == '\n')
4089 *i += 1;
4090 }
4091
4092 break;
4093 }
4094
4095 if (noReencode) {
4096 outBuf->append(uChar);
4097 } else {
4098 CharCodeToUnicode *ccToUnicode = font->getToUnicode();
4099 if (!ccToUnicode) {
4100 // This assumes an identity CMap.
4101 outBuf->append((uChar >> 8) & 0xff);
4102 outBuf->append(uChar & 0xff);
4103 } else if (ccToUnicode->mapToCharCode(&uChar, &c, 2)) {
4104 ccToUnicode->decRefCnt();
4105 if (font->isCIDFont()) {
4106 // TODO: This assumes an identity CMap. It should be extended to
4107 // handle the general case.
4108 outBuf->append((c >> 8) & 0xff);
4109 outBuf->append(c & 0xff);
4110 } else {
4111 // 8-bit font
4112 outBuf->append(c);
4113 }
4114 } else {
4115 ccToUnicode->decRefCnt();
4116 error(errSyntaxError, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar);
4117 }
4118 }
4119
4120 // If we see a space, then we have a linebreak opportunity.
4121 if (uChar == ' ') {
4122 last_i1 = *i;
4123 if (!spacePrev)
4124 last_o1 = last_o2;
4125 spacePrev = gTrue;
4126 } else {
4127 spacePrev = gFalse;
4128 }
4129
4130 // Compute width of character just output
4131 if (outBuf->getLength() > last_o2) {
4132 dx = 0.0;
4133 font->getNextChar(outBuf->getCString() + last_o2,
4134 outBuf->getLength() - last_o2,
4135 &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4136 w += dx;
4137 }
4138
4139 // Current line over-full now?
4140 if (widthLimit > 0.0 && w > widthLimit) {
4141 if (last_o1 > 0) {
4142 // Back up to the previous word which fit, if there was a previous
4143 // word.
4144 *i = last_i1;
4145 outBuf->del(last_o1, outBuf->getLength() - last_o1);
4146 } else if (last_o2 > 0) {
4147 // Otherwise, back up to the previous character (in the only word on
4148 // this line)
4149 *i = last_i2;
4150 outBuf->del(last_o2, outBuf->getLength() - last_o2);
4151 } else {
4152 // Otherwise, we were trying to fit the first character; include it
4153 // anyway even if it overflows the space--no updates to make.
4154 }
4155 break;
4156 }
4157 }
4158
4159 // If splitting input into lines because we reached the width limit, then
4160 // consume any remaining trailing spaces that would go on this line from the
4161 // input. If in doing so we reach a newline, consume that also. This code
4162 // won't run if we stopped at a newline above, since in that case w <=
4163 // widthLimit still.
4164 if (widthLimit > 0.0 && w > widthLimit) {
4165 if (unicode) {
4166 while (*i < text->getLength()
4167 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
4168 *i += 2;
4169 if (*i < text->getLength()
4170 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
4171 *i += 2;
4172 if (*i < text->getLength()
4173 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
4174 *i += 2;
4175 } else {
4176 while (*i < text->getLength() && text->getChar(*i) == ' ')
4177 *i += 1;
4178 if (*i < text->getLength() && text->getChar(*i) == '\r')
4179 *i += 1;
4180 if (*i < text->getLength() && text->getChar(*i) == '\n')
4181 *i += 1;
4182 }
4183 }
4184
4185 // Compute the actual width and character count of the final string, based on
4186 // breakpoint, if this information is requested by the caller.
4187 if (width != NULL || charCount != NULL) {
4188 char *s = outBuf->getCString();
4189 int len = outBuf->getLength();
4190
4191 if (width != NULL)
4192 *width = 0.0;
4193 if (charCount != NULL)
4194 *charCount = 0;
4195
4196 while (len > 0) {
4197 dx = 0.0;
4198 n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4199
4200 if (n == 0) {
4201 break;
4202 }
4203
4204 if (width != NULL)
4205 *width += dx;
4206 if (charCount != NULL)
4207 *charCount += 1;
4208
4209 s += n;
4210 len -= n;
4211 }
4212 }
4213 }
4214
4215 // Copy the given string to appearBuf, adding parentheses around it and
4216 // escaping characters as appropriate.
writeString(GooString * str,GooString * appearBuf)4217 void Annot::writeString(GooString *str, GooString *appearBuf)
4218 {
4219 char c;
4220 int i;
4221
4222 appearBuf->append('(');
4223
4224 for (i = 0; i < str->getLength(); ++i) {
4225 c = str->getChar(i);
4226 if (c == '(' || c == ')' || c == '\\') {
4227 appearBuf->append('\\');
4228 appearBuf->append(c);
4229 } else if (c < 0x20) {
4230 appearBuf->appendf("\\{0:03o}", (unsigned char)c);
4231 } else {
4232 appearBuf->append(c);
4233 }
4234 }
4235
4236 appearBuf->append(')');
4237 }
4238
4239 // Draw the variable text or caption for a field.
drawText(GooString * text,GooString * da,GfxResources * resources,GBool multiline,int comb,int quadding,GBool txField,GBool forceZapfDingbats,GBool password)4240 void AnnotWidget::drawText(GooString *text, GooString *da, GfxResources *resources,
4241 GBool multiline, int comb, int quadding,
4242 GBool txField, GBool forceZapfDingbats,
4243 GBool password) {
4244 GooList *daToks;
4245 GooString *tok, *convertedText;
4246 GfxFont *font;
4247 double dx, dy;
4248 double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
4249 int tfPos, tmPos, i, j;
4250 int rot;
4251 GBool freeText = gFalse; // true if text should be freed before return
4252 GBool freeFont = gFalse;
4253
4254 //~ if there is no MK entry, this should use the existing content stream,
4255 //~ and only replace the marked content portion of it
4256 //~ (this is only relevant for Tx fields)
4257
4258 // parse the default appearance string
4259 tfPos = tmPos = -1;
4260 if (da) {
4261 daToks = new GooList();
4262 i = 0;
4263 while (i < da->getLength()) {
4264 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
4265 ++i;
4266 }
4267 if (i < da->getLength()) {
4268 for (j = i + 1;
4269 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
4270 ++j) ;
4271 daToks->append(new GooString(da, i, j - i));
4272 i = j;
4273 }
4274 }
4275 for (i = 2; i < daToks->getLength(); ++i) {
4276 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
4277 tfPos = i - 2;
4278 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
4279 tmPos = i - 6;
4280 }
4281 }
4282 } else {
4283 daToks = NULL;
4284 }
4285
4286 // force ZapfDingbats
4287 if (forceZapfDingbats) {
4288 if (tfPos >= 0) {
4289 tok = (GooString *)daToks->get(tfPos);
4290 if (tok->cmp("/ZaDb")) {
4291 tok->clear();
4292 tok->append("/ZaDb");
4293 }
4294 }
4295 }
4296 // get the font and font size
4297 font = NULL;
4298 fontSize = 0;
4299 if (tfPos >= 0) {
4300 tok = (GooString *)daToks->get(tfPos);
4301 if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
4302 if (!resources || !(font = resources->lookupFont(tok->getCString() + 1))) {
4303 if (forceZapfDingbats) {
4304 // We are forcing ZaDb but the font does not exist
4305 // so create a fake one
4306 Ref r; // dummy Ref, it's not used at all in this codepath
4307 r.num = -1;
4308 r.gen = -1;
4309 Dict *d = new Dict(xref);
4310 font = new Gfx8BitFont(xref, "ZaDb", r, new GooString("ZapfDingbats"), fontType1, r, d);
4311 delete d;
4312 freeFont = gTrue;
4313 addDingbatsResource = gTrue;
4314 } else {
4315 error(errSyntaxError, -1, "Unknown font in field's DA string");
4316 }
4317 }
4318 } else {
4319 error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
4320 }
4321 tok = (GooString *)daToks->get(tfPos + 1);
4322 fontSize = gatof(tok->getCString());
4323 } else {
4324 error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4325 }
4326 if (!font) {
4327 if (daToks) {
4328 deleteGooList(daToks, GooString);
4329 }
4330 return;
4331 }
4332
4333 // get the border width
4334 borderWidth = border ? border->getWidth() : 0;
4335
4336 // for a password field, replace all characters with asterisks
4337 if (password) {
4338 int len;
4339 if (text->hasUnicodeMarker())
4340 len = (text->getLength() - 2) / 2;
4341 else
4342 len = text->getLength();
4343
4344 text = new GooString;
4345 for (i = 0; i < len; ++i)
4346 text->append('*');
4347 freeText = gTrue;
4348 }
4349
4350 convertedText = new GooString;
4351
4352 // setup
4353 if (txField) {
4354 appearBuf->append("/Tx BMC\n");
4355 }
4356 appearBuf->append("q\n");
4357 rot = appearCharacs ? appearCharacs->getRotation() : 0;
4358 switch (rot) {
4359 case 90:
4360 appearBuf->appendf("0 1 -1 0 {0:.2f} 0 cm\n", rect->x2 - rect->x1);
4361 dx = rect->y2 - rect->y1;
4362 dy = rect->x2 - rect->x1;
4363 break;
4364 case 180:
4365 appearBuf->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n",
4366 rect->x2 - rect->x1, rect->y2 - rect->y1);
4367 dx = rect->x2 - rect->y2;
4368 dy = rect->y2 - rect->y1;
4369 break;
4370 case 270:
4371 appearBuf->appendf("0 -1 1 0 0 {0:.2f} cm\n", rect->y2 - rect->y1);
4372 dx = rect->y2 - rect->y1;
4373 dy = rect->x2 - rect->x1;
4374 break;
4375 default: // assume rot == 0
4376 dx = rect->x2 - rect->x1;
4377 dy = rect->y2 - rect->y1;
4378 break;
4379 }
4380 appearBuf->append("BT\n");
4381 // multi-line text
4382 if (multiline) {
4383 // note: the comb flag is ignored in multiline mode
4384
4385 wMax = dx - 2 * borderWidth - 4;
4386
4387 // compute font autosize
4388 if (fontSize == 0) {
4389 for (fontSize = 20; fontSize > 1; --fontSize) {
4390 y = dy - 3;
4391 i = 0;
4392 while (i < text->getLength()) {
4393 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
4394 forceZapfDingbats);
4395 y -= fontSize;
4396 }
4397 // approximate the descender for the last line
4398 if (y >= 0.33 * fontSize) {
4399 break;
4400 }
4401 }
4402 if (tfPos >= 0) {
4403 tok = (GooString *)daToks->get(tfPos + 1);
4404 tok->clear();
4405 tok->appendf("{0:.2f}", fontSize);
4406 }
4407 }
4408
4409 // starting y coordinate
4410 // (note: each line of text starts with a Td operator that moves
4411 // down a line)
4412 y = dy - 3;
4413
4414 // set the font matrix
4415 if (tmPos >= 0) {
4416 tok = (GooString *)daToks->get(tmPos + 4);
4417 tok->clear();
4418 tok->append('0');
4419 tok = (GooString *)daToks->get(tmPos + 5);
4420 tok->clear();
4421 tok->appendf("{0:.2f}", y);
4422 }
4423
4424 // write the DA string
4425 if (daToks) {
4426 for (i = 0; i < daToks->getLength(); ++i) {
4427 appearBuf->append((GooString *)daToks->get(i))->append(' ');
4428 }
4429 }
4430
4431 // write the font matrix (if not part of the DA string)
4432 if (tmPos < 0) {
4433 appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
4434 }
4435
4436 // write a series of lines of text
4437 i = 0;
4438 xPrev = 0;
4439 while (i < text->getLength()) {
4440 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
4441 forceZapfDingbats);
4442 w *= fontSize;
4443
4444 // compute text start position
4445 switch (quadding) {
4446 case quaddingLeftJustified:
4447 default:
4448 x = borderWidth + 2;
4449 break;
4450 case quaddingCentered:
4451 x = (dx - w) / 2;
4452 break;
4453 case quaddingRightJustified:
4454 x = dx - borderWidth - 2 - w;
4455 break;
4456 }
4457
4458 // draw the line
4459 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
4460 writeString(convertedText, appearBuf);
4461 appearBuf->append(" Tj\n");
4462
4463 // next line
4464 xPrev = x;
4465 }
4466
4467 // single-line text
4468 } else {
4469 //~ replace newlines with spaces? - what does Acrobat do?
4470
4471 // comb formatting
4472 if (comb > 0) {
4473 int charCount;
4474
4475 // compute comb spacing
4476 w = (dx - 2 * borderWidth) / comb;
4477
4478 // compute font autosize
4479 if (fontSize == 0) {
4480 fontSize = dy - 2 * borderWidth;
4481 if (w < fontSize) {
4482 fontSize = w;
4483 }
4484 fontSize = floor(fontSize);
4485 if (tfPos >= 0) {
4486 tok = (GooString *)daToks->get(tfPos + 1);
4487 tok->clear();
4488 tok->appendf("{0:.2f}", fontSize);
4489 }
4490 }
4491
4492 i = 0;
4493 layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
4494 forceZapfDingbats);
4495 if (charCount > comb)
4496 charCount = comb;
4497
4498 // compute starting text cell
4499 switch (quadding) {
4500 case quaddingLeftJustified:
4501 default:
4502 x = borderWidth;
4503 break;
4504 case quaddingCentered:
4505 x = borderWidth + (comb - charCount) / 2 * w;
4506 break;
4507 case quaddingRightJustified:
4508 x = borderWidth + (comb - charCount) * w;
4509 break;
4510 }
4511 y = 0.5 * dy - 0.4 * fontSize;
4512
4513 // set the font matrix
4514 if (tmPos >= 0) {
4515 tok = (GooString *)daToks->get(tmPos + 4);
4516 tok->clear();
4517 tok->appendf("{0:.2f}", x);
4518 tok = (GooString *)daToks->get(tmPos + 5);
4519 tok->clear();
4520 tok->appendf("{0:.2f}", y);
4521 }
4522
4523 // write the DA string
4524 if (daToks) {
4525 for (i = 0; i < daToks->getLength(); ++i) {
4526 appearBuf->append((GooString *)daToks->get(i))->append(' ');
4527 }
4528 }
4529
4530 // write the font matrix (if not part of the DA string)
4531 if (tmPos < 0) {
4532 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
4533 }
4534
4535 // write the text string
4536 char *s = convertedText->getCString();
4537 int len = convertedText->getLength();
4538 i = 0;
4539 xPrev = w; // so that first character is placed properly
4540 while (i < comb && len > 0) {
4541 CharCode code;
4542 Unicode *uAux;
4543 int uLen, n;
4544 double dx, dy, ox, oy;
4545
4546 dx = 0.0;
4547 n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
4548 dx *= fontSize;
4549
4550 // center each character within its cell, by advancing the text
4551 // position the appropriate amount relative to the start of the
4552 // previous character
4553 x = 0.5 * (w - dx);
4554 appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
4555
4556 GooString *charBuf = new GooString(s, n);
4557 writeString(charBuf, appearBuf);
4558 appearBuf->append(" Tj\n");
4559 delete charBuf;
4560
4561 i++;
4562 s += n;
4563 len -= n;
4564 xPrev = x;
4565 }
4566
4567 // regular (non-comb) formatting
4568 } else {
4569 i = 0;
4570 layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
4571 forceZapfDingbats);
4572
4573 // compute font autosize
4574 if (fontSize == 0) {
4575 fontSize = dy - 2 * borderWidth;
4576 fontSize2 = (dx - 4 - 2 * borderWidth) / w;
4577 if (fontSize2 < fontSize) {
4578 fontSize = fontSize2;
4579 }
4580 fontSize = floor(fontSize);
4581 if (tfPos >= 0) {
4582 tok = (GooString *)daToks->get(tfPos + 1);
4583 tok->clear();
4584 tok->appendf("{0:.2f}", fontSize);
4585 }
4586 }
4587
4588 // compute text start position
4589 w *= fontSize;
4590 switch (quadding) {
4591 case quaddingLeftJustified:
4592 default:
4593 x = borderWidth + 2;
4594 break;
4595 case quaddingCentered:
4596 x = (dx - w) / 2;
4597 break;
4598 case quaddingRightJustified:
4599 x = dx - borderWidth - 2 - w;
4600 break;
4601 }
4602 y = 0.5 * dy - 0.4 * fontSize;
4603
4604 // set the font matrix
4605 if (tmPos >= 0) {
4606 tok = (GooString *)daToks->get(tmPos + 4);
4607 tok->clear();
4608 tok->appendf("{0:.2f}", x);
4609 tok = (GooString *)daToks->get(tmPos + 5);
4610 tok->clear();
4611 tok->appendf("{0:.2f}", y);
4612 }
4613
4614 // write the DA string
4615 if (daToks) {
4616 for (i = 0; i < daToks->getLength(); ++i) {
4617 appearBuf->append((GooString *)daToks->get(i))->append(' ');
4618 }
4619 }
4620
4621 // write the font matrix (if not part of the DA string)
4622 if (tmPos < 0) {
4623 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
4624 }
4625
4626 // write the text string
4627 writeString(convertedText, appearBuf);
4628 appearBuf->append(" Tj\n");
4629 }
4630 }
4631 // cleanup
4632 appearBuf->append("ET\n");
4633 appearBuf->append("Q\n");
4634 if (txField) {
4635 appearBuf->append("EMC\n");
4636 }
4637 if (daToks) {
4638 deleteGooList(daToks, GooString);
4639 }
4640 if (freeText) {
4641 delete text;
4642 }
4643 delete convertedText;
4644 if (freeFont) {
4645 font->decRefCnt();
4646 }
4647 }
4648
4649 // Draw the variable text or caption for a field.
drawListBox(FormFieldChoice * fieldChoice,GooString * da,GfxResources * resources,int quadding)4650 void AnnotWidget::drawListBox(FormFieldChoice *fieldChoice,
4651 GooString *da, GfxResources *resources, int quadding) {
4652 GooList *daToks;
4653 GooString *tok, *convertedText;
4654 GfxFont *font;
4655 double fontSize, fontSize2, borderWidth, x, y, w, wMax;
4656 int tfPos, tmPos, i, j;
4657
4658 //~ if there is no MK entry, this should use the existing content stream,
4659 //~ and only replace the marked content portion of it
4660 //~ (this is only relevant for Tx fields)
4661
4662 // parse the default appearance string
4663 tfPos = tmPos = -1;
4664 if (da) {
4665 daToks = new GooList();
4666 i = 0;
4667 while (i < da->getLength()) {
4668 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
4669 ++i;
4670 }
4671 if (i < da->getLength()) {
4672 for (j = i + 1;
4673 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
4674 ++j) ;
4675 daToks->append(new GooString(da, i, j - i));
4676 i = j;
4677 }
4678 }
4679 for (i = 2; i < daToks->getLength(); ++i) {
4680 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
4681 tfPos = i - 2;
4682 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
4683 tmPos = i - 6;
4684 }
4685 }
4686 } else {
4687 daToks = NULL;
4688 }
4689
4690 // get the font and font size
4691 font = NULL;
4692 fontSize = 0;
4693 if (tfPos >= 0) {
4694 tok = (GooString *)daToks->get(tfPos);
4695 if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
4696 if (!resources || !(font = resources->lookupFont(tok->getCString() + 1))) {
4697 error(errSyntaxError, -1, "Unknown font in field's DA string");
4698 }
4699 } else {
4700 error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
4701 }
4702 tok = (GooString *)daToks->get(tfPos + 1);
4703 fontSize = gatof(tok->getCString());
4704 } else {
4705 error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4706 }
4707 if (!font) {
4708 if (daToks) {
4709 deleteGooList(daToks, GooString);
4710 }
4711 return;
4712 }
4713
4714 convertedText = new GooString;
4715
4716 // get the border width
4717 borderWidth = border ? border->getWidth() : 0;
4718
4719 // compute font autosize
4720 if (fontSize == 0) {
4721 wMax = 0;
4722 for (i = 0; i < fieldChoice->getNumChoices(); ++i) {
4723 j = 0;
4724 if (fieldChoice->getChoice(i) == NULL) {
4725 error(errSyntaxError, -1, "Invalid annotation listbox");
4726 if (daToks) {
4727 deleteGooList(daToks, GooString);
4728 }
4729 delete convertedText;
4730 return;
4731 }
4732 layoutText(fieldChoice->getChoice(i), convertedText, &j, font, &w, 0.0, NULL, gFalse);
4733 if (w > wMax) {
4734 wMax = w;
4735 }
4736 }
4737 fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
4738 fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
4739 if (fontSize2 < fontSize) {
4740 fontSize = fontSize2;
4741 }
4742 fontSize = floor(fontSize);
4743 if (tfPos >= 0) {
4744 tok = (GooString *)daToks->get(tfPos + 1);
4745 tok->clear();
4746 tok->appendf("{0:.2f}", fontSize);
4747 }
4748 }
4749 // draw the text
4750 y = rect->y2 - rect->y1 - 1.1 * fontSize;
4751 for (i = fieldChoice->getTopIndex(); i < fieldChoice->getNumChoices(); ++i) {
4752 // setup
4753 appearBuf->append("q\n");
4754
4755 // draw the background if selected
4756 if (fieldChoice->isSelected(i)) {
4757 appearBuf->append("0 g f\n");
4758 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
4759 borderWidth,
4760 y - 0.2 * fontSize,
4761 rect->x2 - rect->x1 - 2 * borderWidth,
4762 1.1 * fontSize);
4763 }
4764
4765 // setup
4766 appearBuf->append("BT\n");
4767
4768 // compute text width and start position
4769 j = 0;
4770 layoutText(fieldChoice->getChoice(i), convertedText, &j, font, &w, 0.0, NULL, gFalse);
4771 w *= fontSize;
4772 switch (quadding) {
4773 case quaddingLeftJustified:
4774 default:
4775 x = borderWidth + 2;
4776 break;
4777 case quaddingCentered:
4778 x = (rect->x2 - rect->x1 - w) / 2;
4779 break;
4780 case quaddingRightJustified:
4781 x = rect->x2 - rect->x1 - borderWidth - 2 - w;
4782 break;
4783 }
4784
4785 // set the font matrix
4786 if (tmPos >= 0) {
4787 tok = (GooString *)daToks->get(tmPos + 4);
4788 tok->clear();
4789 tok->appendf("{0:.2f}", x);
4790 tok = (GooString *)daToks->get(tmPos + 5);
4791 tok->clear();
4792 tok->appendf("{0:.2f}", y);
4793 }
4794
4795 // write the DA string
4796 if (daToks) {
4797 for (j = 0; j < daToks->getLength(); ++j) {
4798 appearBuf->append((GooString *)daToks->get(j))->append(' ');
4799 }
4800 }
4801
4802 // write the font matrix (if not part of the DA string)
4803 if (tmPos < 0) {
4804 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
4805 }
4806
4807 // change the text color if selected
4808 if (fieldChoice->isSelected(i)) {
4809 appearBuf->append("1 g\n");
4810 }
4811
4812 // write the text string
4813 writeString(convertedText, appearBuf);
4814 appearBuf->append(" Tj\n");
4815
4816 // cleanup
4817 appearBuf->append("ET\n");
4818 appearBuf->append("Q\n");
4819
4820 // next line
4821 y -= 1.1 * fontSize;
4822 }
4823
4824 if (daToks) {
4825 deleteGooList(daToks, GooString);
4826 }
4827
4828 delete convertedText;
4829 }
4830
drawBorder()4831 void AnnotWidget::drawBorder() {
4832 int dashLength;
4833 double *dash;
4834 AnnotColor adjustedColor;
4835 double w = border->getWidth();
4836
4837 AnnotColor *aColor = appearCharacs->getBorderColor();
4838 if (!aColor)
4839 aColor = appearCharacs->getBackColor();
4840 if (!aColor)
4841 return;
4842
4843 double dx = rect->x2 - rect->x1;
4844 double dy = rect->y2 - rect->y1;
4845
4846 // radio buttons with no caption have a round border
4847 GBool hasCaption = appearCharacs->getNormalCaption() != NULL;
4848 if (field->getType() == formButton &&
4849 static_cast<FormFieldButton*>(field)->getButtonType() == formButtonRadio && !hasCaption) {
4850 double r = 0.5 * (dx < dy ? dx : dy);
4851 switch (border->getStyle()) {
4852 case AnnotBorder::borderDashed:
4853 appearBuf->append("[");
4854 dashLength = border->getDashLength();
4855 dash = border->getDash();
4856 for (int i = 0; i < dashLength; ++i) {
4857 appearBuf->appendf(" {0:.2f}", dash[i]);
4858 }
4859 appearBuf->append("] 0 d\n");
4860 // fall through to the solid case
4861 case AnnotBorder::borderSolid:
4862 case AnnotBorder::borderUnderlined:
4863 appearBuf->appendf("{0:.2f} w\n", w);
4864 setColor(aColor, gFalse);
4865 drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
4866 break;
4867 case AnnotBorder::borderBeveled:
4868 case AnnotBorder::borderInset:
4869 appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
4870 setColor(aColor, gFalse);
4871 drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
4872 adjustedColor = AnnotColor(*aColor);
4873 adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
4874 setColor(&adjustedColor, gFalse);
4875 drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
4876 adjustedColor = AnnotColor(*aColor);
4877 adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
4878 setColor(&adjustedColor, gFalse);
4879 drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
4880 break;
4881 }
4882 } else {
4883 switch (border->getStyle()) {
4884 case AnnotBorder::borderDashed:
4885 appearBuf->append("[");
4886 dashLength = border->getDashLength();
4887 dash = border->getDash();
4888 for (int i = 0; i < dashLength; ++i) {
4889 appearBuf->appendf(" {0:.2f}", dash[i]);
4890 }
4891 appearBuf->append("] 0 d\n");
4892 // fall through to the solid case
4893 case AnnotBorder::borderSolid:
4894 appearBuf->appendf("{0:.2f} w\n", w);
4895 setColor(aColor, gFalse);
4896 appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
4897 0.5 * w, dx - w, dy - w);
4898 break;
4899 case AnnotBorder::borderBeveled:
4900 case AnnotBorder::borderInset:
4901 adjustedColor = AnnotColor(*aColor);
4902 adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
4903 setColor(&adjustedColor, gTrue);
4904 appearBuf->append("0 0 m\n");
4905 appearBuf->appendf("0 {0:.2f} l\n", dy);
4906 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
4907 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
4908 appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
4909 appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
4910 appearBuf->append("f\n");
4911 adjustedColor = AnnotColor(*aColor);
4912 adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
4913 setColor(&adjustedColor, gTrue);
4914 appearBuf->append("0 0 m\n");
4915 appearBuf->appendf("{0:.2f} 0 l\n", dx);
4916 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
4917 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
4918 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
4919 appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
4920 appearBuf->append("f\n");
4921 break;
4922 case AnnotBorder::borderUnderlined:
4923 appearBuf->appendf("{0:.2f} w\n", w);
4924 setColor(aColor, gFalse);
4925 appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
4926 break;
4927 }
4928
4929 // clip to the inside of the border
4930 appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
4931 w, dx - 2 * w, dy - 2 * w);
4932 }
4933 }
4934
drawFormFieldButton(GfxResources * resources,GooString * da)4935 void AnnotWidget::drawFormFieldButton(GfxResources *resources, GooString *da) {
4936 GooString *caption = NULL;
4937 if (appearCharacs)
4938 caption = appearCharacs->getNormalCaption();
4939
4940 switch (static_cast<FormFieldButton *>(field)->getButtonType()) {
4941 case formButtonRadio: {
4942 //~ Acrobat doesn't draw a caption if there is no AP dict (?)
4943 if (appearState && appearState->cmp("Off") != 0 &&
4944 static_cast<FormFieldButton *>(field)->getState(appearState->getCString())) {
4945 if (caption) {
4946 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter,
4947 gFalse, gTrue);
4948 } else if (appearCharacs) {
4949 AnnotColor *aColor = appearCharacs->getBorderColor();
4950 if (aColor) {
4951 double dx = rect->x2 - rect->x1;
4952 double dy = rect->y2 - rect->y1;
4953 setColor(aColor, gTrue);
4954 drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), gTrue);
4955 }
4956 }
4957 }
4958 }
4959 break;
4960 case formButtonPush:
4961 if (caption)
4962 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gFalse);
4963 break;
4964 case formButtonCheck:
4965 if (appearState && appearState->cmp("Off") != 0) {
4966 if (!caption) {
4967 GooString checkMark("3");
4968 drawText(&checkMark, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gTrue);
4969 } else {
4970 drawText(caption, da, resources, gFalse, 0, fieldQuadCenter, gFalse, gTrue);
4971 }
4972 }
4973 break;
4974 }
4975 }
4976
drawFormFieldText(GfxResources * resources,GooString * da)4977 void AnnotWidget::drawFormFieldText(GfxResources *resources, GooString *da) {
4978 VariableTextQuadding quadding;
4979 GooString *contents;
4980 FormFieldText *fieldText = static_cast<FormFieldText *>(field);
4981
4982 contents = fieldText->getContent();
4983 if (contents) {
4984 quadding = field->hasTextQuadding() ? field->getTextQuadding() : form->getTextQuadding();
4985
4986 int comb = 0;
4987 if (fieldText->isComb())
4988 comb = fieldText->getMaxLen();
4989
4990 drawText(contents, da, resources,
4991 fieldText->isMultiline(), comb, quadding, gTrue, gFalse, fieldText->isPassword());
4992 }
4993 }
4994
drawFormFieldChoice(GfxResources * resources,GooString * da)4995 void AnnotWidget::drawFormFieldChoice(GfxResources *resources, GooString *da) {
4996 GooString *selected;
4997 VariableTextQuadding quadding;
4998 FormFieldChoice *fieldChoice = static_cast<FormFieldChoice *>(field);
4999
5000 quadding = field->hasTextQuadding() ? field->getTextQuadding() : form->getTextQuadding();
5001
5002 if (fieldChoice->isCombo()) {
5003 selected = fieldChoice->getSelectedChoice();
5004 if (selected) {
5005 drawText(selected, da, resources, gFalse, 0, quadding, gTrue, gFalse);
5006 //~ Acrobat draws a popup icon on the right side
5007 }
5008 // list box
5009 } else {
5010 drawListBox(fieldChoice, da, resources, quadding);
5011 }
5012 }
5013
generateFieldAppearance()5014 void AnnotWidget::generateFieldAppearance() {
5015 Object appearDict, obj1, obj2;
5016 GfxResources *resources;
5017 MemStream *appearStream;
5018 GooString *da;
5019
5020 appearBuf = new GooString ();
5021
5022 // draw the background
5023 if (appearCharacs) {
5024 AnnotColor *aColor = appearCharacs->getBackColor();
5025 if (aColor) {
5026 setColor(aColor, gTrue);
5027 appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
5028 rect->x2 - rect->x1, rect->y2 - rect->y1);
5029 }
5030 }
5031
5032 // draw the border
5033 if (appearCharacs && border && border->getWidth() > 0)
5034 drawBorder();
5035
5036 da = field->getDefaultAppearance();
5037 if (!da)
5038 da = form->getDefaultAppearance();
5039
5040 resources = form->getDefaultResources();
5041
5042 // draw the field contents
5043 switch (field->getType()) {
5044 case formButton:
5045 drawFormFieldButton(resources, da);
5046 break;
5047 case formText:
5048 drawFormFieldText(resources, da);
5049 break;
5050 case formChoice:
5051 drawFormFieldChoice(resources, da);
5052 break;
5053 case formSignature:
5054 //~unimp
5055 break;
5056 case formUndef:
5057 default:
5058 error(errSyntaxError, -1, "Unknown field type");
5059 }
5060
5061 // build the appearance stream dictionary
5062 appearDict.initDict(xref);
5063 appearDict.dictAdd(copyString("Length"),
5064 obj1.initInt(appearBuf->getLength()));
5065 appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
5066 obj1.initArray(xref);
5067 obj1.arrayAdd(obj2.initReal(0));
5068 obj1.arrayAdd(obj2.initReal(0));
5069 obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
5070 obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
5071 appearDict.dictAdd(copyString("BBox"), &obj1);
5072
5073 // set the resource dictionary
5074 Object *resDict = form->getDefaultResourcesObj();
5075 if (resDict->isDict()) {
5076 appearDict.dictAdd(copyString("Resources"), resDict->copy(&obj1));
5077 }
5078
5079 // build the appearance stream
5080 appearStream = new MemStream(copyString(appearBuf->getCString()), 0,
5081 appearBuf->getLength(), &appearDict);
5082 appearance.free();
5083 appearance.initStream(appearStream);
5084 delete appearBuf;
5085
5086 appearStream->setNeedFree(gTrue);
5087 }
5088
updateAppearanceStream()5089 void AnnotWidget::updateAppearanceStream()
5090 {
5091 // If this the first time updateAppearanceStream() is called on this widget,
5092 // destroy the AP dictionary because we are going to create a new one.
5093 if (updatedAppearanceStream.num == -1) {
5094 invalidateAppearance(); // Delete AP dictionary and all referenced streams
5095 }
5096
5097 // There's no need to create a new appearance stream if NeedAppearances is
5098 // set, because it will be ignored next time anyway.
5099 if (form && form->getNeedAppearances())
5100 return;
5101
5102 // Create the new appearance
5103 generateFieldAppearance();
5104
5105 // Fetch the appearance stream we've just created
5106 Object obj1;
5107 appearance.fetch(xref, &obj1);
5108
5109 // If this the first time updateAppearanceStream() is called on this widget,
5110 // create a new AP dictionary containing the new appearance stream.
5111 // Otherwise, just update the stream we had created previously.
5112 if (updatedAppearanceStream.num == -1) {
5113 // Write the appearance stream
5114 updatedAppearanceStream = xref->addIndirectObject(&obj1);
5115 obj1.free();
5116
5117 // Write the AP dictionary
5118 Object obj2;
5119 obj1.initDict(xref);
5120 obj1.dictAdd(copyString("N"), obj2.initRef(updatedAppearanceStream.num, updatedAppearanceStream.gen));
5121 update("AP", &obj1);
5122
5123 // Update our internal pointers to the appearance dictionary
5124 appearStreams = new AnnotAppearance(doc, &obj1);
5125 } else {
5126 // Replace the existing appearance stream
5127 xref->setModifiedObject(&obj1, updatedAppearanceStream);
5128 obj1.free();
5129 }
5130 }
5131
draw(Gfx * gfx,GBool printing)5132 void AnnotWidget::draw(Gfx *gfx, GBool printing) {
5133 Object obj;
5134
5135 if (!isVisible (printing))
5136 return;
5137
5138 annotLocker();
5139 addDingbatsResource = gFalse;
5140
5141 // Only construct the appearance stream when
5142 // - annot doesn't have an AP or
5143 // - NeedAppearances is true
5144 if (field) {
5145 if (appearance.isNull() || (form && form->getNeedAppearances()))
5146 generateFieldAppearance();
5147 }
5148
5149 // draw the appearance stream
5150 appearance.fetch(gfx->getXRef(), &obj);
5151 if (addDingbatsResource) {
5152 // We are forcing ZaDb but the font does not exist
5153 // so create a fake one
5154 Object baseFontObj, subtypeObj;
5155 baseFontObj.initName("ZapfDingbats");
5156 subtypeObj.initName("Type1");
5157
5158 Object fontDictObj;
5159 Dict *fontDict = new Dict(gfx->getXRef());
5160 fontDict->decRef();
5161 fontDict->add(copyString("BaseFont"), &baseFontObj);
5162 fontDict->add(copyString("Subtype"), &subtypeObj);
5163 fontDictObj.initDict(fontDict);
5164
5165 Object fontsDictObj;
5166 Dict *fontsDict = new Dict(gfx->getXRef());
5167 fontsDict->decRef();
5168 fontsDict->add(copyString("ZaDb"), &fontDictObj);
5169 fontsDictObj.initDict(fontsDict);
5170
5171 Dict *dict = new Dict(gfx->getXRef());
5172 dict->add(copyString("Font"), &fontsDictObj);
5173 gfx->pushResources(dict);
5174 delete dict;
5175 }
5176 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5177 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5178 if (addDingbatsResource) {
5179 gfx->popResources();
5180 }
5181 obj.free();
5182 }
5183
5184
5185 //------------------------------------------------------------------------
5186 // AnnotMovie
5187 //------------------------------------------------------------------------
AnnotMovie(PDFDoc * docA,PDFRectangle * rect,Movie * movieA)5188 AnnotMovie::AnnotMovie(PDFDoc *docA, PDFRectangle *rect, Movie *movieA) :
5189 Annot(docA, rect) {
5190 Object obj1;
5191
5192 type = typeMovie;
5193 annotObj.dictSet ("Subtype", obj1.initName ("Movie"));
5194
5195 movie = movieA->copy();
5196 // TODO: create movie dict from movieA
5197
5198 initialize(docA, annotObj.getDict());
5199 }
5200
AnnotMovie(PDFDoc * docA,Dict * dict,Object * obj)5201 AnnotMovie::AnnotMovie(PDFDoc *docA, Dict *dict, Object *obj) :
5202 Annot(docA, dict, obj) {
5203 type = typeMovie;
5204 initialize(docA, dict);
5205 }
5206
~AnnotMovie()5207 AnnotMovie::~AnnotMovie() {
5208 if (title)
5209 delete title;
5210 delete movie;
5211 }
5212
initialize(PDFDoc * docA,Dict * dict)5213 void AnnotMovie::initialize(PDFDoc *docA, Dict* dict) {
5214 Object obj1;
5215
5216 if (dict->lookup("T", &obj1)->isString()) {
5217 title = obj1.getString()->copy();
5218 } else {
5219 title = NULL;
5220 }
5221 obj1.free();
5222
5223 Object movieDict;
5224 if (dict->lookup("Movie", &movieDict)->isDict()) {
5225 Object obj2;
5226 dict->lookup("A", &obj2);
5227 if (obj2.isDict())
5228 movie = new Movie (&movieDict, &obj2);
5229 else
5230 movie = new Movie (&movieDict);
5231 if (!movie->isOk()) {
5232 delete movie;
5233 movie = NULL;
5234 ok = gFalse;
5235 }
5236 obj2.free();
5237 } else {
5238 error(errSyntaxError, -1, "Bad Annot Movie");
5239 movie = NULL;
5240 ok = gFalse;
5241 }
5242 movieDict.free();
5243 }
5244
draw(Gfx * gfx,GBool printing)5245 void AnnotMovie::draw(Gfx *gfx, GBool printing) {
5246 Object obj;
5247
5248 if (!isVisible (printing))
5249 return;
5250
5251 annotLocker();
5252 if (appearance.isNull() && movie->getShowPoster()) {
5253 int width, height;
5254 Object poster;
5255 movie->getPoster(&poster);
5256 movie->getAspect(&width, &height);
5257
5258 if (width != -1 && height != -1 && !poster.isNone()) {
5259 MemStream *mStream;
5260
5261 appearBuf = new GooString ();
5262 appearBuf->append ("q\n");
5263 appearBuf->appendf ("{0:d} 0 0 {1:d} 0 0 cm\n", width, height);
5264 appearBuf->append ("/MImg Do\n");
5265 appearBuf->append ("Q\n");
5266
5267 Object imgDict;
5268 imgDict.initDict(gfx->getXRef());
5269 imgDict.dictSet ("MImg", &poster);
5270
5271 Object resDict;
5272 resDict.initDict(gfx->getXRef());
5273 resDict.dictSet ("XObject", &imgDict);
5274
5275 Object formDict, obj1, obj2;
5276 formDict.initDict(gfx->getXRef());
5277 formDict.dictSet("Length", obj1.initInt(appearBuf->getLength()));
5278 formDict.dictSet("Subtype", obj1.initName("Form"));
5279 formDict.dictSet("Name", obj1.initName("FRM"));
5280 obj1.initArray(gfx->getXRef());
5281 obj1.arrayAdd(obj2.initInt(0));
5282 obj1.arrayAdd(obj2.initInt(0));
5283 obj1.arrayAdd(obj2.initInt(width));
5284 obj1.arrayAdd(obj2.initInt(height));
5285 formDict.dictSet("BBox", &obj1);
5286 obj1.initArray(gfx->getXRef());
5287 obj1.arrayAdd(obj2.initInt(1));
5288 obj1.arrayAdd(obj2.initInt(0));
5289 obj1.arrayAdd(obj2.initInt(0));
5290 obj1.arrayAdd(obj2.initInt(1));
5291 obj1.arrayAdd(obj2.initInt(-width / 2));
5292 obj1.arrayAdd(obj2.initInt(-height / 2));
5293 formDict.dictSet("Matrix", &obj1);
5294 formDict.dictSet("Resources", &resDict);
5295
5296 Object aStream;
5297 mStream = new MemStream(copyString(appearBuf->getCString()), 0,
5298 appearBuf->getLength(), &formDict);
5299 mStream->setNeedFree(gTrue);
5300 aStream.initStream(mStream);
5301 delete appearBuf;
5302
5303 Object objDict;
5304 objDict.initDict(gfx->getXRef());
5305 objDict.dictSet ("FRM", &aStream);
5306
5307 resDict.initDict(gfx->getXRef());
5308 resDict.dictSet ("XObject", &objDict);
5309
5310 appearBuf = new GooString ();
5311 appearBuf->append ("q\n");
5312 appearBuf->appendf ("0 0 {0:d} {1:d} re W n\n", width, height);
5313 appearBuf->append ("q\n");
5314 appearBuf->appendf ("0 0 {0:d} {1:d} re W n\n", width, height);
5315 appearBuf->appendf ("1 0 0 1 {0:d} {1:d} cm\n", width / 2, height / 2);
5316 appearBuf->append ("/FRM Do\n");
5317 appearBuf->append ("Q\n");
5318 appearBuf->append ("Q\n");
5319
5320 double bbox[4];
5321 bbox[0] = bbox[1] = 0;
5322 bbox[2] = width;
5323 bbox[3] = height;
5324 createForm(bbox, gFalse, &resDict, &appearance);
5325 delete appearBuf;
5326 }
5327 poster.free();
5328 }
5329
5330 // draw the appearance stream
5331 appearance.fetch(gfx->getXRef(), &obj);
5332 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5333 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5334 obj.free();
5335 }
5336
5337 //------------------------------------------------------------------------
5338 // AnnotScreen
5339 //------------------------------------------------------------------------
AnnotScreen(PDFDoc * docA,PDFRectangle * rect)5340 AnnotScreen::AnnotScreen(PDFDoc *docA, PDFRectangle *rect) :
5341 Annot(docA, rect) {
5342 Object obj1;
5343
5344 type = typeScreen;
5345
5346 annotObj.dictSet ("Subtype", obj1.initName ("Screen"));
5347 initialize(docA, annotObj.getDict());
5348 }
5349
AnnotScreen(PDFDoc * docA,Dict * dict,Object * obj)5350 AnnotScreen::AnnotScreen(PDFDoc *docA, Dict *dict, Object *obj) :
5351 Annot(docA, dict, obj) {
5352 type = typeScreen;
5353 initialize(docA, dict);
5354 }
5355
~AnnotScreen()5356 AnnotScreen::~AnnotScreen() {
5357 if (title)
5358 delete title;
5359 if (appearCharacs)
5360 delete appearCharacs;
5361 if (action)
5362 delete action;
5363
5364 additionalActions.free();
5365 }
5366
initialize(PDFDoc * docA,Dict * dict)5367 void AnnotScreen::initialize(PDFDoc *docA, Dict* dict) {
5368 Object obj1;
5369
5370 title = NULL;
5371 if (dict->lookup("T", &obj1)->isString()) {
5372 title = obj1.getString()->copy();
5373 }
5374 obj1.free();
5375
5376 action = NULL;
5377 if (dict->lookup("A", &obj1)->isDict()) {
5378 action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
5379 if (action->getKind() == actionRendition && page == 0) {
5380 error (errSyntaxError, -1, "Invalid Rendition action: associated screen annotation without P");
5381 delete action;
5382 action = NULL;
5383 ok = gFalse;
5384 }
5385 }
5386 obj1.free();
5387
5388 dict->lookupNF("AA", &additionalActions);
5389
5390 appearCharacs = NULL;
5391 if(dict->lookup("MK", &obj1)->isDict()) {
5392 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
5393 }
5394 obj1.free();
5395 }
5396
getAdditionalAction(AdditionalActionsType type)5397 LinkAction* AnnotScreen::getAdditionalAction(AdditionalActionsType type)
5398 {
5399 if (type == actionFocusIn || type == actionFocusOut) // not defined for screen annotation
5400 return NULL;
5401
5402 return ::getAdditionalAction(type, &additionalActions, doc);
5403 }
5404
5405 //------------------------------------------------------------------------
5406 // AnnotStamp
5407 //------------------------------------------------------------------------
AnnotStamp(PDFDoc * docA,PDFRectangle * rect)5408 AnnotStamp::AnnotStamp(PDFDoc *docA, PDFRectangle *rect) :
5409 AnnotMarkup(docA, rect) {
5410 Object obj1;
5411
5412 type = typeStamp;
5413 annotObj.dictSet ("Subtype", obj1.initName ("Stamp"));
5414 initialize(docA, annotObj.getDict());
5415 }
5416
AnnotStamp(PDFDoc * docA,Dict * dict,Object * obj)5417 AnnotStamp::AnnotStamp(PDFDoc *docA, Dict *dict, Object *obj) :
5418 AnnotMarkup(docA, dict, obj) {
5419 type = typeStamp;
5420 initialize(docA, dict);
5421 }
5422
~AnnotStamp()5423 AnnotStamp::~AnnotStamp() {
5424 delete icon;
5425 }
5426
initialize(PDFDoc * docA,Dict * dict)5427 void AnnotStamp::initialize(PDFDoc *docA, Dict* dict) {
5428 Object obj1;
5429
5430 if (dict->lookup("Name", &obj1)->isName()) {
5431 icon = new GooString(obj1.getName());
5432 } else {
5433 icon = new GooString("Draft");
5434 }
5435 obj1.free();
5436
5437 }
5438
setIcon(GooString * new_icon)5439 void AnnotStamp::setIcon(GooString *new_icon) {
5440 delete icon;
5441
5442 if (new_icon) {
5443 icon = new GooString (new_icon);
5444 } else {
5445 icon = new GooString();
5446 }
5447
5448 Object obj1;
5449 obj1.initName (icon->getCString());
5450 update("Name", &obj1);
5451 invalidateAppearance();
5452 }
5453
5454 //------------------------------------------------------------------------
5455 // AnnotGeometry
5456 //------------------------------------------------------------------------
AnnotGeometry(PDFDoc * docA,PDFRectangle * rect,AnnotSubtype subType)5457 AnnotGeometry::AnnotGeometry(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
5458 AnnotMarkup(docA, rect) {
5459 Object obj1;
5460
5461 switch (subType) {
5462 case typeSquare:
5463 annotObj.dictSet ("Subtype", obj1.initName ("Square"));
5464 break;
5465 case typeCircle:
5466 annotObj.dictSet ("Subtype", obj1.initName ("Circle"));
5467 break;
5468 default:
5469 assert (0 && "Invalid subtype for AnnotGeometry\n");
5470 }
5471
5472 initialize(docA, annotObj.getDict());
5473 }
5474
AnnotGeometry(PDFDoc * docA,Dict * dict,Object * obj)5475 AnnotGeometry::AnnotGeometry(PDFDoc *docA, Dict *dict, Object *obj) :
5476 AnnotMarkup(docA, dict, obj) {
5477 // the real type will be read in initialize()
5478 type = typeSquare;
5479 initialize(docA, dict);
5480 }
5481
~AnnotGeometry()5482 AnnotGeometry::~AnnotGeometry() {
5483 delete interiorColor;
5484 delete borderEffect;
5485 delete geometryRect;
5486 }
5487
initialize(PDFDoc * docA,Dict * dict)5488 void AnnotGeometry::initialize(PDFDoc *docA, Dict* dict) {
5489 Object obj1;
5490
5491 if (dict->lookup("Subtype", &obj1)->isName()) {
5492 GooString typeName(obj1.getName());
5493 if (!typeName.cmp("Square")) {
5494 type = typeSquare;
5495 } else if (!typeName.cmp("Circle")) {
5496 type = typeCircle;
5497 }
5498 }
5499 obj1.free();
5500
5501 if (dict->lookup("IC", &obj1)->isArray()) {
5502 interiorColor = new AnnotColor(obj1.getArray());
5503 } else {
5504 interiorColor = NULL;
5505 }
5506 obj1.free();
5507
5508 if (dict->lookup("BS", &obj1)->isDict()) {
5509 delete border;
5510 border = new AnnotBorderBS(obj1.getDict());
5511 } else if (!border) {
5512 border = new AnnotBorderBS();
5513 }
5514 obj1.free();
5515
5516 if (dict->lookup("BE", &obj1)->isDict()) {
5517 borderEffect = new AnnotBorderEffect(obj1.getDict());
5518 } else {
5519 borderEffect = NULL;
5520 }
5521 obj1.free();
5522
5523 geometryRect = NULL;
5524 if (dict->lookup("RD", &obj1)->isArray()) {
5525 geometryRect = parseDiffRectangle(obj1.getArray(), rect);
5526 }
5527 obj1.free();
5528
5529 }
5530
setType(AnnotSubtype new_type)5531 void AnnotGeometry::setType(AnnotSubtype new_type) {
5532 Object obj1;
5533
5534 switch (new_type) {
5535 case typeSquare:
5536 obj1.initName("Square");
5537 break;
5538 case typeCircle:
5539 obj1.initName("Circle");
5540 break;
5541 default:
5542 assert(!"Invalid subtype");
5543 }
5544
5545 type = new_type;
5546 update("Subtype", &obj1);
5547 invalidateAppearance();
5548 }
5549
setInteriorColor(AnnotColor * new_color)5550 void AnnotGeometry::setInteriorColor(AnnotColor *new_color) {
5551 delete interiorColor;
5552
5553 if (new_color) {
5554 Object obj1;
5555 new_color->writeToObject(xref, &obj1);
5556 update ("IC", &obj1);
5557 interiorColor = new_color;
5558 } else {
5559 interiorColor = NULL;
5560 }
5561 invalidateAppearance();
5562 }
5563
draw(Gfx * gfx,GBool printing)5564 void AnnotGeometry::draw(Gfx *gfx, GBool printing) {
5565 Object obj;
5566 double ca = 1;
5567
5568 if (!isVisible (printing))
5569 return;
5570
5571 annotLocker();
5572 if (appearance.isNull()) {
5573 ca = opacity;
5574
5575 appearBuf = new GooString ();
5576 appearBuf->append ("q\n");
5577 if (color)
5578 setColor(color, gFalse);
5579
5580 double borderWidth = border->getWidth();
5581 setLineStyleForBorder(border);
5582
5583 if (interiorColor)
5584 setColor(interiorColor, gTrue);
5585
5586 if (type == typeSquare) {
5587 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re\n",
5588 borderWidth / 2.0, borderWidth / 2.0,
5589 (rect->x2 - rect->x1) - borderWidth,
5590 (rect->y2 - rect->y1) - borderWidth);
5591 } else {
5592 double width, height;
5593 double b;
5594 double x1, y1, x2, y2, x3, y3;
5595
5596 width = rect->x2 - rect->x1;
5597 height = rect->y2 - rect->y1;
5598 b = borderWidth / 2.0;
5599
5600 x1 = b;
5601 y1 = height / 2.0;
5602 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x1, y1);
5603
5604 y1 += height / 4.0;
5605 x2 = width / 4.0;
5606 y2 = height - b;
5607 x3 = width / 2.0;
5608 y3 = y2;
5609 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5610 x1, y1, x2, y2, x3, y3);
5611 x2 = width - b;
5612 y2 = y1;
5613 x1 = x3 + (width / 4.0);
5614 y1 = y3;
5615 x3 = x2;
5616 y3 = height / 2.0;
5617 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5618 x1, y1, x2, y2, x3, y3);
5619
5620 x2 = x1;
5621 y2 = b;
5622 x1 = x3;
5623 y1 = height / 4.0;
5624 x3 = width / 2.0;
5625 y3 = b;
5626 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5627 x1, y1, x2, y2, x3, y3);
5628
5629 x2 = b;
5630 y2 = y1;
5631 x1 = width / 4.0;
5632 y1 = b;
5633 x3 = b;
5634 y3 = height / 2.0;
5635 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
5636 x1, y1, x2, y2, x3, y3);
5637 }
5638
5639 if (interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent)
5640 appearBuf->append ("b\n");
5641 else
5642 appearBuf->append ("S\n");
5643
5644 appearBuf->append ("Q\n");
5645
5646 double bbox[4];
5647 bbox[0] = bbox[1] = 0;
5648 bbox[2] = rect->x2 - rect->x1;
5649 bbox[3] = rect->y2 - rect->y1;
5650 if (ca == 1) {
5651 createForm(bbox, gFalse, NULL, &appearance);
5652 } else {
5653 Object aStream;
5654
5655 createForm(bbox, gTrue, NULL, &aStream);
5656 delete appearBuf;
5657
5658 Object resDict;
5659 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
5660 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
5661 createForm(bbox, gFalse, &resDict, &appearance);
5662 }
5663 delete appearBuf;
5664 }
5665
5666 // draw the appearance stream
5667 appearance.fetch(gfx->getXRef(), &obj);
5668 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5669 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5670 obj.free();
5671 }
5672
5673 //------------------------------------------------------------------------
5674 // AnnotPolygon
5675 //------------------------------------------------------------------------
AnnotPolygon(PDFDoc * docA,PDFRectangle * rect,AnnotSubtype subType)5676 AnnotPolygon::AnnotPolygon(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType) :
5677 AnnotMarkup(docA, rect) {
5678 Object obj1;
5679
5680 switch (subType) {
5681 case typePolygon:
5682 annotObj.dictSet ("Subtype", obj1.initName ("Polygon"));
5683 break;
5684 case typePolyLine:
5685 annotObj.dictSet ("Subtype", obj1.initName ("PolyLine"));
5686 break;
5687 default:
5688 assert (0 && "Invalid subtype for AnnotGeometry\n");
5689 }
5690
5691 // Store dummy path with one null vertex only
5692 Object obj2, obj3;
5693 obj2.initArray (doc->getXRef());
5694 obj2.arrayAdd (obj3.initReal (0));
5695 obj2.arrayAdd (obj3.initReal (0));
5696 annotObj.dictSet ("Vertices", &obj2);
5697
5698 initialize(docA, annotObj.getDict());
5699 }
5700
AnnotPolygon(PDFDoc * docA,Dict * dict,Object * obj)5701 AnnotPolygon::AnnotPolygon(PDFDoc *docA, Dict *dict, Object *obj) :
5702 AnnotMarkup(docA, dict, obj) {
5703 // the real type will be read in initialize()
5704 type = typePolygon;
5705 initialize(docA, dict);
5706 }
5707
~AnnotPolygon()5708 AnnotPolygon::~AnnotPolygon() {
5709 delete vertices;
5710
5711 if (interiorColor)
5712 delete interiorColor;
5713
5714 if (borderEffect)
5715 delete borderEffect;
5716 }
5717
initialize(PDFDoc * docA,Dict * dict)5718 void AnnotPolygon::initialize(PDFDoc *docA, Dict* dict) {
5719 Object obj1;
5720
5721 if (dict->lookup("Subtype", &obj1)->isName()) {
5722 GooString typeName(obj1.getName());
5723 if (!typeName.cmp("Polygon")) {
5724 type = typePolygon;
5725 } else if (!typeName.cmp("PolyLine")) {
5726 type = typePolyLine;
5727 }
5728 }
5729 obj1.free();
5730
5731 if (dict->lookup("Vertices", &obj1)->isArray()) {
5732 vertices = new AnnotPath(obj1.getArray());
5733 } else {
5734 vertices = new AnnotPath();
5735 error(errSyntaxError, -1, "Bad Annot Polygon Vertices");
5736 ok = gFalse;
5737 }
5738 obj1.free();
5739
5740 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
5741 Object obj2;
5742
5743 if(obj1.arrayGet(0, &obj2)->isString())
5744 startStyle = parseAnnotLineEndingStyle(obj2.getString());
5745 else
5746 startStyle = annotLineEndingNone;
5747 obj2.free();
5748
5749 if(obj1.arrayGet(1, &obj2)->isString())
5750 endStyle = parseAnnotLineEndingStyle(obj2.getString());
5751 else
5752 endStyle = annotLineEndingNone;
5753 obj2.free();
5754
5755 } else {
5756 startStyle = endStyle = annotLineEndingNone;
5757 }
5758 obj1.free();
5759
5760 if (dict->lookup("IC", &obj1)->isArray()) {
5761 interiorColor = new AnnotColor(obj1.getArray());
5762 } else {
5763 interiorColor = NULL;
5764 }
5765 obj1.free();
5766
5767 if (dict->lookup("BS", &obj1)->isDict()) {
5768 delete border;
5769 border = new AnnotBorderBS(obj1.getDict());
5770 } else if (!border) {
5771 border = new AnnotBorderBS();
5772 }
5773 obj1.free();
5774
5775 if (dict->lookup("BE", &obj1)->isDict()) {
5776 borderEffect = new AnnotBorderEffect(obj1.getDict());
5777 } else {
5778 borderEffect = NULL;
5779 }
5780 obj1.free();
5781
5782 if (dict->lookup("IT", &obj1)->isName()) {
5783 const char *intentName = obj1.getName();
5784
5785 if(!strcmp(intentName, "PolygonCloud")) {
5786 intent = polygonCloud;
5787 } else if(!strcmp(intentName, "PolyLineDimension")) {
5788 intent = polylineDimension;
5789 } else {
5790 intent = polygonDimension;
5791 }
5792 } else {
5793 intent = polygonCloud;
5794 }
5795 obj1.free();
5796 }
5797
setType(AnnotSubtype new_type)5798 void AnnotPolygon::setType(AnnotSubtype new_type) {
5799 Object obj1;
5800
5801 switch (new_type) {
5802 case typePolygon:
5803 obj1.initName("Polygon");
5804 break;
5805 case typePolyLine:
5806 obj1.initName("PolyLine");
5807 break;
5808 default:
5809 assert(!"Invalid subtype");
5810 }
5811
5812 type = new_type;
5813 update("Subtype", &obj1);
5814 invalidateAppearance();
5815 }
5816
setVertices(AnnotPath * path)5817 void AnnotPolygon::setVertices(AnnotPath *path) {
5818 Object obj1, obj2;
5819 delete vertices;
5820
5821 obj1.initArray(xref);
5822
5823 for (int i = 0; i < path->getCoordsLength(); i++) {
5824 obj1.arrayAdd (obj2.initReal (path->getX(i)));
5825 obj1.arrayAdd (obj2.initReal (path->getY(i)));
5826 }
5827
5828 vertices = new AnnotPath(obj1.getArray());
5829
5830 update("Vertices", &obj1);
5831 invalidateAppearance();
5832 }
5833
setStartEndStyle(AnnotLineEndingStyle start,AnnotLineEndingStyle end)5834 void AnnotPolygon::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end) {
5835 Object obj1, obj2;
5836
5837 startStyle = start;
5838 endStyle = end;
5839
5840 obj1.initArray(xref);
5841 obj1.arrayAdd( obj2.initName(convertAnnotLineEndingStyle( startStyle )) );
5842 obj1.arrayAdd( obj2.initName(convertAnnotLineEndingStyle( endStyle )) );
5843
5844 update("LE", &obj1);
5845 invalidateAppearance();
5846 }
5847
setInteriorColor(AnnotColor * new_color)5848 void AnnotPolygon::setInteriorColor(AnnotColor *new_color) {
5849 delete interiorColor;
5850
5851 if (new_color) {
5852 Object obj1;
5853 new_color->writeToObject(xref, &obj1);
5854 update ("IC", &obj1);
5855 interiorColor = new_color;
5856 } else {
5857 interiorColor = NULL;
5858 }
5859 invalidateAppearance();
5860 }
5861
setIntent(AnnotPolygonIntent new_intent)5862 void AnnotPolygon::setIntent(AnnotPolygonIntent new_intent) {
5863 Object obj1;
5864
5865 intent = new_intent;
5866 if (new_intent == polygonCloud)
5867 obj1.initName("PolygonCloud");
5868 else if (new_intent == polylineDimension)
5869 obj1.initName("PolyLineDimension");
5870 else // polygonDimension
5871 obj1.initName("PolygonDimension");
5872 update ("IT", &obj1);
5873 }
5874
draw(Gfx * gfx,GBool printing)5875 void AnnotPolygon::draw(Gfx *gfx, GBool printing) {
5876 Object obj;
5877 double ca = 1;
5878
5879 if (!isVisible (printing))
5880 return;
5881
5882 annotLocker();
5883 if (appearance.isNull()) {
5884 appearBBox = new AnnotAppearanceBBox(rect);
5885 ca = opacity;
5886
5887 appearBuf = new GooString ();
5888 appearBuf->append ("q\n");
5889
5890 if (color) {
5891 setColor(color, gFalse);
5892 }
5893
5894 setLineStyleForBorder(border);
5895 appearBBox->setBorderWidth(std::max(1., border->getWidth()));
5896
5897 if (interiorColor) {
5898 setColor(interiorColor, gTrue);
5899 }
5900
5901 if (vertices->getCoordsLength() != 0) {
5902 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1);
5903 appearBBox->extendTo (vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1);
5904
5905 for (int i = 1; i < vertices->getCoordsLength(); ++i) {
5906 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
5907 appearBBox->extendTo (vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
5908 }
5909
5910 if (type == typePolygon) {
5911 if (interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent) {
5912 appearBuf->append ("b\n");
5913 } else {
5914 appearBuf->append ("s\n");
5915 }
5916 } else {
5917 appearBuf->append ("S\n");
5918 }
5919 }
5920
5921 appearBuf->append ("Q\n");
5922
5923 double bbox[4];
5924 appearBBox->getBBoxRect(bbox);
5925 if (ca == 1) {
5926 createForm(bbox, gFalse, NULL, &appearance);
5927 } else {
5928 Object aStream, resDict;
5929
5930 createForm(bbox, gTrue, NULL, &aStream);
5931 delete appearBuf;
5932
5933 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
5934 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
5935 createForm(bbox, gFalse, &resDict, &appearance);
5936 }
5937 delete appearBuf;
5938 }
5939
5940 // draw the appearance stream
5941 appearance.fetch(gfx->getXRef(), &obj);
5942 if (appearBBox) {
5943 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5944 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
5945 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
5946 getRotation());
5947 } else {
5948 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
5949 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5950 }
5951 obj.free();
5952 }
5953
5954 //------------------------------------------------------------------------
5955 // AnnotCaret
5956 //------------------------------------------------------------------------
AnnotCaret(PDFDoc * docA,PDFRectangle * rect)5957 AnnotCaret::AnnotCaret(PDFDoc *docA, PDFRectangle *rect) :
5958 AnnotMarkup(docA, rect) {
5959 Object obj1;
5960
5961 type = typeCaret;
5962
5963 annotObj.dictSet ("Subtype", obj1.initName ("Caret"));
5964 initialize(docA, annotObj.getDict());
5965 }
5966
AnnotCaret(PDFDoc * docA,Dict * dict,Object * obj)5967 AnnotCaret::AnnotCaret(PDFDoc *docA, Dict *dict, Object *obj) :
5968 AnnotMarkup(docA, dict, obj) {
5969 type = typeCaret;
5970 initialize(docA, dict);
5971 }
5972
~AnnotCaret()5973 AnnotCaret::~AnnotCaret() {
5974 delete caretRect;
5975 }
5976
initialize(PDFDoc * docA,Dict * dict)5977 void AnnotCaret::initialize(PDFDoc *docA, Dict* dict) {
5978 Object obj1;
5979
5980 symbol = symbolNone;
5981 if (dict->lookup("Sy", &obj1)->isName()) {
5982 GooString typeName(obj1.getName());
5983 if (!typeName.cmp("P")) {
5984 symbol = symbolP;
5985 } else if (!typeName.cmp("None")) {
5986 symbol = symbolNone;
5987 }
5988 }
5989 obj1.free();
5990
5991 if (dict->lookup("RD", &obj1)->isArray()) {
5992 caretRect = parseDiffRectangle(obj1.getArray(), rect);
5993 } else {
5994 caretRect = NULL;
5995 }
5996 obj1.free();
5997
5998 }
5999
setSymbol(AnnotCaretSymbol new_symbol)6000 void AnnotCaret::setSymbol(AnnotCaretSymbol new_symbol) {
6001 Object obj1;
6002 obj1.initName( new_symbol == symbolP ? "P" : "None" );
6003 symbol = new_symbol;
6004 update("Sy", &obj1);
6005 invalidateAppearance();
6006 }
6007
6008 //------------------------------------------------------------------------
6009 // AnnotInk
6010 //------------------------------------------------------------------------
AnnotInk(PDFDoc * docA,PDFRectangle * rect)6011 AnnotInk::AnnotInk(PDFDoc *docA, PDFRectangle *rect) :
6012 AnnotMarkup(docA, rect) {
6013 Object obj1;
6014
6015 type = typeInk;
6016
6017 annotObj.dictSet ("Subtype", obj1.initName ("Ink"));
6018
6019 // Store dummy path with one null vertex only
6020 Object obj2, obj3, obj4;
6021 obj2.initArray (doc->getXRef());
6022 obj2.arrayAdd (obj3.initArray (doc->getXRef()));
6023 obj3.arrayAdd (obj4.initReal (0));
6024 obj3.arrayAdd (obj4.initReal (0));
6025 annotObj.dictSet ("InkList", &obj2);
6026
6027 initialize(docA, annotObj.getDict());
6028 }
6029
AnnotInk(PDFDoc * docA,Dict * dict,Object * obj)6030 AnnotInk::AnnotInk(PDFDoc *docA, Dict *dict, Object *obj) :
6031 AnnotMarkup(docA, dict, obj) {
6032 type = typeInk;
6033 initialize(docA, dict);
6034 }
6035
~AnnotInk()6036 AnnotInk::~AnnotInk() {
6037 freeInkList();
6038 }
6039
initialize(PDFDoc * docA,Dict * dict)6040 void AnnotInk::initialize(PDFDoc *docA, Dict* dict) {
6041 Object obj1;
6042
6043 if (dict->lookup("InkList", &obj1)->isArray()) {
6044 parseInkList(obj1.getArray());
6045 } else {
6046 inkListLength = 0;
6047 inkList = NULL;
6048 error(errSyntaxError, -1, "Bad Annot Ink List");
6049 ok = gFalse;
6050 }
6051 obj1.free();
6052
6053 if (dict->lookup("BS", &obj1)->isDict()) {
6054 delete border;
6055 border = new AnnotBorderBS(obj1.getDict());
6056 } else if (!border) {
6057 border = new AnnotBorderBS();
6058 }
6059 obj1.free();
6060 }
6061
writeInkList(AnnotPath ** paths,int n_paths,Array * dest_array)6062 void AnnotInk::writeInkList(AnnotPath **paths, int n_paths, Array *dest_array) {
6063 Object obj1, obj2;
6064 for (int i = 0; i < n_paths; ++i) {
6065 AnnotPath *path = paths[i];
6066 obj1.initArray (xref);
6067 for (int j = 0; j < path->getCoordsLength(); ++j) {
6068 obj1.arrayAdd (obj2.initReal (path->getX(j)));
6069 obj1.arrayAdd (obj2.initReal (path->getY(j)));
6070 }
6071 dest_array->add (&obj1);
6072 }
6073 }
6074
parseInkList(Array * array)6075 void AnnotInk::parseInkList(Array *array) {
6076 inkListLength = array->getLength();
6077 inkList = (AnnotPath **) gmallocn ((inkListLength), sizeof(AnnotPath *));
6078 memset(inkList, 0, inkListLength * sizeof(AnnotPath *));
6079 for (int i = 0; i < inkListLength; i++) {
6080 Object obj2;
6081 if (array->get(i, &obj2)->isArray())
6082 inkList[i] = new AnnotPath(obj2.getArray());
6083 obj2.free();
6084 }
6085 }
6086
freeInkList()6087 void AnnotInk::freeInkList() {
6088 if (inkList) {
6089 for (int i = 0; i < inkListLength; ++i)
6090 delete inkList[i];
6091 gfree(inkList);
6092 }
6093 }
6094
setInkList(AnnotPath ** paths,int n_paths)6095 void AnnotInk::setInkList(AnnotPath **paths, int n_paths) {
6096 Object obj1;
6097
6098 freeInkList();
6099
6100 obj1.initArray (xref);
6101 writeInkList(paths, n_paths, obj1.getArray());
6102
6103 parseInkList(obj1.getArray());
6104 annotObj.dictSet ("InkList", &obj1);
6105 invalidateAppearance();
6106 }
6107
draw(Gfx * gfx,GBool printing)6108 void AnnotInk::draw(Gfx *gfx, GBool printing) {
6109 Object obj;
6110 double ca = 1;
6111
6112 if (!isVisible (printing))
6113 return;
6114
6115 annotLocker();
6116 if (appearance.isNull()) {
6117 appearBBox = new AnnotAppearanceBBox(rect);
6118 ca = opacity;
6119
6120 appearBuf = new GooString ();
6121 appearBuf->append ("q\n");
6122
6123 if (color) {
6124 setColor(color, gFalse);
6125 }
6126
6127 setLineStyleForBorder(border);
6128 appearBBox->setBorderWidth(std::max(1., border->getWidth()));
6129
6130 for (int i = 0; i < inkListLength; ++i) {
6131 const AnnotPath * path = inkList[i];
6132 if (path->getCoordsLength() != 0) {
6133 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", path->getX(0) - rect->x1, path->getY(0) - rect->y1);
6134 appearBBox->extendTo (path->getX(0) - rect->x1, path->getY(0) - rect->y1);
6135
6136 for (int j = 1; j < path->getCoordsLength(); ++j) {
6137 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", path->getX(j) - rect->x1, path->getY(j) - rect->y1);
6138 appearBBox->extendTo (path->getX(j) - rect->x1, path->getY(j) - rect->y1);
6139 }
6140
6141 appearBuf->append ("S\n");
6142 }
6143 }
6144
6145 appearBuf->append ("Q\n");
6146
6147 double bbox[4];
6148 appearBBox->getBBoxRect(bbox);
6149 if (ca == 1) {
6150 createForm(bbox, gFalse, NULL, &appearance);
6151 } else {
6152 Object aStream, resDict;
6153
6154 createForm(bbox, gTrue, NULL, &aStream);
6155 delete appearBuf;
6156
6157 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6158 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6159 createForm(bbox, gFalse, &resDict, &appearance);
6160 }
6161 delete appearBuf;
6162 }
6163
6164 // draw the appearance stream
6165 appearance.fetch(gfx->getXRef(), &obj);
6166 if (appearBBox) {
6167 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6168 appearBBox->getPageXMin(), appearBBox->getPageYMin(),
6169 appearBBox->getPageXMax(), appearBBox->getPageYMax(),
6170 getRotation());
6171 } else {
6172 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6173 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6174 }
6175 obj.free();
6176 }
6177
6178 //------------------------------------------------------------------------
6179 // AnnotFileAttachment
6180 //------------------------------------------------------------------------
AnnotFileAttachment(PDFDoc * docA,PDFRectangle * rect,GooString * filename)6181 AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, PDFRectangle *rect, GooString *filename) :
6182 AnnotMarkup(docA, rect) {
6183 Object obj1;
6184
6185 type = typeFileAttachment;
6186
6187 annotObj.dictSet ("Subtype", obj1.initName ("FileAttachment"));
6188
6189 Object obj2;
6190 obj2.initString(filename->copy());
6191 annotObj.dictSet ("FS", &obj2);
6192
6193 initialize(docA, annotObj.getDict());
6194 }
6195
AnnotFileAttachment(PDFDoc * docA,Dict * dict,Object * obj)6196 AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, Dict *dict, Object *obj) :
6197 AnnotMarkup(docA, dict, obj) {
6198 type = typeFileAttachment;
6199 initialize(docA, dict);
6200 }
6201
~AnnotFileAttachment()6202 AnnotFileAttachment::~AnnotFileAttachment() {
6203 file.free();
6204
6205 if (name)
6206 delete name;
6207 }
6208
initialize(PDFDoc * docA,Dict * dict)6209 void AnnotFileAttachment::initialize(PDFDoc *docA, Dict* dict) {
6210 Object obj1;
6211
6212 if (dict->lookup("FS", &obj1)->isDict() || dict->lookup("FS", &obj1)->isString()) {
6213 obj1.copy(&file);
6214 } else {
6215 error(errSyntaxError, -1, "Bad Annot File Attachment");
6216 ok = gFalse;
6217 }
6218 obj1.free();
6219
6220 if (dict->lookup("Name", &obj1)->isName()) {
6221 name = new GooString(obj1.getName());
6222 } else {
6223 name = new GooString("PushPin");
6224 }
6225 obj1.free();
6226 }
6227
6228 #define ANNOT_FILE_ATTACHMENT_AP_PUSHPIN \
6229 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6230 "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" \
6231 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6232 "4.301 23 m f\n" \
6233 "0.533333 0.541176 0.521569 RG 2 w\n" \
6234 "1 J\n" \
6235 "1 j\n" \
6236 "[] 0.0 d\n" \
6237 "4 M 5 4 m 6 5 l S\n" \
6238 "2 w\n" \
6239 "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" \
6240 "17 l 11 14 l h\n" \
6241 "11 14 m S\n" \
6242 "3 w\n" \
6243 "6 5 m 9 8 l S\n" \
6244 "0.729412 0.741176 0.713725 RG 2 w\n" \
6245 "5 5 m 6 6 l S\n" \
6246 "2 w\n" \
6247 "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" \
6248 "18 l 11 15 l h\n" \
6249 "11 15 m S\n" \
6250 "3 w\n" \
6251 "6 6 m 9 9 l S\n"
6252
6253 #define ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP \
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 16.645 12.035 m 12.418 7.707 l 10.902 6.559 6.402 11.203 8.09 12.562 c\n" \
6263 "14.133 18.578 l 14.949 19.387 16.867 19.184 17.539 18.465 c 20.551\n" \
6264 "15.23 l 21.191 14.66 21.336 12.887 20.426 12.102 c 13.18 4.824 l 12.18\n" \
6265 "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" \
6266 "17.887 l S\n" \
6267 "0.729412 0.741176 0.713725 RG 16.645 13.035 m 12.418 8.707 l\n" \
6268 "10.902 7.559 6.402 12.203 8.09 13.562 c\n" \
6269 "14.133 19.578 l 14.949 20.387 16.867 20.184 17.539 19.465 c 20.551\n" \
6270 "16.23 l 21.191 15.66 21.336 13.887 20.426 13.102 c 13.18 5.824 l 12.18\n" \
6271 "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" \
6272 "18.887 l S\n"
6273
6274 #define ANNOT_FILE_ATTACHMENT_AP_GRAPH \
6275 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6276 "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" \
6277 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6278 "4.301 23 m f\n" \
6279 "0.533333 0.541176 0.521569 RG 1 w\n" \
6280 "1 J\n" \
6281 "0 j\n" \
6282 "[] 0.0 d\n" \
6283 "4 M 18.5 15.5 m 18.5 13.086 l 16.086 15.5 l 18.5 15.5 l h\n" \
6284 "18.5 15.5 m S\n" \
6285 "7 7 m 10 11 l 13 9 l 18 15 l S\n" \
6286 "0.729412 0.741176 0.713725 RG 7 8 m 10 12 l 13 10 l 18 16 l S\n" \
6287 "18.5 16.5 m 18.5 14.086 l 16.086 16.5 l 18.5 16.5 l h\n" \
6288 "18.5 16.5 m S\n" \
6289 "0.533333 0.541176 0.521569 RG 2 w\n" \
6290 "1 j\n" \
6291 "3 19 m 3 3 l 21 3 l S\n" \
6292 "0.729412 0.741176 0.713725 RG 3 20 m 3 4 l 21 4 l S\n"
6293
6294 #define ANNOT_FILE_ATTACHMENT_AP_TAG \
6295 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6296 "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" \
6297 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6298 "4.301 23 m f\n" \
6299 "0.533333 0.541176 0.521569 RG 0.999781 w\n" \
6300 "1 J\n" \
6301 "1 j\n" \
6302 "[] 0.0 d\n" \
6303 "4 M q 1 0 0 -1 0 24 cm\n" \
6304 "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" \
6305 "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" \
6306 "8.492 7.879 8.492 8.707 c h\n" \
6307 "8.492 8.707 m S Q\n" \
6308 "2 w\n" \
6309 "20.078 11.414 m 20.891 10.602 20.785 9.293 20.078 8.586 c 14.422 2.93 l\n" \
6310 "13.715 2.223 12.301 2.223 11.594 2.93 c 3.816 10.707 l 3.109 11.414\n" \
6311 "2.402 17.781 3.816 19.195 c 5.23 20.609 11.594 19.902 12.301 19.195 c\n" \
6312 "20.078 11.414 l h\n" \
6313 "20.078 11.414 m S\n" \
6314 "0.729412 0.741176 0.713725 RG 20.078 12.414 m\n" \
6315 "20.891 11.605 20.785 10.293 20.078 9.586 c 14.422 3.93 l\n" \
6316 "13.715 3.223 12.301 3.223 11.594 3.93 c 3.816 11.707 l 3.109 12.414\n" \
6317 "2.402 18.781 3.816 20.195 c 5.23 21.609 11.594 20.902 12.301 20.195 c\n" \
6318 "20.078 12.414 l h\n" \
6319 "20.078 12.414 m S\n" \
6320 "0.533333 0.541176 0.521569 RG 1 w\n" \
6321 "0 j\n" \
6322 "11.949 13.184 m 16.191 8.941 l S\n" \
6323 "0.729412 0.741176 0.713725 RG 11.949 14.184 m 16.191 9.941 l S\n" \
6324 "0.533333 0.541176 0.521569 RG 14.07 6.82 m 9.828 11.062 l S\n" \
6325 "0.729412 0.741176 0.713725 RG 14.07 7.82 m 9.828 12.062 l S\n" \
6326 "0.533333 0.541176 0.521569 RG 6.93 15.141 m 8 20 14.27 20.5 16 20.5 c\n" \
6327 "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" \
6328 "0.729412 0.741176 0.713725 RG 0.999781 w\n" \
6329 "1 j\n" \
6330 "q 1 0 0 -1 0 24 cm\n" \
6331 "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" \
6332 "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" \
6333 "8.492 6.879 8.492 7.707 c h\n" \
6334 "8.492 7.707 m S Q\n" \
6335 "1 w\n" \
6336 "0 j\n" \
6337 "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" \
6338 "19.5 17.699 20.91 17.418 22.5 17.5 c S\n"
6339
draw(Gfx * gfx,GBool printing)6340 void AnnotFileAttachment::draw(Gfx *gfx, GBool printing) {
6341 Object obj;
6342 double ca = 1;
6343
6344 if (!isVisible (printing))
6345 return;
6346
6347 annotLocker();
6348 if (appearance.isNull()) {
6349 ca = opacity;
6350
6351 appearBuf = new GooString ();
6352
6353 appearBuf->append ("q\n");
6354 if (color)
6355 setColor(color, gTrue);
6356 else
6357 appearBuf->append ("1 1 1 rg\n");
6358 if (!name->cmp("PushPin"))
6359 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_PUSHPIN);
6360 else if (!name->cmp("Paperclip"))
6361 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP);
6362 else if (!name->cmp("Graph"))
6363 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_GRAPH);
6364 else if (!name->cmp("Tag"))
6365 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_TAG);
6366 appearBuf->append ("Q\n");
6367
6368 double bbox[4];
6369 bbox[0] = bbox[1] = 0;
6370 bbox[2] = bbox[3] = 24;
6371 if (ca == 1) {
6372 createForm (bbox, gFalse, NULL, &appearance);
6373 } else {
6374 Object aStream;
6375
6376 createForm (bbox, gTrue, NULL, &aStream);
6377 delete appearBuf;
6378
6379 Object resDict;
6380 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6381 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6382 createForm(bbox, gFalse, &resDict, &appearance);
6383 }
6384 delete appearBuf;
6385 }
6386
6387 // draw the appearance stream
6388 appearance.fetch(gfx->getXRef(), &obj);
6389 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6390 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6391 obj.free();
6392 }
6393
6394 //------------------------------------------------------------------------
6395 // AnnotSound
6396 //------------------------------------------------------------------------
AnnotSound(PDFDoc * docA,PDFRectangle * rect,Sound * soundA)6397 AnnotSound::AnnotSound(PDFDoc *docA, PDFRectangle *rect, Sound *soundA) :
6398 AnnotMarkup(docA, rect) {
6399 Object obj1;
6400
6401 type = typeSound;
6402
6403 annotObj.dictSet ("Subtype", obj1.initName ("Sound"));
6404
6405 Object obj2;
6406 Stream *str = soundA->getStream();
6407 obj2.initStream (str);
6408 str->incRef(); //FIXME: initStream should do this?
6409 annotObj.dictSet ("Sound", &obj2);
6410
6411 initialize(docA, annotObj.getDict());
6412 }
6413
AnnotSound(PDFDoc * docA,Dict * dict,Object * obj)6414 AnnotSound::AnnotSound(PDFDoc *docA, Dict *dict, Object *obj) :
6415 AnnotMarkup(docA, dict, obj) {
6416 type = typeSound;
6417 initialize(docA, dict);
6418 }
6419
~AnnotSound()6420 AnnotSound::~AnnotSound() {
6421 delete sound;
6422
6423 delete name;
6424 }
6425
initialize(PDFDoc * docA,Dict * dict)6426 void AnnotSound::initialize(PDFDoc *docA, Dict* dict) {
6427 Object obj1;
6428
6429 sound = Sound::parseSound(dict->lookup("Sound", &obj1));
6430 if (!sound) {
6431 error(errSyntaxError, -1, "Bad Annot Sound");
6432 ok = gFalse;
6433 }
6434 obj1.free();
6435
6436 if (dict->lookup("Name", &obj1)->isName()) {
6437 name = new GooString(obj1.getName());
6438 } else {
6439 name = new GooString("Speaker");
6440 }
6441 obj1.free();
6442 }
6443
6444 #define ANNOT_SOUND_AP_SPEAKER \
6445 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6446 "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" \
6447 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6448 "4.301 23 m f\n" \
6449 "0.533333 0.541176 0.521569 RG 2 w\n" \
6450 "0 J\n" \
6451 "1 j\n" \
6452 "[] 0.0 d\n" \
6453 "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" \
6454 "4 14 m S\n" \
6455 "1 w\n" \
6456 "1 J\n" \
6457 "0 j\n" \
6458 "13.699 15.398 m 14.699 13.398 14.699 9.398 13.699 7.398 c S\n" \
6459 "18.199 19.398 m 21.199 17.398 21.199 5.398 18.199 3.398 c S\n" \
6460 "16 17.398 m 18 16.398 18 7.398 16 5.398 c S\n" \
6461 "0.729412 0.741176 0.713725 RG 2 w\n" \
6462 "0 J\n" \
6463 "1 j\n" \
6464 "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" \
6465 "4 15 m S\n" \
6466 "1 w\n" \
6467 "1 J\n" \
6468 "0 j\n" \
6469 "13.699 16 m 14.699 14 14.699 10 13.699 8 c S\n" \
6470 "18.199 20 m 21.199 18 21.199 6 18.199 4 c S\n" \
6471 "16 18 m 18 17 18 8 16 6 c S\n"
6472
6473 #define ANNOT_SOUND_AP_MIC \
6474 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
6475 "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" \
6476 "l 1 21.523 2.477 23 4.301 23 c h\n" \
6477 "4.301 23 m f\n" \
6478 "0.533333 0.541176 0.521569 RG 2 w\n" \
6479 "1 J\n" \
6480 "0 j\n" \
6481 "[] 0.0 d\n" \
6482 "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" \
6483 "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" \
6484 "20 c h\n" \
6485 "12 20 m S\n" \
6486 "1 w\n" \
6487 "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" \
6488 "8.941 6.5 11.973 c 6.5 14.5 l S\n" \
6489 "2 w\n" \
6490 "0 J\n" \
6491 "12 6.52 m 12 3 l S\n" \
6492 "1 J\n" \
6493 "8 3 m 16 3 l S\n" \
6494 "0.729412 0.741176 0.713725 RG 12 21 m 12 21 l 13.656 21 15 19.656 15 18 c\n" \
6495 "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" \
6496 "9 18 l 9 19.656 10.344 21 12 21 c h\n" \
6497 "12 21 m S\n" \
6498 "1 w\n" \
6499 "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" \
6500 "9.941 6.5 12.973 c 6.5 15.5 l S\n" \
6501 "2 w\n" \
6502 "0 J\n" \
6503 "12 7.52 m 12 4 l S\n" \
6504 "1 J\n" \
6505 "8 4 m 16 4 l S\n"
6506
draw(Gfx * gfx,GBool printing)6507 void AnnotSound::draw(Gfx *gfx, GBool printing) {
6508 Object obj;
6509 double ca = 1;
6510
6511 if (!isVisible (printing))
6512 return;
6513
6514 annotLocker();
6515 if (appearance.isNull()) {
6516 ca = opacity;
6517
6518 appearBuf = new GooString ();
6519
6520 appearBuf->append ("q\n");
6521 if (color)
6522 setColor(color, gTrue);
6523 else
6524 appearBuf->append ("1 1 1 rg\n");
6525 if (!name->cmp("Speaker"))
6526 appearBuf->append (ANNOT_SOUND_AP_SPEAKER);
6527 else if (!name->cmp("Mic"))
6528 appearBuf->append (ANNOT_SOUND_AP_MIC);
6529 appearBuf->append ("Q\n");
6530
6531 double bbox[4];
6532 bbox[0] = bbox[1] = 0;
6533 bbox[2] = bbox[3] = 24;
6534 if (ca == 1) {
6535 createForm(bbox, gFalse, NULL, &appearance);
6536 } else {
6537 Object aStream, resDict;
6538
6539 createForm(bbox, gTrue, NULL, &aStream);
6540 delete appearBuf;
6541
6542 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
6543 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
6544 createForm(bbox, gFalse, &resDict, &appearance);
6545 }
6546 delete appearBuf;
6547 }
6548
6549 // draw the appearance stream
6550 appearance.fetch(gfx->getXRef(), &obj);
6551 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
6552 rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6553 obj.free();
6554 }
6555
6556 //------------------------------------------------------------------------
6557 // Annot3D
6558 //------------------------------------------------------------------------
Annot3D(PDFDoc * docA,PDFRectangle * rect)6559 Annot3D::Annot3D(PDFDoc *docA, PDFRectangle *rect) :
6560 Annot(docA, rect) {
6561 Object obj1;
6562
6563 type = type3D;
6564
6565 annotObj.dictSet ("Subtype", obj1.initName ("3D"));
6566
6567 initialize(docA, annotObj.getDict());
6568 }
6569
Annot3D(PDFDoc * docA,Dict * dict,Object * obj)6570 Annot3D::Annot3D(PDFDoc *docA, Dict *dict, Object *obj) :
6571 Annot(docA, dict, obj) {
6572 type = type3D;
6573 initialize(docA, dict);
6574 }
6575
~Annot3D()6576 Annot3D::~Annot3D() {
6577 if (activation)
6578 delete activation;
6579 }
6580
initialize(PDFDoc * docA,Dict * dict)6581 void Annot3D::initialize(PDFDoc *docA, Dict* dict) {
6582 Object obj1;
6583
6584 if (dict->lookup("3DA", &obj1)->isDict()) {
6585 activation = new Activation(obj1.getDict());
6586 } else {
6587 activation = NULL;
6588 }
6589 obj1.free();
6590 }
6591
Activation(Dict * dict)6592 Annot3D::Activation::Activation(Dict *dict) {
6593 Object obj1;
6594
6595 if (dict->lookup("A", &obj1)->isName()) {
6596 const char *name = obj1.getName();
6597
6598 if(!strcmp(name, "PO")) {
6599 aTrigger = aTriggerPageOpened;
6600 } else if(!strcmp(name, "PV")) {
6601 aTrigger = aTriggerPageVisible;
6602 } else if(!strcmp(name, "XA")) {
6603 aTrigger = aTriggerUserAction;
6604 } else {
6605 aTrigger = aTriggerUnknown;
6606 }
6607 } else {
6608 aTrigger = aTriggerUnknown;
6609 }
6610 obj1.free();
6611
6612 if(dict->lookup("AIS", &obj1)->isName()) {
6613 const char *name = obj1.getName();
6614
6615 if(!strcmp(name, "I")) {
6616 aState = aStateEnabled;
6617 } else if(!strcmp(name, "L")) {
6618 aState = aStateDisabled;
6619 } else {
6620 aState = aStateUnknown;
6621 }
6622 } else {
6623 aState = aStateUnknown;
6624 }
6625 obj1.free();
6626
6627 if(dict->lookup("D", &obj1)->isName()) {
6628 const char *name = obj1.getName();
6629
6630 if(!strcmp(name, "PC")) {
6631 dTrigger = dTriggerPageClosed;
6632 } else if(!strcmp(name, "PI")) {
6633 dTrigger = dTriggerPageInvisible;
6634 } else if(!strcmp(name, "XD")) {
6635 dTrigger = dTriggerUserAction;
6636 } else {
6637 dTrigger = dTriggerUnknown;
6638 }
6639 } else {
6640 dTrigger = dTriggerUnknown;
6641 }
6642 obj1.free();
6643
6644 if(dict->lookup("DIS", &obj1)->isName()) {
6645 const char *name = obj1.getName();
6646
6647 if(!strcmp(name, "U")) {
6648 dState = dStateUninstantiaded;
6649 } else if(!strcmp(name, "I")) {
6650 dState = dStateInstantiated;
6651 } else if(!strcmp(name, "L")) {
6652 dState = dStateLive;
6653 } else {
6654 dState = dStateUnknown;
6655 }
6656 } else {
6657 dState = dStateUnknown;
6658 }
6659 obj1.free();
6660
6661 if (dict->lookup("TB", &obj1)->isBool()) {
6662 displayToolbar = obj1.getBool();
6663 } else {
6664 displayToolbar = gTrue;
6665 }
6666 obj1.free();
6667
6668 if (dict->lookup("NP", &obj1)->isBool()) {
6669 displayNavigation = obj1.getBool();
6670 } else {
6671 displayNavigation = gFalse;
6672 }
6673 obj1.free();
6674 }
6675
6676 //------------------------------------------------------------------------
6677 // Annots
6678 //------------------------------------------------------------------------
6679
Annots(PDFDoc * docA,int page,Object * annotsObj)6680 Annots::Annots(PDFDoc *docA, int page, Object *annotsObj) {
6681 Annot *annot;
6682 Object obj1;
6683 int i;
6684
6685 doc = docA;
6686 annots = NULL;
6687 size = 0;
6688 nAnnots = 0;
6689
6690 if (annotsObj->isArray()) {
6691 for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
6692 //get the Ref to this annot and pass it to Annot constructor
6693 //this way, it'll be possible for the annot to retrieve the corresponding
6694 //form widget
6695 Object obj2;
6696 if (annotsObj->arrayGet(i, &obj1)->isDict()) {
6697 annotsObj->arrayGetNF(i, &obj2);
6698 annot = createAnnot (obj1.getDict(), &obj2);
6699 if (annot) {
6700 if (annot->isOk()) {
6701 annot->setPage(page, gFalse); // Don't change /P
6702 appendAnnot(annot);
6703 }
6704 annot->decRefCnt();
6705 }
6706 }
6707 obj2.free();
6708 obj1.free();
6709 }
6710 }
6711 }
6712
appendAnnot(Annot * annot)6713 void Annots::appendAnnot(Annot *annot) {
6714 if (annot && annot->isOk()) {
6715 if (nAnnots >= size) {
6716 size += 16;
6717 annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
6718 }
6719 annots[nAnnots++] = annot;
6720 annot->incRefCnt();
6721 }
6722 }
6723
removeAnnot(Annot * annot)6724 GBool Annots::removeAnnot(Annot *annot) {
6725 int idx = -1;
6726 // Search annot and determine its index
6727 for (int i = 0; idx == -1 && i < nAnnots; i++) {
6728 if (annots[i] == annot) {
6729 idx = i;
6730 }
6731 }
6732 if (idx == -1) {
6733 return gFalse;
6734 } else {
6735 --nAnnots;
6736 memmove( annots + idx, annots + idx + 1, sizeof(annots[0]) * (nAnnots - idx) );
6737 annot->decRefCnt();
6738 return gTrue;
6739 }
6740 }
6741
createAnnot(Dict * dict,Object * obj)6742 Annot *Annots::createAnnot(Dict* dict, Object *obj) {
6743 Annot *annot = NULL;
6744 Object obj1;
6745
6746 if (dict->lookup("Subtype", &obj1)->isName()) {
6747 const char *typeName = obj1.getName();
6748
6749 if (!strcmp(typeName, "Text")) {
6750 annot = new AnnotText(doc, dict, obj);
6751 } else if (!strcmp(typeName, "Link")) {
6752 annot = new AnnotLink(doc, dict, obj);
6753 } else if (!strcmp(typeName, "FreeText")) {
6754 annot = new AnnotFreeText(doc, dict, obj);
6755 } else if (!strcmp(typeName, "Line")) {
6756 annot = new AnnotLine(doc, dict, obj);
6757 } else if (!strcmp(typeName, "Square")) {
6758 annot = new AnnotGeometry(doc, dict, obj);
6759 } else if (!strcmp(typeName, "Circle")) {
6760 annot = new AnnotGeometry(doc, dict, obj);
6761 } else if (!strcmp(typeName, "Polygon")) {
6762 annot = new AnnotPolygon(doc, dict, obj);
6763 } else if (!strcmp(typeName, "PolyLine")) {
6764 annot = new AnnotPolygon(doc, dict, obj);
6765 } else if (!strcmp(typeName, "Highlight")) {
6766 annot = new AnnotTextMarkup(doc, dict, obj);
6767 } else if (!strcmp(typeName, "Underline")) {
6768 annot = new AnnotTextMarkup(doc, dict, obj);
6769 } else if (!strcmp(typeName, "Squiggly")) {
6770 annot = new AnnotTextMarkup(doc, dict, obj);
6771 } else if (!strcmp(typeName, "StrikeOut")) {
6772 annot = new AnnotTextMarkup(doc, dict, obj);
6773 } else if (!strcmp(typeName, "Stamp")) {
6774 annot = new AnnotStamp(doc, dict, obj);
6775 } else if (!strcmp(typeName, "Caret")) {
6776 annot = new AnnotCaret(doc, dict, obj);
6777 } else if (!strcmp(typeName, "Ink")) {
6778 annot = new AnnotInk(doc, dict, obj);
6779 } else if (!strcmp(typeName, "FileAttachment")) {
6780 annot = new AnnotFileAttachment(doc, dict, obj);
6781 } else if (!strcmp(typeName, "Sound")) {
6782 annot = new AnnotSound(doc, dict, obj);
6783 } else if(!strcmp(typeName, "Movie")) {
6784 annot = new AnnotMovie(doc, dict, obj);
6785 } else if(!strcmp(typeName, "Widget")) {
6786 // Find the annot in forms
6787 if (obj->isRef()) {
6788 Form *form = doc->getCatalog()->getForm();
6789 if (form) {
6790 FormWidget *widget = form->findWidgetByRef(obj->getRef());
6791 if (widget) {
6792 annot = widget->getWidgetAnnotation();
6793 annot->incRefCnt();
6794 }
6795 }
6796 }
6797 if (!annot)
6798 annot = new AnnotWidget(doc, dict, obj);
6799 } else if(!strcmp(typeName, "Screen")) {
6800 annot = new AnnotScreen(doc, dict, obj);
6801 } else if(!strcmp(typeName, "PrinterMark")) {
6802 annot = new Annot(doc, dict, obj);
6803 } else if (!strcmp(typeName, "TrapNet")) {
6804 annot = new Annot(doc, dict, obj);
6805 } else if (!strcmp(typeName, "Watermark")) {
6806 annot = new Annot(doc, dict, obj);
6807 } else if (!strcmp(typeName, "3D")) {
6808 annot = new Annot3D(doc, dict, obj);
6809 } else if (!strcmp(typeName, "Popup")) {
6810 /* Popup annots are already handled by markup annots
6811 * Here we only care about popup annots without a
6812 * markup annotation associated
6813 */
6814 Object obj2;
6815
6816 if (dict->lookup("Parent", &obj2)->isNull())
6817 annot = new AnnotPopup(doc, dict, obj);
6818 else
6819 annot = NULL;
6820
6821 obj2.free();
6822 } else {
6823 annot = new Annot(doc, dict, obj);
6824 }
6825 }
6826 obj1.free();
6827
6828 return annot;
6829 }
6830
findAnnot(Ref * ref)6831 Annot *Annots::findAnnot(Ref *ref) {
6832 int i;
6833
6834 for (i = 0; i < nAnnots; ++i) {
6835 if (annots[i]->match(ref)) {
6836 return annots[i];
6837 }
6838 }
6839 return NULL;
6840 }
6841
6842
~Annots()6843 Annots::~Annots() {
6844 int i;
6845
6846 for (i = 0; i < nAnnots; ++i) {
6847 annots[i]->decRefCnt();
6848 }
6849 gfree(annots);
6850 }
6851