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