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-2011 Albert Astals Cid <aacid@kde.org>
19 // Copyright (C) 2007-2011 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 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 //
27 // To see a description of the changes please see the Changelog file that
28 // came with your tarball or type make ChangeLog if you are building from git
29 //
30 //========================================================================
31
32 #include <config.h>
33
34 #ifdef USE_GCC_PRAGMAS
35 #pragma implementation
36 #endif
37
38 #include <stdlib.h>
39 #include <math.h>
40 #include <assert.h>
41 #include "goo/gmem.h"
42 #include "goo/gstrtod.h"
43 #include "GooList.h"
44 #include "Error.h"
45 #include "Object.h"
46 #include "Catalog.h"
47 #include "Gfx.h"
48 #include "Lexer.h"
49 #include "Annot.h"
50 #include "GfxFont.h"
51 #include "CharCodeToUnicode.h"
52 #include "PDFDocEncoding.h"
53 #include "Form.h"
54 #include "Error.h"
55 #include "Page.h"
56 #include "XRef.h"
57 #include "Movie.h"
58 #include "OptionalContent.h"
59 #include "Sound.h"
60 #include "FileSpec.h"
61 #include "DateInfo.h"
62 #include "Link.h"
63 #include <string.h>
64
65 #define fieldFlagReadOnly 0x00000001
66 #define fieldFlagRequired 0x00000002
67 #define fieldFlagNoExport 0x00000004
68 #define fieldFlagMultiline 0x00001000
69 #define fieldFlagPassword 0x00002000
70 #define fieldFlagNoToggleToOff 0x00004000
71 #define fieldFlagRadio 0x00008000
72 #define fieldFlagPushbutton 0x00010000
73 #define fieldFlagCombo 0x00020000
74 #define fieldFlagEdit 0x00040000
75 #define fieldFlagSort 0x00080000
76 #define fieldFlagFileSelect 0x00100000
77 #define fieldFlagMultiSelect 0x00200000
78 #define fieldFlagDoNotSpellCheck 0x00400000
79 #define fieldFlagDoNotScroll 0x00800000
80 #define fieldFlagComb 0x01000000
81 #define fieldFlagRichText 0x02000000
82 #define fieldFlagRadiosInUnison 0x02000000
83 #define fieldFlagCommitOnSelChange 0x04000000
84
85 #define fieldQuadLeft 0
86 #define fieldQuadCenter 1
87 #define fieldQuadRight 2
88
89 // distance of Bezier control point from center for circle approximation
90 // = (4 * (sqrt(2) - 1) / 3) * r
91 #define bezierCircle 0.55228475
92
93 // Ensures that x is between the limits set by low and high.
94 // If low is greater than high the result is undefined.
95 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
96
parseAnnotLineEndingStyle(GooString * string)97 AnnotLineEndingStyle parseAnnotLineEndingStyle(GooString *string) {
98 if (string != NULL) {
99 if (!string->cmp("Square")) {
100 return annotLineEndingSquare;
101 } else if (!string->cmp("Circle")) {
102 return annotLineEndingCircle;
103 } else if (!string->cmp("Diamond")) {
104 return annotLineEndingDiamond;
105 } else if (!string->cmp("OpenArrow")) {
106 return annotLineEndingOpenArrow;
107 } else if (!string->cmp("ClosedArrow")) {
108 return annotLineEndingClosedArrow;
109 } else if (!string->cmp("Butt")) {
110 return annotLineEndingButt;
111 } else if (!string->cmp("ROpenArrow")) {
112 return annotLineEndingROpenArrow;
113 } else if (!string->cmp("RClosedArrow")) {
114 return annotLineEndingRClosedArrow;
115 } else if (!string->cmp("Slash")) {
116 return annotLineEndingSlash;
117 } else {
118 return annotLineEndingNone;
119 }
120 } else {
121 return annotLineEndingNone;
122 }
123 }
124
parseAnnotExternalData(Dict * dict)125 static AnnotExternalDataType parseAnnotExternalData(Dict* dict) {
126 Object obj1;
127 AnnotExternalDataType type;
128
129 if (dict->lookup("Subtype", &obj1)->isName()) {
130 GooString *typeName = new GooString(obj1.getName());
131
132 if (!typeName->cmp("Markup3D")) {
133 type = annotExternalDataMarkup3D;
134 } else {
135 type = annotExternalDataMarkupUnknown;
136 }
137 delete typeName;
138 } else {
139 type = annotExternalDataMarkupUnknown;
140 }
141 obj1.free();
142
143 return type;
144 }
145
parseDiffRectangle(Array * array,PDFRectangle * rect)146 PDFRectangle *parseDiffRectangle(Array *array, PDFRectangle *rect) {
147 PDFRectangle *newRect = NULL;
148 if (array->getLength() == 4) {
149 // deltas
150 Object obj1;
151 double dx1 = (array->get(0, &obj1)->isNum() ? obj1.getNum() : 0);
152 obj1.free();
153 double dy1 = (array->get(1, &obj1)->isNum() ? obj1.getNum() : 0);
154 obj1.free();
155 double dx2 = (array->get(2, &obj1)->isNum() ? obj1.getNum() : 0);
156 obj1.free();
157 double dy2 = (array->get(3, &obj1)->isNum() ? obj1.getNum() : 0);
158 obj1.free();
159
160 // checking that the numbers are valid (i.e. >= 0),
161 // and that applying the differences still give us a valid rect
162 if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2
163 && (rect->x2 - rect->x1 - dx1 - dx2) >= 0
164 && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
165 newRect = new PDFRectangle();
166 newRect->x1 = rect->x1 + dx1;
167 newRect->y1 = rect->y1 + dy1;
168 newRect->x2 = rect->x2 - dx2;
169 newRect->y2 = rect->y2 - dy2;
170 }
171 }
172 return newRect;
173 }
174
175 //------------------------------------------------------------------------
176 // AnnotBorderEffect
177 //------------------------------------------------------------------------
178
AnnotBorderEffect(Dict * dict)179 AnnotBorderEffect::AnnotBorderEffect(Dict *dict) {
180 Object obj1;
181
182 if (dict->lookup("S", &obj1)->isName()) {
183 GooString *effectName = new GooString(obj1.getName());
184
185 if (!effectName->cmp("C"))
186 effectType = borderEffectCloudy;
187 else
188 effectType = borderEffectNoEffect;
189 delete effectName;
190 } else {
191 effectType = borderEffectNoEffect;
192 }
193 obj1.free();
194
195 if ((dict->lookup("I", &obj1)->isNum()) && effectType == borderEffectCloudy) {
196 intensity = obj1.getNum();
197 } else {
198 intensity = 0;
199 }
200 obj1.free();
201 }
202
203 //------------------------------------------------------------------------
204 // AnnotPath
205 //------------------------------------------------------------------------
206
AnnotPath()207 AnnotPath::AnnotPath() {
208 coords = NULL;
209 coordsLength = 0;
210 }
211
AnnotPath(Array * array)212 AnnotPath::AnnotPath(Array *array) {
213 coords = NULL;
214 coordsLength = 0;
215 parsePathArray(array);
216 }
217
AnnotPath(AnnotCoord ** coords,int coordsLength)218 AnnotPath::AnnotPath(AnnotCoord **coords, int coordsLength) {
219 this->coords = coords;
220 this->coordsLength = coordsLength;
221 }
222
~AnnotPath()223 AnnotPath::~AnnotPath() {
224 if (coords) {
225 for (int i = 0; i < coordsLength; ++i)
226 delete coords[i];
227 gfree(coords);
228 }
229 }
230
getX(int coord) const231 double AnnotPath::getX(int coord) const {
232 if (coord >= 0 && coord < coordsLength)
233 return coords[coord]->getX();
234 return 0;
235 }
236
getY(int coord) const237 double AnnotPath::getY(int coord) const {
238 if (coord >= 0 && coord < coordsLength)
239 return coords[coord]->getY();
240 return 0;
241 }
242
getCoord(int coord) const243 AnnotCoord *AnnotPath::getCoord(int coord) const {
244 if (coord >= 0 && coord < coordsLength)
245 return coords[coord];
246 return NULL;
247 }
248
parsePathArray(Array * array)249 void AnnotPath::parsePathArray(Array *array) {
250 int tempLength;
251 AnnotCoord **tempCoords;
252 GBool correct = gTrue;
253
254 if (array->getLength() % 2) {
255 error(-1, "Bad Annot Path");
256 return;
257 }
258
259 tempLength = array->getLength() / 2;
260 tempCoords = (AnnotCoord **) gmallocn (tempLength, sizeof(AnnotCoord *));
261 memset(tempCoords, 0, tempLength * sizeof(AnnotCoord *));
262 for (int i = 0; i < tempLength && correct; i++) {
263 Object obj1;
264 double x = 0, y = 0;
265
266 if (array->get(i * 2, &obj1)->isNum()) {
267 x = obj1.getNum();
268 } else {
269 correct = gFalse;
270 }
271 obj1.free();
272
273 if (array->get((i * 2) + 1, &obj1)->isNum()) {
274 y = obj1.getNum();
275 } else {
276 correct = gFalse;
277 }
278 obj1.free();
279
280 if (!correct) {
281 for (int j = i - 1; j >= 0; j--)
282 delete tempCoords[j];
283 gfree (tempCoords);
284 return;
285 }
286
287 tempCoords[i] = new AnnotCoord(x, y);
288 }
289
290 coords = tempCoords;
291 coordsLength = tempLength;
292 }
293
294 //------------------------------------------------------------------------
295 // AnnotCalloutLine
296 //------------------------------------------------------------------------
297
AnnotCalloutLine(double x1,double y1,double x2,double y2)298 AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2)
299 : coord1(x1, y1), coord2(x2, y2) {
300 }
301
302 //------------------------------------------------------------------------
303 // AnnotCalloutMultiLine
304 //------------------------------------------------------------------------
305
AnnotCalloutMultiLine(double x1,double y1,double x2,double y2,double x3,double y3)306 AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2,
307 double y2, double x3, double y3)
308 : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) {
309 }
310
311 //------------------------------------------------------------------------
312 // AnnotQuadrilateral
313 //------------------------------------------------------------------------
314
AnnotQuadrilaterals(Array * array,PDFRectangle * rect)315 AnnotQuadrilaterals::AnnotQuadrilaterals(Array *array, PDFRectangle *rect) {
316 int arrayLength = array->getLength();
317 GBool correct = gTrue;
318 int quadsLength = 0;
319 AnnotQuadrilateral **quads;
320 double quadArray[8];
321
322 // default values
323 quadrilaterals = NULL;
324 quadrilateralsLength = 0;
325
326 if ((arrayLength % 8) == 0) {
327 int i;
328
329 quadsLength = arrayLength / 8;
330 quads = (AnnotQuadrilateral **) gmallocn
331 ((quadsLength), sizeof(AnnotQuadrilateral *));
332 memset(quads, 0, quadsLength * sizeof(AnnotQuadrilateral *));
333
334 for (i = 0; i < quadsLength; i++) {
335 for (int j = 0; j < 8; j++) {
336 Object obj;
337 if (array->get(i * 8 + j, &obj)->isNum()) {
338 if (j % 2 == 1)
339 quadArray[j] = CLAMP (obj.getNum(), rect->y1, rect->y2);
340 else
341 quadArray[j] = CLAMP (obj.getNum(), rect->x1, rect->x2);
342 } else {
343 correct = gFalse;
344 obj.free();
345 error (-1, "Invalid QuadPoint in annot");
346 break;
347 }
348 obj.free();
349 }
350
351 if (!correct)
352 break;
353
354 quads[i] = new AnnotQuadrilateral(quadArray[0], quadArray[1],
355 quadArray[2], quadArray[3],
356 quadArray[4], quadArray[5],
357 quadArray[6], quadArray[7]);
358 }
359
360 if (correct) {
361 quadrilateralsLength = quadsLength;
362 quadrilaterals = quads;
363 } else {
364 for (int j = 0; j < i; j++)
365 delete quads[j];
366 gfree (quads);
367 }
368 }
369 }
370
~AnnotQuadrilaterals()371 AnnotQuadrilaterals::~AnnotQuadrilaterals() {
372 if (quadrilaterals) {
373 for(int i = 0; i < quadrilateralsLength; i++)
374 delete quadrilaterals[i];
375
376 gfree (quadrilaterals);
377 }
378 }
379
getX1(int quadrilateral)380 double AnnotQuadrilaterals::getX1(int quadrilateral) {
381 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
382 return quadrilaterals[quadrilateral]->coord1.getX();
383 return 0;
384 }
385
getY1(int quadrilateral)386 double AnnotQuadrilaterals::getY1(int quadrilateral) {
387 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
388 return quadrilaterals[quadrilateral]->coord1.getY();
389 return 0;
390 }
391
getX2(int quadrilateral)392 double AnnotQuadrilaterals::getX2(int quadrilateral) {
393 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
394 return quadrilaterals[quadrilateral]->coord2.getX();
395 return 0;
396 }
397
getY2(int quadrilateral)398 double AnnotQuadrilaterals::getY2(int quadrilateral) {
399 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
400 return quadrilaterals[quadrilateral]->coord2.getY();
401 return 0;
402 }
403
getX3(int quadrilateral)404 double AnnotQuadrilaterals::getX3(int quadrilateral) {
405 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
406 return quadrilaterals[quadrilateral]->coord3.getX();
407 return 0;
408 }
409
getY3(int quadrilateral)410 double AnnotQuadrilaterals::getY3(int quadrilateral) {
411 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
412 return quadrilaterals[quadrilateral]->coord3.getY();
413 return 0;
414 }
415
getX4(int quadrilateral)416 double AnnotQuadrilaterals::getX4(int quadrilateral) {
417 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
418 return quadrilaterals[quadrilateral]->coord4.getX();
419 return 0;
420 }
421
getY4(int quadrilateral)422 double AnnotQuadrilaterals::getY4(int quadrilateral) {
423 if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
424 return quadrilaterals[quadrilateral]->coord4.getY();
425 return 0;
426 }
427
AnnotQuadrilateral(double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4)428 AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1,
429 double x2, double y2, double x3, double y3, double x4, double y4)
430 : coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) {
431 }
432
433 //------------------------------------------------------------------------
434 // AnnotBorder
435 //------------------------------------------------------------------------
AnnotBorder()436 AnnotBorder::AnnotBorder() {
437 type = typeUnknown;
438 width = 1;
439 dashLength = 0;
440 dash = NULL;
441 style = borderSolid;
442 }
443
~AnnotBorder()444 AnnotBorder::~AnnotBorder() {
445 if (dash)
446 gfree (dash);
447 }
448
449 //------------------------------------------------------------------------
450 // AnnotBorderArray
451 //------------------------------------------------------------------------
452
AnnotBorderArray()453 AnnotBorderArray::AnnotBorderArray() {
454 type = typeArray;
455 horizontalCorner = 0;
456 verticalCorner = 0;
457 }
458
AnnotBorderArray(Array * array)459 AnnotBorderArray::AnnotBorderArray(Array *array) {
460 Object obj1;
461 int arrayLength = array->getLength();
462
463 GBool correct = gTrue;
464 if (arrayLength == 3 || arrayLength == 4) {
465 // implementation note 81 in Appendix H.
466
467 if (array->get(0, &obj1)->isNum())
468 horizontalCorner = obj1.getNum();
469 else
470 correct = gFalse;
471 obj1.free();
472
473 if (array->get(1, &obj1)->isNum())
474 verticalCorner = obj1.getNum();
475 else
476 correct = gFalse;
477 obj1.free();
478
479 if (array->get(2, &obj1)->isNum())
480 width = obj1.getNum();
481 else
482 correct = gFalse;
483 obj1.free();
484
485 // TODO: check not all zero ? (Line Dash Pattern Page 217 PDF 8.1)
486 if (arrayLength == 4) {
487 if (array->get(3, &obj1)->isArray()) {
488 Array *dashPattern = obj1.getArray();
489 int tempLength = dashPattern->getLength();
490 double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
491
492 for(int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
493
494 if (dashPattern->get(i, &obj1)->isNum()) {
495 tempDash[i] = obj1.getNum();
496
497 if (tempDash[i] < 0)
498 correct = gFalse;
499
500 } else {
501 correct = gFalse;
502 }
503 obj1.free();
504 }
505
506 if (correct) {
507 dashLength = tempLength;
508 dash = tempDash;
509 style = borderDashed;
510 } else {
511 gfree (tempDash);
512 }
513 } else {
514 correct = gFalse;
515 }
516 obj1.free();
517 }
518 } else {
519 correct = gFalse;
520 }
521
522 if (!correct) {
523 width = 0;
524 }
525 }
526
527 //------------------------------------------------------------------------
528 // AnnotBorderBS
529 //------------------------------------------------------------------------
530
AnnotBorderBS()531 AnnotBorderBS::AnnotBorderBS() {
532 type = typeBS;
533 }
534
AnnotBorderBS(Dict * dict)535 AnnotBorderBS::AnnotBorderBS(Dict *dict) {
536 Object obj1, obj2;
537
538 // acroread 8 seems to need both W and S entries for
539 // any border to be drawn, even though the spec
540 // doesn't claim anything of that sort. We follow
541 // that behaviour by veryifying both entries exist
542 // otherwise we set the borderWidth to 0
543 // --jrmuizel
544 dict->lookup("W", &obj1);
545 dict->lookup("S", &obj2);
546 if (obj1.isNum() && obj2.isName()) {
547 GooString *styleName = new GooString(obj2.getName());
548
549 width = obj1.getNum();
550
551 if (!styleName->cmp("S")) {
552 style = borderSolid;
553 } else if (!styleName->cmp("D")) {
554 style = borderDashed;
555 } else if (!styleName->cmp("B")) {
556 style = borderBeveled;
557 } else if (!styleName->cmp("I")) {
558 style = borderInset;
559 } else if (!styleName->cmp("U")) {
560 style = borderUnderlined;
561 } else {
562 style = borderSolid;
563 }
564 delete styleName;
565 } else {
566 width = 0;
567 }
568 obj2.free();
569 obj1.free();
570
571 // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
572 if (dict->lookup("D", &obj1)->isArray()) {
573 GBool correct = gTrue;
574 int tempLength = obj1.arrayGetLength();
575 double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
576
577 for(int i = 0; i < tempLength && correct; i++) {
578 Object obj2;
579
580 if (obj1.arrayGet(i, &obj2)->isNum()) {
581 tempDash[i] = obj2.getNum();
582
583 if (tempDash[i] < 0)
584 correct = gFalse;
585 } else {
586 correct = gFalse;
587 }
588 obj2.free();
589 }
590
591 if (correct) {
592 dashLength = tempLength;
593 dash = tempDash;
594 style = borderDashed;
595 } else {
596 gfree (tempDash);
597 }
598
599 }
600
601 if (!dash) {
602 dashLength = 1;
603 dash = (double *) gmallocn (dashLength, sizeof (double));
604 dash[0] = 3;
605 }
606 obj1.free();
607 }
608
609 //------------------------------------------------------------------------
610 // AnnotColor
611 //------------------------------------------------------------------------
612
AnnotColor()613 AnnotColor::AnnotColor() {
614 length = 0;
615 }
616
AnnotColor(double gray)617 AnnotColor::AnnotColor(double gray) {
618 length = 1;
619
620 values[0] = gray;
621 }
622
AnnotColor(double r,double g,double b)623 AnnotColor::AnnotColor(double r, double g, double b) {
624 length = 3;
625
626 values[0] = r;
627 values[1] = g;
628 values[2] = b;
629 }
630
AnnotColor(double c,double m,double y,double k)631 AnnotColor::AnnotColor(double c, double m, double y, double k) {
632 length = 4;
633
634 values[0] = c;
635 values[1] = m;
636 values[2] = y;
637 values[3] = k;
638 }
639
640 // If <adjust> is +1, color is brightened;
641 // if <adjust> is -1, color is darkened;
642 // otherwise color is not modified.
AnnotColor(Array * array,int adjust)643 AnnotColor::AnnotColor(Array *array, int adjust) {
644 int i;
645
646 length = array->getLength();
647 if (length > 4)
648 length = 4;
649
650 for (i = 0; i < length; i++) {
651 Object obj1;
652
653 if (array->get(i, &obj1)->isNum()) {
654 values[i] = obj1.getNum();
655
656 if (values[i] < 0 || values[i] > 1)
657 values[i] = 0;
658 } else {
659 values[i] = 0;
660 }
661 obj1.free();
662 }
663
664 if (length == 4) {
665 adjust = -adjust;
666 }
667 if (adjust > 0) {
668 for (i = 0; i < length; ++i) {
669 values[i] = 0.5 * values[i] + 0.5;
670 }
671 } else if (adjust < 0) {
672 for (i = 0; i < length; ++i) {
673 values[i] = 0.5 * values[i];
674 }
675 }
676 }
677
678 //------------------------------------------------------------------------
679 // AnnotBorderStyle
680 //------------------------------------------------------------------------
681
AnnotBorderStyle(AnnotBorderType typeA,double widthA,double * dashA,int dashLengthA,double rA,double gA,double bA)682 AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
683 double *dashA, int dashLengthA,
684 double rA, double gA, double bA) {
685 type = typeA;
686 width = widthA;
687 dash = dashA;
688 dashLength = dashLengthA;
689 r = rA;
690 g = gA;
691 b = bA;
692 }
693
~AnnotBorderStyle()694 AnnotBorderStyle::~AnnotBorderStyle() {
695 if (dash) {
696 gfree(dash);
697 }
698 }
699
700 //------------------------------------------------------------------------
701 // AnnotIconFit
702 //------------------------------------------------------------------------
703
AnnotIconFit(Dict * dict)704 AnnotIconFit::AnnotIconFit(Dict* dict) {
705 Object obj1;
706
707 if (dict->lookup("SW", &obj1)->isName()) {
708 GooString *scaleName = new GooString(obj1.getName());
709
710 if(!scaleName->cmp("B")) {
711 scaleWhen = scaleBigger;
712 } else if(!scaleName->cmp("S")) {
713 scaleWhen = scaleSmaller;
714 } else if(!scaleName->cmp("N")) {
715 scaleWhen = scaleNever;
716 } else {
717 scaleWhen = scaleAlways;
718 }
719 delete scaleName;
720 } else {
721 scaleWhen = scaleAlways;
722 }
723 obj1.free();
724
725 if (dict->lookup("S", &obj1)->isName()) {
726 GooString *scaleName = new GooString(obj1.getName());
727
728 if(!scaleName->cmp("A")) {
729 scale = scaleAnamorphic;
730 } else {
731 scale = scaleProportional;
732 }
733 delete scaleName;
734 } else {
735 scale = scaleProportional;
736 }
737 obj1.free();
738
739 if (dict->lookup("A", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
740 Object obj2;
741 (obj1.arrayGet(0, &obj2)->isNum() ? left = obj2.getNum() : left = 0);
742 obj2.free();
743 (obj1.arrayGet(1, &obj2)->isNum() ? bottom = obj2.getNum() : bottom = 0);
744 obj2.free();
745
746 if (left < 0 || left > 1)
747 left = 0.5;
748
749 if (bottom < 0 || bottom > 1)
750 bottom = 0.5;
751
752 } else {
753 left = bottom = 0.5;
754 }
755 obj1.free();
756
757 if (dict->lookup("FB", &obj1)->isBool()) {
758 fullyBounds = obj1.getBool();
759 } else {
760 fullyBounds = gFalse;
761 }
762 obj1.free();
763 }
764
765 //------------------------------------------------------------------------
766 // AnnotAppearanceCharacs
767 //------------------------------------------------------------------------
768
AnnotAppearanceCharacs(Dict * dict)769 AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) {
770 Object obj1;
771
772 if (dict->lookup("R", &obj1)->isInt()) {
773 rotation = obj1.getInt();
774 } else {
775 rotation = 0;
776 }
777 obj1.free();
778
779 if (dict->lookup("BC", &obj1)->isArray()) {
780 borderColor = new AnnotColor(obj1.getArray());
781 } else {
782 borderColor = NULL;
783 }
784 obj1.free();
785
786 if (dict->lookup("BG", &obj1)->isArray()) {
787 backColor = new AnnotColor(obj1.getArray());
788 } else {
789 backColor = NULL;
790 }
791 obj1.free();
792
793 if (dict->lookup("CA", &obj1)->isName()) {
794 normalCaption = new GooString(obj1.getName());
795 } else {
796 normalCaption = NULL;
797 }
798 obj1.free();
799
800 if (dict->lookup("RC", &obj1)->isName()) {
801 rolloverCaption = new GooString(obj1.getName());
802 } else {
803 rolloverCaption = NULL;
804 }
805 obj1.free();
806
807 if (dict->lookup("AC", &obj1)->isName()) {
808 alternateCaption = new GooString(obj1.getName());
809 } else {
810 alternateCaption = NULL;
811 }
812 obj1.free();
813
814 if (dict->lookup("IF", &obj1)->isDict()) {
815 iconFit = new AnnotIconFit(obj1.getDict());
816 } else {
817 iconFit = NULL;
818 }
819 obj1.free();
820
821 if (dict->lookup("TP", &obj1)->isInt()) {
822 position = (AnnotAppearanceCharacsTextPos) obj1.getInt();
823 } else {
824 position = captionNoIcon;
825 }
826 obj1.free();
827 }
828
~AnnotAppearanceCharacs()829 AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
830 if (borderColor)
831 delete borderColor;
832
833 if (backColor)
834 delete backColor;
835
836 if (normalCaption)
837 delete normalCaption;
838
839 if (rolloverCaption)
840 delete rolloverCaption;
841
842 if (alternateCaption)
843 delete alternateCaption;
844
845 if (iconFit)
846 delete iconFit;
847 }
848
849 //------------------------------------------------------------------------
850 // Annot
851 //------------------------------------------------------------------------
852
Annot(XRef * xrefA,PDFRectangle * rectA,Catalog * catalog)853 Annot::Annot(XRef *xrefA, PDFRectangle *rectA, Catalog *catalog) {
854 Object obj1;
855
856 flags = flagUnknown;
857 type = typeUnknown;
858
859 obj1.initArray (xrefA);
860 Object obj2;
861 obj1.arrayAdd (obj2.initReal (rectA->x1));
862 obj1.arrayAdd (obj2.initReal (rectA->y1));
863 obj1.arrayAdd (obj2.initReal (rectA->x2));
864 obj1.arrayAdd (obj2.initReal (rectA->y2));
865 obj2.free ();
866
867 annotObj.initDict (xrefA);
868 annotObj.dictSet ("Type", obj2.initName ("Annot"));
869 annotObj.dictSet ("Rect", &obj1);
870 // obj1 is owned by the dict
871
872 ref = xrefA->addIndirectObject (&annotObj);
873
874 initialize (xrefA, annotObj.getDict(), catalog);
875 }
876
Annot(XRef * xrefA,Dict * dict,Catalog * catalog)877 Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog) {
878 hasRef = false;
879 flags = flagUnknown;
880 type = typeUnknown;
881 annotObj.initDict (dict);
882 initialize (xrefA, dict, catalog);
883 }
884
Annot(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)885 Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog, Object *obj) {
886 if (obj->isRef()) {
887 hasRef = gTrue;
888 ref = obj->getRef();
889 } else {
890 hasRef = gFalse;
891 }
892 flags = flagUnknown;
893 type = typeUnknown;
894 annotObj.initDict (dict);
895 initialize (xrefA, dict, catalog);
896 }
897
initialize(XRef * xrefA,Dict * dict,Catalog * catalog)898 void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
899 Object asObj, obj1, obj2, obj3;
900
901 appRef.num = 0;
902 appRef.gen = 65535;
903 ok = gTrue;
904 xref = xrefA;
905 appearBuf = NULL;
906 fontSize = 0;
907
908 appearance.initNull();
909
910 //----- parse the rectangle
911 rect = new PDFRectangle();
912 if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
913 Object obj2;
914 (obj1.arrayGet(0, &obj2)->isNum() ? rect->x1 = obj2.getNum() : rect->x1 = 0);
915 obj2.free();
916 (obj1.arrayGet(1, &obj2)->isNum() ? rect->y1 = obj2.getNum() : rect->y1 = 0);
917 obj2.free();
918 (obj1.arrayGet(2, &obj2)->isNum() ? rect->x2 = obj2.getNum() : rect->x2 = 1);
919 obj2.free();
920 (obj1.arrayGet(3, &obj2)->isNum() ? rect->y2 = obj2.getNum() : rect->y2 = 1);
921 obj2.free();
922
923 if (rect->x1 > rect->x2) {
924 double t = rect->x1;
925 rect->x1 = rect->x2;
926 rect->x2 = t;
927 }
928
929 if (rect->y1 > rect->y2) {
930 double t = rect->y1;
931 rect->y1 = rect->y2;
932 rect->y2 = t;
933 }
934 } else {
935 rect->x1 = rect->y1 = 0;
936 rect->x2 = rect->y2 = 1;
937 error(-1, "Bad bounding box for annotation");
938 ok = gFalse;
939 }
940 obj1.free();
941
942 if (dict->lookup("Contents", &obj1)->isString()) {
943 contents = obj1.getString()->copy();
944 } else {
945 contents = NULL;
946 }
947 obj1.free();
948
949 if (dict->lookupNF("P", &obj1)->isRef()) {
950 Ref ref = obj1.getRef();
951
952 page = catalog ? catalog->findPage (ref.num, ref.gen) : -1;
953 } else {
954 page = 0;
955 }
956 obj1.free();
957
958 if (dict->lookup("NM", &obj1)->isString()) {
959 name = obj1.getString()->copy();
960 } else {
961 name = NULL;
962 }
963 obj1.free();
964
965 if (dict->lookup("M", &obj1)->isString()) {
966 modified = obj1.getString()->copy();
967 } else {
968 modified = NULL;
969 }
970 obj1.free();
971
972 //----- get the flags
973 if (dict->lookup("F", &obj1)->isInt()) {
974 flags |= obj1.getInt();
975 } else {
976 flags = flagUnknown;
977 }
978 obj1.free();
979
980 if (dict->lookup("AP", &obj1)->isDict()) {
981 Object obj2;
982
983 if (dict->lookup("AS", &obj2)->isName()) {
984 Object obj3;
985
986 appearState = new GooString(obj2.getName());
987 if (obj1.dictLookup("N", &obj3)->isDict()) {
988 Object obj4;
989
990 if (obj3.dictLookupNF(appearState->getCString(), &obj4)->isRef()) {
991 obj4.copy(&appearance);
992 } else {
993 obj4.free();
994 if (obj3.dictLookupNF("Off", &obj4)->isRef()) {
995 obj4.copy(&appearance);
996 }
997 }
998 obj4.free();
999 }
1000 obj3.free();
1001 } else {
1002 obj2.free();
1003
1004 appearState = NULL;
1005 if (obj1.dictLookupNF("N", &obj2)->isRef()) {
1006 obj2.copy(&appearance);
1007 }
1008 }
1009 obj2.free();
1010 } else {
1011 appearState = NULL;
1012 }
1013 obj1.free();
1014
1015 //----- parse the border style
1016 if (dict->lookup("BS", &obj1)->isDict()) {
1017 border = new AnnotBorderBS(obj1.getDict());
1018 } else {
1019 obj1.free();
1020
1021 if (dict->lookup("Border", &obj1)->isArray())
1022 border = new AnnotBorderArray(obj1.getArray());
1023 else
1024 // Adobe draws no border at all if the last element is of
1025 // the wrong type.
1026 border = NULL;
1027 }
1028 obj1.free();
1029
1030 if (dict->lookup("C", &obj1)->isArray()) {
1031 color = new AnnotColor(obj1.getArray());
1032 } else {
1033 color = NULL;
1034 }
1035 obj1.free();
1036
1037 if (dict->lookup("StructParent", &obj1)->isInt()) {
1038 treeKey = obj1.getInt();
1039 } else {
1040 treeKey = 0;
1041 }
1042 obj1.free();
1043
1044 optContentConfig = catalog ? catalog->getOptContentConfig() : NULL;
1045 dict->lookupNF("OC", &oc);
1046 if (!oc.isRef() && !oc.isNull()) {
1047 error (-1, "Annotation OC value not null or dict: %i", oc.getType());
1048 }
1049 }
1050
update(const char * key,Object * value)1051 void Annot::update(const char *key, Object *value) {
1052 /* Set M to current time */
1053 delete modified;
1054 modified = timeToDateString(NULL);
1055
1056 Object obj1;
1057 obj1.initString (modified->copy());
1058 annotObj.dictSet("M", &obj1);
1059
1060 annotObj.dictSet(const_cast<char*>(key), value);
1061
1062 xref->setModifiedObject(&annotObj, ref);
1063 }
1064
setContents(GooString * new_content)1065 void Annot::setContents(GooString *new_content) {
1066 delete contents;
1067
1068 if (new_content) {
1069 contents = new GooString(new_content);
1070 //append the unicode marker <FE FF> if needed
1071 if (!contents->hasUnicodeMarker()) {
1072 contents->insert(0, 0xff);
1073 contents->insert(0, 0xfe);
1074 }
1075 } else {
1076 contents = new GooString();
1077 }
1078
1079 Object obj1;
1080 obj1.initString(contents->copy());
1081 update ("Contents", &obj1);
1082 }
1083
setColor(AnnotColor * new_color)1084 void Annot::setColor(AnnotColor *new_color) {
1085 delete color;
1086
1087 if (new_color) {
1088 Object obj1, obj2;
1089 const double *values = new_color->getValues();
1090
1091 obj1.initArray(xref);
1092 for (int i = 0; i < (int)new_color->getSpace(); i++)
1093 obj1.arrayAdd(obj2.initReal (values[i]));
1094 update ("C", &obj1);
1095 color = new_color;
1096 } else {
1097 color = NULL;
1098 }
1099 }
1100
setPage(Ref * pageRef,int pageIndex)1101 void Annot::setPage(Ref *pageRef, int pageIndex)
1102 {
1103 Object obj1;
1104
1105 obj1.initRef(pageRef->num, pageRef->gen);
1106 update("P", &obj1);
1107 page = pageIndex;
1108 }
1109
getXMin()1110 double Annot::getXMin() {
1111 return rect->x1;
1112 }
1113
getYMin()1114 double Annot::getYMin() {
1115 return rect->y1;
1116 }
1117
readArrayNum(Object * pdfArray,int key,double * value)1118 void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
1119 Object valueObject;
1120
1121 pdfArray->arrayGet(key, &valueObject);
1122 if (valueObject.isNum()) {
1123 *value = valueObject.getNum();
1124 } else {
1125 *value = 0;
1126 ok = gFalse;
1127 }
1128 valueObject.free();
1129 }
1130
~Annot()1131 Annot::~Annot() {
1132 annotObj.free();
1133
1134 delete rect;
1135
1136 if (contents)
1137 delete contents;
1138
1139 if (name)
1140 delete name;
1141
1142 if (modified)
1143 delete modified;
1144
1145 appearance.free();
1146
1147 if (appearState)
1148 delete appearState;
1149
1150 if (border)
1151 delete border;
1152
1153 if (color)
1154 delete color;
1155
1156 oc.free();
1157 }
1158
setColor(AnnotColor * color,GBool fill)1159 void Annot::setColor(AnnotColor *color, GBool fill) {
1160 const double *values = color->getValues();
1161
1162 switch (color->getSpace()) {
1163 case AnnotColor::colorCMYK:
1164 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1165 values[0], values[1], values[2], values[3],
1166 fill ? 'k' : 'K');
1167 break;
1168 case AnnotColor::colorRGB:
1169 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1170 values[0], values[1], values[2],
1171 fill ? "rg" : "RG");
1172 break;
1173 case AnnotColor::colorGray:
1174 appearBuf->appendf("{0:.2f} {1:c}\n",
1175 values[0],
1176 fill ? 'g' : 'G');
1177 break;
1178 case AnnotColor::colorTransparent:
1179 default:
1180 break;
1181 }
1182 }
1183
1184 // Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1185 // If <fill> is true, the circle is filled; otherwise it is stroked.
drawCircle(double cx,double cy,double r,GBool fill)1186 void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
1187 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1188 cx + r, cy);
1189 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1190 cx + r, cy + bezierCircle * r,
1191 cx + bezierCircle * r, cy + r,
1192 cx, cy + r);
1193 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1194 cx - bezierCircle * r, cy + r,
1195 cx - r, cy + bezierCircle * r,
1196 cx - r, cy);
1197 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1198 cx - r, cy - bezierCircle * r,
1199 cx - bezierCircle * r, cy - r,
1200 cx, cy - r);
1201 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1202 cx + bezierCircle * r, cy - r,
1203 cx + r, cy - bezierCircle * r,
1204 cx + r, cy);
1205 appearBuf->append(fill ? "f\n" : "s\n");
1206 }
1207
1208 // Draw the top-left half of an (approximate) circle of radius <r>
1209 // centered at (<cx>, <cy>).
drawCircleTopLeft(double cx,double cy,double r)1210 void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1211 double r2;
1212
1213 r2 = r / sqrt(2.0);
1214 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1215 cx + r2, cy + r2);
1216 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1217 cx + (1 - bezierCircle) * r2,
1218 cy + (1 + bezierCircle) * r2,
1219 cx - (1 - bezierCircle) * r2,
1220 cy + (1 + bezierCircle) * r2,
1221 cx - r2,
1222 cy + r2);
1223 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1224 cx - (1 + bezierCircle) * r2,
1225 cy + (1 - bezierCircle) * r2,
1226 cx - (1 + bezierCircle) * r2,
1227 cy - (1 - bezierCircle) * r2,
1228 cx - r2,
1229 cy - r2);
1230 appearBuf->append("S\n");
1231 }
1232
1233 // Draw the bottom-right half of an (approximate) circle of radius <r>
1234 // centered at (<cx>, <cy>).
drawCircleBottomRight(double cx,double cy,double r)1235 void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1236 double r2;
1237
1238 r2 = r / sqrt(2.0);
1239 appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1240 cx - r2, cy - r2);
1241 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1242 cx - (1 - bezierCircle) * r2,
1243 cy - (1 + bezierCircle) * r2,
1244 cx + (1 - bezierCircle) * r2,
1245 cy - (1 + bezierCircle) * r2,
1246 cx + r2,
1247 cy - r2);
1248 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1249 cx + (1 + bezierCircle) * r2,
1250 cy - (1 - bezierCircle) * r2,
1251 cx + (1 + bezierCircle) * r2,
1252 cy + (1 - bezierCircle) * r2,
1253 cx + r2,
1254 cy + r2);
1255 appearBuf->append("S\n");
1256 }
1257
createForm(double * bbox,GBool transparencyGroup,Object * resDict,Object * aStream)1258 void Annot::createForm(double *bbox, GBool transparencyGroup, Object *resDict, Object *aStream) {
1259 Object obj1, obj2;
1260 Object appearDict;
1261
1262 appearDict.initDict(xref);
1263 appearDict.dictSet("Length", obj1.initInt(appearBuf->getLength()));
1264 appearDict.dictSet("Subtype", obj1.initName("Form"));
1265 obj1.initArray(xref);
1266 obj1.arrayAdd(obj2.initReal(bbox[0]));
1267 obj1.arrayAdd(obj2.initReal(bbox[1]));
1268 obj1.arrayAdd(obj2.initReal(bbox[2]));
1269 obj1.arrayAdd(obj2.initReal(bbox[3]));
1270 appearDict.dictSet("BBox", &obj1);
1271 if (transparencyGroup) {
1272 Object transDict;
1273 transDict.initDict(xref);
1274 transDict.dictSet("S", obj1.initName("Transparency"));
1275 appearDict.dictSet("Group", &transDict);
1276 }
1277 if (resDict)
1278 appearDict.dictSet("Resources", resDict);
1279
1280 MemStream *mStream = new MemStream(copyString(appearBuf->getCString()), 0,
1281 appearBuf->getLength(), &appearDict);
1282 mStream->setNeedFree(gTrue);
1283 aStream->initStream(mStream);
1284 }
1285
createResourcesDict(char * formName,Object * formStream,char * stateName,double opacity,char * blendMode,Object * resDict)1286 void Annot::createResourcesDict(char *formName, Object *formStream,
1287 char *stateName,
1288 double opacity, char *blendMode,
1289 Object *resDict) {
1290 Object gsDict, stateDict, formDict, obj1;
1291
1292 gsDict.initDict(xref);
1293 if (opacity != 1) {
1294 gsDict.dictSet("CA", obj1.initReal(opacity));
1295 gsDict.dictSet("ca", obj1.initReal(opacity));
1296 }
1297 if (blendMode)
1298 gsDict.dictSet("BM", obj1.initName(blendMode));
1299 stateDict.initDict(xref);
1300 stateDict.dictSet(stateName, &gsDict);
1301 formDict.initDict(xref);
1302 formDict.dictSet(formName, formStream);
1303
1304 resDict->initDict(xref);
1305 resDict->dictSet("ExtGState", &stateDict);
1306 resDict->dictSet("XObject", &formDict);
1307 }
1308
isVisible(GBool printing)1309 GBool Annot::isVisible(GBool printing) {
1310 // check the flags
1311 if ((flags & flagHidden) ||
1312 (printing && !(flags & flagPrint)) ||
1313 (!printing && (flags & flagNoView))) {
1314 return gFalse;
1315 }
1316
1317 // check the OC
1318 if (optContentConfig && oc.isRef()) {
1319 if (! optContentConfig->optContentIsVisible(&oc))
1320 return gFalse;
1321 }
1322
1323 return gTrue;
1324 }
1325
draw(Gfx * gfx,GBool printing)1326 void Annot::draw(Gfx *gfx, GBool printing) {
1327 Object obj;
1328
1329 if (!isVisible (printing))
1330 return;
1331
1332 // draw the appearance stream
1333 appearance.fetch(xref, &obj);
1334 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
1335 rect->x1, rect->y1, rect->x2, rect->y2);
1336 obj.free();
1337 }
1338
1339 //------------------------------------------------------------------------
1340 // AnnotPopup
1341 //------------------------------------------------------------------------
1342
AnnotPopup(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)1343 AnnotPopup::AnnotPopup(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
1344 Annot(xrefA, rect, catalog) {
1345 Object obj1;
1346
1347 type = typePopup;
1348
1349 annotObj.dictSet ("Subtype", obj1.initName ("Popup"));
1350 initialize (xrefA, annotObj.getDict(), catalog);
1351 }
1352
AnnotPopup(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)1353 AnnotPopup::AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1354 Annot(xrefA, dict, catalog, obj) {
1355 type = typePopup;
1356 initialize(xrefA, dict, catalog);
1357 }
1358
~AnnotPopup()1359 AnnotPopup::~AnnotPopup() {
1360 parent.free();
1361 }
1362
initialize(XRef * xrefA,Dict * dict,Catalog * catalog)1363 void AnnotPopup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
1364 Object obj1;
1365
1366 if (!dict->lookupNF("Parent", &parent)->isRef()) {
1367 parent.initNull();
1368 }
1369
1370 if (dict->lookup("Open", &obj1)->isBool()) {
1371 open = obj1.getBool();
1372 } else {
1373 open = gFalse;
1374 }
1375 obj1.free();
1376 }
1377
setParent(Object * parentA)1378 void AnnotPopup::setParent(Object *parentA) {
1379 parentA->copy(&parent);
1380 update ("Parent", &parent);
1381 }
1382
setParent(Annot * parentA)1383 void AnnotPopup::setParent(Annot *parentA) {
1384 Ref parentRef = parentA->getRef();
1385 parent.initRef(parentRef.num, parentRef.gen);
1386 update ("Parent", &parent);
1387 }
1388
setOpen(GBool openA)1389 void AnnotPopup::setOpen(GBool openA) {
1390 Object obj1;
1391
1392 open = openA;
1393 obj1.initBool(open);
1394 update ("Open", &obj1);
1395 }
1396
1397 //------------------------------------------------------------------------
1398 // AnnotMarkup
1399 //------------------------------------------------------------------------
AnnotMarkup(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)1400 AnnotMarkup::AnnotMarkup(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
1401 Annot(xrefA, rect, catalog) {
1402 initialize(xrefA, annotObj.getDict(), catalog, &annotObj);
1403 }
1404
AnnotMarkup(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)1405 AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1406 Annot(xrefA, dict, catalog, obj) {
1407 initialize(xrefA, dict, catalog, obj);
1408 }
1409
~AnnotMarkup()1410 AnnotMarkup::~AnnotMarkup() {
1411 if (label)
1412 delete label;
1413
1414 if (popup)
1415 delete popup;
1416
1417 if (date)
1418 delete date;
1419
1420 if (subject)
1421 delete subject;
1422 }
1423
initialize(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)1424 void AnnotMarkup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
1425 Object obj1;
1426
1427 if (dict->lookup("T", &obj1)->isString()) {
1428 label = obj1.getString()->copy();
1429 } else {
1430 label = NULL;
1431 }
1432 obj1.free();
1433
1434 if (dict->lookup("Popup", &obj1)->isDict()) {
1435 popup = new AnnotPopup(xrefA, obj1.getDict(), catalog, obj);
1436 } else {
1437 popup = NULL;
1438 }
1439 obj1.free();
1440
1441 if (dict->lookup("CA", &obj1)->isNum()) {
1442 opacity = obj1.getNum();
1443 } else {
1444 opacity = 1.0;
1445 }
1446 obj1.free();
1447
1448 if (dict->lookup("CreationDate", &obj1)->isString()) {
1449 date = obj1.getString()->copy();
1450 } else {
1451 date = NULL;
1452 }
1453 obj1.free();
1454
1455 if (dict->lookupNF("IRT", &obj1)->isRef()) {
1456 inReplyTo = obj1.getRef();
1457 } else {
1458 inReplyTo.num = 0;
1459 inReplyTo.gen = 0;
1460 }
1461 obj1.free();
1462
1463 if (dict->lookup("Subj", &obj1)->isString()) {
1464 subject = obj1.getString()->copy();
1465 } else {
1466 subject = NULL;
1467 }
1468 obj1.free();
1469
1470 if (dict->lookup("RT", &obj1)->isName()) {
1471 GooString *replyName = new GooString(obj1.getName());
1472
1473 if (!replyName->cmp("R")) {
1474 replyTo = replyTypeR;
1475 } else if (!replyName->cmp("Group")) {
1476 replyTo = replyTypeGroup;
1477 } else {
1478 replyTo = replyTypeR;
1479 }
1480 delete replyName;
1481 } else {
1482 replyTo = replyTypeR;
1483 }
1484 obj1.free();
1485
1486 if (dict->lookup("ExData", &obj1)->isDict()) {
1487 exData = parseAnnotExternalData(obj1.getDict());
1488 } else {
1489 exData = annotExternalDataMarkupUnknown;
1490 }
1491 obj1.free();
1492 }
1493
setLabel(GooString * new_label)1494 void AnnotMarkup::setLabel(GooString *new_label) {
1495 delete label;
1496
1497 if (new_label) {
1498 label = new GooString(new_label);
1499 //append the unicode marker <FE FF> if needed
1500 if (!label->hasUnicodeMarker()) {
1501 label->insert(0, 0xff);
1502 label->insert(0, 0xfe);
1503 }
1504 } else {
1505 label = new GooString();
1506 }
1507
1508 Object obj1;
1509 obj1.initString(label->copy());
1510 update ("T", &obj1);
1511 }
1512
setPopup(AnnotPopup * new_popup)1513 void AnnotMarkup::setPopup(AnnotPopup *new_popup) {
1514 delete popup;
1515
1516 if (new_popup) {
1517 Object obj1;
1518 Ref popupRef = new_popup->getRef();
1519
1520 obj1.initRef (popupRef.num, popupRef.gen);
1521 update ("Popup", &obj1);
1522
1523 new_popup->setParent(this);
1524 popup = new_popup;
1525 } else {
1526 popup = NULL;
1527 }
1528 }
1529
setOpacity(double opacityA)1530 void AnnotMarkup::setOpacity(double opacityA) {
1531 Object obj1;
1532
1533 opacity = opacityA;
1534 obj1.initReal(opacity);
1535 update ("CA", &obj1);
1536 }
1537
1538 //------------------------------------------------------------------------
1539 // AnnotText
1540 //------------------------------------------------------------------------
1541
AnnotText(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)1542 AnnotText::AnnotText(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
1543 AnnotMarkup(xrefA, rect, catalog) {
1544 Object obj1;
1545
1546 type = typeText;
1547 flags |= flagNoZoom | flagNoRotate;
1548
1549 annotObj.dictSet ("Subtype", obj1.initName ("Text"));
1550 initialize (xrefA, catalog, annotObj.getDict());
1551 }
1552
AnnotText(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)1553 AnnotText::AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1554 AnnotMarkup(xrefA, dict, catalog, obj) {
1555
1556 type = typeText;
1557 flags |= flagNoZoom | flagNoRotate;
1558 initialize (xrefA, catalog, dict);
1559 }
1560
~AnnotText()1561 AnnotText::~AnnotText() {
1562 delete icon;
1563 }
1564
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)1565 void AnnotText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1566 Object obj1;
1567
1568 if (dict->lookup("Open", &obj1)->isBool())
1569 open = obj1.getBool();
1570 else
1571 open = gFalse;
1572 obj1.free();
1573
1574 if (dict->lookup("Name", &obj1)->isName()) {
1575 icon = new GooString(obj1.getName());
1576 } else {
1577 icon = new GooString("Note");
1578 }
1579 obj1.free();
1580
1581 if (dict->lookup("StateModel", &obj1)->isString()) {
1582 Object obj2;
1583 GooString *modelName = obj1.getString();
1584
1585 if (dict->lookup("State", &obj2)->isString()) {
1586 GooString *stateName = obj2.getString();
1587
1588 if (!stateName->cmp("Marked")) {
1589 state = stateMarked;
1590 } else if (!stateName->cmp("Unmarked")) {
1591 state = stateUnmarked;
1592 } else if (!stateName->cmp("Accepted")) {
1593 state = stateAccepted;
1594 } else if (!stateName->cmp("Rejected")) {
1595 state = stateRejected;
1596 } else if (!stateName->cmp("Cancelled")) {
1597 state = stateCancelled;
1598 } else if (!stateName->cmp("Completed")) {
1599 state = stateCompleted;
1600 } else if (!stateName->cmp("None")) {
1601 state = stateNone;
1602 } else {
1603 state = stateUnknown;
1604 }
1605 } else {
1606 state = stateUnknown;
1607 }
1608 obj2.free();
1609
1610 if (!modelName->cmp("Marked")) {
1611 switch (state) {
1612 case stateUnknown:
1613 state = stateMarked;
1614 break;
1615 case stateAccepted:
1616 case stateRejected:
1617 case stateCancelled:
1618 case stateCompleted:
1619 case stateNone:
1620 state = stateUnknown;
1621 break;
1622 default:
1623 break;
1624 }
1625 } else if (!modelName->cmp("Review")) {
1626 switch (state) {
1627 case stateUnknown:
1628 state = stateNone;
1629 break;
1630 case stateMarked:
1631 case stateUnmarked:
1632 state = stateUnknown;
1633 break;
1634 default:
1635 break;
1636 }
1637 } else {
1638 state = stateUnknown;
1639 }
1640 } else {
1641 state = stateUnknown;
1642 }
1643 obj1.free();
1644 }
1645
setOpen(GBool openA)1646 void AnnotText::setOpen(GBool openA) {
1647 Object obj1;
1648
1649 open = openA;
1650 obj1.initBool(open);
1651 update ("Open", &obj1);
1652 }
1653
setIcon(GooString * new_icon)1654 void AnnotText::setIcon(GooString *new_icon) {
1655 if (new_icon && icon->cmp(new_icon) == 0)
1656 return;
1657
1658 delete icon;
1659
1660 if (new_icon) {
1661 icon = new GooString (new_icon);
1662 } else {
1663 icon = new GooString("Note");
1664 }
1665
1666 Object obj1;
1667 obj1.initName (icon->getCString());
1668 update("Name", &obj1);
1669 }
1670
1671 #define ANNOT_TEXT_AP_NOTE \
1672 "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \
1673 "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" \
1674 "l 0 22.387 1.613 24 3.602 24 c h\n" \
1675 "3.602 24 m f\n" \
1676 "0.533333 0.541176 0.521569 RG 2 w\n" \
1677 "1 J\n" \
1678 "1 j\n" \
1679 "[] 0.0 d\n" \
1680 "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" \
1681 "1.5 w\n" \
1682 "0 j\n" \
1683 "10 16 m 14 21 l S\n" \
1684 "1.85625 w\n" \
1685 "1 j\n" \
1686 "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \
1687 "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \
1688 "14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \
1689 "15.07 20.523 m S\n" \
1690 "1 w\n" \
1691 "0 j\n" \
1692 "6.5 13.5 m 15.5 13.5 l S\n" \
1693 "6.5 10.5 m 13.5 10.5 l S\n" \
1694 "6.801 7.5 m 15.5 7.5 l S\n" \
1695 "0.729412 0.741176 0.713725 RG 2 w\n" \
1696 "1 j\n" \
1697 "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" \
1698 "1.5 w\n" \
1699 "0 j\n" \
1700 "10 17 m 14 22 l S\n" \
1701 "1.85625 w\n" \
1702 "1 j\n" \
1703 "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \
1704 "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \
1705 "14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \
1706 "15.07 21.523 m S\n" \
1707 "1 w\n" \
1708 "0 j\n" \
1709 "6.5 14.5 m 15.5 14.5 l S\n" \
1710 "6.5 11.5 m 13.5 11.5 l S\n" \
1711 "6.801 8.5 m 15.5 8.5 l S\n"
1712
1713 #define ANNOT_TEXT_AP_COMMENT \
1714 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1715 "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" \
1716 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1717 "4.301 23 m f\n" \
1718 "0.533333 0.541176 0.521569 RG 2 w\n" \
1719 "0 J\n" \
1720 "1 j\n" \
1721 "[] 0.0 d\n" \
1722 "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" \
1723 "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" \
1724 "5.637 20 8 20 c h\n" \
1725 "8 20 m S\n" \
1726 "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \
1727 "c 20 14 l 20 11.785 18.363 10\n" \
1728 "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" \
1729 "19.215 5.637 21 8 21 c h\n" \
1730 "8 21 m S\n"
1731
1732 #define ANNOT_TEXT_AP_KEY \
1733 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1734 "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" \
1735 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1736 "4.301 23 m f\n" \
1737 "0.533333 0.541176 0.521569 RG 2 w\n" \
1738 "1 J\n" \
1739 "0 j\n" \
1740 "[] 0.0 d\n" \
1741 "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \
1742 "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \
1743 "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \
1744 "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" \
1745 "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \
1746 "11.895 18.754 m S\n" \
1747 "1.5 w\n" \
1748 "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \
1749 "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \
1750 "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \
1751 "16.059 15.586 m S\n" \
1752 "0.729412 0.741176 0.713725 RG 2 w\n" \
1753 "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \
1754 "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \
1755 "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \
1756 "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" \
1757 "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \
1758 "11.895 19.754 m S\n" \
1759 "1.5 w\n" \
1760 "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \
1761 "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \
1762 "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \
1763 "16.059 16.586 m S\n"
1764
1765 #define ANNOT_TEXT_AP_HELP \
1766 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1767 "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" \
1768 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1769 "4.301 23 m f\n" \
1770 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
1771 "1 J\n" \
1772 "1 j\n" \
1773 "[] 0.0 d\n" \
1774 "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" \
1775 "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \
1776 "c S\n" \
1777 "0 j\n" \
1778 "12 8 m 12 12 16 11 16 15 c S\n" \
1779 "1.539286 w\n" \
1780 "1 j\n" \
1781 "q 1 0 0 -0.999991 0 24 cm\n" \
1782 "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \
1783 "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \
1784 "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \
1785 "0.729412 0.741176 0.713725 RG 2.5 w\n" \
1786 "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \
1787 "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \
1788 "c S\n" \
1789 "0 j\n" \
1790 "12 9 m 12 13 16 12 16 16 c S\n" \
1791 "1.539286 w\n" \
1792 "1 j\n" \
1793 "q 1 0 0 -0.999991 0 24 cm\n" \
1794 "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \
1795 "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \
1796 "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
1797
1798 #define ANNOT_TEXT_AP_NEW_PARAGRAPH \
1799 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1800 "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" \
1801 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1802 "4.301 23 m f\n" \
1803 "0.533333 0.541176 0.521569 RG 4 w\n" \
1804 "0 J\n" \
1805 "2 j\n" \
1806 "[] 0.0 d\n" \
1807 "4 M q 1 0 0 -1 0 24 cm\n" \
1808 "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \
1809 "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" \
1810 "1.004413 w\n" \
1811 "1 J\n" \
1812 "1 j\n" \
1813 "q 1 0 0 -0.991232 0 24 cm\n" \
1814 "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \
1815 "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \
1816 "17.461 8.252 l 18.07 11.511 l h\n" \
1817 "18.07 11.511 m S Q\n" \
1818 "2 w\n" \
1819 "0 j\n" \
1820 "11 17 m 10 17 l 10 3 l S\n" \
1821 "14 3 m 14 13 l S\n" \
1822 "0.729412 0.741176 0.713725 RG 4 w\n" \
1823 "0 J\n" \
1824 "2 j\n" \
1825 "q 1 0 0 -1 0 24 cm\n" \
1826 "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \
1827 "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" \
1828 "1.004413 w\n" \
1829 "1 J\n" \
1830 "1 j\n" \
1831 "q 1 0 0 -0.991232 0 24 cm\n" \
1832 "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \
1833 "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \
1834 "17.461 7.243 l 18.07 10.502 l h\n" \
1835 "18.07 10.502 m S Q\n" \
1836 "2 w\n" \
1837 "0 j\n" \
1838 "11 18 m 10 18 l 10 4 l S\n" \
1839 "14 4 m 14 14 l S\n"
1840
1841 #define ANNOT_TEXT_AP_PARAGRAPH \
1842 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1843 "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" \
1844 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1845 "4.301 23 m f\n" \
1846 "0.533333 0.541176 0.521569 RG 2 w\n" \
1847 "1 J\n" \
1848 "1 j\n" \
1849 "[] 0.0 d\n" \
1850 "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
1851 "4 w\n" \
1852 "q 1 0 0 -1 0 24 cm\n" \
1853 "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \
1854 "7.066 9.879 7.004 c S Q\n" \
1855 "0.729412 0.741176 0.713725 RG 2 w\n" \
1856 "15 4 m 15 19 l 11 19 l 11 4 l S\n" \
1857 "4 w\n" \
1858 "q 1 0 0 -1 0 24 cm\n" \
1859 "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" \
1860 "9.879 6.004 c S Q\n"
1861
1862 #define ANNOT_TEXT_AP_INSERT \
1863 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1864 "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" \
1865 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1866 "4.301 23 m f\n" \
1867 "0.533333 0.541176 0.521569 RG 2 w\n" \
1868 "1 J\n" \
1869 "0 j\n" \
1870 "[] 0.0 d\n" \
1871 "4 M 12 18.012 m 20 18 l S\n" \
1872 "9 10 m 17 10 l S\n" \
1873 "12 14.012 m 20 14 l S\n" \
1874 "12 6.012 m 20 6.012 l S\n" \
1875 "4 12 m 6 10 l 4 8 l S\n" \
1876 "4 12 m 4 8 l S\n" \
1877 "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \
1878 "9 11 m 17 11 l S\n" \
1879 "12 15.012 m 20 15 l S\n" \
1880 "12 7.012 m 20 7.012 l S\n" \
1881 "4 13 m 6 11 l 4 9 l S\n" \
1882 "4 13 m 4 9 l S\n"
1883
1884 #define ANNOT_TEXT_AP_CROSS \
1885 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1886 "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" \
1887 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1888 "4.301 23 m f\n" \
1889 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
1890 "1 J\n" \
1891 "0 j\n" \
1892 "[] 0.0 d\n" \
1893 "4 M 18 5 m 6 17 l S\n" \
1894 "6 5 m 18 17 l S\n" \
1895 "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \
1896 "6 6 m 18 18 l S\n"
1897
1898 #define ANNOT_TEXT_AP_CIRCLE \
1899 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
1900 "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" \
1901 "l 1 21.523 2.477 23 4.301 23 c h\n" \
1902 "4.301 23 m f\n" \
1903 "0.533333 0.541176 0.521569 RG 2.5 w\n" \
1904 "1 J\n" \
1905 "1 j\n" \
1906 "[] 0.0 d\n" \
1907 "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" \
1908 "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \
1909 "19.5 11.5 m S\n" \
1910 "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \
1911 "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \
1912 "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \
1913 "19.5 12.5 m S\n"
1914
draw(Gfx * gfx,GBool printing)1915 void AnnotText::draw(Gfx *gfx, GBool printing) {
1916 Object obj;
1917 double ca = 1;
1918
1919 if (!isVisible (printing))
1920 return;
1921
1922 double rectx2 = rect->x2;
1923 double recty2 = rect->y2;
1924 if (appearance.isNull()) {
1925 ca = opacity;
1926
1927 appearBuf = new GooString ();
1928
1929 appearBuf->append ("q\n");
1930 if (color)
1931 setColor(color, gTrue);
1932 else
1933 appearBuf->append ("1 1 1 rg\n");
1934 if (!icon->cmp("Note"))
1935 appearBuf->append (ANNOT_TEXT_AP_NOTE);
1936 else if (!icon->cmp("Comment"))
1937 appearBuf->append (ANNOT_TEXT_AP_COMMENT);
1938 else if (!icon->cmp("Key"))
1939 appearBuf->append (ANNOT_TEXT_AP_KEY);
1940 else if (!icon->cmp("Help"))
1941 appearBuf->append (ANNOT_TEXT_AP_HELP);
1942 else if (!icon->cmp("NewParagraph"))
1943 appearBuf->append (ANNOT_TEXT_AP_NEW_PARAGRAPH);
1944 else if (!icon->cmp("Paragraph"))
1945 appearBuf->append (ANNOT_TEXT_AP_PARAGRAPH);
1946 else if (!icon->cmp("Insert"))
1947 appearBuf->append (ANNOT_TEXT_AP_INSERT);
1948 else if (!icon->cmp("Cross"))
1949 appearBuf->append (ANNOT_TEXT_AP_CROSS);
1950 else if (!icon->cmp("Circle"))
1951 appearBuf->append (ANNOT_TEXT_AP_CIRCLE);
1952 appearBuf->append ("Q\n");
1953
1954 double bbox[4];
1955 bbox[0] = bbox[1] = 0;
1956 bbox[2] = bbox[3] = 24;
1957 if (ca == 1) {
1958 createForm(bbox, gFalse, NULL, &appearance);
1959 } else {
1960 Object aStream, resDict;
1961
1962 createForm(bbox, gTrue, NULL, &aStream);
1963 delete appearBuf;
1964
1965 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
1966 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
1967 createForm(bbox, gFalse, &resDict, &appearance);
1968 }
1969 delete appearBuf;
1970
1971 rectx2 = rect->x1 + 24;
1972 recty2 = rect->y1 + 24;
1973 }
1974
1975 // draw the appearance stream
1976 appearance.fetch(xref, &obj);
1977 gfx->drawAnnot(&obj, border, color,
1978 rect->x1, rect->y1, rectx2, recty2);
1979 obj.free();
1980 }
1981
1982 //------------------------------------------------------------------------
1983 // AnnotLink
1984 //------------------------------------------------------------------------
AnnotLink(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)1985 AnnotLink::AnnotLink(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
1986 Annot(xrefA, rect, catalog) {
1987 Object obj1;
1988
1989 type = typeLink;
1990 annotObj.dictSet ("Subtype", obj1.initName ("Link"));
1991 initialize (xrefA, catalog, annotObj.getDict());
1992 }
1993
AnnotLink(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)1994 AnnotLink::AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1995 Annot(xrefA, dict, catalog, obj) {
1996
1997 type = typeLink;
1998 initialize (xrefA, catalog, dict);
1999 }
2000
~AnnotLink()2001 AnnotLink::~AnnotLink() {
2002 /*
2003 if (actionDict)
2004 delete actionDict;
2005 */
2006 dest.free();
2007 /*
2008 if (uriAction)
2009 delete uriAction;
2010 */
2011 if (quadrilaterals)
2012 delete quadrilaterals;
2013 }
2014
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)2015 void AnnotLink::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
2016 Object obj1;
2017 /*
2018 if (dict->lookup("A", &obj1)->isDict()) {
2019 actionDict = NULL;
2020 } else {
2021 actionDict = NULL;
2022 }
2023 obj1.free();
2024 */
2025 dict->lookup("Dest", &dest);
2026 if (dict->lookup("H", &obj1)->isName()) {
2027 GooString *effect = new GooString(obj1.getName());
2028
2029 if (!effect->cmp("N")) {
2030 linkEffect = effectNone;
2031 } else if (!effect->cmp("I")) {
2032 linkEffect = effectInvert;
2033 } else if (!effect->cmp("O")) {
2034 linkEffect = effectOutline;
2035 } else if (!effect->cmp("P")) {
2036 linkEffect = effectPush;
2037 } else {
2038 linkEffect = effectInvert;
2039 }
2040 delete effect;
2041 } else {
2042 linkEffect = effectInvert;
2043 }
2044 obj1.free();
2045 /*
2046 if (dict->lookup("PA", &obj1)->isDict()) {
2047 uriAction = NULL;
2048 } else {
2049 uriAction = NULL;
2050 }
2051 obj1.free();
2052 */
2053 if (dict->lookup("QuadPoints", &obj1)->isArray()) {
2054 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
2055 } else {
2056 quadrilaterals = NULL;
2057 }
2058 obj1.free();
2059 }
2060
draw(Gfx * gfx,GBool printing)2061 void AnnotLink::draw(Gfx *gfx, GBool printing) {
2062 Object obj;
2063
2064 if (!isVisible (printing))
2065 return;
2066
2067 // draw the appearance stream
2068 appearance.fetch(xref, &obj);
2069 gfx->drawAnnot(&obj, border, color,
2070 rect->x1, rect->y1, rect->x2, rect->y2);
2071 obj.free();
2072 }
2073
2074 //------------------------------------------------------------------------
2075 // AnnotFreeText
2076 //------------------------------------------------------------------------
AnnotFreeText(XRef * xrefA,PDFRectangle * rect,GooString * da,Catalog * catalog)2077 AnnotFreeText::AnnotFreeText(XRef *xrefA, PDFRectangle *rect, GooString *da, Catalog *catalog) :
2078 AnnotMarkup(xrefA, rect, catalog) {
2079 Object obj1;
2080
2081 type = typeFreeText;
2082
2083 annotObj.dictSet ("Subtype", obj1.initName ("FreeText"));
2084
2085 Object obj2;
2086 obj2.initString (da->copy());
2087 annotObj.dictSet("DA", &obj2);
2088
2089 initialize (xrefA, catalog, annotObj.getDict());
2090 }
2091
AnnotFreeText(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)2092 AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
2093 AnnotMarkup(xrefA, dict, catalog, obj) {
2094 type = typeFreeText;
2095 initialize(xrefA, catalog, dict);
2096 }
2097
~AnnotFreeText()2098 AnnotFreeText::~AnnotFreeText() {
2099 delete appearanceString;
2100
2101 if (styleString)
2102 delete styleString;
2103
2104 if (calloutLine)
2105 delete calloutLine;
2106
2107 if (borderEffect)
2108 delete borderEffect;
2109
2110 if (rectangle)
2111 delete rectangle;
2112 }
2113
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)2114 void AnnotFreeText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
2115 Object obj1;
2116
2117 if (dict->lookup("DA", &obj1)->isString()) {
2118 appearanceString = obj1.getString()->copy();
2119 } else {
2120 appearanceString = new GooString();
2121 error(-1, "Bad appearance for annotation");
2122 ok = gFalse;
2123 }
2124 obj1.free();
2125
2126 if (dict->lookup("Q", &obj1)->isInt()) {
2127 quadding = (AnnotFreeTextQuadding) obj1.getInt();
2128 } else {
2129 quadding = quaddingLeftJustified;
2130 }
2131 obj1.free();
2132
2133 if (dict->lookup("DS", &obj1)->isString()) {
2134 styleString = obj1.getString()->copy();
2135 } else {
2136 styleString = NULL;
2137 }
2138 obj1.free();
2139
2140 if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
2141 double x1, y1, x2, y2;
2142 Object obj2;
2143
2144 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
2145 obj2.free();
2146 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
2147 obj2.free();
2148 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
2149 obj2.free();
2150 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
2151 obj2.free();
2152
2153 if (obj1.arrayGetLength() == 6) {
2154 double x3, y3;
2155 (obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
2156 obj2.free();
2157 (obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
2158 obj2.free();
2159 calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
2160 } else {
2161 calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
2162 }
2163 } else {
2164 calloutLine = NULL;
2165 }
2166 obj1.free();
2167
2168 if (dict->lookup("IT", &obj1)->isName()) {
2169 GooString *intentName = new GooString(obj1.getName());
2170
2171 if (!intentName->cmp("FreeText")) {
2172 intent = intentFreeText;
2173 } else if (!intentName->cmp("FreeTextCallout")) {
2174 intent = intentFreeTextCallout;
2175 } else if (!intentName->cmp("FreeTextTypeWriter")) {
2176 intent = intentFreeTextTypeWriter;
2177 } else {
2178 intent = intentFreeText;
2179 }
2180 delete intentName;
2181 } else {
2182 intent = intentFreeText;
2183 }
2184 obj1.free();
2185
2186 if (dict->lookup("BE", &obj1)->isDict()) {
2187 borderEffect = new AnnotBorderEffect(obj1.getDict());
2188 } else {
2189 borderEffect = NULL;
2190 }
2191 obj1.free();
2192
2193 if (dict->lookup("RD", &obj1)->isArray()) {
2194 rectangle = parseDiffRectangle(obj1.getArray(), rect);
2195 } else {
2196 rectangle = NULL;
2197 }
2198 obj1.free();
2199
2200 if (dict->lookup("LE", &obj1)->isName()) {
2201 GooString *styleName = new GooString(obj1.getName());
2202 endStyle = parseAnnotLineEndingStyle(styleName);
2203 delete styleName;
2204 } else {
2205 endStyle = annotLineEndingNone;
2206 }
2207 obj1.free();
2208 }
2209
2210 //------------------------------------------------------------------------
2211 // AnnotLine
2212 //------------------------------------------------------------------------
2213
AnnotLine(XRef * xrefA,PDFRectangle * rect,PDFRectangle * lRect,Catalog * catalog)2214 AnnotLine::AnnotLine(XRef *xrefA, PDFRectangle *rect, PDFRectangle *lRect, Catalog *catalog) :
2215 AnnotMarkup(xrefA, rect, catalog) {
2216 Object obj1;
2217
2218 type = typeLine;
2219 annotObj.dictSet ("Subtype", obj1.initName ("Line"));
2220
2221 Object obj2, obj3;
2222 obj2.initArray (xrefA);
2223 obj2.arrayAdd (obj3.initReal (lRect->x1));
2224 obj2.arrayAdd (obj3.initReal (lRect->y1));
2225 obj2.arrayAdd (obj3.initReal (lRect->x2));
2226 obj2.arrayAdd (obj3.initReal (lRect->y2));
2227 annotObj.dictSet ("L", &obj2);
2228
2229 initialize (xrefA, catalog, annotObj.getDict());
2230 }
2231
AnnotLine(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)2232 AnnotLine::AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
2233 AnnotMarkup(xrefA, dict, catalog, obj) {
2234 type = typeLine;
2235 initialize(xrefA, catalog, dict);
2236 }
2237
~AnnotLine()2238 AnnotLine::~AnnotLine() {
2239 delete coord1;
2240 delete coord2;
2241
2242 if (interiorColor)
2243 delete interiorColor;
2244
2245 if (measure)
2246 delete measure;
2247 }
2248
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)2249 void AnnotLine::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
2250 Object obj1;
2251
2252 if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
2253 Object obj2;
2254 double x1, y1, x2, y2;
2255
2256 (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
2257 obj2.free();
2258 (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
2259 obj2.free();
2260 (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
2261 obj2.free();
2262 (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
2263 obj2.free();
2264
2265 coord1 = new AnnotCoord(x1, y1);
2266 coord2 = new AnnotCoord(x2, y2);
2267 } else {
2268 coord1 = new AnnotCoord();
2269 coord2 = new AnnotCoord();
2270 }
2271 obj1.free();
2272
2273 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
2274 Object obj2;
2275
2276 if(obj1.arrayGet(0, &obj2)->isString())
2277 startStyle = parseAnnotLineEndingStyle(obj2.getString());
2278 else
2279 startStyle = annotLineEndingNone;
2280 obj2.free();
2281
2282 if(obj1.arrayGet(1, &obj2)->isString())
2283 endStyle = parseAnnotLineEndingStyle(obj2.getString());
2284 else
2285 endStyle = annotLineEndingNone;
2286 obj2.free();
2287
2288 } else {
2289 startStyle = endStyle = annotLineEndingNone;
2290 }
2291 obj1.free();
2292
2293 if (dict->lookup("IC", &obj1)->isArray()) {
2294 interiorColor = new AnnotColor(obj1.getArray());
2295 } else {
2296 interiorColor = NULL;
2297 }
2298 obj1.free();
2299
2300 if (dict->lookup("LL", &obj1)->isNum()) {
2301 leaderLineLength = obj1.getNum();
2302 } else {
2303 leaderLineLength = 0;
2304 }
2305 obj1.free();
2306
2307 if (dict->lookup("LLE", &obj1)->isNum()) {
2308 leaderLineExtension = obj1.getNum();
2309
2310 if (leaderLineExtension < 0)
2311 leaderLineExtension = 0;
2312 } else {
2313 leaderLineExtension = 0;
2314 }
2315 obj1.free();
2316
2317 if (dict->lookup("Cap", &obj1)->isBool()) {
2318 caption = obj1.getBool();
2319 } else {
2320 caption = gFalse;
2321 }
2322 obj1.free();
2323
2324 if (dict->lookup("IT", &obj1)->isName()) {
2325 GooString *intentName = new GooString(obj1.getName());
2326
2327 if(!intentName->cmp("LineArrow")) {
2328 intent = intentLineArrow;
2329 } else if(!intentName->cmp("LineDimension")) {
2330 intent = intentLineDimension;
2331 } else {
2332 intent = intentLineArrow;
2333 }
2334 delete intentName;
2335 } else {
2336 intent = intentLineArrow;
2337 }
2338 obj1.free();
2339
2340 if (dict->lookup("LLO", &obj1)->isNum()) {
2341 leaderLineOffset = obj1.getNum();
2342
2343 if (leaderLineOffset < 0)
2344 leaderLineOffset = 0;
2345 } else {
2346 leaderLineOffset = 0;
2347 }
2348 obj1.free();
2349
2350 if (dict->lookup("CP", &obj1)->isName()) {
2351 GooString *captionName = new GooString(obj1.getName());
2352
2353 if(!captionName->cmp("Inline")) {
2354 captionPos = captionPosInline;
2355 } else if(!captionName->cmp("Top")) {
2356 captionPos = captionPosTop;
2357 } else {
2358 captionPos = captionPosInline;
2359 }
2360 delete captionName;
2361 } else {
2362 captionPos = captionPosInline;
2363 }
2364 obj1.free();
2365
2366 if (dict->lookup("Measure", &obj1)->isDict()) {
2367 measure = NULL;
2368 } else {
2369 measure = NULL;
2370 }
2371 obj1.free();
2372
2373 if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
2374 Object obj2;
2375
2376 (obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
2377 captionTextHorizontal = 0);
2378 obj2.free();
2379 (obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
2380 captionTextVertical = 0);
2381 obj2.free();
2382 } else {
2383 captionTextHorizontal = captionTextVertical = 0;
2384 }
2385 obj1.free();
2386 }
2387
draw(Gfx * gfx,GBool printing)2388 void AnnotLine::draw(Gfx *gfx, GBool printing) {
2389 Object obj;
2390 double ca = 1;
2391
2392 if (!isVisible (printing))
2393 return;
2394
2395 /* Some documents like pdf_commenting_new.pdf,
2396 * have y1 = y2 but line_width > 0, acroread
2397 * renders the lines in such cases even though
2398 * the annot bbox is empty. We adjust the bbox here
2399 * to avoid having an empty bbox so that lines
2400 * are rendered
2401 */
2402 if (rect->y1 == rect->y2)
2403 rect->y2 += border ? border->getWidth() : 1;
2404
2405 if (appearance.isNull()) {
2406 ca = opacity;
2407
2408 appearBuf = new GooString ();
2409 appearBuf->append ("q\n");
2410 if (color)
2411 setColor(color, gFalse);
2412
2413 if (border) {
2414 int i, dashLength;
2415 double *dash;
2416
2417 switch (border->getStyle()) {
2418 case AnnotBorder::borderDashed:
2419 appearBuf->append("[");
2420 dashLength = border->getDashLength();
2421 dash = border->getDash();
2422 for (i = 0; i < dashLength; ++i)
2423 appearBuf->appendf(" {0:.2f}", dash[i]);
2424 appearBuf->append(" ] 0 d\n");
2425 break;
2426 default:
2427 appearBuf->append("[] 0 d\n");
2428 break;
2429 }
2430 appearBuf->appendf("{0:.2f} w\n", border->getWidth());
2431 }
2432 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", coord1->getX() - rect->x1, coord1->getY() - rect->y1);
2433 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", coord2->getX() - rect->x1, coord2->getY() - rect->y1);
2434 // TODO: Line ending, caption, leader lines
2435 appearBuf->append ("S\n");
2436 appearBuf->append ("Q\n");
2437
2438 double bbox[4];
2439 bbox[0] = bbox[1] = 0;
2440 bbox[2] = rect->x2 - rect->x1;
2441 bbox[3] = rect->y2 - rect->y1;
2442 if (ca == 1) {
2443 createForm(bbox, gFalse, NULL, &appearance);
2444 } else {
2445 Object aStream, resDict;
2446
2447 createForm(bbox, gTrue, NULL, &aStream);
2448 delete appearBuf;
2449
2450 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
2451 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
2452 createForm(bbox, gFalse, &resDict, &appearance);
2453 }
2454 delete appearBuf;
2455 }
2456
2457 // draw the appearance stream
2458 appearance.fetch(xref, &obj);
2459 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2460 rect->x1, rect->y1, rect->x2, rect->y2);
2461 obj.free();
2462 }
2463
2464 //------------------------------------------------------------------------
2465 // AnnotTextMarkup
2466 //------------------------------------------------------------------------
AnnotTextMarkup(XRef * xrefA,PDFRectangle * rect,AnnotSubtype subType,AnnotQuadrilaterals * quadPoints,Catalog * catalog)2467 AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, PDFRectangle *rect, AnnotSubtype subType,
2468 AnnotQuadrilaterals *quadPoints, Catalog *catalog) :
2469 AnnotMarkup(xrefA, rect, catalog) {
2470 Object obj1;
2471
2472 switch (subType) {
2473 case typeHighlight:
2474 annotObj.dictSet ("Subtype", obj1.initName ("Highlight"));
2475 break;
2476 case typeUnderline:
2477 annotObj.dictSet ("Subtype", obj1.initName ("Underline"));
2478 break;
2479 case typeSquiggly:
2480 annotObj.dictSet ("Subtype", obj1.initName ("Squiggly"));
2481 break;
2482 case typeStrikeOut:
2483 annotObj.dictSet ("Subtype", obj1.initName ("StrikeOut"));
2484 break;
2485 default:
2486 assert (0 && "Invalid subtype for AnnotTextMarkup\n");
2487 }
2488
2489 Object obj2;
2490 obj2.initArray (xrefA);
2491
2492 for (int i = 0; i < quadPoints->getQuadrilateralsLength(); ++i) {
2493 Object obj3;
2494
2495 obj2.arrayAdd (obj3.initReal (quadPoints->getX1(i)));
2496 obj2.arrayAdd (obj3.initReal (quadPoints->getY1(i)));
2497 obj2.arrayAdd (obj3.initReal (quadPoints->getX2(i)));
2498 obj2.arrayAdd (obj3.initReal (quadPoints->getY2(i)));
2499 obj2.arrayAdd (obj3.initReal (quadPoints->getX3(i)));
2500 obj2.arrayAdd (obj3.initReal (quadPoints->getY3(i)));
2501 obj2.arrayAdd (obj3.initReal (quadPoints->getX4(i)));
2502 obj2.arrayAdd (obj3.initReal (quadPoints->getY4(i)));
2503 }
2504
2505 annotObj.dictSet ("QuadPoints", &obj2);
2506
2507 initialize(xrefA, catalog, annotObj.getDict());
2508 }
2509
AnnotTextMarkup(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)2510 AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
2511 AnnotMarkup(xrefA, dict, catalog, obj) {
2512 // the real type will be read in initialize()
2513 type = typeHighlight;
2514 initialize(xrefA, catalog, dict);
2515 }
2516
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)2517 void AnnotTextMarkup::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
2518 Object obj1;
2519
2520 if (dict->lookup("Subtype", &obj1)->isName()) {
2521 GooString typeName(obj1.getName());
2522 if (!typeName.cmp("Highlight")) {
2523 type = typeHighlight;
2524 } else if (!typeName.cmp("Underline")) {
2525 type = typeUnderline;
2526 } else if (!typeName.cmp("Squiggly")) {
2527 type = typeSquiggly;
2528 } else if (!typeName.cmp("StrikeOut")) {
2529 type = typeStrikeOut;
2530 }
2531 }
2532 obj1.free();
2533
2534 if(dict->lookup("QuadPoints", &obj1)->isArray()) {
2535 quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
2536 } else {
2537 error(-1, "Bad Annot Text Markup QuadPoints");
2538 quadrilaterals = NULL;
2539 ok = gFalse;
2540 }
2541 obj1.free();
2542 }
2543
~AnnotTextMarkup()2544 AnnotTextMarkup::~AnnotTextMarkup() {
2545 if(quadrilaterals) {
2546 delete quadrilaterals;
2547 }
2548 }
2549
2550
2551
draw(Gfx * gfx,GBool printing)2552 void AnnotTextMarkup::draw(Gfx *gfx, GBool printing) {
2553 Object obj;
2554 double ca = 1;
2555 int i;
2556 Object obj1, obj2;
2557
2558 if (!isVisible (printing))
2559 return;
2560
2561 if (appearance.isNull() || type == typeHighlight) {
2562 ca = opacity;
2563
2564 appearBuf = new GooString ();
2565
2566 switch (type) {
2567 case typeUnderline:
2568 if (color) {
2569 setColor(color, gFalse);
2570 setColor(color, gTrue);
2571 }
2572
2573 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
2574 double x1, y1, x2, y2, x3, y3;
2575 double x, y;
2576
2577 x1 = quadrilaterals->getX1(i);
2578 y1 = quadrilaterals->getY1(i);
2579 x2 = quadrilaterals->getX2(i);
2580 y2 = quadrilaterals->getY2(i);
2581 x3 = quadrilaterals->getX3(i);
2582 y3 = quadrilaterals->getY3(i);
2583
2584 x = x1 - rect->x1;
2585 y = y3 - rect->y1;
2586 appearBuf->append ("[]0 d 2 w\n");
2587 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x, y);
2588 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x + (x2 - x1), y);
2589 appearBuf->append ("S\n");
2590 }
2591 break;
2592 case typeStrikeOut:
2593 if (color) {
2594 setColor(color, gFalse);
2595 setColor(color, gTrue);
2596 }
2597
2598 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
2599 double x1, y1, x2, y2, x3, y3;
2600 double x, y;
2601 double h2;
2602
2603 x1 = quadrilaterals->getX1(i);
2604 y1 = quadrilaterals->getY1(i);
2605 x2 = quadrilaterals->getX2(i);
2606 y2 = quadrilaterals->getY2(i);
2607 x3 = quadrilaterals->getX3(i);
2608 y3 = quadrilaterals->getY3(i);
2609 h2 = (y1 - y3) / 2.0;
2610
2611 x = x1 - rect->x1;
2612 y = (y3 - rect->y1) + h2;
2613 appearBuf->append ("[]0 d 2 w\n");
2614 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x, y);
2615 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x + (x2 - x1), y);
2616 appearBuf->append ("S\n");
2617 }
2618 break;
2619 case typeSquiggly:
2620 // TODO
2621 default:
2622 case typeHighlight:
2623 appearance.free();
2624
2625 if (color)
2626 setColor(color, gTrue);
2627
2628 for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
2629 double x1, y1, x2, y2, x3, y3, x4, y4;
2630 double h4;
2631
2632 x1 = quadrilaterals->getX1(i);
2633 y1 = quadrilaterals->getY1(i);
2634 x2 = quadrilaterals->getX2(i);
2635 y2 = quadrilaterals->getY2(i);
2636 x3 = quadrilaterals->getX3(i);
2637 y3 = quadrilaterals->getY3(i);
2638 x4 = quadrilaterals->getX4(i);
2639 y4 = quadrilaterals->getY4(i);
2640 h4 = abs(y1 - y3) / 4.0;
2641
2642 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x3, y3);
2643 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
2644 x3 - h4, y3 + h4, x1 - h4, y1 - h4, x1, y1);
2645 appearBuf->appendf ("{0:.2f} {1:.2f} l\n", x2, y2);
2646 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
2647 x2 + h4, y2 - h4, x4 + h4, y4 + h4, x4, y4);
2648 appearBuf->append ("f\n");
2649 }
2650
2651 Object aStream, resDict;
2652 double bbox[4];
2653 bbox[0] = rect->x1;
2654 bbox[1] = rect->y1;
2655 bbox[2] = rect->x2;
2656 bbox[3] = rect->y2;
2657 createForm(bbox, gTrue, NULL, &aStream);
2658 delete appearBuf;
2659
2660 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
2661 createResourcesDict("Fm0", &aStream, "GS0", 1, "Multiply", &resDict);
2662 if (ca == 1) {
2663 createForm(bbox, gFalse, &resDict, &appearance);
2664 } else {
2665 createForm(bbox, gTrue, &resDict, &aStream);
2666 delete appearBuf;
2667
2668 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
2669 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
2670 createForm(bbox, gFalse, &resDict, &appearance);
2671 }
2672 delete appearBuf;
2673 break;
2674 }
2675 }
2676
2677 // draw the appearance stream
2678 appearance.fetch(xref, &obj);
2679 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
2680 rect->x1, rect->y1, rect->x2, rect->y2);
2681 obj.free();
2682 }
2683
2684 //------------------------------------------------------------------------
2685 // AnnotWidget
2686 //------------------------------------------------------------------------
2687
AnnotWidget(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)2688 AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
2689 Annot(xrefA, dict, catalog, obj) {
2690 type = typeWidget;
2691 widget = NULL;
2692 initialize(xrefA, catalog, dict);
2693 }
2694
~AnnotWidget()2695 AnnotWidget::~AnnotWidget() {
2696 if (appearCharacs)
2697 delete appearCharacs;
2698
2699 if (action)
2700 delete action;
2701
2702 if (additionActions)
2703 delete additionActions;
2704
2705 if (parent)
2706 delete parent;
2707 }
2708
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)2709 void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
2710 Object obj1;
2711
2712 if ((form = catalog->getForm ())) {
2713 widget = form->findWidgetByRef (ref);
2714
2715 // check if field apperances need to be regenerated
2716 // Only text or choice fields needs to have appearance regenerated
2717 // see section 8.6.2 "Variable Text" of PDFReference
2718 regen = gFalse;
2719 if (widget != NULL && (widget->getType () == formText || widget->getType () == formChoice)) {
2720 regen = form->getNeedAppearances ();
2721 }
2722 }
2723
2724 // If field doesn't have an AP we'll have to generate it
2725 if (appearance.isNone () || appearance.isNull ())
2726 regen = gTrue;
2727
2728 if(dict->lookup("H", &obj1)->isName()) {
2729 GooString *modeName = new GooString(obj1.getName());
2730
2731 if(!modeName->cmp("N")) {
2732 mode = highlightModeNone;
2733 } else if(!modeName->cmp("O")) {
2734 mode = highlightModeOutline;
2735 } else if(!modeName->cmp("P") || !modeName->cmp("T")) {
2736 mode = highlightModePush;
2737 } else {
2738 mode = highlightModeInvert;
2739 }
2740 delete modeName;
2741 } else {
2742 mode = highlightModeInvert;
2743 }
2744 obj1.free();
2745
2746 if(dict->lookup("MK", &obj1)->isDict()) {
2747 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
2748 } else {
2749 appearCharacs = NULL;
2750 }
2751 obj1.free();
2752
2753 if(dict->lookup("A", &obj1)->isDict()) {
2754 action = NULL;
2755 } else {
2756 action = NULL;
2757 }
2758 obj1.free();
2759
2760 if(dict->lookup("AA", &obj1)->isDict()) {
2761 additionActions = NULL;
2762 } else {
2763 additionActions = NULL;
2764 }
2765 obj1.free();
2766
2767 if(dict->lookup("Parent", &obj1)->isDict()) {
2768 parent = NULL;
2769 } else {
2770 parent = NULL;
2771 }
2772 obj1.free();
2773 }
2774
2775 // Grand unified handler for preparing text strings to be drawn into form
2776 // fields. Takes as input a text string (in PDFDocEncoding or UTF-16).
2777 // Converts some or all of this string to the appropriate encoding for the
2778 // specified font, and computes the width of the text. Can optionally stop
2779 // converting when a specified width has been reached, to perform line-breaking
2780 // for multi-line fields.
2781 //
2782 // Parameters:
2783 // text: input text string to convert
2784 // outBuf: buffer for writing re-encoded string
2785 // i: index at which to start converting; will be updated to point just after
2786 // last character processed
2787 // font: the font which will be used for display
2788 // width: computed width (unscaled by font size) will be stored here
2789 // widthLimit: if non-zero, stop converting to keep width under this value
2790 // (should be scaled down by font size)
2791 // charCount: count of number of characters will be stored here
2792 // noReencode: if set, do not try to translate the character encoding
2793 // (useful for Zapf Dingbats or other unusual encodings)
2794 // can only be used with simple fonts, not CID-keyed fonts
2795 //
2796 // TODO: Handle surrogate pairs in UTF-16.
2797 // Should be able to generate output for any CID-keyed font.
2798 // Doesn't handle vertical fonts--should it?
layoutText(GooString * text,GooString * outBuf,int * i,GfxFont * font,double * width,double widthLimit,int * charCount,GBool noReencode)2799 void AnnotWidget::layoutText(GooString *text, GooString *outBuf, int *i,
2800 GfxFont *font, double *width, double widthLimit,
2801 int *charCount, GBool noReencode)
2802 {
2803 CharCode c;
2804 Unicode uChar, *uAux;
2805 double w = 0.0;
2806 int uLen, n;
2807 double dx, dy, ox, oy;
2808 GBool unicode = text->hasUnicodeMarker();
2809 GBool spacePrev; // previous character was a space
2810
2811 // State for backtracking when more text has been processed than fits within
2812 // widthLimit. We track for both input (text) and output (outBuf) the offset
2813 // to the first character to discard.
2814 //
2815 // We keep track of several points:
2816 // 1 - end of previous completed word which fits
2817 // 2 - previous character which fit
2818 int last_i1, last_i2, last_o1, last_o2;
2819
2820 if (unicode && text->getLength() % 2 != 0) {
2821 error(-1, "AnnotWidget::layoutText, bad unicode string");
2822 return;
2823 }
2824
2825 // skip Unicode marker on string if needed
2826 if (unicode && *i == 0)
2827 *i = 2;
2828
2829 // Start decoding and copying characters, until either:
2830 // we reach the end of the string
2831 // we reach the maximum width
2832 // we reach a newline character
2833 // As we copy characters, keep track of the last full word to fit, so that we
2834 // can backtrack if we exceed the maximum width.
2835 last_i1 = last_i2 = *i;
2836 last_o1 = last_o2 = 0;
2837 spacePrev = gFalse;
2838 outBuf->clear();
2839
2840 while (*i < text->getLength()) {
2841 last_i2 = *i;
2842 last_o2 = outBuf->getLength();
2843
2844 if (unicode) {
2845 uChar = (unsigned char)(text->getChar(*i)) << 8;
2846 uChar += (unsigned char)(text->getChar(*i + 1));
2847 *i += 2;
2848 } else {
2849 if (noReencode)
2850 uChar = text->getChar(*i) & 0xff;
2851 else
2852 uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
2853 *i += 1;
2854 }
2855
2856 // Explicit line break?
2857 if (uChar == '\r' || uChar == '\n') {
2858 // Treat a <CR><LF> sequence as a single line break
2859 if (uChar == '\r' && *i < text->getLength()) {
2860 if (unicode && text->getChar(*i) == '\0'
2861 && text->getChar(*i + 1) == '\n')
2862 *i += 2;
2863 else if (!unicode && text->getChar(*i) == '\n')
2864 *i += 1;
2865 }
2866
2867 break;
2868 }
2869
2870 if (noReencode) {
2871 outBuf->append(uChar);
2872 } else {
2873 CharCodeToUnicode *ccToUnicode = font->getToUnicode();
2874 if (!ccToUnicode) {
2875 // This assumes an identity CMap.
2876 outBuf->append((uChar >> 8) & 0xff);
2877 outBuf->append(uChar & 0xff);
2878 } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
2879 ccToUnicode->decRefCnt();
2880 if (font->isCIDFont()) {
2881 // TODO: This assumes an identity CMap. It should be extended to
2882 // handle the general case.
2883 outBuf->append((c >> 8) & 0xff);
2884 outBuf->append(c & 0xff);
2885 } else {
2886 // 8-bit font
2887 outBuf->append(c);
2888 }
2889 } else {
2890 ccToUnicode->decRefCnt();
2891 fprintf(stderr,
2892 "warning: layoutText: cannot convert U+%04X\n", uChar);
2893 }
2894 }
2895
2896 // If we see a space, then we have a linebreak opportunity.
2897 if (uChar == ' ') {
2898 last_i1 = *i;
2899 if (!spacePrev)
2900 last_o1 = last_o2;
2901 spacePrev = gTrue;
2902 } else {
2903 spacePrev = gFalse;
2904 }
2905
2906 // Compute width of character just output
2907 if (outBuf->getLength() > last_o2) {
2908 dx = 0.0;
2909 font->getNextChar(outBuf->getCString() + last_o2,
2910 outBuf->getLength() - last_o2,
2911 &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2912 w += dx;
2913 }
2914
2915 // Current line over-full now?
2916 if (widthLimit > 0.0 && w > widthLimit) {
2917 if (last_o1 > 0) {
2918 // Back up to the previous word which fit, if there was a previous
2919 // word.
2920 *i = last_i1;
2921 outBuf->del(last_o1, outBuf->getLength() - last_o1);
2922 } else if (last_o2 > 0) {
2923 // Otherwise, back up to the previous character (in the only word on
2924 // this line)
2925 *i = last_i2;
2926 outBuf->del(last_o2, outBuf->getLength() - last_o2);
2927 } else {
2928 // Otherwise, we were trying to fit the first character; include it
2929 // anyway even if it overflows the space--no updates to make.
2930 }
2931 break;
2932 }
2933 }
2934
2935 // If splitting input into lines because we reached the width limit, then
2936 // consume any remaining trailing spaces that would go on this line from the
2937 // input. If in doing so we reach a newline, consume that also. This code
2938 // won't run if we stopped at a newline above, since in that case w <=
2939 // widthLimit still.
2940 if (widthLimit > 0.0 && w > widthLimit) {
2941 if (unicode) {
2942 while (*i < text->getLength()
2943 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
2944 *i += 2;
2945 if (*i < text->getLength()
2946 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
2947 *i += 2;
2948 if (*i < text->getLength()
2949 && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
2950 *i += 2;
2951 } else {
2952 while (*i < text->getLength() && text->getChar(*i) == ' ')
2953 *i += 1;
2954 if (*i < text->getLength() && text->getChar(*i) == '\r')
2955 *i += 1;
2956 if (*i < text->getLength() && text->getChar(*i) == '\n')
2957 *i += 1;
2958 }
2959 }
2960
2961 // Compute the actual width and character count of the final string, based on
2962 // breakpoint, if this information is requested by the caller.
2963 if (width != NULL || charCount != NULL) {
2964 char *s = outBuf->getCString();
2965 int len = outBuf->getLength();
2966
2967 if (width != NULL)
2968 *width = 0.0;
2969 if (charCount != NULL)
2970 *charCount = 0;
2971
2972 while (len > 0) {
2973 dx = 0.0;
2974 n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2975
2976 if (n == 0) {
2977 break;
2978 }
2979
2980 if (width != NULL)
2981 *width += dx;
2982 if (charCount != NULL)
2983 *charCount += 1;
2984
2985 s += n;
2986 len -= n;
2987 }
2988 }
2989 }
2990
2991 // Copy the given string to appearBuf, adding parentheses around it and
2992 // escaping characters as appropriate.
writeString(GooString * str,GooString * appearBuf)2993 void AnnotWidget::writeString(GooString *str, GooString *appearBuf)
2994 {
2995 char c;
2996 int i;
2997
2998 appearBuf->append('(');
2999
3000 for (i = 0; i < str->getLength(); ++i) {
3001 c = str->getChar(i);
3002 if (c == '(' || c == ')' || c == '\\') {
3003 appearBuf->append('\\');
3004 appearBuf->append(c);
3005 } else if (c < 0x20) {
3006 appearBuf->appendf("\\{0:03o}", (unsigned char)c);
3007 } else {
3008 appearBuf->append(c);
3009 }
3010 }
3011
3012 appearBuf->append(')');
3013 }
3014
3015 // Draw the variable text or caption for a field.
drawText(GooString * text,GooString * da,GfxFontDict * fontDict,GBool multiline,int comb,int quadding,GBool txField,GBool forceZapfDingbats,GBool password)3016 void AnnotWidget::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
3017 GBool multiline, int comb, int quadding,
3018 GBool txField, GBool forceZapfDingbats,
3019 GBool password) {
3020 GooList *daToks;
3021 GooString *tok, *convertedText;
3022 GfxFont *font;
3023 double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
3024 int tfPos, tmPos, i, j;
3025 GBool freeText = gFalse; // true if text should be freed before return
3026 GBool freeFont = gFalse;
3027
3028 //~ if there is no MK entry, this should use the existing content stream,
3029 //~ and only replace the marked content portion of it
3030 //~ (this is only relevant for Tx fields)
3031
3032 // parse the default appearance string
3033 tfPos = tmPos = -1;
3034 if (da) {
3035 daToks = new GooList();
3036 i = 0;
3037 while (i < da->getLength()) {
3038 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
3039 ++i;
3040 }
3041 if (i < da->getLength()) {
3042 for (j = i + 1;
3043 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
3044 ++j) ;
3045 daToks->append(new GooString(da, i, j - i));
3046 i = j;
3047 }
3048 }
3049 for (i = 2; i < daToks->getLength(); ++i) {
3050 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
3051 tfPos = i - 2;
3052 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
3053 tmPos = i - 6;
3054 }
3055 }
3056 } else {
3057 daToks = NULL;
3058 }
3059
3060 // force ZapfDingbats
3061 if (forceZapfDingbats) {
3062 if (tfPos >= 0) {
3063 tok = (GooString *)daToks->get(tfPos);
3064 if (tok->cmp("/ZaDb")) {
3065 tok->clear();
3066 tok->append("/ZaDb");
3067 }
3068 }
3069 }
3070 // get the font and font size
3071 font = NULL;
3072 fontSize = 0;
3073 if (tfPos >= 0) {
3074 tok = (GooString *)daToks->get(tfPos);
3075 if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
3076 if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
3077 if (forceZapfDingbats) {
3078 // We are forcing ZaDb but the font does not exist
3079 // so create a fake one
3080 Ref r; // dummy Ref, it's not used at all in this codepath
3081 r.num = 0;
3082 r.gen = 0;
3083 Dict *d = new Dict(xref);
3084 font = new Gfx8BitFont(xref, "ZaDb", r, new GooString("ZapfDingbats"), fontType1, d);
3085 delete d;
3086 freeFont = gTrue;
3087 addDingbatsResource = gTrue;
3088 } else {
3089 error(-1, "Unknown font in field's DA string");
3090 }
3091 }
3092 } else {
3093 error(-1, "Invalid font name in 'Tf' operator in field's DA string");
3094 }
3095 tok = (GooString *)daToks->get(tfPos + 1);
3096 fontSize = gatof(tok->getCString());
3097 } else {
3098 error(-1, "Missing 'Tf' operator in field's DA string");
3099 }
3100 if (!font) {
3101 if (daToks) {
3102 deleteGooList(daToks, GooString);
3103 }
3104 return;
3105 }
3106
3107 // get the border width
3108 borderWidth = border ? border->getWidth() : 0;
3109
3110 // for a password field, replace all characters with asterisks
3111 if (password) {
3112 int len;
3113 if (text->hasUnicodeMarker())
3114 len = (text->getLength() - 2) / 2;
3115 else
3116 len = text->getLength();
3117
3118 text = new GooString;
3119 for (i = 0; i < len; ++i)
3120 text->append('*');
3121 freeText = gTrue;
3122 }
3123
3124 convertedText = new GooString;
3125
3126 // setup
3127 if (txField) {
3128 appearBuf->append("/Tx BMC\n");
3129 }
3130 appearBuf->append("q\n");
3131 appearBuf->append("BT\n");
3132 // multi-line text
3133 if (multiline) {
3134 // note: the comb flag is ignored in multiline mode
3135
3136 wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
3137
3138 // compute font autosize
3139 if (fontSize == 0) {
3140 for (fontSize = 20; fontSize > 1; --fontSize) {
3141 y = rect->y2 - rect->y1;
3142 i = 0;
3143 while (i < text->getLength()) {
3144 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
3145 forceZapfDingbats);
3146 y -= fontSize;
3147 }
3148 // approximate the descender for the last line
3149 if (y >= 0.33 * fontSize) {
3150 break;
3151 }
3152 }
3153 if (tfPos >= 0) {
3154 tok = (GooString *)daToks->get(tfPos + 1);
3155 tok->clear();
3156 tok->appendf("{0:.2f}", fontSize);
3157 }
3158 }
3159
3160 // starting y coordinate
3161 // (note: each line of text starts with a Td operator that moves
3162 // down a line)
3163 y = rect->y2 - rect->y1;
3164
3165 // set the font matrix
3166 if (tmPos >= 0) {
3167 tok = (GooString *)daToks->get(tmPos + 4);
3168 tok->clear();
3169 tok->append('0');
3170 tok = (GooString *)daToks->get(tmPos + 5);
3171 tok->clear();
3172 tok->appendf("{0:.2f}", y);
3173 }
3174
3175 // write the DA string
3176 if (daToks) {
3177 for (i = 0; i < daToks->getLength(); ++i) {
3178 appearBuf->append((GooString *)daToks->get(i))->append(' ');
3179 }
3180 }
3181
3182 // write the font matrix (if not part of the DA string)
3183 if (tmPos < 0) {
3184 appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
3185 }
3186
3187 // write a series of lines of text
3188 i = 0;
3189 xPrev = 0;
3190 while (i < text->getLength()) {
3191 layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
3192 forceZapfDingbats);
3193 w *= fontSize;
3194
3195 // compute text start position
3196 switch (quadding) {
3197 case fieldQuadLeft:
3198 default:
3199 x = borderWidth + 2;
3200 break;
3201 case fieldQuadCenter:
3202 x = (rect->x2 - rect->x1 - w) / 2;
3203 break;
3204 case fieldQuadRight:
3205 x = rect->x2 - rect->x1 - borderWidth - 2 - w;
3206 break;
3207 }
3208
3209 // draw the line
3210 appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
3211 writeString(convertedText, appearBuf);
3212 appearBuf->append(" Tj\n");
3213
3214 // next line
3215 xPrev = x;
3216 }
3217
3218 // single-line text
3219 } else {
3220 //~ replace newlines with spaces? - what does Acrobat do?
3221
3222 // comb formatting
3223 if (comb > 0) {
3224 int charCount;
3225
3226 // compute comb spacing
3227 w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
3228
3229 // compute font autosize
3230 if (fontSize == 0) {
3231 fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
3232 if (w < fontSize) {
3233 fontSize = w;
3234 }
3235 fontSize = floor(fontSize);
3236 if (tfPos >= 0) {
3237 tok = (GooString *)daToks->get(tfPos + 1);
3238 tok->clear();
3239 tok->appendf("{0:.2f}", fontSize);
3240 }
3241 }
3242
3243 i = 0;
3244 layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
3245 forceZapfDingbats);
3246 if (charCount > comb)
3247 charCount = comb;
3248
3249 // compute starting text cell
3250 switch (quadding) {
3251 case fieldQuadLeft:
3252 default:
3253 x = borderWidth;
3254 break;
3255 case fieldQuadCenter:
3256 x = borderWidth + (comb - charCount) / 2 * w;
3257 break;
3258 case fieldQuadRight:
3259 x = borderWidth + (comb - charCount) * w;
3260 break;
3261 }
3262 y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
3263
3264 // set the font matrix
3265 if (tmPos >= 0) {
3266 tok = (GooString *)daToks->get(tmPos + 4);
3267 tok->clear();
3268 tok->appendf("{0:.2f}", x);
3269 tok = (GooString *)daToks->get(tmPos + 5);
3270 tok->clear();
3271 tok->appendf("{0:.2f}", y);
3272 }
3273
3274 // write the DA string
3275 if (daToks) {
3276 for (i = 0; i < daToks->getLength(); ++i) {
3277 appearBuf->append((GooString *)daToks->get(i))->append(' ');
3278 }
3279 }
3280
3281 // write the font matrix (if not part of the DA string)
3282 if (tmPos < 0) {
3283 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
3284 }
3285
3286 // write the text string
3287 char *s = convertedText->getCString();
3288 int len = convertedText->getLength();
3289 i = 0;
3290 xPrev = w; // so that first character is placed properly
3291 while (i < comb && len > 0) {
3292 CharCode code;
3293 Unicode *uAux;
3294 int uLen, n;
3295 double dx, dy, ox, oy;
3296
3297 dx = 0.0;
3298 n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
3299 dx *= fontSize;
3300
3301 // center each character within its cell, by advancing the text
3302 // position the appropriate amount relative to the start of the
3303 // previous character
3304 x = 0.5 * (w - dx);
3305 appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
3306
3307 GooString *charBuf = new GooString(s, n);
3308 writeString(charBuf, appearBuf);
3309 appearBuf->append(" Tj\n");
3310 delete charBuf;
3311
3312 i++;
3313 s += n;
3314 len -= n;
3315 xPrev = x;
3316 }
3317
3318 // regular (non-comb) formatting
3319 } else {
3320 i = 0;
3321 layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
3322 forceZapfDingbats);
3323
3324 // compute font autosize
3325 if (fontSize == 0) {
3326 fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
3327 fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
3328 if (fontSize2 < fontSize) {
3329 fontSize = fontSize2;
3330 }
3331 fontSize = floor(fontSize);
3332 if (tfPos >= 0) {
3333 tok = (GooString *)daToks->get(tfPos + 1);
3334 tok->clear();
3335 tok->appendf("{0:.2f}", fontSize);
3336 }
3337 }
3338
3339 // compute text start position
3340 w *= fontSize;
3341 switch (quadding) {
3342 case fieldQuadLeft:
3343 default:
3344 x = borderWidth + 2;
3345 break;
3346 case fieldQuadCenter:
3347 x = (rect->x2 - rect->x1 - w) / 2;
3348 break;
3349 case fieldQuadRight:
3350 x = rect->x2 - rect->x1 - borderWidth - 2 - w;
3351 break;
3352 }
3353 y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
3354
3355 // set the font matrix
3356 if (tmPos >= 0) {
3357 tok = (GooString *)daToks->get(tmPos + 4);
3358 tok->clear();
3359 tok->appendf("{0:.2f}", x);
3360 tok = (GooString *)daToks->get(tmPos + 5);
3361 tok->clear();
3362 tok->appendf("{0:.2f}", y);
3363 }
3364
3365 // write the DA string
3366 if (daToks) {
3367 for (i = 0; i < daToks->getLength(); ++i) {
3368 appearBuf->append((GooString *)daToks->get(i))->append(' ');
3369 }
3370 }
3371
3372 // write the font matrix (if not part of the DA string)
3373 if (tmPos < 0) {
3374 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
3375 }
3376
3377 // write the text string
3378 writeString(convertedText, appearBuf);
3379 appearBuf->append(" Tj\n");
3380 }
3381 }
3382 // cleanup
3383 appearBuf->append("ET\n");
3384 appearBuf->append("Q\n");
3385 if (txField) {
3386 appearBuf->append("EMC\n");
3387 }
3388 if (daToks) {
3389 deleteGooList(daToks, GooString);
3390 }
3391 if (freeText) {
3392 delete text;
3393 }
3394 delete convertedText;
3395 if (freeFont) {
3396 font->decRefCnt();
3397 }
3398 }
3399
3400 // Draw the variable text or caption for a field.
drawListBox(GooString ** text,GBool * selection,int nOptions,int topIdx,GooString * da,GfxFontDict * fontDict,int quadding)3401 void AnnotWidget::drawListBox(GooString **text, GBool *selection,
3402 int nOptions, int topIdx,
3403 GooString *da, GfxFontDict *fontDict, int quadding) {
3404 GooList *daToks;
3405 GooString *tok, *convertedText;
3406 GfxFont *font;
3407 double fontSize, fontSize2, borderWidth, x, y, w, wMax;
3408 int tfPos, tmPos, i, j;
3409
3410 //~ if there is no MK entry, this should use the existing content stream,
3411 //~ and only replace the marked content portion of it
3412 //~ (this is only relevant for Tx fields)
3413
3414 // parse the default appearance string
3415 tfPos = tmPos = -1;
3416 if (da) {
3417 daToks = new GooList();
3418 i = 0;
3419 while (i < da->getLength()) {
3420 while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
3421 ++i;
3422 }
3423 if (i < da->getLength()) {
3424 for (j = i + 1;
3425 j < da->getLength() && !Lexer::isSpace(da->getChar(j));
3426 ++j) ;
3427 daToks->append(new GooString(da, i, j - i));
3428 i = j;
3429 }
3430 }
3431 for (i = 2; i < daToks->getLength(); ++i) {
3432 if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
3433 tfPos = i - 2;
3434 } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
3435 tmPos = i - 6;
3436 }
3437 }
3438 } else {
3439 daToks = NULL;
3440 }
3441
3442 // get the font and font size
3443 font = NULL;
3444 fontSize = 0;
3445 if (tfPos >= 0) {
3446 tok = (GooString *)daToks->get(tfPos);
3447 if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
3448 if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
3449 error(-1, "Unknown font in field's DA string");
3450 }
3451 } else {
3452 error(-1, "Invalid font name in 'Tf' operator in field's DA string");
3453 }
3454 tok = (GooString *)daToks->get(tfPos + 1);
3455 fontSize = gatof(tok->getCString());
3456 } else {
3457 error(-1, "Missing 'Tf' operator in field's DA string");
3458 }
3459 if (!font) {
3460 if (daToks) {
3461 deleteGooList(daToks, GooString);
3462 }
3463 return;
3464 }
3465
3466 convertedText = new GooString;
3467
3468 // get the border width
3469 borderWidth = border ? border->getWidth() : 0;
3470
3471 // compute font autosize
3472 if (fontSize == 0) {
3473 wMax = 0;
3474 for (i = 0; i < nOptions; ++i) {
3475 j = 0;
3476 layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
3477 if (w > wMax) {
3478 wMax = w;
3479 }
3480 }
3481 fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
3482 fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
3483 if (fontSize2 < fontSize) {
3484 fontSize = fontSize2;
3485 }
3486 fontSize = floor(fontSize);
3487 if (tfPos >= 0) {
3488 tok = (GooString *)daToks->get(tfPos + 1);
3489 tok->clear();
3490 tok->appendf("{0:.2f}", fontSize);
3491 }
3492 }
3493 // draw the text
3494 y = rect->y2 - rect->y1 - 1.1 * fontSize;
3495 for (i = topIdx; i < nOptions; ++i) {
3496 // setup
3497 appearBuf->append("q\n");
3498
3499 // draw the background if selected
3500 if (selection[i]) {
3501 appearBuf->append("0 g f\n");
3502 appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
3503 borderWidth,
3504 y - 0.2 * fontSize,
3505 rect->x2 - rect->x1 - 2 * borderWidth,
3506 1.1 * fontSize);
3507 }
3508
3509 // setup
3510 appearBuf->append("BT\n");
3511
3512 // compute text width and start position
3513 j = 0;
3514 layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
3515 w *= fontSize;
3516 switch (quadding) {
3517 case fieldQuadLeft:
3518 default:
3519 x = borderWidth + 2;
3520 break;
3521 case fieldQuadCenter:
3522 x = (rect->x2 - rect->x1 - w) / 2;
3523 break;
3524 case fieldQuadRight:
3525 x = rect->x2 - rect->x1 - borderWidth - 2 - w;
3526 break;
3527 }
3528
3529 // set the font matrix
3530 if (tmPos >= 0) {
3531 tok = (GooString *)daToks->get(tmPos + 4);
3532 tok->clear();
3533 tok->appendf("{0:.2f}", x);
3534 tok = (GooString *)daToks->get(tmPos + 5);
3535 tok->clear();
3536 tok->appendf("{0:.2f}", y);
3537 }
3538
3539 // write the DA string
3540 if (daToks) {
3541 for (j = 0; j < daToks->getLength(); ++j) {
3542 appearBuf->append((GooString *)daToks->get(j))->append(' ');
3543 }
3544 }
3545
3546 // write the font matrix (if not part of the DA string)
3547 if (tmPos < 0) {
3548 appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
3549 }
3550
3551 // change the text color if selected
3552 if (selection[i]) {
3553 appearBuf->append("1 g\n");
3554 }
3555
3556 // write the text string
3557 writeString(convertedText, appearBuf);
3558 appearBuf->append(" Tj\n");
3559
3560 // cleanup
3561 appearBuf->append("ET\n");
3562 appearBuf->append("Q\n");
3563
3564 // next line
3565 y -= 1.1 * fontSize;
3566 }
3567
3568 if (daToks) {
3569 deleteGooList(daToks, GooString);
3570 }
3571
3572 delete convertedText;
3573 }
3574
generateFieldAppearance()3575 void AnnotWidget::generateFieldAppearance() {
3576 Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
3577 Dict *field;
3578 Dict *annot;
3579 Dict *acroForm;
3580 Dict *mkDict;
3581 MemStream *appearStream;
3582 GfxFontDict *fontDict;
3583 GBool hasCaption;
3584 double w, dx, dy, r;
3585 double *dash;
3586 GooString *caption, *da;
3587 GooString **text;
3588 GBool *selection;
3589 int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
3590 GBool modified;
3591 AnnotColor aColor;
3592
3593 if (widget == NULL || !widget->getField () || !widget->getField ()->getObj ()->isDict ())
3594 return;
3595
3596 field = widget->getField ()->getObj ()->getDict ();
3597 annot = widget->getObj ()->getDict ();
3598 acroForm = form->getObj ()->getDict ();
3599
3600 // do not regenerate appearence if widget has not changed
3601 modified = widget->isModified ();
3602
3603 // only regenerate when it doesn't have an AP or
3604 // it already has an AP but widget has been modified
3605 if (!regen && !modified) {
3606 return;
3607 }
3608
3609 appearBuf = new GooString ();
3610 // get the appearance characteristics (MK) dictionary
3611 if (annot->lookup("MK", &mkObj)->isDict()) {
3612 mkDict = mkObj.getDict();
3613 } else {
3614 mkDict = NULL;
3615 }
3616 // draw the background
3617 if (mkDict) {
3618 if (mkDict->lookup("BG", &obj1)->isArray() &&
3619 obj1.arrayGetLength() > 0) {
3620 AnnotColor aColor = AnnotColor (obj1.getArray());
3621 setColor(&aColor, gTrue);
3622 appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
3623 rect->x2 - rect->x1, rect->y2 - rect->y1);
3624 }
3625 obj1.free();
3626 }
3627
3628 // get the field type
3629 Form::fieldLookup(field, "FT", &ftObj);
3630
3631 // get the field flags (Ff) value
3632 if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
3633 ff = obj1.getInt();
3634 } else {
3635 ff = 0;
3636 }
3637 obj1.free();
3638
3639 // draw the border
3640 if (mkDict && border) {
3641 w = border->getWidth();
3642 if (w > 0) {
3643 mkDict->lookup("BC", &obj1);
3644 if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
3645 mkDict->lookup("BG", &obj1);
3646 }
3647 if (obj1.isArray() && obj1.arrayGetLength() > 0) {
3648 dx = rect->x2 - rect->x1;
3649 dy = rect->y2 - rect->y1;
3650
3651 // radio buttons with no caption have a round border
3652 hasCaption = mkDict->lookup("CA", &obj2)->isString();
3653 obj2.free();
3654 if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
3655 r = 0.5 * (dx < dy ? dx : dy);
3656 switch (border->getStyle()) {
3657 case AnnotBorder::borderDashed:
3658 appearBuf->append("[");
3659 dashLength = border->getDashLength();
3660 dash = border->getDash();
3661 for (i = 0; i < dashLength; ++i) {
3662 appearBuf->appendf(" {0:.2f}", dash[i]);
3663 }
3664 appearBuf->append("] 0 d\n");
3665 // fall through to the solid case
3666 case AnnotBorder::borderSolid:
3667 case AnnotBorder::borderUnderlined:
3668 appearBuf->appendf("{0:.2f} w\n", w);
3669 aColor = AnnotColor (obj1.getArray());
3670 setColor(&aColor, gFalse);
3671 drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
3672 break;
3673 case AnnotBorder::borderBeveled:
3674 case AnnotBorder::borderInset:
3675 appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
3676 aColor = AnnotColor (obj1.getArray());
3677 setColor(&aColor, gFalse);
3678 drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
3679 aColor = AnnotColor (obj1.getArray(),
3680 border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
3681 setColor(&aColor, gFalse);
3682 drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
3683 aColor = AnnotColor (obj1.getArray(),
3684 border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
3685 setColor(&aColor, gFalse);
3686 drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
3687 break;
3688 }
3689
3690 } else {
3691 switch (border->getStyle()) {
3692 case AnnotBorder::borderDashed:
3693 appearBuf->append("[");
3694 dashLength = border->getDashLength();
3695 dash = border->getDash();
3696 for (i = 0; i < dashLength; ++i) {
3697 appearBuf->appendf(" {0:.2f}", dash[i]);
3698 }
3699 appearBuf->append("] 0 d\n");
3700 // fall through to the solid case
3701 case AnnotBorder::borderSolid:
3702 appearBuf->appendf("{0:.2f} w\n", w);
3703 aColor = AnnotColor (obj1.getArray());
3704 setColor(&aColor, gFalse);
3705 appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
3706 0.5 * w, dx - w, dy - w);
3707 break;
3708 case AnnotBorder::borderBeveled:
3709 case AnnotBorder::borderInset:
3710 aColor = AnnotColor (obj1.getArray(),
3711 border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
3712 setColor(&aColor, gTrue);
3713 appearBuf->append("0 0 m\n");
3714 appearBuf->appendf("0 {0:.2f} l\n", dy);
3715 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
3716 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
3717 appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
3718 appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
3719 appearBuf->append("f\n");
3720 aColor = AnnotColor (obj1.getArray(),
3721 border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
3722 setColor(&aColor, gTrue);
3723 appearBuf->append("0 0 m\n");
3724 appearBuf->appendf("{0:.2f} 0 l\n", dx);
3725 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
3726 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
3727 appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
3728 appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
3729 appearBuf->append("f\n");
3730 break;
3731 case AnnotBorder::borderUnderlined:
3732 appearBuf->appendf("{0:.2f} w\n", w);
3733 aColor = AnnotColor (obj1.getArray());
3734 setColor(&aColor, gFalse);
3735 appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
3736 break;
3737 }
3738
3739 // clip to the inside of the border
3740 appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
3741 w, dx - 2 * w, dy - 2 * w);
3742 }
3743 }
3744 obj1.free();
3745 }
3746 }
3747
3748 // get the resource dictionary
3749 acroForm->lookup("DR", &drObj);
3750
3751 // build the font dictionary
3752 if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
3753 fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
3754 } else {
3755 fontDict = NULL;
3756 }
3757 obj1.free();
3758
3759 // get the default appearance string
3760 if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
3761 obj1.free();
3762 acroForm->lookup("DA", &obj1);
3763 }
3764 if (obj1.isString()) {
3765 da = obj1.getString()->copy();
3766 //TODO: look for a font size / name HERE
3767 // => create a function
3768 } else {
3769 da = NULL;
3770 }
3771 obj1.free();
3772
3773 // draw the field contents
3774 if (ftObj.isName("Btn")) {
3775 caption = NULL;
3776 if (mkDict) {
3777 if (mkDict->lookup("CA", &obj1)->isString()) {
3778 caption = obj1.getString()->copy();
3779 }
3780 obj1.free();
3781 }
3782 // radio button
3783 if (ff & fieldFlagRadio) {
3784 //~ Acrobat doesn't draw a caption if there is no AP dict (?)
3785 if (Form::fieldLookup(field, "V", &obj1)->isName()) {
3786 if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
3787 strcmp (obj1.getName(), "Off") != 0) {
3788 if (caption) {
3789 drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
3790 gFalse, gTrue);
3791 } else {
3792 if (mkDict) {
3793 if (mkDict->lookup("BC", &obj3)->isArray() &&
3794 obj3.arrayGetLength() > 0) {
3795 dx = rect->x2 - rect->x1;
3796 dy = rect->y2 - rect->y1;
3797 aColor = AnnotColor (obj3.getArray());
3798 setColor(&aColor, gTrue);
3799 drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
3800 gTrue);
3801 }
3802 obj3.free();
3803 }
3804 }
3805 }
3806 obj2.free();
3807 }
3808 obj1.free();
3809 // pushbutton
3810 } else if (ff & fieldFlagPushbutton) {
3811 if (caption) {
3812 drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
3813 gFalse, gFalse);
3814 }
3815 // checkbox
3816 } else {
3817 if (annot->lookup("AS", &obj1)->isName() &&
3818 strcmp(obj1.getName(), "Off") != 0) {
3819 if (!caption) {
3820 caption = new GooString("3"); // ZapfDingbats checkmark
3821 }
3822 drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
3823 gFalse, gTrue);
3824 }
3825 obj1.free();
3826 }
3827 if (caption) {
3828 delete caption;
3829 }
3830 } else if (ftObj.isName("Tx")) {
3831 if (Form::fieldLookup(field, "V", &obj1)->isString()) {
3832 if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
3833 quadding = obj2.getInt();
3834 } else {
3835 quadding = fieldQuadLeft;
3836 }
3837 obj2.free();
3838 comb = 0;
3839 if (ff & fieldFlagComb) {
3840 if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
3841 comb = obj2.getInt();
3842 }
3843 obj2.free();
3844 }
3845 drawText(obj1.getString(), da, fontDict,
3846 ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
3847 }
3848 obj1.free();
3849 } else if (ftObj.isName("Ch")) {
3850 if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
3851 quadding = obj1.getInt();
3852 } else {
3853 quadding = fieldQuadLeft;
3854 }
3855 obj1.free();
3856 // combo box
3857 if (ff & fieldFlagCombo) {
3858 if (Form::fieldLookup(field, "V", &obj1)->isString()) {
3859 drawText(obj1.getString(), da, fontDict,
3860 gFalse, 0, quadding, gTrue, gFalse);
3861 //~ Acrobat draws a popup icon on the right side
3862 }
3863 obj1.free();
3864 // list box
3865 } else {
3866 if (field->lookup("Opt", &obj1)->isArray()) {
3867 nOptions = obj1.arrayGetLength();
3868 // get the option text
3869 text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
3870 for (i = 0; i < nOptions; ++i) {
3871 text[i] = NULL;
3872 obj1.arrayGet(i, &obj2);
3873 if (obj2.isString()) {
3874 text[i] = obj2.getString()->copy();
3875 } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
3876 if (obj2.arrayGet(1, &obj3)->isString()) {
3877 text[i] = obj3.getString()->copy();
3878 }
3879 obj3.free();
3880 }
3881 obj2.free();
3882 if (!text[i]) {
3883 text[i] = new GooString();
3884 }
3885 }
3886 // get the selected option(s)
3887 selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
3888 //~ need to use the I field in addition to the V field
3889 Form::fieldLookup(field, "V", &obj2);
3890 for (i = 0; i < nOptions; ++i) {
3891 selection[i] = gFalse;
3892 if (obj2.isString()) {
3893 if (!obj2.getString()->cmp(text[i])) {
3894 selection[i] = gTrue;
3895 }
3896 } else if (obj2.isArray()) {
3897 for (j = 0; j < obj2.arrayGetLength(); ++j) {
3898 if (obj2.arrayGet(j, &obj3)->isString() &&
3899 !obj3.getString()->cmp(text[i])) {
3900 selection[i] = gTrue;
3901 }
3902 obj3.free();
3903 }
3904 }
3905 }
3906 obj2.free();
3907 // get the top index
3908 if (field->lookup("TI", &obj2)->isInt()) {
3909 topIdx = obj2.getInt();
3910 } else {
3911 topIdx = 0;
3912 }
3913 obj2.free();
3914 // draw the text
3915 drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
3916 for (i = 0; i < nOptions; ++i) {
3917 delete text[i];
3918 }
3919 gfree(text);
3920 gfree(selection);
3921 }
3922 obj1.free();
3923 }
3924 } else if (ftObj.isName("Sig")) {
3925 //~unimp
3926 } else {
3927 error(-1, "Unknown field type");
3928 }
3929
3930 if (da) {
3931 delete da;
3932 }
3933
3934 // build the appearance stream dictionary
3935 appearDict.initDict(xref);
3936 appearDict.dictAdd(copyString("Length"),
3937 obj1.initInt(appearBuf->getLength()));
3938 appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
3939 obj1.initArray(xref);
3940 obj1.arrayAdd(obj2.initReal(0));
3941 obj1.arrayAdd(obj2.initReal(0));
3942 obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
3943 obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
3944 appearDict.dictAdd(copyString("BBox"), &obj1);
3945
3946 // set the resource dictionary
3947 if (drObj.isDict()) {
3948 appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
3949 }
3950 drObj.free();
3951
3952 // build the appearance stream
3953 appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
3954 appearBuf->getLength(), &appearDict);
3955 appearance.free();
3956 appearance.initStream(appearStream);
3957 delete appearBuf;
3958
3959 appearStream->setNeedFree(gTrue);
3960
3961 if (widget->isModified()) {
3962 //create a new object that will contains the new appearance
3963
3964 //if we already have a N entry in our AP dict, reuse it
3965 if (annot->lookup("AP", &obj1)->isDict() &&
3966 obj1.dictLookupNF("N", &obj2)->isRef()) {
3967 appRef = obj2.getRef();
3968 }
3969
3970 obj2.free();
3971 obj1.free();
3972
3973 // this annot doesn't have an AP yet, create one
3974 if (appRef.num == 0)
3975 appRef = xref->addIndirectObject(&appearance);
3976 else // since we reuse the already existing AP, we have to notify the xref about this update
3977 xref->setModifiedObject(&appearance, appRef);
3978
3979 // update object's AP and AS
3980 Object apObj;
3981 apObj.initDict(xref);
3982
3983 Object oaRef;
3984 oaRef.initRef(appRef.num, appRef.gen);
3985
3986 apObj.dictSet("N", &oaRef);
3987 annot->set("AP", &apObj);
3988 Dict* d = new Dict(annot);
3989 d->decRef();
3990 Object dictObj;
3991 dictObj.initDict(d);
3992
3993 xref->setModifiedObject(&dictObj, ref);
3994 dictObj.free();
3995 }
3996
3997 if (fontDict) {
3998 delete fontDict;
3999 }
4000 ftObj.free();
4001 mkObj.free();
4002 }
4003
4004
draw(Gfx * gfx,GBool printing)4005 void AnnotWidget::draw(Gfx *gfx, GBool printing) {
4006 Object obj;
4007
4008 if (!isVisible (printing))
4009 return;
4010
4011 addDingbatsResource = gFalse;
4012 generateFieldAppearance ();
4013
4014 // draw the appearance stream
4015 appearance.fetch(xref, &obj);
4016 if (addDingbatsResource) {
4017 // We are forcing ZaDb but the font does not exist
4018 // so create a fake one
4019 Object baseFontObj, subtypeObj;
4020 baseFontObj.initName("ZapfDingbats");
4021 subtypeObj.initName("Type1");
4022
4023 Object fontDictObj;
4024 Dict *fontDict = new Dict(xref);
4025 fontDict->decRef();
4026 fontDict->add(copyString("BaseFont"), &baseFontObj);
4027 fontDict->add(copyString("Subtype"), &subtypeObj);
4028 fontDictObj.initDict(fontDict);
4029
4030 Object fontsDictObj;
4031 Dict *fontsDict = new Dict(xref);
4032 fontsDict->decRef();
4033 fontsDict->add(copyString("ZaDb"), &fontDictObj);
4034 fontsDictObj.initDict(fontsDict);
4035
4036 Dict *dict = new Dict(xref);
4037 dict->add(copyString("Font"), &fontsDictObj);
4038 gfx->pushResources(dict);
4039 delete dict;
4040 }
4041 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
4042 rect->x1, rect->y1, rect->x2, rect->y2);
4043 if (addDingbatsResource) {
4044 gfx->popResources();
4045 }
4046 obj.free();
4047 }
4048
4049
4050 //------------------------------------------------------------------------
4051 // AnnotMovie
4052 //------------------------------------------------------------------------
AnnotMovie(XRef * xrefA,PDFRectangle * rect,Movie * movieA,Catalog * catalog)4053 AnnotMovie::AnnotMovie(XRef *xrefA, PDFRectangle *rect, Movie *movieA, Catalog *catalog) :
4054 Annot(xrefA, rect, catalog) {
4055 Object obj1;
4056
4057 type = typeMovie;
4058 annotObj.dictSet ("Subtype", obj1.initName ("Movie"));
4059
4060 movie = movieA->copy();
4061 // TODO: create movie dict from movieA
4062
4063 initialize(xrefA, catalog, annotObj.getDict());
4064 }
4065
AnnotMovie(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4066 AnnotMovie::AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4067 Annot(xrefA, dict, catalog, obj) {
4068 type = typeMovie;
4069 initialize(xrefA, catalog, dict);
4070 }
4071
~AnnotMovie()4072 AnnotMovie::~AnnotMovie() {
4073 if (title)
4074 delete title;
4075 delete movie;
4076 }
4077
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4078 void AnnotMovie::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4079 Object obj1;
4080
4081 if (dict->lookup("T", &obj1)->isString()) {
4082 title = obj1.getString()->copy();
4083 } else {
4084 title = NULL;
4085 }
4086 obj1.free();
4087
4088 Object movieDict;
4089 if (dict->lookup("Movie", &movieDict)->isDict()) {
4090 Object obj2;
4091 dict->lookup("A", &obj2);
4092 if (obj2.isDict())
4093 movie = new Movie (&movieDict, &obj2);
4094 else
4095 movie = new Movie (&movieDict);
4096 if (!movie->isOk()) {
4097 delete movie;
4098 movie = NULL;
4099 ok = gFalse;
4100 }
4101 obj2.free();
4102 } else {
4103 error(-1, "Bad Annot Movie");
4104 movie = NULL;
4105 ok = gFalse;
4106 }
4107 movieDict.free();
4108 }
4109
draw(Gfx * gfx,GBool printing)4110 void AnnotMovie::draw(Gfx *gfx, GBool printing) {
4111 Object obj;
4112
4113 if (!isVisible (printing))
4114 return;
4115
4116 if (appearance.isNull() && movie->getShowPoster()) {
4117 int width, height;
4118 Object poster;
4119 movie->getPoster(&poster);
4120 movie->getAspect(&width, &height);
4121
4122 if (width != -1 && height != -1 && !poster.isNone()) {
4123 MemStream *mStream;
4124
4125 appearBuf = new GooString ();
4126 appearBuf->append ("q\n");
4127 appearBuf->appendf ("{0:d} 0 0 {1:d} 0 0 cm\n", width, height);
4128 appearBuf->append ("/MImg Do\n");
4129 appearBuf->append ("Q\n");
4130
4131 Object imgDict;
4132 imgDict.initDict(xref);
4133 imgDict.dictSet ("MImg", &poster);
4134
4135 Object resDict;
4136 resDict.initDict(xref);
4137 resDict.dictSet ("XObject", &imgDict);
4138
4139 Object formDict, obj1, obj2;
4140 formDict.initDict(xref);
4141 formDict.dictSet("Length", obj1.initInt(appearBuf->getLength()));
4142 formDict.dictSet("Subtype", obj1.initName("Form"));
4143 formDict.dictSet("Name", obj1.initName("FRM"));
4144 obj1.initArray(xref);
4145 obj1.arrayAdd(obj2.initInt(0));
4146 obj1.arrayAdd(obj2.initInt(0));
4147 obj1.arrayAdd(obj2.initInt(width));
4148 obj1.arrayAdd(obj2.initInt(height));
4149 formDict.dictSet("BBox", &obj1);
4150 obj1.initArray(xref);
4151 obj1.arrayAdd(obj2.initInt(1));
4152 obj1.arrayAdd(obj2.initInt(0));
4153 obj1.arrayAdd(obj2.initInt(0));
4154 obj1.arrayAdd(obj2.initInt(1));
4155 obj1.arrayAdd(obj2.initInt(-width / 2));
4156 obj1.arrayAdd(obj2.initInt(-height / 2));
4157 formDict.dictSet("Matrix", &obj1);
4158 formDict.dictSet("Resources", &resDict);
4159
4160 Object aStream;
4161 mStream = new MemStream(copyString(appearBuf->getCString()), 0,
4162 appearBuf->getLength(), &formDict);
4163 mStream->setNeedFree(gTrue);
4164 aStream.initStream(mStream);
4165 delete appearBuf;
4166
4167 Object objDict;
4168 objDict.initDict(xref);
4169 objDict.dictSet ("FRM", &aStream);
4170
4171 resDict.initDict(xref);
4172 resDict.dictSet ("XObject", &objDict);
4173
4174 appearBuf = new GooString ();
4175 appearBuf->append ("q\n");
4176 appearBuf->appendf ("0 0 {0:d} {1:d} re W n\n", width, height);
4177 appearBuf->append ("q\n");
4178 appearBuf->appendf ("0 0 {0:d} {1:d} re W n\n", width, height);
4179 appearBuf->appendf ("1 0 0 1 {0:d} {1:d} cm\n", width / 2, height / 2);
4180 appearBuf->append ("/FRM Do\n");
4181 appearBuf->append ("Q\n");
4182 appearBuf->append ("Q\n");
4183
4184 double bbox[4];
4185 bbox[0] = bbox[1] = 0;
4186 bbox[2] = width;
4187 bbox[3] = height;
4188 createForm(bbox, gFalse, &resDict, &appearance);
4189 delete appearBuf;
4190 }
4191 poster.free();
4192 }
4193
4194 // draw the appearance stream
4195 appearance.fetch(xref, &obj);
4196 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
4197 rect->x1, rect->y1, rect->x2, rect->y2);
4198 obj.free();
4199 }
4200
4201 //------------------------------------------------------------------------
4202 // AnnotScreen
4203 //------------------------------------------------------------------------
AnnotScreen(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)4204 AnnotScreen::AnnotScreen(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
4205 Annot(xrefA, rect, catalog) {
4206 Object obj1;
4207
4208 type = typeScreen;
4209
4210 annotObj.dictSet ("Subtype", obj1.initName ("Screen"));
4211 initialize(xrefA, catalog, annotObj.getDict());
4212 }
4213
AnnotScreen(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4214 AnnotScreen::AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4215 Annot(xrefA, dict, catalog, obj) {
4216 type = typeScreen;
4217 initialize(xrefA, catalog, dict);
4218 }
4219
~AnnotScreen()4220 AnnotScreen::~AnnotScreen() {
4221 if (title)
4222 delete title;
4223 if (appearCharacs)
4224 delete appearCharacs;
4225 if (action)
4226 delete action;
4227
4228 additionAction.free();
4229 }
4230
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4231 void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4232 Object obj1;
4233
4234 title = NULL;
4235 if (dict->lookup("T", &obj1)->isString()) {
4236 title = obj1.getString()->copy();
4237 }
4238 obj1.free();
4239
4240 action = NULL;
4241 if (dict->lookup("A", &obj1)->isDict()) {
4242 action = LinkAction::parseAction(&obj1, catalog->getBaseURI());
4243 if (action->getKind() == actionRendition && page == 0) {
4244 error (-1, "Invalid Rendition action: associated screen annotation without P");
4245 delete action;
4246 action = NULL;
4247 ok = gFalse;
4248 }
4249 }
4250
4251 dict->lookup("AA", &additionAction);
4252
4253 appearCharacs = NULL;
4254 if(dict->lookup("MK", &obj1)->isDict()) {
4255 appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
4256 }
4257 obj1.free();
4258
4259 }
4260
4261 //------------------------------------------------------------------------
4262 // AnnotStamp
4263 //------------------------------------------------------------------------
AnnotStamp(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)4264 AnnotStamp::AnnotStamp(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
4265 AnnotMarkup(xrefA, rect, catalog) {
4266 Object obj1;
4267
4268 type = typeStamp;
4269 annotObj.dictSet ("Subtype", obj1.initName ("Stamp"));
4270 initialize(xrefA, catalog, annotObj.getDict());
4271 }
4272
AnnotStamp(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4273 AnnotStamp::AnnotStamp(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4274 AnnotMarkup(xrefA, dict, catalog, obj) {
4275 type = typeStamp;
4276 initialize(xrefA, catalog, dict);
4277 }
4278
~AnnotStamp()4279 AnnotStamp::~AnnotStamp() {
4280 delete icon;
4281 }
4282
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4283 void AnnotStamp::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4284 Object obj1;
4285
4286 if (dict->lookup("Name", &obj1)->isName()) {
4287 icon = new GooString(obj1.getName());
4288 } else {
4289 icon = new GooString("Draft");
4290 }
4291 obj1.free();
4292
4293 }
4294
4295 //------------------------------------------------------------------------
4296 // AnnotGeometry
4297 //------------------------------------------------------------------------
AnnotGeometry(XRef * xrefA,PDFRectangle * rect,AnnotSubtype subType,Catalog * catalog)4298 AnnotGeometry::AnnotGeometry(XRef *xrefA, PDFRectangle *rect, AnnotSubtype subType, Catalog *catalog) :
4299 AnnotMarkup(xrefA, rect, catalog) {
4300 Object obj1;
4301
4302 switch (subType) {
4303 case typeSquare:
4304 annotObj.dictSet ("Subtype", obj1.initName ("Square"));
4305 break;
4306 case typeCircle:
4307 annotObj.dictSet ("Subtype", obj1.initName ("Circle"));
4308 break;
4309 default:
4310 assert (0 && "Invalid subtype for AnnotGeometry\n");
4311 }
4312
4313 initialize(xrefA, catalog, annotObj.getDict());
4314 }
4315
AnnotGeometry(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4316 AnnotGeometry::AnnotGeometry(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4317 AnnotMarkup(xrefA, dict, catalog, obj) {
4318 // the real type will be read in initialize()
4319 type = typeSquare;
4320 initialize(xrefA, catalog, dict);
4321 }
4322
~AnnotGeometry()4323 AnnotGeometry::~AnnotGeometry() {
4324 delete interiorColor;
4325 delete borderEffect;
4326 delete geometryRect;
4327 }
4328
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4329 void AnnotGeometry::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4330 Object obj1;
4331
4332 if (dict->lookup("Subtype", &obj1)->isName()) {
4333 GooString typeName(obj1.getName());
4334 if (!typeName.cmp("Square")) {
4335 type = typeSquare;
4336 } else if (!typeName.cmp("Circle")) {
4337 type = typeCircle;
4338 }
4339 }
4340 obj1.free();
4341
4342 if (dict->lookup("IC", &obj1)->isArray()) {
4343 interiorColor = new AnnotColor(obj1.getArray());
4344 } else {
4345 interiorColor = NULL;
4346 }
4347 obj1.free();
4348
4349 if (dict->lookup("BE", &obj1)->isDict()) {
4350 borderEffect = new AnnotBorderEffect(obj1.getDict());
4351 } else {
4352 borderEffect = NULL;
4353 }
4354 obj1.free();
4355
4356 geometryRect = NULL;
4357 if (dict->lookup("RD", &obj1)->isArray()) {
4358 geometryRect = parseDiffRectangle(obj1.getArray(), rect);
4359 }
4360 obj1.free();
4361
4362 }
4363
draw(Gfx * gfx,GBool printing)4364 void AnnotGeometry::draw(Gfx *gfx, GBool printing) {
4365 Object obj;
4366 double ca = 1;
4367
4368 if (!isVisible (printing))
4369 return;
4370
4371 if (appearance.isNull()) {
4372 ca = opacity;
4373
4374 appearBuf = new GooString ();
4375 appearBuf->append ("q\n");
4376 if (color)
4377 setColor(color, gFalse);
4378
4379 if (border) {
4380 int i, dashLength;
4381 double *dash;
4382 double borderWidth = border->getWidth();
4383
4384 switch (border->getStyle()) {
4385 case AnnotBorder::borderDashed:
4386 appearBuf->append("[");
4387 dashLength = border->getDashLength();
4388 dash = border->getDash();
4389 for (i = 0; i < dashLength; ++i)
4390 appearBuf->appendf(" {0:.2f}", dash[i]);
4391 appearBuf->append(" ] 0 d\n");
4392 break;
4393 default:
4394 appearBuf->append("[] 0 d\n");
4395 break;
4396 }
4397 appearBuf->appendf("{0:.2f} w\n", borderWidth);
4398
4399 if (interiorColor)
4400 setColor(interiorColor, gTrue);
4401
4402 if (type == typeSquare) {
4403 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re\n",
4404 borderWidth / 2.0, borderWidth / 2.0,
4405 (rect->x2 - rect->x1) - borderWidth,
4406 (rect->y2 - rect->y1) - borderWidth);
4407 } else {
4408 double width, height;
4409 double b;
4410 double x1, y1, x2, y2, x3, y3;
4411
4412 width = rect->x2 - rect->x1;
4413 height = rect->y2 - rect->y1;
4414 b = borderWidth / 2.0;
4415
4416 x1 = b;
4417 y1 = height / 2.0;
4418 appearBuf->appendf ("{0:.2f} {1:.2f} m\n", x1, y1);
4419
4420 y1 += height / 4.0;
4421 x2 = width / 4.0;
4422 y2 = height - b;
4423 x3 = width / 2.0;
4424 y3 = y2;
4425 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
4426 x1, y1, x2, y2, x3, y3);
4427 x2 = width - b;
4428 y2 = y1;
4429 x1 = x3 + (width / 4.0);
4430 y1 = y3;
4431 x3 = x2;
4432 y3 = height / 2.0;
4433 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
4434 x1, y1, x2, y2, x3, y3);
4435
4436 x2 = x1;
4437 y2 = b;
4438 x1 = x3;
4439 y1 = height / 4.0;
4440 x3 = width / 2.0;
4441 y3 = b;
4442 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
4443 x1, y1, x2, y2, x3, y3);
4444
4445 x2 = b;
4446 y2 = y1;
4447 x1 = width / 4.0;
4448 y1 = b;
4449 x3 = b;
4450 y3 = height / 2.0;
4451 appearBuf->appendf ("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
4452 x1, y1, x2, y2, x3, y3);
4453
4454 }
4455
4456 if (interiorColor)
4457 appearBuf->append ("b\n");
4458 else
4459 appearBuf->append ("S\n");
4460 }
4461 appearBuf->append ("Q\n");
4462
4463 double bbox[4];
4464 bbox[0] = bbox[1] = 0;
4465 bbox[2] = rect->x2 - rect->x1;
4466 bbox[3] = rect->y2 - rect->y1;
4467 if (ca == 1) {
4468 createForm(bbox, gFalse, NULL, &appearance);
4469 } else {
4470 Object aStream;
4471
4472 createForm(bbox, gTrue, NULL, &aStream);
4473 delete appearBuf;
4474
4475 Object resDict;
4476 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
4477 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
4478 createForm(bbox, gFalse, &resDict, &appearance);
4479 }
4480 delete appearBuf;
4481 }
4482
4483 // draw the appearance stream
4484 appearance.fetch(xref, &obj);
4485 gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
4486 rect->x1, rect->y1, rect->x2, rect->y2);
4487 obj.free();
4488 }
4489
4490 //------------------------------------------------------------------------
4491 // AnnotPolygon
4492 //------------------------------------------------------------------------
AnnotPolygon(XRef * xrefA,PDFRectangle * rect,AnnotSubtype subType,AnnotPath * path,Catalog * catalog)4493 AnnotPolygon::AnnotPolygon(XRef *xrefA, PDFRectangle *rect, AnnotSubtype subType,
4494 AnnotPath *path, Catalog *catalog) :
4495 AnnotMarkup(xrefA, rect, catalog) {
4496 Object obj1;
4497
4498 switch (subType) {
4499 case typePolygon:
4500 annotObj.dictSet ("Subtype", obj1.initName ("Polygon"));
4501 break;
4502 case typePolyLine:
4503 annotObj.dictSet ("Subtype", obj1.initName ("PolyLine"));
4504 break;
4505 default:
4506 assert (0 && "Invalid subtype for AnnotGeometry\n");
4507 }
4508
4509 Object obj2;
4510 obj2.initArray (xrefA);
4511
4512 for (int i = 0; i < path->getCoordsLength(); ++i) {
4513 Object obj3;
4514
4515 obj2.arrayAdd (obj3.initReal (path->getX(i)));
4516 obj2.arrayAdd (obj3.initReal (path->getY(i)));
4517 }
4518
4519 annotObj.dictSet ("Vertices", &obj2);
4520
4521 initialize(xrefA, catalog, annotObj.getDict());
4522 }
4523
AnnotPolygon(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4524 AnnotPolygon::AnnotPolygon(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4525 AnnotMarkup(xrefA, dict, catalog, obj) {
4526 // the real type will be read in initialize()
4527 type = typePolygon;
4528 initialize(xrefA, catalog, dict);
4529 }
4530
~AnnotPolygon()4531 AnnotPolygon::~AnnotPolygon() {
4532 delete vertices;
4533
4534 if (interiorColor)
4535 delete interiorColor;
4536
4537 if (borderEffect)
4538 delete borderEffect;
4539 }
4540
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4541 void AnnotPolygon::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4542 Object obj1;
4543
4544 if (dict->lookup("Subtype", &obj1)->isName()) {
4545 GooString typeName(obj1.getName());
4546 if (!typeName.cmp("Polygon")) {
4547 type = typePolygon;
4548 } else if (!typeName.cmp("PolyLine")) {
4549 type = typePolyLine;
4550 }
4551 }
4552 obj1.free();
4553
4554 if (dict->lookup("Vertices", &obj1)->isArray()) {
4555 vertices = new AnnotPath(obj1.getArray());
4556 } else {
4557 vertices = new AnnotPath();
4558 error(-1, "Bad Annot Polygon Vertices");
4559 ok = gFalse;
4560 }
4561 obj1.free();
4562
4563 if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
4564 Object obj2;
4565
4566 if(obj1.arrayGet(0, &obj2)->isString())
4567 startStyle = parseAnnotLineEndingStyle(obj2.getString());
4568 else
4569 startStyle = annotLineEndingNone;
4570 obj2.free();
4571
4572 if(obj1.arrayGet(1, &obj2)->isString())
4573 endStyle = parseAnnotLineEndingStyle(obj2.getString());
4574 else
4575 endStyle = annotLineEndingNone;
4576 obj2.free();
4577
4578 } else {
4579 startStyle = endStyle = annotLineEndingNone;
4580 }
4581 obj1.free();
4582
4583 if (dict->lookup("IC", &obj1)->isArray()) {
4584 interiorColor = new AnnotColor(obj1.getArray());
4585 } else {
4586 interiorColor = NULL;
4587 }
4588 obj1.free();
4589
4590 if (dict->lookup("BE", &obj1)->isDict()) {
4591 borderEffect = new AnnotBorderEffect(obj1.getDict());
4592 } else {
4593 borderEffect = NULL;
4594 }
4595 obj1.free();
4596
4597 if (dict->lookup("IT", &obj1)->isName()) {
4598 GooString *intentName = new GooString(obj1.getName());
4599
4600 if(!intentName->cmp("PolygonCloud")) {
4601 intent = polygonCloud;
4602 } else if(!intentName->cmp("PolyLineDimension")) {
4603 intent = polylineDimension;
4604 } else {
4605 intent = polygonDimension;
4606 }
4607 delete intentName;
4608 } else {
4609 intent = polygonCloud;
4610 }
4611 obj1.free();
4612 }
4613
4614 //------------------------------------------------------------------------
4615 // AnnotCaret
4616 //------------------------------------------------------------------------
AnnotCaret(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)4617 AnnotCaret::AnnotCaret(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
4618 AnnotMarkup(xrefA, rect, catalog) {
4619 Object obj1;
4620
4621 type = typeCaret;
4622
4623 annotObj.dictSet ("Subtype", obj1.initName ("Caret"));
4624 initialize(xrefA, catalog, annotObj.getDict());
4625 }
4626
AnnotCaret(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4627 AnnotCaret::AnnotCaret(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4628 AnnotMarkup(xrefA, dict, catalog, obj) {
4629 type = typeCaret;
4630 initialize(xrefA, catalog, dict);
4631 }
4632
~AnnotCaret()4633 AnnotCaret::~AnnotCaret() {
4634 delete caretRect;
4635 }
4636
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4637 void AnnotCaret::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4638 Object obj1;
4639
4640 symbol = symbolNone;
4641 if (dict->lookup("Sy", &obj1)->isName()) {
4642 GooString typeName(obj1.getName());
4643 if (!typeName.cmp("P")) {
4644 symbol = symbolP;
4645 } else if (!typeName.cmp("None")) {
4646 symbol = symbolNone;
4647 }
4648 }
4649 obj1.free();
4650
4651 if (dict->lookup("RD", &obj1)->isArray()) {
4652 caretRect = parseDiffRectangle(obj1.getArray(), rect);
4653 } else {
4654 caretRect = NULL;
4655 }
4656 obj1.free();
4657
4658 }
4659
4660 //------------------------------------------------------------------------
4661 // AnnotInk
4662 //------------------------------------------------------------------------
AnnotInk(XRef * xrefA,PDFRectangle * rect,AnnotPath ** paths,int n_paths,Catalog * catalog)4663 AnnotInk::AnnotInk(XRef *xrefA, PDFRectangle *rect, AnnotPath **paths, int n_paths, Catalog *catalog) :
4664 AnnotMarkup(xrefA, rect, catalog) {
4665 Object obj1;
4666
4667 type = typeInk;
4668
4669 annotObj.dictSet ("Subtype", obj1.initName ("Ink"));
4670
4671 Object obj2;
4672 obj2.initArray (xrefA);
4673
4674 for (int i = 0; i < n_paths; ++i) {
4675 AnnotPath *path = paths[i];
4676 Object obj3;
4677 obj3.initArray (xrefA);
4678
4679 for (int j = 0; j < path->getCoordsLength(); ++j) {
4680 Object obj4;
4681
4682 obj3.arrayAdd (obj4.initReal (path->getX(j)));
4683 obj3.arrayAdd (obj4.initReal (path->getY(j)));
4684 }
4685
4686 obj2.arrayAdd (&obj3);
4687 }
4688
4689 annotObj.dictSet ("InkList", &obj2);
4690
4691 initialize(xrefA, catalog, annotObj.getDict());
4692 }
4693
AnnotInk(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4694 AnnotInk::AnnotInk(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4695 AnnotMarkup(xrefA, dict, catalog, obj) {
4696 type = typeInk;
4697 initialize(xrefA, catalog, dict);
4698 }
4699
~AnnotInk()4700 AnnotInk::~AnnotInk() {
4701 if (inkList) {
4702 for (int i = 0; i < inkListLength; ++i)
4703 delete inkList[i];
4704 gfree(inkList);
4705 }
4706 }
4707
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4708 void AnnotInk::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4709 Object obj1;
4710
4711 if (dict->lookup("InkList", &obj1)->isArray()) {
4712 Array *array = obj1.getArray();
4713 inkListLength = array->getLength();
4714 inkList = (AnnotPath **) gmallocn ((inkListLength), sizeof(AnnotPath *));
4715 memset(inkList, 0, inkListLength * sizeof(AnnotPath *));
4716 for (int i = 0; i < inkListLength; i++) {
4717 Object obj2;
4718 if (array->get(i, &obj2)->isArray())
4719 inkList[i] = new AnnotPath(obj2.getArray());
4720 obj2.free();
4721 }
4722 } else {
4723 inkListLength = 0;
4724 inkList = NULL;
4725 error(-1, "Bad Annot Ink List");
4726 ok = gFalse;
4727 }
4728 obj1.free();
4729 }
4730
4731 //------------------------------------------------------------------------
4732 // AnnotFileAttachment
4733 //------------------------------------------------------------------------
AnnotFileAttachment(XRef * xrefA,PDFRectangle * rect,GooString * filename,Catalog * catalog)4734 AnnotFileAttachment::AnnotFileAttachment(XRef *xrefA, PDFRectangle *rect, GooString *filename, Catalog *catalog) :
4735 AnnotMarkup(xrefA, rect, catalog) {
4736 Object obj1;
4737
4738 type = typeFileAttachment;
4739
4740 annotObj.dictSet ("Subtype", obj1.initName ("FileAttachment"));
4741
4742 Object obj2;
4743 obj2.initString(filename->copy());
4744 annotObj.dictSet ("FS", &obj2);
4745
4746 initialize(xrefA, catalog, annotObj.getDict());
4747 }
4748
AnnotFileAttachment(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4749 AnnotFileAttachment::AnnotFileAttachment(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4750 AnnotMarkup(xrefA, dict, catalog, obj) {
4751 type = typeFileAttachment;
4752 initialize(xrefA, catalog, dict);
4753 }
4754
~AnnotFileAttachment()4755 AnnotFileAttachment::~AnnotFileAttachment() {
4756 file.free();
4757
4758 if (name)
4759 delete name;
4760 }
4761
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4762 void AnnotFileAttachment::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4763 Object obj1;
4764
4765 if (dict->lookup("FS", &obj1)->isDict() || dict->lookup("FS", &obj1)->isString()) {
4766 obj1.copy(&file);
4767 } else {
4768 error(-1, "Bad Annot File Attachment");
4769 ok = gFalse;
4770 }
4771 obj1.free();
4772
4773 if (dict->lookup("Name", &obj1)->isName()) {
4774 name = new GooString(obj1.getName());
4775 } else {
4776 name = new GooString("PushPin");
4777 }
4778 obj1.free();
4779 }
4780
4781 #define ANNOT_FILE_ATTACHMENT_AP_PUSHPIN \
4782 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
4783 "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" \
4784 "l 1 21.523 2.477 23 4.301 23 c h\n" \
4785 "4.301 23 m f\n" \
4786 "0.533333 0.541176 0.521569 RG 2 w\n" \
4787 "1 J\n" \
4788 "1 j\n" \
4789 "[] 0.0 d\n" \
4790 "4 M 5 4 m 6 5 l S\n" \
4791 "2 w\n" \
4792 "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" \
4793 "17 l 11 14 l h\n" \
4794 "11 14 m S\n" \
4795 "3 w\n" \
4796 "6 5 m 9 8 l S\n" \
4797 "0.729412 0.741176 0.713725 RG 2 w\n" \
4798 "5 5 m 6 6 l S\n" \
4799 "2 w\n" \
4800 "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" \
4801 "18 l 11 15 l h\n" \
4802 "11 15 m S\n" \
4803 "3 w\n" \
4804 "6 6 m 9 9 l S\n"
4805
4806 #define ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP \
4807 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
4808 "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" \
4809 "l 1 21.523 2.477 23 4.301 23 c h\n" \
4810 "4.301 23 m f\n" \
4811 "0.533333 0.541176 0.521569 RG 2 w\n" \
4812 "1 J\n" \
4813 "1 j\n" \
4814 "[] 0.0 d\n" \
4815 "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" \
4816 "14.133 18.578 l 14.949 19.387 16.867 19.184 17.539 18.465 c 20.551\n" \
4817 "15.23 l 21.191 14.66 21.336 12.887 20.426 12.102 c 13.18 4.824 l 12.18\n" \
4818 "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" \
4819 "17.887 l S\n" \
4820 "0.729412 0.741176 0.713725 RG 16.645 13.035 m 12.418 8.707 l\n" \
4821 "10.902 7.559 6.402 12.203 8.09 13.562 c\n" \
4822 "14.133 19.578 l 14.949 20.387 16.867 20.184 17.539 19.465 c 20.551\n" \
4823 "16.23 l 21.191 15.66 21.336 13.887 20.426 13.102 c 13.18 5.824 l 12.18\n" \
4824 "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" \
4825 "18.887 l S\n"
4826
4827 #define ANNOT_FILE_ATTACHMENT_AP_GRAPH \
4828 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
4829 "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" \
4830 "l 1 21.523 2.477 23 4.301 23 c h\n" \
4831 "4.301 23 m f\n" \
4832 "0.533333 0.541176 0.521569 RG 1 w\n" \
4833 "1 J\n" \
4834 "0 j\n" \
4835 "[] 0.0 d\n" \
4836 "4 M 18.5 15.5 m 18.5 13.086 l 16.086 15.5 l 18.5 15.5 l h\n" \
4837 "18.5 15.5 m S\n" \
4838 "7 7 m 10 11 l 13 9 l 18 15 l S\n" \
4839 "0.729412 0.741176 0.713725 RG 7 8 m 10 12 l 13 10 l 18 16 l S\n" \
4840 "18.5 16.5 m 18.5 14.086 l 16.086 16.5 l 18.5 16.5 l h\n" \
4841 "18.5 16.5 m S\n" \
4842 "0.533333 0.541176 0.521569 RG 2 w\n" \
4843 "1 j\n" \
4844 "3 19 m 3 3 l 21 3 l S\n" \
4845 "0.729412 0.741176 0.713725 RG 3 20 m 3 4 l 21 4 l S\n"
4846
4847 #define ANNOT_FILE_ATTACHMENT_AP_TAG \
4848 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
4849 "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" \
4850 "l 1 21.523 2.477 23 4.301 23 c h\n" \
4851 "4.301 23 m f\n" \
4852 "0.533333 0.541176 0.521569 RG 0.999781 w\n" \
4853 "1 J\n" \
4854 "1 j\n" \
4855 "[] 0.0 d\n" \
4856 "4 M q 1 0 0 -1 0 24 cm\n" \
4857 "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" \
4858 "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" \
4859 "8.492 7.879 8.492 8.707 c h\n" \
4860 "8.492 8.707 m S Q\n" \
4861 "2 w\n" \
4862 "20.078 11.414 m 20.891 10.602 20.785 9.293 20.078 8.586 c 14.422 2.93 l\n" \
4863 "13.715 2.223 12.301 2.223 11.594 2.93 c 3.816 10.707 l 3.109 11.414\n" \
4864 "2.402 17.781 3.816 19.195 c 5.23 20.609 11.594 19.902 12.301 19.195 c\n" \
4865 "20.078 11.414 l h\n" \
4866 "20.078 11.414 m S\n" \
4867 "0.729412 0.741176 0.713725 RG 20.078 12.414 m\n" \
4868 "20.891 11.605 20.785 10.293 20.078 9.586 c 14.422 3.93 l\n" \
4869 "13.715 3.223 12.301 3.223 11.594 3.93 c 3.816 11.707 l 3.109 12.414\n" \
4870 "2.402 18.781 3.816 20.195 c 5.23 21.609 11.594 20.902 12.301 20.195 c\n" \
4871 "20.078 12.414 l h\n" \
4872 "20.078 12.414 m S\n" \
4873 "0.533333 0.541176 0.521569 RG 1 w\n" \
4874 "0 j\n" \
4875 "11.949 13.184 m 16.191 8.941 l S\n" \
4876 "0.729412 0.741176 0.713725 RG 11.949 14.184 m 16.191 9.941 l S\n" \
4877 "0.533333 0.541176 0.521569 RG 14.07 6.82 m 9.828 11.062 l S\n" \
4878 "0.729412 0.741176 0.713725 RG 14.07 7.82 m 9.828 12.062 l S\n" \
4879 "0.533333 0.541176 0.521569 RG 6.93 15.141 m 8 20 14.27 20.5 16 20.5 c\n" \
4880 "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" \
4881 "0.729412 0.741176 0.713725 RG 0.999781 w\n" \
4882 "1 j\n" \
4883 "q 1 0 0 -1 0 24 cm\n" \
4884 "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" \
4885 "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" \
4886 "8.492 6.879 8.492 7.707 c h\n" \
4887 "8.492 7.707 m S Q\n" \
4888 "1 w\n" \
4889 "0 j\n" \
4890 "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" \
4891 "19.5 17.699 20.91 17.418 22.5 17.5 c S\n"
4892
draw(Gfx * gfx,GBool printing)4893 void AnnotFileAttachment::draw(Gfx *gfx, GBool printing) {
4894 Object obj;
4895 double ca = 1;
4896
4897 if (!isVisible (printing))
4898 return;
4899
4900 if (appearance.isNull()) {
4901 ca = opacity;
4902
4903 appearBuf = new GooString ();
4904
4905 appearBuf->append ("q\n");
4906 if (color)
4907 setColor(color, gTrue);
4908 else
4909 appearBuf->append ("1 1 1 rg\n");
4910 if (!name->cmp("PushPin"))
4911 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_PUSHPIN);
4912 else if (!name->cmp("Paperclip"))
4913 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP);
4914 else if (!name->cmp("Graph"))
4915 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_GRAPH);
4916 else if (!name->cmp("Tag"))
4917 appearBuf->append (ANNOT_FILE_ATTACHMENT_AP_TAG);
4918 appearBuf->append ("Q\n");
4919
4920 double bbox[4];
4921 bbox[0] = bbox[1] = 0;
4922 bbox[2] = bbox[3] = 24;
4923 if (ca == 1) {
4924 createForm (bbox, gFalse, NULL, &appearance);
4925 } else {
4926 Object aStream;
4927
4928 createForm (bbox, gTrue, NULL, &aStream);
4929 delete appearBuf;
4930
4931 Object resDict;
4932 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
4933 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
4934 createForm(bbox, gFalse, &resDict, &appearance);
4935 }
4936 delete appearBuf;
4937 }
4938
4939 // draw the appearance stream
4940 appearance.fetch(xref, &obj);
4941 gfx->drawAnnot(&obj, border, color,
4942 rect->x1, rect->y1, rect->x2, rect->y2);
4943 obj.free();
4944 }
4945
4946 //------------------------------------------------------------------------
4947 // AnnotSound
4948 //------------------------------------------------------------------------
AnnotSound(XRef * xrefA,PDFRectangle * rect,Sound * soundA,Catalog * catalog)4949 AnnotSound::AnnotSound(XRef *xrefA, PDFRectangle *rect, Sound *soundA, Catalog *catalog) :
4950 AnnotMarkup(xrefA, rect, catalog) {
4951 Object obj1;
4952
4953 type = typeSound;
4954
4955 annotObj.dictSet ("Subtype", obj1.initName ("Sound"));
4956
4957 Object obj2;
4958 Stream *str = soundA->getStream();
4959 obj2.initStream (str);
4960 str->incRef(); //FIXME: initStream should do this?
4961 annotObj.dictSet ("Sound", &obj2);
4962
4963 initialize(xrefA, catalog, annotObj.getDict());
4964 }
4965
AnnotSound(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)4966 AnnotSound::AnnotSound(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
4967 AnnotMarkup(xrefA, dict, catalog, obj) {
4968 type = typeSound;
4969 initialize(xrefA, catalog, dict);
4970 }
4971
~AnnotSound()4972 AnnotSound::~AnnotSound() {
4973 delete sound;
4974
4975 delete name;
4976 }
4977
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)4978 void AnnotSound::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
4979 Object obj1;
4980
4981 sound = Sound::parseSound(dict->lookup("Sound", &obj1));
4982 if (!sound) {
4983 error(-1, "Bad Annot Sound");
4984 ok = gFalse;
4985 }
4986 obj1.free();
4987
4988 if (dict->lookup("Name", &obj1)->isName()) {
4989 name = new GooString(obj1.getName());
4990 } else {
4991 name = new GooString("Speaker");
4992 }
4993 obj1.free();
4994 }
4995
4996 #define ANNOT_SOUND_AP_SPEAKER \
4997 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
4998 "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" \
4999 "l 1 21.523 2.477 23 4.301 23 c h\n" \
5000 "4.301 23 m f\n" \
5001 "0.533333 0.541176 0.521569 RG 2 w\n" \
5002 "0 J\n" \
5003 "1 j\n" \
5004 "[] 0.0 d\n" \
5005 "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" \
5006 "4 14 m S\n" \
5007 "1 w\n" \
5008 "1 J\n" \
5009 "0 j\n" \
5010 "13.699 15.398 m 14.699 13.398 14.699 9.398 13.699 7.398 c S\n" \
5011 "18.199 19.398 m 21.199 17.398 21.199 5.398 18.199 3.398 c S\n" \
5012 "16 17.398 m 18 16.398 18 7.398 16 5.398 c S\n" \
5013 "0.729412 0.741176 0.713725 RG 2 w\n" \
5014 "0 J\n" \
5015 "1 j\n" \
5016 "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" \
5017 "4 15 m S\n" \
5018 "1 w\n" \
5019 "1 J\n" \
5020 "0 j\n" \
5021 "13.699 16 m 14.699 14 14.699 10 13.699 8 c S\n" \
5022 "18.199 20 m 21.199 18 21.199 6 18.199 4 c S\n" \
5023 "16 18 m 18 17 18 8 16 6 c S\n"
5024
5025 #define ANNOT_SOUND_AP_MIC \
5026 "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
5027 "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" \
5028 "l 1 21.523 2.477 23 4.301 23 c h\n" \
5029 "4.301 23 m f\n" \
5030 "0.533333 0.541176 0.521569 RG 2 w\n" \
5031 "1 J\n" \
5032 "0 j\n" \
5033 "[] 0.0 d\n" \
5034 "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" \
5035 "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" \
5036 "20 c h\n" \
5037 "12 20 m S\n" \
5038 "1 w\n" \
5039 "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" \
5040 "8.941 6.5 11.973 c 6.5 14.5 l S\n" \
5041 "2 w\n" \
5042 "0 J\n" \
5043 "12 6.52 m 12 3 l S\n" \
5044 "1 J\n" \
5045 "8 3 m 16 3 l S\n" \
5046 "0.729412 0.741176 0.713725 RG 12 21 m 12 21 l 13.656 21 15 19.656 15 18 c\n" \
5047 "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" \
5048 "9 18 l 9 19.656 10.344 21 12 21 c h\n" \
5049 "12 21 m S\n" \
5050 "1 w\n" \
5051 "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" \
5052 "9.941 6.5 12.973 c 6.5 15.5 l S\n" \
5053 "2 w\n" \
5054 "0 J\n" \
5055 "12 7.52 m 12 4 l S\n" \
5056 "1 J\n" \
5057 "8 4 m 16 4 l S\n"
5058
draw(Gfx * gfx,GBool printing)5059 void AnnotSound::draw(Gfx *gfx, GBool printing) {
5060 Object obj;
5061 double ca = 1;
5062
5063 if (!isVisible (printing))
5064 return;
5065
5066 if (appearance.isNull()) {
5067 ca = opacity;
5068
5069 appearBuf = new GooString ();
5070
5071 appearBuf->append ("q\n");
5072 if (color)
5073 setColor(color, gTrue);
5074 else
5075 appearBuf->append ("1 1 1 rg\n");
5076 if (!name->cmp("Speaker"))
5077 appearBuf->append (ANNOT_SOUND_AP_SPEAKER);
5078 else if (!name->cmp("Mic"))
5079 appearBuf->append (ANNOT_SOUND_AP_MIC);
5080 appearBuf->append ("Q\n");
5081
5082 double bbox[4];
5083 bbox[0] = bbox[1] = 0;
5084 bbox[2] = bbox[3] = 24;
5085 if (ca == 1) {
5086 createForm(bbox, gFalse, NULL, &appearance);
5087 } else {
5088 Object aStream, resDict;
5089
5090 createForm(bbox, gTrue, NULL, &aStream);
5091 delete appearBuf;
5092
5093 appearBuf = new GooString ("/GS0 gs\n/Fm0 Do");
5094 createResourcesDict("Fm0", &aStream, "GS0", ca, NULL, &resDict);
5095 createForm(bbox, gFalse, &resDict, &appearance);
5096 }
5097 delete appearBuf;
5098 }
5099
5100 // draw the appearance stream
5101 appearance.fetch(xref, &obj);
5102 gfx->drawAnnot(&obj, border, color,
5103 rect->x1, rect->y1, rect->x2, rect->y2);
5104 obj.free();
5105 }
5106
5107 //------------------------------------------------------------------------
5108 // Annot3D
5109 //------------------------------------------------------------------------
Annot3D(XRef * xrefA,PDFRectangle * rect,Catalog * catalog)5110 Annot3D::Annot3D(XRef *xrefA, PDFRectangle *rect, Catalog *catalog) :
5111 Annot(xrefA, rect, catalog) {
5112 Object obj1;
5113
5114 type = type3D;
5115
5116 annotObj.dictSet ("Subtype", obj1.initName ("3D"));
5117
5118 initialize(xrefA, catalog, annotObj.getDict());
5119 }
5120
Annot3D(XRef * xrefA,Dict * dict,Catalog * catalog,Object * obj)5121 Annot3D::Annot3D(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
5122 Annot(xrefA, dict, catalog, obj) {
5123 type = type3D;
5124 initialize(xrefA, catalog, dict);
5125 }
5126
~Annot3D()5127 Annot3D::~Annot3D() {
5128 if (activation)
5129 delete activation;
5130 }
5131
initialize(XRef * xrefA,Catalog * catalog,Dict * dict)5132 void Annot3D::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
5133 Object obj1;
5134
5135 if (dict->lookup("3DA", &obj1)->isDict()) {
5136 activation = new Activation(obj1.getDict());
5137 } else {
5138 activation = NULL;
5139 }
5140 obj1.free();
5141 }
5142
Activation(Dict * dict)5143 Annot3D::Activation::Activation(Dict *dict) {
5144 Object obj1;
5145
5146 if (dict->lookup("A", &obj1)->isName()) {
5147 GooString *name = new GooString(obj1.getName());
5148
5149 if(!name->cmp("PO")) {
5150 aTrigger = aTriggerPageOpened;
5151 } else if(!name->cmp("PV")) {
5152 aTrigger = aTriggerPageVisible;
5153 } else if(!name->cmp("XA")) {
5154 aTrigger = aTriggerUserAction;
5155 } else {
5156 aTrigger = aTriggerUnknown;
5157 }
5158 delete name;
5159 } else {
5160 aTrigger = aTriggerUnknown;
5161 }
5162 obj1.free();
5163
5164 if(dict->lookup("AIS", &obj1)->isName()) {
5165 GooString *name = new GooString(obj1.getName());
5166
5167 if(!name->cmp("I")) {
5168 aState = aStateEnabled;
5169 } else if(!name->cmp("L")) {
5170 aState = aStateDisabled;
5171 } else {
5172 aState = aStateUnknown;
5173 }
5174 delete name;
5175 } else {
5176 aState = aStateUnknown;
5177 }
5178 obj1.free();
5179
5180 if(dict->lookup("D", &obj1)->isName()) {
5181 GooString *name = new GooString(obj1.getName());
5182
5183 if(!name->cmp("PC")) {
5184 dTrigger = dTriggerPageClosed;
5185 } else if(!name->cmp("PI")) {
5186 dTrigger = dTriggerPageInvisible;
5187 } else if(!name->cmp("XD")) {
5188 dTrigger = dTriggerUserAction;
5189 } else {
5190 dTrigger = dTriggerUnknown;
5191 }
5192 delete name;
5193 } else {
5194 dTrigger = dTriggerUnknown;
5195 }
5196 obj1.free();
5197
5198 if(dict->lookup("DIS", &obj1)->isName()) {
5199 GooString *name = new GooString(obj1.getName());
5200
5201 if(!name->cmp("U")) {
5202 dState = dStateUninstantiaded;
5203 } else if(!name->cmp("I")) {
5204 dState = dStateInstantiated;
5205 } else if(!name->cmp("L")) {
5206 dState = dStateLive;
5207 } else {
5208 dState = dStateUnknown;
5209 }
5210 delete name;
5211 } else {
5212 dState = dStateUnknown;
5213 }
5214 obj1.free();
5215
5216 if (dict->lookup("TB", &obj1)->isBool()) {
5217 displayToolbar = obj1.getBool();
5218 } else {
5219 displayToolbar = gTrue;
5220 }
5221 obj1.free();
5222
5223 if (dict->lookup("NP", &obj1)->isBool()) {
5224 displayNavigation = obj1.getBool();
5225 } else {
5226 displayNavigation = gFalse;
5227 }
5228 obj1.free();
5229 }
5230
5231 //------------------------------------------------------------------------
5232 // Annots
5233 //------------------------------------------------------------------------
5234
Annots(XRef * xref,Catalog * catalog,Object * annotsObj)5235 Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
5236 Annot *annot;
5237 Object obj1;
5238 int size;
5239 int i;
5240
5241 annots = NULL;
5242 size = 0;
5243 nAnnots = 0;
5244
5245 if (annotsObj->isArray()) {
5246 for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
5247 //get the Ref to this annot and pass it to Annot constructor
5248 //this way, it'll be possible for the annot to retrieve the corresponding
5249 //form widget
5250 Object obj2;
5251 if (annotsObj->arrayGet(i, &obj1)->isDict()) {
5252 annotsObj->arrayGetNF(i, &obj2);
5253 annot = createAnnot (xref, obj1.getDict(), catalog, &obj2);
5254 if (annot && annot->isOk()) {
5255 if (nAnnots >= size) {
5256 size += 16;
5257 annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
5258 }
5259 annots[nAnnots++] = annot;
5260 } else {
5261 delete annot;
5262 }
5263 }
5264 obj2.free();
5265 obj1.free();
5266 }
5267 }
5268 }
5269
createAnnot(XRef * xref,Dict * dict,Catalog * catalog,Object * obj)5270 Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj) {
5271 Annot *annot;
5272 Object obj1;
5273
5274 if (dict->lookup("Subtype", &obj1)->isName()) {
5275 GooString *typeName = new GooString(obj1.getName());
5276
5277 if (!typeName->cmp("Text")) {
5278 annot = new AnnotText(xref, dict, catalog, obj);
5279 } else if (!typeName->cmp("Link")) {
5280 annot = new AnnotLink(xref, dict, catalog, obj);
5281 } else if (!typeName->cmp("FreeText")) {
5282 annot = new AnnotFreeText(xref, dict, catalog, obj);
5283 } else if (!typeName->cmp("Line")) {
5284 annot = new AnnotLine(xref, dict, catalog, obj);
5285 } else if (!typeName->cmp("Square")) {
5286 annot = new AnnotGeometry(xref, dict, catalog, obj);
5287 } else if (!typeName->cmp("Circle")) {
5288 annot = new AnnotGeometry(xref, dict, catalog, obj);
5289 } else if (!typeName->cmp("Polygon")) {
5290 annot = new AnnotPolygon(xref, dict, catalog, obj);
5291 } else if (!typeName->cmp("PolyLine")) {
5292 annot = new AnnotPolygon(xref, dict, catalog, obj);
5293 } else if (!typeName->cmp("Highlight")) {
5294 annot = new AnnotTextMarkup(xref, dict, catalog, obj);
5295 } else if (!typeName->cmp("Underline")) {
5296 annot = new AnnotTextMarkup(xref, dict, catalog, obj);
5297 } else if (!typeName->cmp("Squiggly")) {
5298 annot = new AnnotTextMarkup(xref, dict, catalog, obj);
5299 } else if (!typeName->cmp("StrikeOut")) {
5300 annot = new AnnotTextMarkup(xref, dict, catalog, obj);
5301 } else if (!typeName->cmp("Stamp")) {
5302 annot = new AnnotStamp(xref, dict, catalog, obj);
5303 } else if (!typeName->cmp("Caret")) {
5304 annot = new AnnotCaret(xref, dict, catalog, obj);
5305 } else if (!typeName->cmp("Ink")) {
5306 annot = new AnnotInk(xref, dict, catalog, obj);
5307 } else if (!typeName->cmp("FileAttachment")) {
5308 annot = new AnnotFileAttachment(xref, dict, catalog, obj);
5309 } else if (!typeName->cmp("Sound")) {
5310 annot = new AnnotSound(xref, dict, catalog, obj);
5311 } else if(!typeName->cmp("Movie")) {
5312 annot = new AnnotMovie(xref, dict, catalog, obj);
5313 } else if(!typeName->cmp("Widget")) {
5314 annot = new AnnotWidget(xref, dict, catalog, obj);
5315 } else if(!typeName->cmp("Screen")) {
5316 annot = new AnnotScreen(xref, dict, catalog, obj);
5317 } else if(!typeName->cmp("PrinterMark")) {
5318 annot = new Annot(xref, dict, catalog, obj);
5319 } else if (!typeName->cmp("TrapNet")) {
5320 annot = new Annot(xref, dict, catalog, obj);
5321 } else if (!typeName->cmp("Watermark")) {
5322 annot = new Annot(xref, dict, catalog, obj);
5323 } else if (!typeName->cmp("3D")) {
5324 annot = new Annot3D(xref, dict, catalog, obj);
5325 } else if (!typeName->cmp("Popup")) {
5326 /* Popup annots are already handled by markup annots
5327 * Here we only care about popup annots without a
5328 * markup annotation associated
5329 */
5330 Object obj2;
5331
5332 if (dict->lookup("Parent", &obj2)->isNull())
5333 annot = new AnnotPopup(xref, dict, catalog, obj);
5334 else
5335 annot = NULL;
5336
5337 obj2.free();
5338 } else {
5339 annot = new Annot(xref, dict, catalog, obj);
5340 }
5341
5342 delete typeName;
5343 } else {
5344 annot = NULL;
5345 }
5346 obj1.free();
5347
5348 return annot;
5349 }
5350
findAnnot(Ref * ref)5351 Annot *Annots::findAnnot(Ref *ref) {
5352 int i;
5353
5354 for (i = 0; i < nAnnots; ++i) {
5355 if (annots[i]->match(ref)) {
5356 return annots[i];
5357 }
5358 }
5359 return NULL;
5360 }
5361
5362
~Annots()5363 Annots::~Annots() {
5364 int i;
5365
5366 for (i = 0; i < nAnnots; ++i) {
5367 delete annots[i];
5368 }
5369 gfree(annots);
5370 }
5371