w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Annot.cc
Go to the documentation of this file.
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-2020 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 //
52 // To see a description of the changes please see the Changelog file that
53 // came with your tarball or type make ChangeLog if you are building from git
54 //
55 //========================================================================
56 
57 #include <config.h>
58 
59 #include <cstdlib>
60 #include <cmath>
61 #include <cassert>
62 #include "goo/gmem.h"
63 #include "goo/gstrtod.h"
64 #include "Error.h"
65 #include "Object.h"
66 #include "Catalog.h"
67 #include "Gfx.h"
68 #include "Lexer.h"
69 #include "PDFDoc.h"
70 #include "Page.h"
71 #include "Annot.h"
72 #include "GfxFont.h"
73 #include "CharCodeToUnicode.h"
74 #include "PDFDocEncoding.h"
75 #include "Form.h"
76 #include "Error.h"
77 #include "XRef.h"
78 #include "Movie.h"
79 #include "OptionalContent.h"
80 #include "Sound.h"
81 #include "FileSpec.h"
82 #include "DateInfo.h"
83 #include "Link.h"
84 #include <cstring>
85 #include <algorithm>
86 
87 #ifndef M_PI
88 # define M_PI 3.14159265358979323846
89 #endif
90 
91 #define fieldFlagReadOnly 0x00000001
92 #define fieldFlagRequired 0x00000002
93 #define fieldFlagNoExport 0x00000004
94 #define fieldFlagMultiline 0x00001000
95 #define fieldFlagPassword 0x00002000
96 #define fieldFlagNoToggleToOff 0x00004000
97 #define fieldFlagRadio 0x00008000
98 #define fieldFlagPushbutton 0x00010000
99 #define fieldFlagCombo 0x00020000
100 #define fieldFlagEdit 0x00040000
101 #define fieldFlagSort 0x00080000
102 #define fieldFlagFileSelect 0x00100000
103 #define fieldFlagMultiSelect 0x00200000
104 #define fieldFlagDoNotSpellCheck 0x00400000
105 #define fieldFlagDoNotScroll 0x00800000
106 #define fieldFlagComb 0x01000000
107 #define fieldFlagRichText 0x02000000
108 #define fieldFlagRadiosInUnison 0x02000000
109 #define fieldFlagCommitOnSelChange 0x04000000
110 
111 #define fieldQuadLeft 0
112 #define fieldQuadCenter 1
113 #define fieldQuadRight 2
114 
115 // distance of Bezier control point from center for circle approximation
116 // = (4 * (sqrt(2) - 1) / 3) * r
117 #define bezierCircle 0.55228475
118 
120 {
121  if (string != nullptr) {
122  if (!string->cmp("Square")) {
123  return annotLineEndingSquare;
124  } else if (!string->cmp("Circle")) {
125  return annotLineEndingCircle;
126  } else if (!string->cmp("Diamond")) {
127  return annotLineEndingDiamond;
128  } else if (!string->cmp("OpenArrow")) {
130  } else if (!string->cmp("ClosedArrow")) {
132  } else if (!string->cmp("Butt")) {
133  return annotLineEndingButt;
134  } else if (!string->cmp("ROpenArrow")) {
136  } else if (!string->cmp("RClosedArrow")) {
138  } else if (!string->cmp("Slash")) {
139  return annotLineEndingSlash;
140  } else {
141  return annotLineEndingNone;
142  }
143  } else {
144  return annotLineEndingNone;
145  }
146 }
147 
149 {
150  switch (style) {
152  return "Square";
154  return "Circle";
156  return "Diamond";
158  return "OpenArrow";
160  return "ClosedArrow";
161  case annotLineEndingButt:
162  return "Butt";
164  return "ROpenArrow";
166  return "RClosedArrow";
168  return "Slash";
169  default:
170  return "None";
171  }
172 }
173 
175 {
177 
178  Object obj1 = dict->lookup("Subtype");
179  if (obj1.isName()) {
180  const char *typeName = obj1.getName();
181 
182  if (!strcmp(typeName, "Markup3D")) {
184  } else {
186  }
187  } else {
189  }
190 
191  return type;
192 }
193 
194 static std::unique_ptr<PDFRectangle> parseDiffRectangle(Array *array, PDFRectangle *rect)
195 {
196  if (array->getLength() == 4) {
197  // deltas
198  const double dx1 = array->get(0).getNumWithDefaultValue(0);
199  const double dy1 = array->get(1).getNumWithDefaultValue(0);
200  const double dx2 = array->get(2).getNumWithDefaultValue(0);
201  const double dy2 = array->get(3).getNumWithDefaultValue(0);
202 
203  // checking that the numbers are valid (i.e. >= 0),
204  // and that applying the differences still give us a valid rect
205  if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2 && (rect->x2 - rect->x1 - dx1 - dx2) >= 0 && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
206  auto newRect = std::make_unique<PDFRectangle>();
207  newRect->x1 = rect->x1 + dx1;
208  newRect->y1 = rect->y1 + dy1;
209  newRect->x2 = rect->x2 - dx2;
210  newRect->y2 = rect->y2 - dy2;
211  return newRect;
212  }
213  }
214  return nullptr;
215 }
216 
217 static std::unique_ptr<LinkAction> getAdditionalAction(Annot::AdditionalActionsType type, Object *additionalActions, PDFDoc *doc)
218 {
219  Object additionalActionsObject = additionalActions->fetch(doc->getXRef());
220 
221  if (additionalActionsObject.isDict()) {
222  const char *key = (type == Annot::actionCursorEntering ? "E"
226  : type == Annot::actionFocusIn ? "Fo"
227  : type == Annot::actionFocusOut ? "Bl"
228  : type == Annot::actionPageOpening ? "PO"
229  : type == Annot::actionPageClosing ? "PC"
230  : type == Annot::actionPageVisible ? "PV"
231  : type == Annot::actionPageInvisible ? "PI"
232  : nullptr);
233 
234  Object actionObject = additionalActionsObject.dictLookup(key);
235  if (actionObject.isDict())
236  return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
237  }
238 
239  return nullptr;
240 }
241 
243 {
245 }
246 
247 //------------------------------------------------------------------------
248 // AnnotBorderEffect
249 //------------------------------------------------------------------------
250 
252 {
253  Object obj1;
254 
255  obj1 = dict->lookup("S");
256  if (obj1.isName()) {
257  const char *effectName = obj1.getName();
258 
259  if (!strcmp(effectName, "C"))
261  else
263  } else {
265  }
266 
268  intensity = dict->lookup("I").getNumWithDefaultValue(0);
269  } else {
270  intensity = 0;
271  }
272 }
273 
274 //------------------------------------------------------------------------
275 // AnnotPath
276 //------------------------------------------------------------------------
277 
278 AnnotPath::AnnotPath() = default;
279 
281 {
283 }
284 
285 AnnotPath::AnnotPath(std::vector<AnnotCoord> &&coordsA)
286 {
287  coords = std::move(coordsA);
288 }
289 
290 AnnotPath::~AnnotPath() = default;
291 
292 double AnnotPath::getX(int coord) const
293 {
294  if (coord >= 0 && coord < getCoordsLength())
295  return coords[coord].getX();
296  return 0;
297 }
298 
299 double AnnotPath::getY(int coord) const
300 {
301  if (coord >= 0 && coord < getCoordsLength())
302  return coords[coord].getY();
303  return 0;
304 }
305 
307 {
308  if (coord >= 0 && coord < getCoordsLength())
309  return &coords[coord];
310  return nullptr;
311 }
312 
314 {
315  if (array->getLength() % 2) {
316  error(errSyntaxError, -1, "Bad Annot Path");
317  return;
318  }
319 
320  const auto tempLength = array->getLength() / 2;
321  std::vector<AnnotCoord> tempCoords;
322  tempCoords.reserve(tempLength);
323  for (int i = 0; i < tempLength; i++) {
324  double x = 0, y = 0;
325 
326  Object obj1 = array->get(i * 2);
327  if (obj1.isNum()) {
328  x = obj1.getNum();
329  } else {
330  return;
331  }
332 
333  obj1 = array->get((i * 2) + 1);
334  if (obj1.isNum()) {
335  y = obj1.getNum();
336  } else {
337  return;
338  }
339 
340  tempCoords.emplace_back(x, y);
341  }
342 
343  coords = std::move(tempCoords);
344 }
345 
346 //------------------------------------------------------------------------
347 // AnnotCalloutLine
348 //------------------------------------------------------------------------
349 
350 AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2) : coord1(x1, y1), coord2(x2, y2) { }
351 
353 
354 //------------------------------------------------------------------------
355 // AnnotCalloutMultiLine
356 //------------------------------------------------------------------------
357 
358 AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2, double y2, double x3, double y3) : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) { }
359 
361 
362 //------------------------------------------------------------------------
363 // AnnotQuadrilateral
364 //------------------------------------------------------------------------
365 
367 {
368  int arrayLength = array->getLength();
369  int quadsLength = 0;
370  double quadArray[8];
371 
372  // default values
374 
375  if ((arrayLength % 8) == 0) {
376  int i;
377 
378  quadsLength = arrayLength / 8;
379  auto quads = std::make_unique<AnnotQuadrilateral[]>(quadsLength);
380  for (i = 0; i < quadsLength; i++) {
381  for (int j = 0; j < 8; j++) {
382  Object obj = array->get(i * 8 + j);
383  if (obj.isNum()) {
384  quadArray[j] = obj.getNum();
385  } else {
386  error(errSyntaxError, -1, "Invalid QuadPoint in annot");
387  return;
388  }
389  }
390 
391  quads[i] = AnnotQuadrilateral(quadArray[0], quadArray[1], quadArray[2], quadArray[3], quadArray[4], quadArray[5], quadArray[6], quadArray[7]);
392  }
393 
394  quadrilateralsLength = quadsLength;
395  quadrilaterals = std::move(quads);
396  }
397 }
398 
399 AnnotQuadrilaterals::AnnotQuadrilaterals(std::unique_ptr<AnnotQuadrilateral[]> &&quads, int quadsLength)
400 {
401  quadrilaterals = std::move(quads);
402  quadrilateralsLength = quadsLength;
403 }
404 
406 
407 double AnnotQuadrilaterals::getX1(int quadrilateral)
408 {
409  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
410  return quadrilaterals[quadrilateral].coord1.getX();
411  return 0;
412 }
413 
414 double AnnotQuadrilaterals::getY1(int quadrilateral)
415 {
416  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
417  return quadrilaterals[quadrilateral].coord1.getY();
418  return 0;
419 }
420 
421 double AnnotQuadrilaterals::getX2(int quadrilateral)
422 {
423  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
424  return quadrilaterals[quadrilateral].coord2.getX();
425  return 0;
426 }
427 
428 double AnnotQuadrilaterals::getY2(int quadrilateral)
429 {
430  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
431  return quadrilaterals[quadrilateral].coord2.getY();
432  return 0;
433 }
434 
435 double AnnotQuadrilaterals::getX3(int quadrilateral)
436 {
437  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
438  return quadrilaterals[quadrilateral].coord3.getX();
439  return 0;
440 }
441 
442 double AnnotQuadrilaterals::getY3(int quadrilateral)
443 {
444  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
445  return quadrilaterals[quadrilateral].coord3.getY();
446  return 0;
447 }
448 
449 double AnnotQuadrilaterals::getX4(int quadrilateral)
450 {
451  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
452  return quadrilaterals[quadrilateral].coord4.getX();
453  return 0;
454 }
455 
456 double AnnotQuadrilaterals::getY4(int quadrilateral)
457 {
458  if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength)
459  return quadrilaterals[quadrilateral].coord4.getY();
460  return 0;
461 }
462 
464 
465 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) { }
466 
467 //------------------------------------------------------------------------
468 // AnnotBorder
469 //------------------------------------------------------------------------
471 {
472  width = 1;
473  dashLength = 0;
474  dash = nullptr;
475  style = borderSolid;
476 }
477 
479 {
480  bool correct = true;
481  const int tempLength = dashObj->arrayGetLength();
482  double *tempDash = (double *)gmallocn(tempLength, sizeof(double));
483 
484  // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
485  for (int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
486  const Object obj1 = dashObj->arrayGet(i);
487  if (obj1.isNum()) {
488  tempDash[i] = obj1.getNum();
489 
490  correct = tempDash[i] >= 0;
491  } else {
492  correct = false;
493  }
494  }
495 
496  if (correct) {
497  dashLength = tempLength;
498  dash = tempDash;
499  style = borderDashed;
500  } else {
501  gfree(tempDash);
502  }
503 
504  return correct;
505 }
506 
508 {
509  if (dash)
510  gfree(dash);
511 }
512 
513 //------------------------------------------------------------------------
514 // AnnotBorderArray
515 //------------------------------------------------------------------------
516 
518 {
519  horizontalCorner = 0;
520  verticalCorner = 0;
521 }
522 
524 {
525  Object obj1;
526  int arrayLength = array->getLength();
527 
528  bool correct = true;
529  if (arrayLength == 3 || arrayLength == 4) {
530  // implementation note 81 in Appendix H.
531 
532  obj1 = array->get(0);
533  if (obj1.isNum())
534  horizontalCorner = obj1.getNum();
535  else
536  correct = false;
537 
538  obj1 = array->get(1);
539  if (obj1.isNum())
540  verticalCorner = obj1.getNum();
541  else
542  correct = false;
543 
544  obj1 = array->get(2);
545  if (obj1.isNum())
546  width = obj1.getNum();
547  else
548  correct = false;
549 
550  if (arrayLength == 4) {
551  obj1 = array->get(3);
552  if (obj1.isArray())
553  correct = parseDashArray(&obj1);
554  else
555  correct = false;
556  }
557  } else {
558  correct = false;
559  }
560 
561  if (!correct) {
562  width = 0;
563  }
564 }
565 
567 {
568  Array *borderArray = new Array(xref);
569  borderArray->add(Object(horizontalCorner));
570  borderArray->add(Object(verticalCorner));
571  borderArray->add(Object(width));
572 
573  if (dashLength > 0) {
574  Array *a = new Array(xref);
575 
576  for (int i = 0; i < dashLength; i++)
577  a->add(Object(dash[i]));
578 
579  borderArray->add(Object(a));
580  }
581 
582  return Object(borderArray);
583 }
584 
585 //------------------------------------------------------------------------
586 // AnnotBorderBS
587 //------------------------------------------------------------------------
588 
590 
592 {
593  // Border width (in points)
594  Object obj1 = dict->lookup("W");
595  width = obj1.getNumWithDefaultValue(1.0);
596 
597  // Border style
598  obj1 = dict->lookup("S");
599  if (obj1.isName()) {
600  const char *styleName = obj1.getName();
601 
602  if (!strcmp(styleName, "S")) {
603  style = borderSolid;
604  } else if (!strcmp(styleName, "D")) {
605  style = borderDashed;
606  } else if (!strcmp(styleName, "B")) {
607  style = borderBeveled;
608  } else if (!strcmp(styleName, "I")) {
609  style = borderInset;
610  } else if (!strcmp(styleName, "U")) {
611  style = borderUnderlined;
612  } else {
613  style = borderSolid;
614  }
615  } else {
616  style = borderSolid;
617  }
618 
619  // Border dash style
620  if (style == borderDashed) {
621  obj1 = dict->lookup("D");
622  if (obj1.isArray())
623  parseDashArray(&obj1);
624 
625  if (!dash) {
626  dashLength = 1;
627  dash = (double *)gmallocn(dashLength, sizeof(double));
628  dash[0] = 3;
629  }
630  }
631 }
632 
633 const char *AnnotBorderBS::getStyleName() const
634 {
635  switch (style) {
636  case borderSolid:
637  return "S";
638  case borderDashed:
639  return "D";
640  case borderBeveled:
641  return "B";
642  case borderInset:
643  return "I";
644  case borderUnderlined:
645  return "U";
646  }
647 
648  return "S";
649 }
650 
652 {
653  Dict *dict = new Dict(xref);
654  dict->set("W", Object(width));
655  dict->set("S", Object(objName, getStyleName()));
656  if (style == borderDashed && dashLength > 0) {
657  Array *a = new Array(xref);
658 
659  for (int i = 0; i < dashLength; i++)
660  a->add(Object(dash[i]));
661  dict->set("D", Object(a));
662  }
663  return Object(dict);
664 }
665 
666 //------------------------------------------------------------------------
667 // AnnotColor
668 //------------------------------------------------------------------------
669 
671 {
672  length = 0;
673 }
674 
676 {
677  length = 1;
678 
679  values[0] = gray;
680 }
681 
682 AnnotColor::AnnotColor(double r, double g, double b)
683 {
684  length = 3;
685 
686  values[0] = r;
687  values[1] = g;
688  values[2] = b;
689 }
690 
691 AnnotColor::AnnotColor(double c, double m, double y, double k)
692 {
693  length = 4;
694 
695  values[0] = c;
696  values[1] = m;
697  values[2] = y;
698  values[3] = k;
699 }
700 
701 // If <adjust> is +1, color is brightened;
702 // if <adjust> is -1, color is darkened;
703 // otherwise color is not modified.
705 {
706  int i;
707 
708  length = array->getLength();
709  if (length > 4)
710  length = 4;
711 
712  for (i = 0; i < length; i++) {
713  Object obj1 = array->get(i);
714  if (obj1.isNum()) {
715  values[i] = obj1.getNum();
716 
717  if (values[i] < 0 || values[i] > 1)
718  values[i] = 0;
719  } else {
720  values[i] = 0;
721  }
722  }
723 
724  if (adjust != 0)
725  adjustColor(adjust);
726 }
727 
729 {
730  int i;
731 
732  if (length == 4) {
733  adjust = -adjust;
734  }
735  if (adjust > 0) {
736  for (i = 0; i < length; ++i) {
737  values[i] = 0.5 * values[i] + 0.5;
738  }
739  } else if (adjust < 0) {
740  for (i = 0; i < length; ++i) {
741  values[i] = 0.5 * values[i];
742  }
743  }
744 }
745 
747 {
748  if (length == 0) {
749  return Object(objNull); // Transparent (no color)
750  } else {
751  Array *a = new Array(xref);
752  for (int i = 0; i < length; ++i)
753  a->add(Object(values[i]));
754  return Object(a);
755  }
756 }
757 
758 //------------------------------------------------------------------------
759 // DefaultAppearance
760 //------------------------------------------------------------------------
761 
762 DefaultAppearance::DefaultAppearance(Object &&fontNameA, double fontPtSizeA, std::unique_ptr<AnnotColor> fontColorA) : fontName(std::move(fontNameA)), fontPtSize(fontPtSizeA), fontColor(std::move(fontColorA)) { }
763 
765 {
766  fontPtSize = -1;
767 
768  if (da) {
769  std::vector<GooString *> *daToks = new std::vector<GooString *>();
770  int i = FormFieldText::tokenizeDA(da, daToks, "Tf");
771 
772  if (i >= 1) {
773  fontPtSize = gatof((*daToks)[i - 1]->c_str());
774  }
775  if (i >= 2) {
776  // We are expecting a name, therefore the first letter should be '/'.
777  const GooString *fontToken = (*daToks)[i - 2];
778  if (fontToken && fontToken->getLength() > 1 && fontToken->getChar(0) == '/') {
779  // The +1 is here to skip the leading '/'.
780  fontName = Object(objName, fontToken->c_str() + 1);
781  }
782  }
783  // Scan backwards: we are looking for the last set value
784  for (i = daToks->size() - 1; i >= 0; --i) {
785  if (!fontColor) {
786  if (!((*daToks)[i])->cmp("g") && i >= 1) {
787  fontColor = std::make_unique<AnnotColor>(gatof(((*daToks)[i - 1])->c_str()));
788  } else if (!((*daToks)[i])->cmp("rg") && i >= 3) {
789  fontColor = std::make_unique<AnnotColor>(gatof(((*daToks)[i - 3])->c_str()), gatof(((*daToks)[i - 2])->c_str()), gatof(((*daToks)[i - 1])->c_str()));
790  } else if (!((*daToks)[i])->cmp("k") && i >= 4) {
791  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()));
792  }
793  }
794  }
795  for (auto entry : *daToks) {
796  delete entry;
797  }
798  delete daToks;
799  }
800 }
801 
803 {
804  fontName = std::move(fontNameA);
805 }
806 
807 void DefaultAppearance::setFontPtSize(double fontPtSizeA)
808 {
809  fontPtSize = fontPtSizeA;
810 }
811 
812 void DefaultAppearance::setFontColor(std::unique_ptr<AnnotColor> fontColorA)
813 {
814  fontColor = std::move(fontColorA);
815 }
816 
818 {
819  AnnotAppearanceBuilder appearBuilder;
820  if (fontColor) {
821  appearBuilder.setDrawColor(fontColor.get(), true);
822  }
823  appearBuilder.setTextFont(fontName, fontPtSize);
824  return appearBuilder.buffer()->copy();
825 }
826 
827 //------------------------------------------------------------------------
828 // AnnotIconFit
829 //------------------------------------------------------------------------
830 
832 {
833  Object obj1;
834 
835  obj1 = dict->lookup("SW");
836  if (obj1.isName()) {
837  const char *scaleName = obj1.getName();
838 
839  if (!strcmp(scaleName, "B")) {
841  } else if (!strcmp(scaleName, "S")) {
843  } else if (!strcmp(scaleName, "N")) {
845  } else {
847  }
848  } else {
850  }
851 
852  obj1 = dict->lookup("S");
853  if (obj1.isName()) {
854  const char *scaleName = obj1.getName();
855 
856  if (!strcmp(scaleName, "A")) {
858  } else {
860  }
861  } else {
863  }
864 
865  obj1 = dict->lookup("A");
866  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
867  left = obj1.arrayGet(0).getNumWithDefaultValue(0);
869 
870  if (left < 0 || left > 1)
871  left = 0.5;
872 
873  if (bottom < 0 || bottom > 1)
874  bottom = 0.5;
875 
876  } else {
877  left = bottom = 0.5;
878  }
879 
880  fullyBounds = dict->lookup("FB").getBoolWithDefaultValue(false);
881 }
882 
883 //------------------------------------------------------------------------
884 // AnnotAppearance
885 //------------------------------------------------------------------------
886 
888 {
889  assert(dict->isDict());
890  doc = docA;
891  appearDict = dict->copy();
892 }
893 
895 
897 {
898  Object apData;
899 
900  // Obtain dictionary or stream associated to appearance type
901  switch (type) {
902  case appearRollover:
903  apData = appearDict.dictLookupNF("R").copy();
904  if (apData.isNull())
905  apData = appearDict.dictLookupNF("N").copy();
906  break;
907  case appearDown:
908  apData = appearDict.dictLookupNF("D").copy();
909  if (apData.isNull())
910  apData = appearDict.dictLookupNF("N").copy();
911  break;
912  case appearNormal:
913  apData = appearDict.dictLookupNF("N").copy();
914  break;
915  }
916 
917  if (apData.isDict() && state)
918  return apData.dictLookupNF(state).copy();
919  else if (apData.isRef())
920  return apData;
921 
922  return Object();
923 }
924 
925 std::unique_ptr<GooString> AnnotAppearance::getStateKey(int i)
926 {
927  const Object &obj1 = appearDict.dictLookupNF("N");
928  if (obj1.isDict())
929  return std::make_unique<GooString>(obj1.dictGetKey(i));
930  return nullptr;
931 }
932 
934 {
935  int res = 0;
936  const Object &obj1 = appearDict.dictLookupNF("N");
937  if (obj1.isDict())
938  res = obj1.dictGetLength();
939  return res;
940 }
941 
942 // Test if stateObj (a Ref or a Dict) points to the specified stream
943 bool AnnotAppearance::referencesStream(const Object *stateObj, Ref refToStream)
944 {
945  if (stateObj->isRef()) {
946  const Ref r = stateObj->getRef();
947  if (r == refToStream) {
948  return true;
949  }
950  } else if (stateObj->isDict()) { // Test each value
951  const int size = stateObj->dictGetLength();
952  for (int i = 0; i < size; ++i) {
953  const Object &obj1 = stateObj->dictGetValNF(i);
954  if (obj1.isRef()) {
955  const Ref r = obj1.getRef();
956  if (r == refToStream) {
957  return true;
958  }
959  }
960  }
961  }
962  return false; // Not found
963 }
964 
965 // Test if this AnnotAppearance references the specified stream
967 {
968  bool found;
969 
970  // Scan each state's ref/subdictionary
971  const Object &objN = appearDict.dictLookupNF("N");
972  found = referencesStream(&objN, refToStream);
973  if (found)
974  return true;
975 
976  const Object &objR = appearDict.dictLookupNF("R");
977  found = referencesStream(&objR, refToStream);
978  if (found)
979  return true;
980 
981  const Object &objD = appearDict.dictLookupNF("D");
982  found = referencesStream(&objD, refToStream);
983  return found;
984 }
985 
986 // If this is the only annotation in the document that references the
987 // specified appearance stream, remove the appearance stream
989 {
990  const int lastpage = doc->getNumPages();
991  for (int pg = 1; pg <= lastpage; ++pg) { // Scan all annotations in the document
992  Page *page = doc->getPage(pg);
993  if (!page) {
994  error(errSyntaxError, -1, "Failed check for shared annotation stream at page {0:d}", pg);
995  continue;
996  }
997  Annots *annots = page->getAnnots();
998  for (int i = 0; i < annots->getNumAnnots(); ++i) {
999  AnnotAppearance *annotAp = annots->getAnnot(i)->getAppearStreams();
1000  if (annotAp && annotAp != this && annotAp->referencesStream(refToStream)) {
1001  return; // Another annotation points to the stream -> Don't delete it
1002  }
1003  }
1004  }
1005 
1006  // TODO: stream resources (e.g. font), AP name tree
1007  doc->getXRef()->removeIndirectObject(refToStream);
1008 }
1009 
1010 // Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict
1012 {
1013  if (state->isRef()) {
1014  removeStream(state->getRef());
1015  } else if (state->isDict()) {
1016  const int size = state->dictGetLength();
1017  for (int i = 0; i < size; ++i) {
1018  const Object &obj2 = state->dictGetValNF(i);
1019  if (obj2.isRef()) {
1020  removeStream(obj2.getRef());
1021  }
1022  }
1023  }
1024 }
1025 
1027 {
1028  const Object &objN = appearDict.dictLookupNF("N");
1029  removeStateStreams(&objN);
1030  const Object &objR = appearDict.dictLookupNF("R");
1031  removeStateStreams(&objR);
1032  const Object &objD = appearDict.dictLookupNF("D");
1033  removeStateStreams(&objD);
1034 }
1035 
1036 //------------------------------------------------------------------------
1037 // AnnotAppearanceCharacs
1038 //------------------------------------------------------------------------
1039 
1041 {
1042  Object obj1;
1043 
1044  obj1 = dict->lookup("R");
1045  if (obj1.isInt()) {
1046  rotation = obj1.getInt();
1047  } else {
1048  rotation = 0;
1049  }
1050 
1051  obj1 = dict->lookup("BC");
1052  if (obj1.isArray()) {
1053  Array *colorComponents = obj1.getArray();
1054  if (colorComponents->getLength() > 0) {
1055  borderColor = std::make_unique<AnnotColor>(colorComponents);
1056  }
1057  }
1058 
1059  obj1 = dict->lookup("BG");
1060  if (obj1.isArray()) {
1061  Array *colorComponents = obj1.getArray();
1062  if (colorComponents->getLength() > 0) {
1063  backColor = std::make_unique<AnnotColor>(colorComponents);
1064  }
1065  }
1066 
1067  obj1 = dict->lookup("CA");
1068  if (obj1.isString()) {
1069  normalCaption = std::make_unique<GooString>(obj1.getString());
1070  }
1071 
1072  obj1 = dict->lookup("RC");
1073  if (obj1.isString()) {
1074  rolloverCaption = std::make_unique<GooString>(obj1.getString());
1075  }
1076 
1077  obj1 = dict->lookup("AC");
1078  if (obj1.isString()) {
1079  alternateCaption = std::make_unique<GooString>(obj1.getString());
1080  }
1081 
1082  obj1 = dict->lookup("IF");
1083  if (obj1.isDict()) {
1084  iconFit = std::make_unique<AnnotIconFit>(obj1.getDict());
1085  }
1086 
1087  obj1 = dict->lookup("TP");
1088  if (obj1.isInt()) {
1090  } else {
1092  }
1093 }
1094 
1096 
1097 //------------------------------------------------------------------------
1098 // AnnotAppearanceBBox
1099 //------------------------------------------------------------------------
1100 
1102 {
1103  origX = rect->x1;
1104  origY = rect->y1;
1105  borderWidth = 0;
1106 
1107  // Initially set the same size as rect
1108  minX = 0;
1109  minY = 0;
1110  maxX = rect->x2 - rect->x1;
1111  maxY = rect->y2 - rect->y1;
1112 }
1113 
1114 void AnnotAppearanceBBox::extendTo(double x, double y)
1115 {
1116  if (x < minX) {
1117  minX = x;
1118  } else if (x > maxX) {
1119  maxX = x;
1120  }
1121  if (y < minY) {
1122  minY = y;
1123  } else if (y > maxY) {
1124  maxY = y;
1125  }
1126 }
1127 
1129 {
1130  bbox[0] = minX - borderWidth;
1131  bbox[1] = minY - borderWidth;
1132  bbox[2] = maxX + borderWidth;
1133  bbox[3] = maxY + borderWidth;
1134 }
1135 
1137 {
1138  return origX + minX - borderWidth;
1139 }
1140 
1142 {
1143  return origY + minY - borderWidth;
1144 }
1145 
1147 {
1148  return origX + maxX + borderWidth;
1149 }
1150 
1152 {
1153  return origY + maxY + borderWidth;
1154 }
1155 
1156 //------------------------------------------------------------------------
1157 // Annot
1158 //------------------------------------------------------------------------
1159 
1160 #define annotLocker() std::unique_lock<std::recursive_mutex> locker(mutex)
1161 
1163 {
1164 
1165  refCnt = 1;
1166  flags = flagUnknown;
1167  type = typeUnknown;
1168 
1169  Array *a = new Array(docA->getXRef());
1170  a->add(Object(rectA->x1));
1171  a->add(Object(rectA->y1));
1172  a->add(Object(rectA->x2));
1173  a->add(Object(rectA->y2));
1174 
1175  annotObj = Object(new Dict(docA->getXRef()));
1176  annotObj.dictSet("Type", Object(objName, "Annot"));
1177  annotObj.dictSet("Rect", Object(a));
1178 
1179  ref = docA->getXRef()->addIndirectObject(&annotObj);
1180 
1181  initialize(docA, annotObj.getDict());
1182 }
1183 
1184 Annot::Annot(PDFDoc *docA, Object &&dictObject)
1185 {
1186  refCnt = 1;
1187  hasRef = false;
1188  flags = flagUnknown;
1189  type = typeUnknown;
1190  annotObj = std::move(dictObject);
1191  initialize(docA, annotObj.getDict());
1192 }
1193 
1194 Annot::Annot(PDFDoc *docA, Object &&dictObject, const Object *obj)
1195 {
1196  refCnt = 1;
1197  if (obj->isRef()) {
1198  hasRef = true;
1199  ref = obj->getRef();
1200  } else {
1201  hasRef = false;
1202  }
1203  flags = flagUnknown;
1204  type = typeUnknown;
1205  annotObj = std::move(dictObject);
1206  initialize(docA, annotObj.getDict());
1207 }
1208 
1209 void Annot::initialize(PDFDoc *docA, Dict *dict)
1210 {
1211  Object apObj, asObj, obj1;
1212 
1213  ok = true;
1214  doc = docA;
1215 
1217 
1218  //----- parse the rectangle
1219  rect = std::make_unique<PDFRectangle>();
1220  obj1 = dict->lookup("Rect");
1221  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
1222  rect->x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
1223  rect->y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
1224  rect->x2 = obj1.arrayGet(2).getNumWithDefaultValue(1);
1225  rect->y2 = obj1.arrayGet(3).getNumWithDefaultValue(1);
1226 
1227  if (rect->x1 > rect->x2) {
1228  double t = rect->x1;
1229  rect->x1 = rect->x2;
1230  rect->x2 = t;
1231  }
1232 
1233  if (rect->y1 > rect->y2) {
1234  double t = rect->y1;
1235  rect->y1 = rect->y2;
1236  rect->y2 = t;
1237  }
1238  } else {
1239  rect->x1 = rect->y1 = 0;
1240  rect->x2 = rect->y2 = 1;
1241  error(errSyntaxError, -1, "Bad bounding box for annotation");
1242  ok = false;
1243  }
1244 
1245  obj1 = dict->lookup("Contents");
1246  if (obj1.isString()) {
1247  contents.reset(obj1.getString()->copy());
1248  } else {
1249  contents = std::make_unique<GooString>();
1250  }
1251 
1252  // Note: This value is overwritten by Annots ctor
1253  const Object &pObj = dict->lookupNF("P");
1254  if (pObj.isRef()) {
1255  const Ref pRef = pObj.getRef();
1256 
1257  page = doc->getCatalog()->findPage(pRef);
1258  } else {
1259  page = 0;
1260  }
1261 
1262  obj1 = dict->lookup("NM");
1263  if (obj1.isString()) {
1264  name.reset(obj1.getString()->copy());
1265  }
1266 
1267  obj1 = dict->lookup("M");
1268  if (obj1.isString()) {
1269  modified.reset(obj1.getString()->copy());
1270  }
1271 
1272  //----- get the flags
1273  obj1 = dict->lookup("F");
1274  if (obj1.isInt()) {
1275  flags |= obj1.getInt();
1276  } else {
1277  flags = flagUnknown;
1278  }
1279 
1280  //----- get the annotation appearance dictionary
1281  apObj = dict->lookup("AP");
1282  if (apObj.isDict()) {
1283  appearStreams = std::make_unique<AnnotAppearance>(doc, &apObj);
1284  }
1285 
1286  //----- get the appearance state
1287  asObj = dict->lookup("AS");
1288  if (asObj.isName()) {
1289  appearState = std::make_unique<GooString>(asObj.getName());
1290  } else if (appearStreams && appearStreams->getNumStates() != 0) {
1291  error(errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries");
1292  // AS value is required in this case, but if the
1293  // N dictionary contains only one entry
1294  // take it as default appearance.
1295  if (appearStreams->getNumStates() == 1) {
1296  appearState = appearStreams->getStateKey(0);
1297  }
1298  }
1299  if (!appearState) {
1300  appearState = std::make_unique<GooString>("Off");
1301  }
1302 
1303  //----- get the annotation appearance
1304  if (appearStreams) {
1305  appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str());
1306  }
1307 
1308  //----- parse the border style
1309  // According to the spec if neither the Border nor the BS entry is present,
1310  // the border shall be drawn as a solid line with a width of 1 point. But acroread
1311  // seems to ignore the Border entry for annots that can't have a BS entry. So, we only
1312  // follow this rule for annots tha can have a BS entry.
1313  obj1 = dict->lookup("Border");
1314  if (obj1.isArray()) {
1315  border = std::make_unique<AnnotBorderArray>(obj1.getArray());
1316  }
1317 
1318  obj1 = dict->lookup("C");
1319  if (obj1.isArray()) {
1320  color = std::make_unique<AnnotColor>(obj1.getArray());
1321  }
1322 
1323  obj1 = dict->lookup("StructParent");
1324  if (obj1.isInt()) {
1325  treeKey = obj1.getInt();
1326  } else {
1327  treeKey = 0;
1328  }
1329 
1330  oc = dict->lookupNF("OC").copy();
1331 }
1332 
1333 void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const
1334 {
1335  *x1 = rect->x1;
1336  *y1 = rect->y1;
1337  *x2 = rect->x2;
1338  *y2 = rect->y2;
1339 }
1340 
1341 void Annot::setRect(const PDFRectangle *rectA)
1342 {
1343  setRect(rectA->x1, rectA->y1, rectA->x2, rectA->y2);
1344 }
1345 
1346 void Annot::setRect(double x1, double y1, double x2, double y2)
1347 {
1348  if (x1 < x2) {
1349  rect->x1 = x1;
1350  rect->x2 = x2;
1351  } else {
1352  rect->x1 = x2;
1353  rect->x2 = x1;
1354  }
1355 
1356  if (y1 < y2) {
1357  rect->y1 = y1;
1358  rect->y2 = y2;
1359  } else {
1360  rect->y1 = y2;
1361  rect->y2 = y1;
1362  }
1363 
1364  Array *a = new Array(doc->getXRef());
1365  a->add(Object(rect->x1));
1366  a->add(Object(rect->y1));
1367  a->add(Object(rect->x2));
1368  a->add(Object(rect->y2));
1369 
1370  update("Rect", Object(a));
1372 }
1373 
1374 bool Annot::inRect(double x, double y) const
1375 {
1376  return rect->contains(x, y);
1377 }
1378 
1379 void Annot::update(const char *key, Object &&value)
1380 {
1381  annotLocker();
1382  /* Set M to current time, unless we are updating M itself */
1383  if (strcmp(key, "M") != 0) {
1384  modified.reset(timeToDateString(nullptr));
1385 
1386  annotObj.dictSet("M", Object(modified->copy()));
1387  }
1388 
1389  annotObj.dictSet(const_cast<char *>(key), std::move(value));
1390 
1392 }
1393 
1394 void Annot::setContents(GooString *new_content)
1395 {
1396  annotLocker();
1397 
1398  if (new_content) {
1399  contents = std::make_unique<GooString>(new_content);
1400  // append the unicode marker <FE FF> if needed
1401  if (!contents->hasUnicodeMarker()) {
1402  contents->prependUnicodeMarker();
1403  }
1404  } else {
1405  contents = std::make_unique<GooString>();
1406  }
1407 
1408  update("Contents", Object(contents->copy()));
1409 }
1410 
1411 void Annot::setName(GooString *new_name)
1412 {
1413  annotLocker();
1414 
1415  if (new_name) {
1416  name = std::make_unique<GooString>(new_name);
1417  } else {
1418  name = std::make_unique<GooString>();
1419  }
1420 
1421  update("NM", Object(name->copy()));
1422 }
1423 
1424 void Annot::setModified(GooString *new_modified)
1425 {
1426  annotLocker();
1427 
1428  if (new_modified) {
1429  modified = std::make_unique<GooString>(new_modified);
1430  update("M", Object(modified->copy()));
1431  } else {
1432  modified.reset(nullptr);
1433  update("M", Object(objNull));
1434  }
1435 }
1436 
1437 void Annot::setFlags(unsigned int new_flags)
1438 {
1439  annotLocker();
1440  flags = new_flags;
1441  update("F", Object(int(flags)));
1442 }
1443 
1444 void Annot::setBorder(std::unique_ptr<AnnotBorder> &&new_border)
1445 {
1446  annotLocker();
1447 
1448  if (new_border) {
1449  Object obj1 = new_border->writeToObject(doc->getXRef());
1450  update(new_border->getType() == AnnotBorder::typeArray ? "Border" : "BS", std::move(obj1));
1451  border = std::move(new_border);
1452  } else {
1453  border = nullptr;
1454  }
1456 }
1457 
1458 void Annot::setColor(std::unique_ptr<AnnotColor> &&new_color)
1459 {
1460  annotLocker();
1461 
1462  if (new_color) {
1463  Object obj1 = new_color->writeToObject(doc->getXRef());
1464  update("C", std::move(obj1));
1465  color = std::move(new_color);
1466  } else {
1467  color = nullptr;
1468  }
1470 }
1471 
1472 void Annot::setPage(int pageIndex, bool updateP)
1473 {
1474  annotLocker();
1475  Page *pageobj = doc->getPage(pageIndex);
1476  Object obj1(objNull);
1477 
1478  if (pageobj) {
1479  const Ref pageRef = pageobj->getRef();
1480  obj1 = Object(pageRef);
1481  page = pageIndex;
1482  } else {
1483  page = 0;
1484  }
1485 
1486  if (updateP) {
1487  update("P", std::move(obj1));
1488  }
1489 }
1490 
1492 {
1493  annotLocker();
1494  if (!state)
1495  return;
1496 
1497  appearState = std::make_unique<GooString>(state);
1498  appearBBox = nullptr;
1499 
1500  update("AS", Object(objName, state));
1501 
1502  // The appearance state determines the current appearance stream
1503  if (appearStreams) {
1504  appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str());
1505  } else {
1507  }
1508 }
1509 
1511 {
1512  annotLocker();
1513  if (appearStreams) { // Remove existing appearance streams
1514  appearStreams->removeAllStreams();
1515  }
1516  appearStreams = nullptr;
1517  appearState = nullptr;
1518  appearBBox = nullptr;
1520 
1521  Object obj2 = annotObj.dictLookup("AP");
1522  if (!obj2.isNull())
1523  update("AP", Object(objNull)); // Remove AP
1524 
1525  obj2 = annotObj.dictLookup("AS");
1526  if (!obj2.isNull())
1527  update("AS", Object(objNull)); // Remove AS
1528 }
1529 
1530 double Annot::getXMin()
1531 {
1532  return rect->x1;
1533 }
1534 
1535 double Annot::getYMin()
1536 {
1537  return rect->y1;
1538 }
1539 
1540 double Annot::getXMax()
1541 {
1542  return rect->x2;
1543 }
1544 
1545 double Annot::getYMax()
1546 {
1547  return rect->y2;
1548 }
1549 
1550 void Annot::readArrayNum(Object *pdfArray, int key, double *value)
1551 {
1552  Object valueObject = pdfArray->arrayGet(key);
1553  if (valueObject.isNum()) {
1554  *value = valueObject.getNum();
1555  } else {
1556  *value = 0;
1557  ok = false;
1558  }
1559 }
1560 
1562 {
1563  // Remove appearance streams (if any)
1565 }
1566 
1568 {
1569  refCnt++;
1570 }
1571 
1573 {
1574  if (--refCnt == 0) {
1575  delete this;
1576  }
1577 }
1578 
1579 Annot::~Annot() { }
1580 
1582 {
1583  const double *values = drawColor->getValues();
1584 
1585  switch (drawColor->getSpace()) {
1586  case AnnotColor::colorCMYK:
1587  appearBuf->appendf("{0:.5f} {1:.5f} {2:.5f} {3:.5f} {4:c}\n", values[0], values[1], values[2], values[3], fill ? 'k' : 'K');
1588  break;
1589  case AnnotColor::colorRGB:
1590  appearBuf->appendf("{0:.5f} {1:.5f} {2:.5f} {3:s}\n", values[0], values[1], values[2], fill ? "rg" : "RG");
1591  break;
1592  case AnnotColor::colorGray:
1593  appearBuf->appendf("{0:.5f} {1:c}\n", values[0], fill ? 'g' : 'G');
1594  break;
1596  default:
1597  break;
1598  }
1599 }
1600 
1602 {
1603  if (fontName.isName() && strlen(fontName.getName()) > 0)
1604  appearBuf->appendf("/{0:s} {1:.2f} Tf\n", fontName.getName(), fontSize);
1605 }
1606 
1608 {
1609  int i, dashLength;
1610  double *dash;
1611 
1612  switch (border->getStyle()) {
1614  appearBuf->append("[");
1615  dashLength = border->getDashLength();
1616  dash = border->getDash();
1617  for (i = 0; i < dashLength; ++i)
1618  appearBuf->appendf(" {0:.2f}", dash[i]);
1619  appearBuf->append(" ] 0 d\n");
1620  break;
1621  default:
1622  appearBuf->append("[] 0 d\n");
1623  break;
1624  }
1625  appearBuf->appendf("{0:.2f} w\n", border->getWidth());
1626 }
1627 
1628 // Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1629 // If <fill> is true, the circle is filled; otherwise it is stroked.
1630 void AnnotAppearanceBuilder::drawCircle(double cx, double cy, double r, bool fill)
1631 {
1632  if (fill)
1633  drawEllipse(cx, cy, r, r, true, false);
1634  else
1635  drawEllipse(cx, cy, r, r, false, true);
1636 }
1637 
1638 // Draw an (approximate) ellipse of radius <rx> on x-axis and <ry> on y-axis, centered at (<cx>, <cy>).
1639 // If <fill> is true, the ellipse is filled with current color for non-stroking operations.
1640 // If <stroke> is true, the ellipse path ist stroked with current color and color space for stroking operations.
1641 // Path will be closed if either fill or stroke is true; otherwise it's left open.
1642 void AnnotAppearanceBuilder::drawEllipse(double cx, double cy, double rx, double ry, bool fill, bool stroke)
1643 {
1644  appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx + rx, cy);
1645  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);
1646  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);
1647  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);
1648  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);
1649  if (!fill && stroke)
1650  appearBuf->append("s\n");
1651  else if (fill && !stroke)
1652  appearBuf->append("f\n");
1653  else if (fill && stroke)
1654  appearBuf->append("b\n");
1655 }
1656 
1657 // Draw the top-left half of an (approximate) circle of radius <r>
1658 // centered at (<cx>, <cy>).
1659 void AnnotAppearanceBuilder::drawCircleTopLeft(double cx, double cy, double r)
1660 {
1661  double r2;
1662 
1663  r2 = r / sqrt(2.0);
1664  appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx + r2, cy + r2);
1665  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);
1666  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);
1667  appearBuf->append("S\n");
1668 }
1669 
1670 // Draw the bottom-right half of an (approximate) circle of radius <r>
1671 // centered at (<cx>, <cy>).
1673 {
1674  double r2;
1675 
1676  r2 = r / sqrt(2.0);
1677  appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx - r2, cy - r2);
1678  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);
1679  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);
1680  appearBuf->append("S\n");
1681 }
1682 
1683 void AnnotAppearanceBuilder::drawLineEndSquare(double x, double y, double size, bool fill, const Matrix &m)
1684 {
1685  const double halfSize { size / 2. };
1686  const double x1[3] { x - size, x - size, x };
1687  const double y1[3] { y + halfSize, y - halfSize, y - halfSize };
1688  double tx, ty;
1689 
1690  m.transform(x, y + halfSize, &tx, &ty);
1691  appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1692  for (int i = 0; i < 3; i++) {
1693  m.transform(x1[i], y1[i], &tx, &ty);
1694  appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1695  }
1696  appearBuf->append(fill ? "b\n" : "s\n");
1697 }
1698 
1699 void AnnotAppearanceBuilder::drawLineEndCircle(double x, double y, double size, bool fill, const Matrix &m)
1700 {
1701  const double halfSize { size / 2. };
1702  const double x1[4] { x, x - halfSize - bezierCircle * halfSize, x - size, x - halfSize + bezierCircle * halfSize };
1703  const double x2[4] { x - halfSize + bezierCircle * halfSize, x - size, x - halfSize - bezierCircle * halfSize, x };
1704  const double x3[4] { x - halfSize, x - size, x - halfSize, x };
1705  const double y1[4] { y + bezierCircle * halfSize, y + halfSize, y - bezierCircle * halfSize, y - halfSize };
1706  const double y2[4] { y + halfSize, y + bezierCircle * halfSize, y - halfSize, y - bezierCircle * halfSize };
1707  const double y3[4] { y + halfSize, y, y - halfSize, y };
1708  double tx[3];
1709  double ty[3];
1710 
1711  m.transform(x, y, &tx[0], &ty[0]);
1712  appearBuf->appendf("{0:.2f} {1:.2f} m\n", tx[0], ty[0]);
1713  for (int i = 0; i < 4; i++) {
1714  m.transform(x1[i], y1[i], &tx[0], &ty[0]);
1715  m.transform(x2[i], y2[i], &tx[1], &ty[1]);
1716  m.transform(x3[i], y3[i], &tx[2], &ty[2]);
1717  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]);
1718  }
1719  appearBuf->append(fill ? "b\n" : "s\n");
1720 }
1721 
1722 void AnnotAppearanceBuilder::drawLineEndDiamond(double x, double y, double size, bool fill, const Matrix &m)
1723 {
1724  const double halfSize { size / 2. };
1725  const double x1[3] { x - halfSize, x - size, x - halfSize };
1726  const double y1[3] { y + halfSize, y, y - halfSize };
1727  double tx, ty;
1728 
1729  m.transform(x, y, &tx, &ty);
1730  appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1731  for (int i = 0; i < 3; i++) {
1732  m.transform(x1[i], y1[i], &tx, &ty);
1733  appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1734  }
1735  appearBuf->append(fill ? "b\n" : "s\n");
1736 }
1737 
1738 void AnnotAppearanceBuilder::drawLineEndArrow(double x, double y, double size, int orientation, bool isOpen, bool fill, const Matrix &m)
1739 {
1740  const double alpha { M_PI / 6. };
1741  const double xOffs { orientation * size };
1742  const double yOffs { tan(alpha) * size };
1743  double tx, ty;
1744 
1745  m.transform(x - xOffs, y + yOffs, &tx, &ty);
1746  appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1747  m.transform(x, y, &tx, &ty);
1748  appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1749  m.transform(x - xOffs, y - yOffs, &tx, &ty);
1750  appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1751 
1752  if (isOpen) {
1753  appearBuf->append("S\n");
1754  } else {
1755  appearBuf->append(fill ? "b\n" : "s\n");
1756  }
1757 }
1758 
1759 void AnnotAppearanceBuilder::drawLineEndSlash(double x, double y, double size, const Matrix &m)
1760 {
1761  const double halfSize { size / 2. };
1762  const double xOffs { cos(M_PI / 3.) * halfSize };
1763  double tx, ty;
1764 
1765  m.transform(x - xOffs, y - halfSize, &tx, &ty);
1766  appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1767  m.transform(x + xOffs, y + halfSize, &tx, &ty);
1768  appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1769  appearBuf->append("S\n");
1770 }
1771 
1772 void AnnotAppearanceBuilder::drawLineEnding(AnnotLineEndingStyle endingStyle, double x, double y, double size, bool fill, const Matrix &m)
1773 {
1774  switch (endingStyle) {
1775  case annotLineEndingSquare:
1776  drawLineEndSquare(x, y, size, fill, m);
1777  break;
1778  case annotLineEndingCircle:
1779  drawLineEndCircle(x, y, size, fill, m);
1780  break;
1783  break;
1785  drawLineEndArrow(x, y, size, 1, true, fill, m);
1786  break;
1788  drawLineEndArrow(x, y, size, 1, false, fill, m);
1789  break;
1790  case annotLineEndingButt: {
1791  const double halfSize { size / 2. };
1792  double tx, ty;
1793  m.transform(x, y + halfSize, &tx, &ty);
1794  appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1795  m.transform(x, y - halfSize, &tx, &ty);
1796  appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
1797  } break;
1799  drawLineEndArrow(x, y, size, -1, true, fill, m);
1800  break;
1802  drawLineEndArrow(x, y, size, -1, false, fill, m);
1803  break;
1804  case annotLineEndingSlash:
1805  drawLineEndSlash(x, y, size, m);
1806  break;
1807  default:
1808  break;
1809  }
1810 }
1811 
1813 {
1814  switch (endingStyle) {
1815  case annotLineEndingCircle:
1818  case annotLineEndingSquare:
1819  return size;
1820  default:
1821  break;
1822  }
1823  return 0;
1824 }
1825 
1827 {
1828  switch (endingStyle) {
1831  return size;
1832  case annotLineEndingSlash:
1833  return cos(M_PI / 3.) * size / 2.;
1834  default:
1835  break;
1836  }
1837  return 0;
1838 }
1839 
1840 Object Annot::createForm(const GooString *appearBuf, double *bbox, bool transparencyGroup, Dict *resDict)
1841 {
1842  return createForm(appearBuf, bbox, transparencyGroup, resDict ? Object(resDict) : Object());
1843 }
1844 
1845 Object Annot::createForm(const GooString *appearBuf, double *bbox, bool transparencyGroup, Object &&resDictObject)
1846 {
1847  Dict *appearDict = new Dict(doc->getXRef());
1848  appearDict->set("Length", Object(appearBuf->getLength()));
1849  appearDict->set("Subtype", Object(objName, "Form"));
1850 
1851  Array *a = new Array(doc->getXRef());
1852  a->add(Object(bbox[0]));
1853  a->add(Object(bbox[1]));
1854  a->add(Object(bbox[2]));
1855  a->add(Object(bbox[3]));
1856  appearDict->set("BBox", Object(a));
1857  if (transparencyGroup) {
1858  Dict *d = new Dict(doc->getXRef());
1859  d->set("S", Object(objName, "Transparency"));
1860  appearDict->set("Group", Object(d));
1861  }
1862  if (resDictObject.isDict())
1863  appearDict->set("Resources", std::move(resDictObject));
1864 
1865  Stream *mStream = new AutoFreeMemStream(copyString(appearBuf->c_str()), 0, appearBuf->getLength(), Object(appearDict));
1866  return Object(mStream);
1867 }
1868 
1869 Dict *Annot::createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode)
1870 {
1871  Dict *gsDict = new Dict(doc->getXRef());
1872  if (opacity != 1) {
1873  gsDict->set("CA", Object(opacity));
1874  gsDict->set("ca", Object(opacity));
1875  }
1876  if (blendMode)
1877  gsDict->set("BM", Object(objName, blendMode));
1878  Dict *stateDict = new Dict(doc->getXRef());
1879  stateDict->set(stateName, Object(gsDict));
1880  Dict *formDict = new Dict(doc->getXRef());
1881  formDict->set(formName, std::move(formStream));
1882 
1883  Dict *resDict = new Dict(doc->getXRef());
1884  resDict->set("ExtGState", Object(stateDict));
1885  resDict->set("XObject", Object(formDict));
1886 
1887  return resDict;
1888 }
1889 
1891 {
1892  Object obj1, obj2;
1893 
1894  // Fetch appearance's resource dict (if any)
1895  obj1 = appearance.fetch(doc->getXRef());
1896  if (obj1.isStream()) {
1897  obj2 = obj1.streamGetDict()->lookup("Resources");
1898  if (obj2.isDict()) {
1899  return obj2;
1900  }
1901  }
1902 
1903  return Object(objNull);
1904 }
1905 
1907 {
1908  // check the flags
1909  if ((flags & flagHidden) || (printing && !(flags & flagPrint)) || (!printing && (flags & flagNoView))) {
1910  return false;
1911  }
1912 
1913  // check the OC
1914  OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig();
1915  if (optContentConfig) {
1916  if (!optContentConfig->optContentIsVisible(&oc))
1917  return false;
1918  }
1919 
1920  return true;
1921 }
1922 
1924 {
1925  Page *pageobj = doc->getPage(page);
1926  assert(pageobj != nullptr);
1927 
1928  if (flags & flagNoRotate) {
1929  return (360 - pageobj->getRotate()) % 360;
1930  } else {
1931  return 0;
1932  }
1933 }
1934 
1935 void Annot::draw(Gfx *gfx, bool printing)
1936 {
1937  annotLocker();
1938  if (!isVisible(printing))
1939  return;
1940 
1941  // draw the appearance stream
1942  Object obj = appearance.fetch(gfx->getXRef());
1943  gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
1944 }
1945 
1946 //------------------------------------------------------------------------
1947 // AnnotPopup
1948 //------------------------------------------------------------------------
1949 
1950 AnnotPopup::AnnotPopup(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA)
1951 {
1952  type = typePopup;
1953 
1954  annotObj.dictSet("Subtype", Object(objName, "Popup"));
1955  initialize(docA, annotObj.getDict());
1956 }
1957 
1958 AnnotPopup::AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj)
1959 {
1960  type = typePopup;
1961  initialize(docA, annotObj.getDict());
1962 }
1963 
1965 
1967 {
1968  const Object &parentObj = dict->lookupNF("Parent");
1969  if (parentObj.isRef()) {
1970  parentRef = parentObj.getRef();
1971  } else {
1972  parentRef = Ref::INVALID();
1973  }
1974 
1975  open = dict->lookup("Open").getBoolWithDefaultValue(false);
1976 }
1977 
1979 {
1980  parentRef = parentA->getRef();
1981  update("Parent", Object(parentRef));
1982 }
1983 
1984 void AnnotPopup::setOpen(bool openA)
1985 {
1986  open = openA;
1987  update("Open", Object(open));
1988 }
1989 
1990 //------------------------------------------------------------------------
1991 // AnnotMarkup
1992 //------------------------------------------------------------------------
1993 AnnotMarkup::AnnotMarkup(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA)
1994 {
1995  initialize(docA, annotObj.getDict());
1996 }
1997 
1998 AnnotMarkup::AnnotMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj)
1999 {
2000  initialize(docA, annotObj.getDict());
2001 }
2002 
2003 AnnotMarkup::~AnnotMarkup() = default;
2004 
2006 {
2007  Object obj1;
2008 
2009  obj1 = dict->lookup("T");
2010  if (obj1.isString()) {
2011  label.reset(obj1.getString()->copy());
2012  }
2013 
2014  Object popupObj = dict->lookup("Popup");
2015  const Object &obj2 = dict->lookupNF("Popup");
2016  if (popupObj.isDict() && obj2.isRef()) {
2017  popup = std::make_unique<AnnotPopup>(docA, std::move(popupObj), &obj2);
2018  }
2019 
2020  opacity = dict->lookup("CA").getNumWithDefaultValue(1.0);
2021 
2022  obj1 = dict->lookup("CreationDate");
2023  if (obj1.isString()) {
2024  date.reset(obj1.getString()->copy());
2025  }
2026 
2027  const Object &irtObj = dict->lookupNF("IRT");
2028  if (irtObj.isRef()) {
2029  inReplyTo = irtObj.getRef();
2030  } else {
2031  inReplyTo = Ref::INVALID();
2032  }
2033 
2034  obj1 = dict->lookup("Subj");
2035  if (obj1.isString()) {
2036  subject.reset(obj1.getString()->copy());
2037  }
2038 
2039  obj1 = dict->lookup("RT");
2040  if (obj1.isName()) {
2041  const char *replyName = obj1.getName();
2042 
2043  if (!strcmp(replyName, "R")) {
2044  replyTo = replyTypeR;
2045  } else if (!strcmp(replyName, "Group")) {
2047  } else {
2048  replyTo = replyTypeR;
2049  }
2050  } else {
2051  replyTo = replyTypeR;
2052  }
2053 
2054  obj1 = dict->lookup("ExData");
2055  if (obj1.isDict()) {
2057  } else {
2059  }
2060 }
2061 
2063 {
2064  if (new_label) {
2065  label = std::make_unique<GooString>(new_label);
2066  // append the unicode marker <FE FF> if needed
2067  if (!label->hasUnicodeMarker()) {
2068  label->prependUnicodeMarker();
2069  }
2070  } else {
2071  label = std::make_unique<GooString>();
2072  }
2073 
2074  update("T", Object(label->copy()));
2075 }
2076 
2077 void AnnotMarkup::setPopup(std::unique_ptr<AnnotPopup> &&new_popup)
2078 {
2079  // If there exists an old popup annotation that is already
2080  // associated with a page, then we need to remove that
2081  // popup annotation from the page. Otherwise we would have
2082  // dangling references to it.
2083  if (popup && popup->getPageNum() != 0) {
2084  Page *pageobj = doc->getPage(popup->getPageNum());
2085  if (pageobj) {
2086  pageobj->removeAnnot(popup.get());
2087  }
2088  }
2089 
2090  if (new_popup) {
2091  const Ref popupRef = new_popup->getRef();
2092  update("Popup", Object(popupRef));
2093 
2094  new_popup->setParent(this);
2095  popup = std::move(new_popup);
2096 
2097  // If this annotation is already added to a page, then we
2098  // add the new popup annotation to the same page.
2099  if (page != 0) {
2100  Page *pageobj = doc->getPage(page);
2101  assert(pageobj != nullptr); // pageobj should exist in doc (see setPage())
2102 
2103  pageobj->addAnnot(popup.get());
2104  }
2105  } else {
2106  popup = nullptr;
2107  }
2108 }
2109 
2110 void AnnotMarkup::setOpacity(double opacityA)
2111 {
2112  opacity = opacityA;
2113  update("CA", Object(opacity));
2115 }
2116 
2118 {
2119  if (new_date) {
2120  date = std::make_unique<GooString>(new_date);
2121  update("CreationDate", Object(date->copy()));
2122  } else {
2123  date.reset(nullptr);
2124  update("CreationDate", Object(objNull));
2125  }
2126 }
2127 
2129 {
2130  Page *pageobj = doc->getPage(page);
2131  assert(pageobj != nullptr); // We're called when removing an annot from a page
2132 
2133  // Remove popup
2134  if (popup) {
2135  pageobj->removeAnnot(popup.get());
2136  }
2137 
2139 }
2140 
2141 //------------------------------------------------------------------------
2142 // AnnotText
2143 //------------------------------------------------------------------------
2144 
2146 {
2147  type = typeText;
2149 
2150  annotObj.dictSet("Subtype", Object(objName, "Text"));
2151  initialize(docA, annotObj.getDict());
2152 }
2153 
2154 AnnotText::AnnotText(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj)
2155 {
2156 
2157  type = typeText;
2159  initialize(docA, annotObj.getDict());
2160 }
2161 
2162 AnnotText::~AnnotText() = default;
2163 
2165 {
2166  Object obj1;
2167 
2168  open = dict->lookup("Open").getBoolWithDefaultValue(false);
2169 
2170  obj1 = dict->lookup("Name");
2171  if (obj1.isName()) {
2172  icon = std::make_unique<GooString>(obj1.getName());
2173  } else {
2174  icon = std::make_unique<GooString>("Note");
2175  }
2176 
2177  obj1 = dict->lookup("StateModel");
2178  if (obj1.isString()) {
2179  const GooString *modelName = obj1.getString();
2180 
2181  Object obj2 = dict->lookup("State");
2182  if (obj2.isString()) {
2183  const GooString *stateName = obj2.getString();
2184 
2185  if (!stateName->cmp("Marked")) {
2186  state = stateMarked;
2187  } else if (!stateName->cmp("Unmarked")) {
2188  state = stateUnmarked;
2189  } else if (!stateName->cmp("Accepted")) {
2190  state = stateAccepted;
2191  } else if (!stateName->cmp("Rejected")) {
2192  state = stateRejected;
2193  } else if (!stateName->cmp("Cancelled")) {
2195  } else if (!stateName->cmp("Completed")) {
2197  } else if (!stateName->cmp("None")) {
2198  state = stateNone;
2199  } else {
2200  state = stateUnknown;
2201  }
2202  } else {
2203  state = stateUnknown;
2204  }
2205 
2206  if (!modelName->cmp("Marked")) {
2207  switch (state) {
2208  case stateUnknown:
2209  state = stateMarked;
2210  break;
2211  case stateAccepted:
2212  case stateRejected:
2213  case stateCancelled:
2214  case stateCompleted:
2215  case stateNone:
2216  state = stateUnknown;
2217  break;
2218  default:
2219  break;
2220  }
2221  } else if (!modelName->cmp("Review")) {
2222  switch (state) {
2223  case stateUnknown:
2224  state = stateNone;
2225  break;
2226  case stateMarked:
2227  case stateUnmarked:
2228  state = stateUnknown;
2229  break;
2230  default:
2231  break;
2232  }
2233  } else {
2234  state = stateUnknown;
2235  }
2236  } else {
2237  state = stateUnknown;
2238  }
2239 }
2240 
2241 void AnnotText::setOpen(bool openA)
2242 {
2243  open = openA;
2244  update("Open", Object(open));
2245 }
2246 
2248 {
2249  if (new_icon && icon->cmp(new_icon) == 0)
2250  return;
2251 
2252  if (new_icon) {
2253  icon = std::make_unique<GooString>(new_icon);
2254  } else {
2255  icon = std::make_unique<GooString>("Note");
2256  }
2257 
2258  update("Name", Object(objName, icon->c_str()));
2260 }
2261 
2262 #define ANNOT_TEXT_AP_NOTE \
2263  "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \
2264  "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" \
2265  "l 0 22.387 1.613 24 3.602 24 c h\n" \
2266  "3.602 24 m f\n" \
2267  "0.533333 0.541176 0.521569 RG 2 w\n" \
2268  "1 J\n" \
2269  "1 j\n" \
2270  "[] 0.0 d\n" \
2271  "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" \
2272  "1.5 w\n" \
2273  "0 j\n" \
2274  "10 16 m 14 21 l S\n" \
2275  "1.85625 w\n" \
2276  "1 j\n" \
2277  "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \
2278  "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \
2279  "14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \
2280  "15.07 20.523 m S\n" \
2281  "1 w\n" \
2282  "0 j\n" \
2283  "6.5 13.5 m 15.5 13.5 l S\n" \
2284  "6.5 10.5 m 13.5 10.5 l S\n" \
2285  "6.801 7.5 m 15.5 7.5 l S\n" \
2286  "0.729412 0.741176 0.713725 RG 2 w\n" \
2287  "1 j\n" \
2288  "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" \
2289  "1.5 w\n" \
2290  "0 j\n" \
2291  "10 17 m 14 22 l S\n" \
2292  "1.85625 w\n" \
2293  "1 j\n" \
2294  "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \
2295  "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \
2296  "14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \
2297  "15.07 21.523 m S\n" \
2298  "1 w\n" \
2299  "0 j\n" \
2300  "6.5 14.5 m 15.5 14.5 l S\n" \
2301  "6.5 11.5 m 13.5 11.5 l S\n" \
2302  "6.801 8.5 m 15.5 8.5 l S\n"
2303 
2304 #define ANNOT_TEXT_AP_COMMENT \
2305  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2306  "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" \
2307  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2308  "4.301 23 m f\n" \
2309  "0.533333 0.541176 0.521569 RG 2 w\n" \
2310  "0 J\n" \
2311  "1 j\n" \
2312  "[] 0.0 d\n" \
2313  "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" \
2314  "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" \
2315  "5.637 20 8 20 c h\n" \
2316  "8 20 m S\n" \
2317  "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \
2318  "c 20 14 l 20 11.785 18.363 10\n" \
2319  "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" \
2320  "19.215 5.637 21 8 21 c h\n" \
2321  "8 21 m S\n"
2322 
2323 #define ANNOT_TEXT_AP_KEY \
2324  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2325  "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" \
2326  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2327  "4.301 23 m f\n" \
2328  "0.533333 0.541176 0.521569 RG 2 w\n" \
2329  "1 J\n" \
2330  "0 j\n" \
2331  "[] 0.0 d\n" \
2332  "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \
2333  "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \
2334  "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \
2335  "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" \
2336  "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \
2337  "11.895 18.754 m S\n" \
2338  "1.5 w\n" \
2339  "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \
2340  "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \
2341  "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \
2342  "16.059 15.586 m S\n" \
2343  "0.729412 0.741176 0.713725 RG 2 w\n" \
2344  "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \
2345  "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \
2346  "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \
2347  "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" \
2348  "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \
2349  "11.895 19.754 m S\n" \
2350  "1.5 w\n" \
2351  "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \
2352  "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \
2353  "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \
2354  "16.059 16.586 m S\n"
2355 
2356 #define ANNOT_TEXT_AP_HELP \
2357  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2358  "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" \
2359  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2360  "4.301 23 m f\n" \
2361  "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2362  "1 J\n" \
2363  "1 j\n" \
2364  "[] 0.0 d\n" \
2365  "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" \
2366  "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \
2367  "c S\n" \
2368  "0 j\n" \
2369  "12 8 m 12 12 16 11 16 15 c S\n" \
2370  "1.539286 w\n" \
2371  "1 j\n" \
2372  "q 1 0 0 -0.999991 0 24 cm\n" \
2373  "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \
2374  "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \
2375  "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \
2376  "0.729412 0.741176 0.713725 RG 2.5 w\n" \
2377  "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \
2378  "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \
2379  "c S\n" \
2380  "0 j\n" \
2381  "12 9 m 12 13 16 12 16 16 c S\n" \
2382  "1.539286 w\n" \
2383  "1 j\n" \
2384  "q 1 0 0 -0.999991 0 24 cm\n" \
2385  "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \
2386  "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \
2387  "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
2388 
2389 #define ANNOT_TEXT_AP_NEW_PARAGRAPH \
2390  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2391  "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" \
2392  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2393  "4.301 23 m f\n" \
2394  "0.533333 0.541176 0.521569 RG 4 w\n" \
2395  "0 J\n" \
2396  "2 j\n" \
2397  "[] 0.0 d\n" \
2398  "4 M q 1 0 0 -1 0 24 cm\n" \
2399  "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \
2400  "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" \
2401  "1.004413 w\n" \
2402  "1 J\n" \
2403  "1 j\n" \
2404  "q 1 0 0 -0.991232 0 24 cm\n" \
2405  "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \
2406  "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \
2407  "17.461 8.252 l 18.07 11.511 l h\n" \
2408  "18.07 11.511 m S Q\n" \
2409  "2 w\n" \
2410  "0 j\n" \
2411  "11 17 m 10 17 l 10 3 l S\n" \
2412  "14 3 m 14 13 l S\n" \
2413  "0.729412 0.741176 0.713725 RG 4 w\n" \
2414  "0 J\n" \
2415  "2 j\n" \
2416  "q 1 0 0 -1 0 24 cm\n" \
2417  "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \
2418  "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" \
2419  "1.004413 w\n" \
2420  "1 J\n" \
2421  "1 j\n" \
2422  "q 1 0 0 -0.991232 0 24 cm\n" \
2423  "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \
2424  "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \
2425  "17.461 7.243 l 18.07 10.502 l h\n" \
2426  "18.07 10.502 m S Q\n" \
2427  "2 w\n" \
2428  "0 j\n" \
2429  "11 18 m 10 18 l 10 4 l S\n" \
2430  "14 4 m 14 14 l S\n"
2431 
2432 #define ANNOT_TEXT_AP_PARAGRAPH \
2433  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2434  "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" \
2435  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2436  "4.301 23 m f\n" \
2437  "0.533333 0.541176 0.521569 RG 2 w\n" \
2438  "1 J\n" \
2439  "1 j\n" \
2440  "[] 0.0 d\n" \
2441  "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \
2442  "4 w\n" \
2443  "q 1 0 0 -1 0 24 cm\n" \
2444  "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \
2445  "7.066 9.879 7.004 c S Q\n" \
2446  "0.729412 0.741176 0.713725 RG 2 w\n" \
2447  "15 4 m 15 19 l 11 19 l 11 4 l S\n" \
2448  "4 w\n" \
2449  "q 1 0 0 -1 0 24 cm\n" \
2450  "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" \
2451  "9.879 6.004 c S Q\n"
2452 
2453 #define ANNOT_TEXT_AP_INSERT \
2454  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2455  "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" \
2456  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2457  "4.301 23 m f\n" \
2458  "0.533333 0.541176 0.521569 RG 2 w\n" \
2459  "1 J\n" \
2460  "0 j\n" \
2461  "[] 0.0 d\n" \
2462  "4 M 12 18.012 m 20 18 l S\n" \
2463  "9 10 m 17 10 l S\n" \
2464  "12 14.012 m 20 14 l S\n" \
2465  "12 6.012 m 20 6.012 l S\n" \
2466  "4 12 m 6 10 l 4 8 l S\n" \
2467  "4 12 m 4 8 l S\n" \
2468  "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \
2469  "9 11 m 17 11 l S\n" \
2470  "12 15.012 m 20 15 l S\n" \
2471  "12 7.012 m 20 7.012 l S\n" \
2472  "4 13 m 6 11 l 4 9 l S\n" \
2473  "4 13 m 4 9 l S\n"
2474 
2475 #define ANNOT_TEXT_AP_CROSS \
2476  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2477  "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" \
2478  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2479  "4.301 23 m f\n" \
2480  "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2481  "1 J\n" \
2482  "0 j\n" \
2483  "[] 0.0 d\n" \
2484  "4 M 18 5 m 6 17 l S\n" \
2485  "6 5 m 18 17 l S\n" \
2486  "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \
2487  "6 6 m 18 18 l S\n"
2488 
2489 #define ANNOT_TEXT_AP_CIRCLE \
2490  "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \
2491  "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \
2492  "l 1 21.523 2.477 23 4.301 23 c h\n" \
2493  "4.301 23 m f\n" \
2494  "0.533333 0.541176 0.521569 RG 2.5 w\n" \
2495  "1 J\n" \
2496  "1 j\n" \
2497  "[] 0.0 d\n" \
2498  "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" \
2499  "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \
2500  "19.5 11.5 m S\n" \
2501  "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \
2502  "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \
2503  "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \
2504  "19.5 12.5 m S\n"
2505 
2506 void AnnotText::draw(Gfx *gfx, bool printing)
2507 {
2508  double ca = 1;
2509 
2510  if (!isVisible(printing))
2511  return;
2512 
2513  annotLocker();
2514  if (appearance.isNull()) {
2515  ca = opacity;
2516 
2517  AnnotAppearanceBuilder appearBuilder;
2518 
2519  appearBuilder.append("q\n");
2520  if (color)
2521  appearBuilder.setDrawColor(color.get(), true);
2522  else
2523  appearBuilder.append("1 1 1 rg\n");
2524  if (!icon->cmp("Note"))
2525  appearBuilder.append(ANNOT_TEXT_AP_NOTE);
2526  else if (!icon->cmp("Comment"))
2527  appearBuilder.append(ANNOT_TEXT_AP_COMMENT);
2528  else if (!icon->cmp("Key"))
2529  appearBuilder.append(ANNOT_TEXT_AP_KEY);
2530  else if (!icon->cmp("Help"))
2531  appearBuilder.append(ANNOT_TEXT_AP_HELP);
2532  else if (!icon->cmp("NewParagraph"))
2533  appearBuilder.append(ANNOT_TEXT_AP_NEW_PARAGRAPH);
2534  else if (!icon->cmp("Paragraph"))
2535  appearBuilder.append(ANNOT_TEXT_AP_PARAGRAPH);
2536  else if (!icon->cmp("Insert"))
2537  appearBuilder.append(ANNOT_TEXT_AP_INSERT);
2538  else if (!icon->cmp("Cross"))
2539  appearBuilder.append(ANNOT_TEXT_AP_CROSS);
2540  else if (!icon->cmp("Circle"))
2541  appearBuilder.append(ANNOT_TEXT_AP_CIRCLE);
2542  appearBuilder.append("Q\n");
2543 
2544  // Force 24x24 rectangle
2545  PDFRectangle fixedRect(rect->x1, rect->y2 - 24, rect->x1 + 24, rect->y2);
2546  appearBBox = std::make_unique<AnnotAppearanceBBox>(&fixedRect);
2547  double bbox[4];
2548  appearBBox->getBBoxRect(bbox);
2549  if (ca == 1) {
2550  appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr);
2551  } else {
2552  Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr);
2553 
2554  GooString appearBuf("/GS0 gs\n/Fm0 Do");
2555  Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
2556  appearance = createForm(&appearBuf, bbox, false, resDict);
2557  }
2558  }
2559 
2560  // draw the appearance stream
2561  Object obj = appearance.fetch(gfx->getXRef());
2562  if (appearBBox) {
2563  gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
2564  } else {
2565  gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2566  }
2567 }
2568 
2569 //------------------------------------------------------------------------
2570 // AnnotLink
2571 //------------------------------------------------------------------------
2572 AnnotLink::AnnotLink(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA)
2573 {
2574  type = typeLink;
2575  annotObj.dictSet("Subtype", Object(objName, "Link"));
2576  initialize(docA, annotObj.getDict());
2577 }
2578 
2579 AnnotLink::AnnotLink(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj)
2580 {
2581 
2582  type = typeLink;
2583  initialize(docA, annotObj.getDict());
2584 }
2585 
2586 AnnotLink::~AnnotLink() = default;
2587 
2589 {
2590  Object obj1;
2591 
2592  // look for destination
2593  obj1 = dict->lookup("Dest");
2594  if (!obj1.isNull()) {
2595  action = LinkAction::parseDest(&obj1);
2596  // look for action
2597  } else {
2598  obj1 = dict->lookup("A");
2599  if (obj1.isDict()) {
2601  }
2602  }
2603 
2604  obj1 = dict->lookup("H");
2605  if (obj1.isName()) {
2606  const char *effect = obj1.getName();
2607 
2608  if (!strcmp(effect, "N")) {
2610  } else if (!strcmp(effect, "I")) {
2612  } else if (!strcmp(effect, "O")) {
2614  } else if (!strcmp(effect, "P")) {
2616  } else {
2618  }
2619  } else {
2621  }
2622  /*
2623  obj1 = dict->lookup("PA");
2624  if (obj1.isDict()) {
2625  uriAction = NULL;
2626  } else {
2627  uriAction = NULL;
2628  }
2629  obj1.free();
2630  */
2631  obj1 = dict->lookup("QuadPoints");
2632  if (obj1.isArray()) {
2633  quadrilaterals = std::make_unique<AnnotQuadrilaterals>(obj1.getArray(), rect.get());
2634  }
2635 
2636  obj1 = dict->lookup("BS");
2637  if (obj1.isDict()) {
2638  border = std::make_unique<AnnotBorderBS>(obj1.getDict());
2639  } else if (!border) {
2640  border = std::make_unique<AnnotBorderBS>();
2641  }
2642 }
2643 
2644 void AnnotLink::draw(Gfx *gfx, bool printing)
2645 {
2646  if (!isVisible(printing))
2647  return;
2648 
2649  annotLocker();
2650  // draw the appearance stream
2651  Object obj = appearance.fetch(gfx->getXRef());
2652  gfx->drawAnnot(&obj, border.get(), color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2653 }
2654 
2655 //------------------------------------------------------------------------
2656 // AnnotFreeText
2657 //------------------------------------------------------------------------
2658 const double AnnotFreeText::undefinedFontPtSize = 10.;
2659 
2661 {
2662  type = typeFreeText;
2663 
2664  GooString *daStr = da.toAppearanceString();
2665  annotObj.dictSet("Subtype", Object(objName, "FreeText"));
2666  annotObj.dictSet("DA", Object(daStr));
2667 
2668  initialize(docA, annotObj.getDict());
2669 }
2670 
2671 AnnotFreeText::AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj)
2672 {
2673  type = typeFreeText;
2674  initialize(docA, annotObj.getDict());
2675 }
2676 
2677 AnnotFreeText::~AnnotFreeText() = default;
2678 
2680 {
2681  Object obj1;
2682 
2683  obj1 = dict->lookup("DA");
2684  if (obj1.isString()) {
2685  appearanceString.reset(obj1.getString()->copy());
2686  } else {
2687  appearanceString = std::make_unique<GooString>();
2688  error(errSyntaxWarning, -1, "Bad appearance for annotation");
2689  }
2690 
2691  obj1 = dict->lookup("Q");
2692  if (obj1.isInt()) {
2694  } else {
2696  }
2697 
2698  obj1 = dict->lookup("DS");
2699  if (obj1.isString()) {
2700  styleString.reset(obj1.getString()->copy());
2701  }
2702 
2703  obj1 = dict->lookup("CL");
2704  if (obj1.isArray() && obj1.arrayGetLength() >= 4) {
2705  const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
2706  const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
2707  const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0);
2708  const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0);
2709 
2710  if (obj1.arrayGetLength() == 6) {
2711  const double x3 = obj1.arrayGet(4).getNumWithDefaultValue(0);
2712  const double y3 = obj1.arrayGet(5).getNumWithDefaultValue(0);
2713  calloutLine = std::make_unique<AnnotCalloutMultiLine>(x1, y1, x2, y2, x3, y3);
2714  } else {
2715  calloutLine = std::make_unique<AnnotCalloutLine>(x1, y1, x2, y2);
2716  }
2717  }
2718 
2719  obj1 = dict->lookup("IT");
2720  if (obj1.isName()) {
2721  const char *intentName = obj1.getName();
2722 
2723  if (!strcmp(intentName, "FreeText")) {
2725  } else if (!strcmp(intentName, "FreeTextCallout")) {
2727  } else if (!strcmp(intentName, "FreeTextTypeWriter")) {
2729  } else {
2731  }
2732  } else {
2734  }
2735 
2736  obj1 = dict->lookup("BS");
2737  if (obj1.isDict()) {
2738  border = std::make_unique<AnnotBorderBS>(obj1.getDict());
2739  } else if (!border) {
2740  border = std::make_unique<AnnotBorderBS>();
2741  }
2742 
2743  obj1 = dict->lookup("BE");
2744  if (obj1.isDict()) {
2745  borderEffect = std::make_unique<AnnotBorderEffect>(obj1.getDict());
2746  }
2747 
2748  obj1 = dict->lookup("RD");
2749  if (obj1.isArray()) {
2750  rectangle = parseDiffRectangle(obj1.getArray(), rect.get());
2751  }
2752 
2753  obj1 = dict->lookup("LE");
2754  if (obj1.isName()) {
2755  GooString styleName(obj1.getName());
2756  endStyle = parseAnnotLineEndingStyle(&styleName);
2757  } else {
2759  }
2760 }
2761 
2763 {
2764  Annot::setContents(new_content);
2766 }
2767 
2769 {
2770  appearanceString = std::unique_ptr<GooString>(da.toAppearanceString());
2771 
2772  update("DA", Object(appearanceString->copy()));
2774 }
2775 
2777 {
2778  quadding = new_quadding;
2779  update("Q", Object((int)quadding));
2781 }
2782 
2784 {
2785  if (new_string) {
2786  styleString = std::make_unique<GooString>(new_string);
2787  // append the unicode marker <FE FF> if needed
2788  if (!styleString->hasUnicodeMarker()) {
2789  styleString->prependUnicodeMarker();
2790  }
2791  } else {
2792  styleString = std::make_unique<GooString>();
2793  }
2794 
2795  update("DS", Object(styleString->copy()));
2796 }
2797 
2799 {
2800  Object obj1;
2801  if (line == nullptr) {
2802  obj1.setToNull();
2803  calloutLine = nullptr;
2804  } else {
2805  double x1 = line->getX1(), y1 = line->getY1();
2806  double x2 = line->getX2(), y2 = line->getY2();
2807  obj1 = Object(new Array(doc->getXRef()));
2808  obj1.arrayAdd(Object(x1));
2809  obj1.arrayAdd(Object(y1));
2810  obj1.arrayAdd(Object(x2));
2811  obj1.arrayAdd(Object(y2));
2812 
2813  AnnotCalloutMultiLine *mline = dynamic_cast<AnnotCalloutMultiLine *>(line);
2814  if (mline) {
2815  double x3 = mline->getX3(), y3 = mline->getY3();
2816  obj1.arrayAdd(Object(x3));
2817  obj1.arrayAdd(Object(y3));
2818  calloutLine = std::make_unique<AnnotCalloutMultiLine>(x1, y1, x2, y2, x3, y3);
2819  } else {
2820  calloutLine = std::make_unique<AnnotCalloutLine>(x1, y1, x2, y2);
2821  }
2822  }
2823 
2824  update("CL", std::move(obj1));
2826 }
2827 
2829 {
2830  const char *intentName;
2831 
2832  intent = new_intent;
2833  if (new_intent == intentFreeText)
2834  intentName = "FreeText";
2835  else if (new_intent == intentFreeTextCallout)
2836  intentName = "FreeTextCallout";
2837  else // intentFreeTextTypeWriter
2838  intentName = "FreeTextTypeWriter";
2839  update("IT", Object(objName, intentName));
2840 }
2841 
2842 std::unique_ptr<DefaultAppearance> AnnotFreeText::getDefaultAppearance() const
2843 {
2844  return std::make_unique<DefaultAppearance>(appearanceString.get());
2845 }
2846 
2847 static GfxFont *createAnnotDrawFont(XRef *xref, Dict *fontParentDict, const char *resourceName = "AnnotDrawFont", const char *fontname = "Helvetica")
2848 {
2849  const Ref dummyRef = { -1, -1 };
2850 
2851  Dict *fontDict = new Dict(xref);
2852  fontDict->add("BaseFont", Object(objName, fontname));
2853  fontDict->add("Subtype", Object(objName, "Type1"));
2854 
2855  Object fontsDictObj = fontParentDict->lookup("Font");
2856  if (!fontsDictObj.isDict()) {
2857  fontsDictObj = Object(new Dict(xref));
2858  fontParentDict->add("Font", fontsDictObj.copy()); // This is not a copy it's a ref
2859  }
2860 
2861  fontsDictObj.dictSet(resourceName, Object(fontDict));
2862 
2863  return GfxFont::makeFont(xref, resourceName, dummyRef, fontDict);
2864 }
2865 
2867 {
2868  double borderWidth, ca = opacity;
2869 
2870  AnnotAppearanceBuilder appearBuilder;
2871  appearBuilder.append("q\n");
2872 
2873  borderWidth = border->getWidth();
2874  if (borderWidth > 0)
2875  appearBuilder.setLineStyleForBorder(border.get());
2876 
2877  // Box size
2878  const double width = rect->x2 - rect->x1;
2879  const double height = rect->y2 - rect->y1;
2880 
2881  // Parse some properties from the appearance string
2882  DefaultAppearance da { appearanceString.get() };
2883 
2884  // Default values
2885  if (!da.getFontName().isName())
2886  da.setFontName(Object(objName, "AnnotDrawFont"));
2887  if (da.getFontPtSize() <= 0)
2888  da.setFontPtSize(undefinedFontPtSize);
2889  if (!da.getFontColor())
2890  da.setFontColor(std::make_unique<AnnotColor>(0, 0, 0));
2891  if (!contents)
2892  contents = std::make_unique<GooString>();
2893 
2894  // Draw box
2895  bool doFill = (color && color->getSpace() != AnnotColor::colorTransparent);
2896  bool doStroke = (borderWidth != 0);
2897  if (doFill || doStroke) {
2898  if (doStroke) {
2899  appearBuilder.setDrawColor(da.getFontColor(), false); // Border color: same as font color
2900  }
2901  appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth / 2, width - borderWidth, height - borderWidth);
2902  if (doFill) {
2903  appearBuilder.setDrawColor(color.get(), true);
2904  appearBuilder.append(doStroke ? "B\n" : "f\n");
2905  } else {
2906  appearBuilder.append("S\n");
2907  }
2908  }
2909 
2910  // Setup text clipping
2911  const double textmargin = borderWidth * 2;
2912  const double textwidth = width - 2 * textmargin;
2913  appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2 * textmargin);
2914 
2915  GfxFont *font = nullptr;
2916 
2917  // look for font name in the default resources
2918  Form *form = doc->getCatalog()->getForm(); // form is owned by catalog, no need to clean it up
2919 
2920  Object resourceObj;
2921  if (form && form->getDefaultResourcesObj() && form->getDefaultResourcesObj()->isDict()) {
2922  resourceObj = form->getDefaultResourcesObj()->copy(); // No real copy, but increment refcount of /DR Dict
2923 
2924  Dict *resDict = resourceObj.getDict();
2925  Object fontResources = resDict->lookup("Font"); // The 'Font' subdictionary
2926 
2927  if (!fontResources.isDict()) {
2928  error(errSyntaxWarning, -1, "Font subdictionary is not a dictionary");
2929  } else {
2930  // Get the font dictionary for the actual requested font
2931  Ref fontReference;
2932  Object fontDictionary = fontResources.getDict()->lookup(da.getFontName().getName(), &fontReference);
2933 
2934  if (fontDictionary.isDict()) {
2935  font = GfxFont::makeFont(doc->getXRef(), da.getFontName().getName(), fontReference, fontDictionary.getDict());
2936  } else {
2937  error(errSyntaxWarning, -1, "Font dictionary is not a dictionary");
2938  }
2939  }
2940  }
2941 
2942  // if fontname is not in the default resources, create a Helvetica fake font
2943  if (!font) {
2944  Dict *fontResDict = new Dict(doc->getXRef());
2945  resourceObj = Object(fontResDict);
2946  font = createAnnotDrawFont(doc->getXRef(), fontResDict, da.getFontName().getName());
2947  }
2948 
2949  // Set font state
2950  appearBuilder.setDrawColor(da.getFontColor(), true);
2951  appearBuilder.appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - da.getFontPtSize() * font->getDescent());
2952  appearBuilder.setTextFont(da.getFontName(), da.getFontPtSize());
2953 
2954  int i = 0;
2955  double xposPrev = 0;
2956  while (i < contents->getLength()) {
2957  GooString out;
2958  double linewidth, xpos;
2959  layoutText(contents.get(), &out, &i, font, &linewidth, textwidth / da.getFontPtSize(), nullptr, false);
2960  linewidth *= da.getFontPtSize();
2961  switch (quadding) {
2962  case quaddingCentered:
2963  xpos = (textwidth - linewidth) / 2;
2964  break;
2966  xpos = textwidth - linewidth;
2967  break;
2968  default: // quaddingLeftJustified:
2969  xpos = 0;
2970  break;
2971  }
2972  appearBuilder.appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -da.getFontPtSize());
2973  appearBuilder.writeString(out);
2974  appearBuilder.append("Tj\n");
2975  xposPrev = xpos;
2976  }
2977 
2978  font->decRefCnt();
2979  appearBuilder.append("ET Q\n");
2980 
2981  double bbox[4];
2982  bbox[0] = bbox[1] = 0;
2983  bbox[2] = rect->x2 - rect->x1;
2984  bbox[3] = rect->y2 - rect->y1;
2985 
2986  if (ca == 1) {
2987  appearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resourceObj));
2988  } else {
2989  Object aStream = createForm(appearBuilder.buffer(), bbox, true, std::move(resourceObj));
2990 
2991  GooString appearBuf("/GS0 gs\n/Fm0 Do");
2992  Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
2993  appearance = createForm(&appearBuf, bbox, false, resDict);
2994  }
2995 }
2996 
2998 {
2999  if (!isVisible(printing))
3000  return;
3001 
3002  annotLocker();
3003  if (appearance.isNull()) {
3005  }
3006 
3007  // draw the appearance stream
3008  Object obj = appearance.fetch(gfx->getXRef());
3009  gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3010 }
3011 
3012 // Before retrieving the res dict, regenerate the appearance stream if needed,
3013 // because AnnotFreeText::draw needs to store font info in the res dict
3015 {
3016  if (appearance.isNull()) {
3018  }
3019  return Annot::getAppearanceResDict();
3020 }
3021 
3022 //------------------------------------------------------------------------
3023 // AnnotLine
3024 //------------------------------------------------------------------------
3025 
3027 {
3028  type = typeLine;
3029  annotObj.dictSet("Subtype", Object(objName, "Line"));
3030 
3031  initialize(docA, annotObj.getDict());
3032 }
3033 
3034 AnnotLine::AnnotLine(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj)
3035 {
3036  type = typeLine;
3037  initialize(docA, annotObj.getDict());
3038 }
3039 
3040 AnnotLine::~AnnotLine() = default;
3041 
3043 {
3044  Object obj1;
3045 
3046  obj1 = dict->lookup("L");
3047  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3048  const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
3049  const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
3050  const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0);
3051  const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0);
3052 
3053  coord1 = std::make_unique<AnnotCoord>(x1, y1);
3054  coord2 = std::make_unique<AnnotCoord>(x2, y2);
3055  } else {
3056  coord1 = std::make_unique<AnnotCoord>();
3057  coord2 = std::make_unique<AnnotCoord>();
3058  }
3059 
3060  obj1 = dict->lookup("LE");
3061  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3062  Object obj2;
3063 
3064  obj2 = obj1.arrayGet(0);
3065  if (obj2.isName()) {
3066  GooString leName(obj2.getName());
3068  } else {
3070  }
3071 
3072  obj2 = obj1.arrayGet(1);
3073  if (obj2.isName()) {
3074  GooString leName(obj2.getName());
3076  } else {
3078  }
3079 
3080  } else {
3082  }
3083 
3084  obj1 = dict->lookup("IC");
3085  if (obj1.isArray()) {
3086  interiorColor = std::make_unique<AnnotColor>(obj1.getArray());
3087  }
3088 
3090 
3092  if (leaderLineExtension < 0)
3093  leaderLineExtension = 0;
3094 
3095  caption = dict->lookup("Cap").getBoolWithDefaultValue(false);
3096 
3097  obj1 = dict->lookup("IT");
3098  if (obj1.isName()) {
3099  const char *intentName = obj1.getName();
3100 
3101  if (!strcmp(intentName, "LineArrow")) {
3103  } else if (!strcmp(intentName, "LineDimension")) {
3105  } else {
3107  }
3108  } else {
3110  }
3111 
3113  if (leaderLineOffset < 0)
3114  leaderLineOffset = 0;
3115 
3116  obj1 = dict->lookup("CP");
3117  if (obj1.isName()) {
3118  const char *captionName = obj1.getName();
3119 
3120  if (!strcmp(captionName, "Inline")) {
3122  } else if (!strcmp(captionName, "Top")) {
3124  } else {
3126  }
3127  } else {
3129  }
3130 
3131  obj1 = dict->lookup("Measure");
3132  if (obj1.isDict()) {
3133  measure = nullptr;
3134  } else {
3135  measure = nullptr;
3136  }
3137 
3138  obj1 = dict->lookup("CO");
3139  if (obj1.isArray() && (obj1.arrayGetLength() == 2)) {
3142  } else {
3144  }
3145 
3146  obj1 = dict->lookup("BS");
3147  if (obj1.isDict()) {
3148  border = std::make_unique<AnnotBorderBS>(obj1.getDict());
3149  } else if (!border) {
3150  border = std::make_unique<AnnotBorderBS>();
3151  }
3152 }
3153 
3155 {
3156  Annot::setContents(new_content);
3157  if (caption)
3159 }
3160 
3161 void AnnotLine::setVertices(double x1, double y1, double x2, double y2)
3162 {
3163  coord1 = std::make_unique<AnnotCoord>(x1, y1);
3164  coord2 = std::make_unique<AnnotCoord>(x2, y2);
3165 
3166  Array *lArray = new Array(doc->getXRef());
3167  lArray->add(Object(x1));
3168  lArray->add(Object(y1));
3169  lArray->add(Object(x2));
3170  lArray->add(Object(y2));
3171 
3172  update("L", Object(lArray));
3174 }
3175 
3177 {
3178  startStyle = start;
3179  endStyle = end;
3180 
3181  Array *leArray = new Array(doc->getXRef());
3184 
3185  update("LE", Object(leArray));
3187 }
3188 
3189 void AnnotLine::setInteriorColor(std::unique_ptr<AnnotColor> &&new_color)
3190 {
3191  if (new_color) {
3192  Object obj1 = new_color->writeToObject(doc->getXRef());
3193  update("IC", std::move(obj1));
3194  interiorColor = std::move(new_color);
3195  } else {
3196  interiorColor = nullptr;
3197  }
3199 }
3200 
3202 {
3204  update("LL", Object(len));
3206 }
3207 
3209 {
3211  update("LLE", Object(len));
3212 
3213  // LL is required if LLE is present
3214  update("LL", Object(leaderLineLength));
3216 }
3217 
3218 void AnnotLine::setCaption(bool new_cap)
3219 {
3220  caption = new_cap;
3221  update("Cap", Object(new_cap));
3223 }
3224 
3226 {
3227  const char *intentName;
3228 
3229  intent = new_intent;
3230  if (new_intent == intentLineArrow)
3231  intentName = "LineArrow";
3232  else // intentLineDimension
3233  intentName = "LineDimension";
3234  update("IT", Object(objName, intentName));
3235 }
3236 
3238 {
3239  double borderWidth, ca = opacity;
3240  bool fill = false;
3241 
3242  appearBBox = std::make_unique<AnnotAppearanceBBox>(rect.get());
3243  AnnotAppearanceBuilder appearBuilder;
3244  appearBuilder.append("q\n");
3245  if (color) {
3246  appearBuilder.setDrawColor(color.get(), false);
3247  }
3248  if (interiorColor) {
3249  appearBuilder.setDrawColor(interiorColor.get(), true);
3250  fill = true;
3251  }
3252  appearBuilder.setLineStyleForBorder(border.get());
3253  borderWidth = border->getWidth();
3254  appearBBox->setBorderWidth(std::max(1., borderWidth));
3255 
3256  const double x1 = coord1->getX();
3257  const double y1 = coord1->getY();
3258  const double x2 = coord2->getX();
3259  const double y2 = coord2->getY();
3260 
3261  // Main segment length
3262  const double main_len = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
3263 
3264  // Main segment becomes positive x direction, coord1 becomes (0,0)
3265  Matrix matr;
3266  const double angle = atan2(y2 - y1, x2 - x1);
3267  matr.m[0] = matr.m[3] = cos(angle);
3268  matr.m[1] = sin(angle);
3269  matr.m[2] = -matr.m[1];
3270  matr.m[4] = x1 - rect->x1;
3271  matr.m[5] = y1 - rect->y1;
3272 
3273  double tx, ty, captionwidth = 0, captionheight = 0;
3274  AnnotLineCaptionPos actualCaptionPos = captionPos;
3275  const double fontsize = 9;
3276  const double captionhmargin = 2; // Left and right margin (inline caption only)
3277  const double captionmaxwidth = main_len - 2 * captionhmargin;
3278  const double lineendingSize = std::min(6. * borderWidth, main_len / 2);
3279 
3280  Dict *fontResDict;
3281  GfxFont *font;
3282 
3283  // Calculate caption width and height
3284  if (caption) {
3285  fontResDict = new Dict(doc->getXRef());
3286  font = createAnnotDrawFont(doc->getXRef(), fontResDict);
3287  int lines = 0;
3288  int i = 0;
3289  while (i < contents->getLength()) {
3290  GooString out;
3291  double linewidth;
3292  layoutText(contents.get(), &out, &i, font, &linewidth, 0, nullptr, false);
3293  linewidth *= fontsize;
3294  if (linewidth > captionwidth) {
3295  captionwidth = linewidth;
3296  }
3297  ++lines;
3298  }
3299  captionheight = lines * fontsize;
3300  // If text is longer than available space, turn into captionPosTop
3301  if (captionwidth > captionmaxwidth) {
3302  actualCaptionPos = captionPosTop;
3303  }
3304  } else {
3305  fontResDict = nullptr;
3306  font = nullptr;
3307  }
3308 
3309  // Draw main segment
3311  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3312  appearBBox->extendTo(tx, ty);
3313 
3314  if (captionwidth != 0 && actualCaptionPos == captionPosInline) { // Break in the middle
3315  matr.transform((main_len - captionwidth) / 2 - captionhmargin, leaderLineLength, &tx, &ty);
3316  appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3317 
3318  matr.transform((main_len + captionwidth) / 2 + captionhmargin, leaderLineLength, &tx, &ty);
3319  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3320  }
3321 
3322  matr.transform(main_len - AnnotAppearanceBuilder::lineEndingXShorten(endStyle, lineendingSize), leaderLineLength, &tx, &ty);
3323  appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3324  appearBBox->extendTo(tx, ty);
3325 
3327  const double extendX { -AnnotAppearanceBuilder::lineEndingXExtendBBox(startStyle, lineendingSize) };
3328  appearBuilder.drawLineEnding(startStyle, 0, leaderLineLength, -lineendingSize, fill, matr);
3329  matr.transform(extendX, leaderLineLength + lineendingSize / 2., &tx, &ty);
3330  appearBBox->extendTo(tx, ty);
3331  matr.transform(extendX, leaderLineLength - lineendingSize / 2., &tx, &ty);
3332  appearBBox->extendTo(tx, ty);
3333  }
3334 
3335  if (endStyle != annotLineEndingNone) {
3336  const double extendX { AnnotAppearanceBuilder::lineEndingXExtendBBox(endStyle, lineendingSize) };
3337  appearBuilder.drawLineEnding(endStyle, main_len, leaderLineLength, lineendingSize, fill, matr);
3338  matr.transform(main_len + extendX, leaderLineLength + lineendingSize / 2., &tx, &ty);
3339  appearBBox->extendTo(tx, ty);
3340  matr.transform(main_len + extendX, leaderLineLength - lineendingSize / 2., &tx, &ty);
3341  appearBBox->extendTo(tx, ty);
3342  }
3343 
3344  // Draw caption text
3345  if (caption) {
3346  double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords
3347  if (actualCaptionPos == captionPosInline) {
3348  tly = leaderLineLength + captionheight / 2;
3349  } else {
3350  tly = leaderLineLength + captionheight + 2 * borderWidth;
3351  }
3352 
3353  tlx += captionTextHorizontal;
3354  tly += captionTextVertical;
3355 
3356  // Adjust bounding box
3357  matr.transform(tlx, tly - captionheight, &tx, &ty);
3358  appearBBox->extendTo(tx, ty);
3359  matr.transform(tlx + captionwidth, tly - captionheight, &tx, &ty);
3360  appearBBox->extendTo(tx, ty);
3361  matr.transform(tlx + captionwidth, tly, &tx, &ty);
3362  appearBBox->extendTo(tx, ty);
3363  matr.transform(tlx, tly, &tx, &ty);
3364  appearBBox->extendTo(tx, ty);
3365 
3366  // Setup text state (reusing transformed top-left coord)
3367  appearBuilder.appendf("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize); // Font color: black
3368  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);
3369  appearBuilder.appendf("0 {0:.2f} Td\n", -fontsize * font->getDescent());
3370  // Draw text
3371  int i = 0;
3372  double xposPrev = 0;
3373  while (i < contents->getLength()) {
3374  GooString out;
3375  double linewidth, xpos;
3376  layoutText(contents.get(), &out, &i, font, &linewidth, 0, nullptr, false);
3377  linewidth *= fontsize;
3378  xpos = (captionwidth - linewidth) / 2;
3379  appearBuilder.appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize);
3380  appearBuilder.writeString(out);
3381  appearBuilder.append("Tj\n");
3382  xposPrev = xpos;
3383  }
3384  appearBuilder.append("ET\n");
3385  font->decRefCnt();
3386  }
3387 
3388  // Draw leader lines
3389  double ll_len = fabs(leaderLineLength) + leaderLineExtension;
3390  double sign = leaderLineLength >= 0 ? 1 : -1;
3391  if (ll_len != 0) {
3392  matr.transform(0, 0, &tx, &ty);
3393  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3394  appearBBox->extendTo(tx, ty);
3395  matr.transform(0, sign * ll_len, &tx, &ty);
3396  appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3397  appearBBox->extendTo(tx, ty);
3398 
3399  matr.transform(main_len, 0, &tx, &ty);
3400  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3401  appearBBox->extendTo(tx, ty);
3402  matr.transform(main_len, sign * ll_len, &tx, &ty);
3403  appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3404  appearBBox->extendTo(tx, ty);
3405  }
3406 
3407  appearBuilder.append("Q\n");
3408 
3409  double bbox[4];
3410  appearBBox->getBBoxRect(bbox);
3411  if (ca == 1) {
3412  appearance = createForm(appearBuilder.buffer(), bbox, false, fontResDict);
3413  } else {
3414  Object aStream = createForm(appearBuilder.buffer(), bbox, true, fontResDict);
3415 
3416  GooString appearBuf("/GS0 gs\n/Fm0 Do");
3417  Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
3418  appearance = createForm(&appearBuf, bbox, false, resDict);
3419  }
3420 }
3421 
3422 void AnnotLine::draw(Gfx *gfx, bool printing)
3423 {
3424  if (!isVisible(printing))
3425  return;
3426 
3427  annotLocker();
3428  if (appearance.isNull()) {
3430  }
3431 
3432  // draw the appearance stream
3433  Object obj = appearance.fetch(gfx->getXRef());
3434  if (appearBBox) {
3435  gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
3436  } else {
3437  gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3438  }
3439 }
3440 
3441 // Before retrieving the res dict, regenerate the appearance stream if needed,
3442 // because AnnotLine::draw may need to store font info in the res dict
3444 {
3445  if (appearance.isNull()) {
3447  }
3448  return Annot::getAppearanceResDict();
3449 }
3450 
3451 //------------------------------------------------------------------------
3452 // AnnotTextMarkup
3453 //------------------------------------------------------------------------
3455 {
3456  switch (subType) {
3457  case typeHighlight:
3458  annotObj.dictSet("Subtype", Object(objName, "Highlight"));
3459  break;
3460  case typeUnderline:
3461  annotObj.dictSet("Subtype", Object(objName, "Underline"));
3462  break;
3463  case typeSquiggly:
3464  annotObj.dictSet("Subtype", Object(objName, "Squiggly"));
3465  break;
3466  case typeStrikeOut:
3467  annotObj.dictSet("Subtype", Object(objName, "StrikeOut"));
3468  break;
3469  default:
3470  assert(0 && "Invalid subtype for AnnotTextMarkup\n");
3471  }
3472 
3473  // Store dummy quadrilateral with null coordinates
3474  Array *quadPoints = new Array(doc->getXRef());
3475  for (int i = 0; i < 4 * 2; ++i) {
3476  quadPoints->add(Object(0.));
3477  }
3478  annotObj.dictSet("QuadPoints", Object(quadPoints));
3479 
3480  initialize(docA, annotObj.getDict());
3481 }
3482 
3483 AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj)
3484 {
3485  // the real type will be read in initialize()
3486  type = typeHighlight;
3487  initialize(docA, annotObj.getDict());
3488 }
3489 
3491 
3493 {
3494  Object obj1;
3495 
3496  obj1 = dict->lookup("Subtype");
3497  if (obj1.isName()) {
3498  GooString typeName(obj1.getName());
3499  if (!typeName.cmp("Highlight")) {
3500  type = typeHighlight;
3501  } else if (!typeName.cmp("Underline")) {
3502  type = typeUnderline;
3503  } else if (!typeName.cmp("Squiggly")) {
3504  type = typeSquiggly;
3505  } else if (!typeName.cmp("StrikeOut")) {
3506  type = typeStrikeOut;
3507  }
3508  }
3509 
3510  obj1 = dict->lookup("QuadPoints");
3511  if (obj1.isArray()) {
3512  quadrilaterals = std::make_unique<AnnotQuadrilaterals>(obj1.getArray(), rect.get());
3513  } else {
3514  error(errSyntaxError, -1, "Bad Annot Text Markup QuadPoints");
3515  ok = false;
3516  }
3517 }
3518 
3520 {
3521  const char *typeName = nullptr; /* squelch bogus compiler warning */
3522 
3523  switch (new_type) {
3524  case typeHighlight:
3525  typeName = "Highlight";
3526  break;
3527  case typeUnderline:
3528  typeName = "Underline";
3529  break;
3530  case typeSquiggly:
3531  typeName = "Squiggly";
3532  break;
3533  case typeStrikeOut:
3534  typeName = "StrikeOut";
3535  break;
3536  default:
3537  assert(!"Invalid subtype");
3538  }
3539 
3540  type = new_type;
3541  update("Subtype", Object(objName, typeName));
3543 }
3544 
3546 {
3547  Array *a = new Array(doc->getXRef());
3548 
3549  for (int i = 0; i < quadPoints->getQuadrilateralsLength(); ++i) {
3550  a->add(Object(quadPoints->getX1(i)));
3551  a->add(Object(quadPoints->getY1(i)));
3552  a->add(Object(quadPoints->getX2(i)));
3553  a->add(Object(quadPoints->getY2(i)));
3554  a->add(Object(quadPoints->getX3(i)));
3555  a->add(Object(quadPoints->getY3(i)));
3556  a->add(Object(quadPoints->getX4(i)));
3557  a->add(Object(quadPoints->getY4(i)));
3558  }
3559 
3560  quadrilaterals = std::make_unique<AnnotQuadrilaterals>(a, rect.get());
3561 
3562  annotObj.dictSet("QuadPoints", Object(a));
3564 }
3565 
3567 {
3568  if (appearance.isNull())
3569  return true;
3570 
3571  // Adobe Reader seems to have a complex condition for when to use the
3572  // appearance stream of typeHighlight, which is "use it if it has a Resources dictionary with ExtGState"
3573  // this is reverse engineering of me editing a file by hand and checking what it does so the real
3574  // condition may be more or less complex
3575  if (type == typeHighlight) {
3576  XRef *xref = gfx->getXRef();
3577  const Object fetchedApperance = appearance.fetch(xref);
3578  if (fetchedApperance.isStream()) {
3579  const Object resources = fetchedApperance.streamGetDict()->lookup("Resources");
3580  if (resources.isDict()) {
3581  if (resources.dictLookup("ExtGState").isDict()) {
3582  return false;
3583  }
3584  }
3585  }
3586  return true;
3587  }
3588 
3589  return false;
3590 }
3591 
3593 {
3594  double ca = 1;
3595  int i;
3596 
3597  if (!isVisible(printing))
3598  return;
3599 
3600  annotLocker();
3601  if (shouldCreateApperance(gfx)) {
3602  bool blendMultiply = true;
3603  ca = opacity;
3604 
3605  AnnotAppearanceBuilder appearBuilder;
3606  appearBuilder.append("q\n");
3607 
3608  /* Adjust BBox */
3609  appearBBox = std::make_unique<AnnotAppearanceBBox>(rect.get());
3610  for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3611  appearBBox->extendTo(quadrilaterals->getX1(i) - rect->x1, quadrilaterals->getY1(i) - rect->y1);
3612  appearBBox->extendTo(quadrilaterals->getX2(i) - rect->x1, quadrilaterals->getY2(i) - rect->y1);
3613  appearBBox->extendTo(quadrilaterals->getX3(i) - rect->x1, quadrilaterals->getY3(i) - rect->y1);
3614  appearBBox->extendTo(quadrilaterals->getX4(i) - rect->x1, quadrilaterals->getY4(i) - rect->y1);
3615  }
3616 
3617  switch (type) {
3618  case typeUnderline:
3619  if (color) {
3620  appearBuilder.setDrawColor(color.get(), false);
3621  }
3622  appearBuilder.append("[] 0 d 1 w\n");
3623 
3624  for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3625  double x3, y3, x4, y4;
3626 
3627  x3 = quadrilaterals->getX3(i);
3628  y3 = quadrilaterals->getY3(i);
3629  x4 = quadrilaterals->getX4(i);
3630  y4 = quadrilaterals->getY4(i);
3631 
3632  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3);
3633  appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x4, y4);
3634  appearBuilder.append("S\n");
3635  }
3636  break;
3637  case typeStrikeOut:
3638  if (color) {
3639  appearBuilder.setDrawColor(color.get(), false);
3640  }
3641  blendMultiply = false;
3642  appearBuilder.append("[] 0 d 1 w\n");
3643 
3644  for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3645  double x1, y1, x2, y2;
3646  double x3, y3, x4, y4;
3647 
3648  x1 = quadrilaterals->getX1(i);
3649  y1 = quadrilaterals->getY1(i);
3650  x2 = quadrilaterals->getX2(i);
3651  y2 = quadrilaterals->getY2(i);
3652 
3653  x3 = quadrilaterals->getX3(i);
3654  y3 = quadrilaterals->getY3(i);
3655  x4 = quadrilaterals->getX4(i);
3656  y4 = quadrilaterals->getY4(i);
3657 
3658  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", (x1 + x3) / 2., (y1 + y3) / 2.);
3659  appearBuilder.appendf("{0:.2f} {1:.2f} l\n", (x2 + x4) / 2., (y2 + y4) / 2.);
3660  appearBuilder.append("S\n");
3661  }
3662  break;
3663  case typeSquiggly:
3664  if (color) {
3665  appearBuilder.setDrawColor(color.get(), false);
3666  }
3667  appearBuilder.append("[] 0 d 1 w\n");
3668 
3669  for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3670  double x1, y1, x2, y3;
3671  double h6;
3672 
3673  x1 = quadrilaterals->getX1(i);
3674  y1 = quadrilaterals->getY1(i);
3675  x2 = quadrilaterals->getX2(i);
3676  y3 = quadrilaterals->getY3(i);
3677  h6 = (y1 - y3) / 6.0;
3678 
3679  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x1, y3 + h6);
3680  bool down = false;
3681  do {
3682  down = !down; // Zigzag line
3683  x1 += 2;
3684  appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x1, y3 + (down ? 0 : h6));
3685  } while (x1 < x2);
3686  appearBuilder.append("S\n");
3687  }
3688  break;
3689  default:
3690  case typeHighlight:
3691  if (color)
3692  appearBuilder.setDrawColor(color.get(), true);
3693 
3694  double biggestBorder = 0;
3695  for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
3696  double x1, y1, x2, y2, x3, y3, x4, y4;
3697  double h4;
3698 
3699  x1 = quadrilaterals->getX1(i);
3700  y1 = quadrilaterals->getY1(i);
3701  x2 = quadrilaterals->getX2(i);
3702  y2 = quadrilaterals->getY2(i);
3703  x3 = quadrilaterals->getX3(i);
3704  y3 = quadrilaterals->getY3(i);
3705  x4 = quadrilaterals->getX4(i);
3706  y4 = quadrilaterals->getY4(i);
3707  h4 = fabs(y1 - y3) / 4.0;
3708 
3709  if (h4 > biggestBorder) {
3710  biggestBorder = h4;
3711  }
3712 
3713  appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3);
3714  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);
3715  appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x2, y2);
3716  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);
3717  appearBuilder.append("f\n");
3718  }
3719  appearBBox->setBorderWidth(biggestBorder);
3720  break;
3721  }
3722  appearBuilder.append("Q\n");
3723 
3724  double bbox[4];
3725  bbox[0] = appearBBox->getPageXMin();
3726  bbox[1] = appearBBox->getPageYMin();
3727  bbox[2] = appearBBox->getPageXMax();
3728  bbox[3] = appearBBox->getPageYMax();
3729  Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr);
3730 
3731  GooString appearBuf("/GS0 gs\n/Fm0 Do");
3732  Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", 1, blendMultiply ? "Multiply" : nullptr);
3733  if (ca == 1) {
3734  appearance = createForm(&appearBuf, bbox, false, resDict);
3735  } else {
3736  aStream = createForm(&appearBuf, bbox, true, resDict);
3737 
3738  Dict *resDict2 = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
3739  appearance = createForm(&appearBuf, bbox, false, resDict2);
3740  }
3741  }
3742 
3743  // draw the appearance stream
3744  Object obj = appearance.fetch(gfx->getXRef());
3745  if (appearBBox) {
3746  gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
3747  } else {
3748  gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3749  }
3750 }
3751 
3752 //------------------------------------------------------------------------
3753 // AnnotWidget
3754 //------------------------------------------------------------------------
3755 
3756 AnnotWidget::AnnotWidget(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj)
3757 {
3758  type = typeWidget;
3759  field = nullptr;
3760  initialize(docA, annotObj.getDict());
3761 }
3762 
3763 AnnotWidget::AnnotWidget(PDFDoc *docA, Object *dictObject, Object *obj, FormField *fieldA) : Annot(docA, dictObject->copy(), obj)
3764 {
3765  type = typeWidget;
3766  field = fieldA;
3767  initialize(docA, dictObject->getDict());
3768 }
3769 
3770 AnnotWidget::~AnnotWidget() = default;
3771 
3773 {
3774  Object obj1;
3775 
3776  form = doc->getCatalog()->getForm();
3777 
3778  obj1 = dict->lookup("H");
3779  if (obj1.isName()) {
3780  const char *modeName = obj1.getName();
3781 
3782  if (!strcmp(modeName, "N")) {
3784  } else if (!strcmp(modeName, "O")) {
3786  } else if (!strcmp(modeName, "P") || !strcmp(modeName, "T")) {
3788  } else {
3790  }
3791  } else {
3793  }
3794 
3795  obj1 = dict->lookup("MK");
3796  if (obj1.isDict()) {
3797  appearCharacs = std::make_unique<AnnotAppearanceCharacs>(obj1.getDict());
3798  }
3799 
3800  obj1 = dict->lookup("A");
3801  if (obj1.isDict()) {
3803  }
3804 
3805  additionalActions = dict->lookupNF("AA").copy();
3806 
3807  obj1 = dict->lookup("Parent");
3808  if (obj1.isDict()) {
3809  parent = nullptr;
3810  } else {
3811  parent = nullptr;
3812  }
3813 
3814  obj1 = dict->lookup("BS");
3815  if (obj1.isDict()) {
3816  border = std::make_unique<AnnotBorderBS>(obj1.getDict());
3817  }
3818 
3820 }
3821 
3822 std::unique_ptr<LinkAction> AnnotWidget::getAdditionalAction(AdditionalActionsType additionalActionType)
3823 {
3824  return ::getAdditionalAction(additionalActionType, &additionalActions, doc);
3825 }
3826 
3827 std::unique_ptr<LinkAction> AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType)
3828 {
3829  Object additionalActionsObject = additionalActions.fetch(doc->getXRef());
3830 
3831  if (additionalActionsObject.isDict()) {
3832  const char *key = getFormAdditionalActionKey(formAdditionalActionType);
3833 
3834  Object actionObject = additionalActionsObject.dictLookup(key);
3835  if (actionObject.isDict())
3836  return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
3837  }
3838 
3839  return nullptr;
3840 }
3841 
3843 {
3844  Object additionalActionsObject = additionalActions.fetch(doc->getXRef());
3845 
3846  if (!additionalActionsObject.isDict()) {
3847  additionalActionsObject = Object(new Dict(doc->getXRef()));
3848  annotObj.dictSet("AA", additionalActionsObject.copy());
3849  }
3850 
3851  additionalActionsObject.dictSet(getFormAdditionalActionKey(formAdditionalActionType), LinkJavaScript::createObject(doc->getXRef(), js));
3852 
3853  if (additionalActions.isRef()) {
3854  doc->getXRef()->setModifiedObject(&additionalActionsObject, additionalActions.getRef());
3855  } else if (hasRef) {
3857  } else {
3858  error(errInternal, -1, "AnnotWidget::setFormAdditionalAction, where neither additionalActions is ref nor annotobj itself is ref");
3859  return false;
3860  }
3861  return true;
3862 }
3863 
3865 {
3866  if (!newAppearance.isNull()) {
3867  appearStreams = std::make_unique<AnnotAppearance>(doc, &newAppearance);
3868  update("AP", std::move(newAppearance));
3869  }
3870 
3871  if (appearStreams)
3872  appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str());
3873 }
3874 
3875 // Grand unified handler for preparing text strings to be drawn into form
3876 // fields. Takes as input a text string (in PDFDocEncoding or UTF-16).
3877 // Converts some or all of this string to the appropriate encoding for the
3878 // specified font, and computes the width of the text. Can optionally stop
3879 // converting when a specified width has been reached, to perform line-breaking
3880 // for multi-line fields.
3881 //
3882 // Parameters:
3883 // text: input text string to convert
3884 // outBuf: buffer for writing re-encoded string
3885 // i: index at which to start converting; will be updated to point just after
3886 // last character processed
3887 // font: the font which will be used for display
3888 // width: computed width (unscaled by font size) will be stored here
3889 // widthLimit: if non-zero, stop converting to keep width under this value
3890 // (should be scaled down by font size)
3891 // charCount: count of number of characters will be stored here
3892 // noReencode: if set, do not try to translate the character encoding
3893 // (useful for Zapf Dingbats or other unusual encodings)
3894 // can only be used with simple fonts, not CID-keyed fonts
3895 //
3896 // TODO: Handle surrogate pairs in UTF-16.
3897 // Should be able to generate output for any CID-keyed font.
3898 // Doesn't handle vertical fonts--should it?
3899 void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont *font, double *width, double widthLimit, int *charCount, bool noReencode)
3900 {
3901  CharCode c;
3902  Unicode uChar;
3903  const Unicode *uAux;
3904  double w = 0.0;
3905  int uLen, n;
3906  double dx, dy, ox, oy;
3907 
3908  if (width != nullptr)
3909  *width = 0.0;
3910  if (charCount != nullptr)
3911  *charCount = 0;
3912 
3913  if (!text) {
3914  return;
3915  }
3916  bool unicode = text->hasUnicodeMarker();
3917  bool spacePrev; // previous character was a space
3918 
3919  // State for backtracking when more text has been processed than fits within
3920  // widthLimit. We track for both input (text) and output (outBuf) the offset
3921  // to the first character to discard.
3922  //
3923  // We keep track of several points:
3924  // 1 - end of previous completed word which fits
3925  // 2 - previous character which fit
3926  int last_i1, last_i2, last_o1, last_o2;
3927 
3928  if (unicode && text->getLength() % 2 != 0) {
3929  error(errSyntaxError, -1, "AnnotWidget::layoutText, bad unicode string");
3930  return;
3931  }
3932 
3933  // skip Unicode marker on string if needed
3934  if (unicode && *i == 0)
3935  *i = 2;
3936 
3937  // Start decoding and copying characters, until either:
3938  // we reach the end of the string
3939  // we reach the maximum width
3940  // we reach a newline character
3941  // As we copy characters, keep track of the last full word to fit, so that we
3942  // can backtrack if we exceed the maximum width.
3943  last_i1 = last_i2 = *i;
3944  last_o1 = last_o2 = 0;
3945  spacePrev = false;
3946  outBuf->clear();
3947 
3948  while (*i < text->getLength()) {
3949  last_i2 = *i;
3950  last_o2 = outBuf->getLength();
3951 
3952  if (unicode) {
3953  uChar = (unsigned char)(text->getChar(*i)) << 8;
3954  uChar += (unsigned char)(text->getChar(*i + 1));
3955  *i += 2;
3956  } else {
3957  if (noReencode)
3958  uChar = text->getChar(*i) & 0xff;
3959  else
3960  uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
3961  *i += 1;
3962  }
3963 
3964  // Explicit line break?
3965  if (uChar == '\r' || uChar == '\n') {
3966  // Treat a <CR><LF> sequence as a single line break
3967  if (uChar == '\r' && *i < text->getLength()) {
3968  if (unicode && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
3969  *i += 2;
3970  else if (!unicode && text->getChar(*i) == '\n')
3971  *i += 1;
3972  }
3973 
3974  break;
3975  }
3976 
3977  if (noReencode) {
3978  outBuf->append(uChar);
3979  } else {
3980  const CharCodeToUnicode *ccToUnicode = font->getToUnicode();
3981  if (!ccToUnicode) {
3982  // This assumes an identity CMap.
3983  outBuf->append((uChar >> 8) & 0xff);
3984  outBuf->append(uChar & 0xff);
3985  } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
3986  if (font->isCIDFont()) {
3987  // TODO: This assumes an identity CMap. It should be extended to
3988  // handle the general case.
3989  outBuf->append((c >> 8) & 0xff);
3990  outBuf->append(c & 0xff);
3991  } else {
3992  // 8-bit font
3993  outBuf->append(c);
3994  }
3995  } else {
3996  error(errSyntaxError, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar);
3997  }
3998  }
3999 
4000  // If we see a space, then we have a linebreak opportunity.
4001  if (uChar == ' ') {
4002  last_i1 = *i;
4003  if (!spacePrev)
4004  last_o1 = last_o2;
4005  spacePrev = true;
4006  } else {
4007  spacePrev = false;
4008  }
4009 
4010  // Compute width of character just output
4011  if (outBuf->getLength() > last_o2) {
4012  dx = 0.0;
4013  font->getNextChar(outBuf->c_str() + last_o2, outBuf->getLength() - last_o2, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4014  w += dx;
4015  }
4016 
4017  // Current line over-full now?
4018  if (widthLimit > 0.0 && w > widthLimit) {
4019  if (last_o1 > 0) {
4020  // Back up to the previous word which fit, if there was a previous
4021  // word.
4022  *i = last_i1;
4023  outBuf->del(last_o1, outBuf->getLength() - last_o1);
4024  } else if (last_o2 > 0) {
4025  // Otherwise, back up to the previous character (in the only word on
4026  // this line)
4027  *i = last_i2;
4028  outBuf->del(last_o2, outBuf->getLength() - last_o2);
4029  } else {
4030  // Otherwise, we were trying to fit the first character; include it
4031  // anyway even if it overflows the space--no updates to make.
4032  }
4033  break;
4034  }
4035  }
4036 
4037  // If splitting input into lines because we reached the width limit, then
4038  // consume any remaining trailing spaces that would go on this line from the
4039  // input. If in doing so we reach a newline, consume that also. This code
4040  // won't run if we stopped at a newline above, since in that case w <=
4041  // widthLimit still.
4042  if (widthLimit > 0.0 && w > widthLimit) {
4043  if (unicode) {
4044  while (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
4045  *i += 2;
4046  if (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
4047  *i += 2;
4048  if (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
4049  *i += 2;
4050  } else {
4051  while (*i < text->getLength() && text->getChar(*i) == ' ')
4052  *i += 1;
4053  if (*i < text->getLength() && text->getChar(*i) == '\r')
4054  *i += 1;
4055  if (*i < text->getLength() && text->getChar(*i) == '\n')
4056  *i += 1;
4057  }
4058  }
4059 
4060  // Compute the actual width and character count of the final string, based on
4061  // breakpoint, if this information is requested by the caller.
4062  if (width != nullptr || charCount != nullptr) {
4063  const char *s = outBuf->c_str();
4064  int len = outBuf->getLength();
4065 
4066  while (len > 0) {
4067  dx = 0.0;
4068  n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4069 
4070  if (n == 0) {
4071  break;
4072  }
4073 
4074  if (width != nullptr)
4075  *width += dx;
4076  if (charCount != nullptr)
4077  *charCount += 1;
4078 
4079  s += n;
4080  len -= n;
4081  }
4082  }
4083 }
4084 
4085 // Copy the given string to appearBuf, adding parentheses around it and
4086 // escaping characters as appropriate.
4088 {
4089  char c;
4090  int i;
4091 
4092  appearBuf->append('(');
4093 
4094  for (i = 0; i < str.getLength(); ++i) {
4095  c = str.getChar(i);
4096  if (c == '(' || c == ')' || c == '\\') {
4097  appearBuf->append('\\');
4098  appearBuf->append(c);
4099  } else if (c < 0x20) {
4100  appearBuf->appendf("\\{0:03o}", (unsigned char)c);
4101  } else {
4102  appearBuf->append(c);
4103  }
4104  }
4105 
4106  appearBuf->append(')');
4107 }
4108 
4109 // Draw the variable text or caption for a field.
4110 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,
4111  int quadding, bool txField, bool forceZapfDingbats, XRef *xref, bool *addedDingbatsResource, bool password)
4112 {
4113  std::vector<GooString *> *daToks;
4114  GooString *tok;
4115  GooString convertedText;
4116  const GfxFont *font;
4117  double dx, dy;
4118  double fontSize, borderWidth, x, xPrev, y, w, wMax;
4119  int tfPos, tmPos, j;
4120  int rot;
4121  bool freeText = false; // true if text should be freed before return
4122  GfxFont *fontToFree = nullptr;
4123 
4124  //~ if there is no MK entry, this should use the existing content stream,
4125  //~ and only replace the marked content portion of it
4126  //~ (this is only relevant for Tx fields)
4127 
4128  // parse the default appearance string
4129  tfPos = tmPos = -1;
4130  if (da) {
4131  daToks = new std::vector<GooString *>();
4132  int i = 0;
4133  while (i < da->getLength()) {
4134  while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
4135  ++i;
4136  }
4137  if (i < da->getLength()) {
4138  for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j)
4139  ;
4140  daToks->push_back(new GooString(da, i, j - i));
4141  i = j;
4142  }
4143  }
4144  for (i = 2; i < (int)daToks->size(); ++i) {
4145  if (i >= 2 && !((*daToks)[i])->cmp("Tf")) {
4146  tfPos = i - 2;
4147  } else if (i >= 6 && !((*daToks)[i])->cmp("Tm")) {
4148  tmPos = i - 6;
4149  }
4150  }
4151  } else {
4152  daToks = nullptr;
4153  }
4154 
4155  // force ZapfDingbats
4156  if (forceZapfDingbats) {
4157  assert(xref != nullptr);
4158  assert(addedDingbatsResource != nullptr);
4159  *addedDingbatsResource = false;
4160 
4161  if (tfPos >= 0) {
4162  tok = (*daToks)[tfPos];
4163  if (tok->cmp("/ZaDb")) {
4164  tok->clear();
4165  tok->append("/ZaDb");
4166  }
4167  }
4168  }
4169  // get the font and font size
4170  font = nullptr;
4171  fontSize = 0;
4172  if (tfPos >= 0) {
4173  tok = (*daToks)[tfPos];
4174  if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
4175  if (!resources || !(font = resources->lookupFont(tok->c_str() + 1))) {
4176  if (forceZapfDingbats) {
4177  // We are forcing ZaDb but the font does not exist
4178  // so create a fake one
4179  Ref r = Ref::INVALID(); // dummy Ref, it's not used at all in this codepath
4180  Dict *d = new Dict(xref);
4181  fontToFree = new Gfx8BitFont(xref, "ZaDb", r, new GooString("ZapfDingbats"), fontType1, r, d);
4182  delete d;
4183  font = fontToFree;
4184  *addedDingbatsResource = true;
4185  } else {
4186  error(errSyntaxError, -1, "Unknown font in field's DA string");
4187  }
4188  }
4189  } else {
4190  error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
4191  }
4192  tok = (*daToks)[tfPos + 1];
4193  fontSize = gatof(tok->c_str());
4194  } else {
4195  error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4196  }
4197  if (!font) {
4198  if (daToks) {
4199  for (auto entry : *daToks) {
4200  delete entry;
4201  }
4202  delete daToks;
4203  }
4204  return false;
4205  }
4206 
4207  // get the border width
4208  borderWidth = border ? border->getWidth() : 0;
4209 
4210  // for a password field, replace all characters with asterisks
4211  if (password) {
4212  int len;
4213  if (text->hasUnicodeMarker())
4214  len = (text->getLength() - 2) / 2;
4215  else
4216  len = text->getLength();
4217 
4218  GooString *newText = new GooString;
4219  for (int i = 0; i < len; ++i)
4220  newText->append('*');
4221  text = newText;
4222  freeText = true;
4223  }
4224 
4225  // setup
4226  if (txField) {
4227  appearBuf->append("/Tx BMC\n");
4228  }
4229  appearBuf->append("q\n");
4230  rot = appearCharacs ? appearCharacs->getRotation() : 0;
4231  switch (rot) {
4232  case 90:
4233  appearBuf->appendf("0 1 -1 0 {0:.2f} 0 cm\n", rect->x2 - rect->x1);
4234  dx = rect->y2 - rect->y1;
4235  dy = rect->x2 - rect->x1;
4236  break;
4237  case 180:
4238  appearBuf->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n", rect->x2 - rect->x1, rect->y2 - rect->y1);
4239  dx = rect->x2 - rect->y2;
4240  dy = rect->y2 - rect->y1;
4241  break;
4242  case 270:
4243  appearBuf->appendf("0 -1 1 0 0 {0:.2f} cm\n", rect->y2 - rect->y1);
4244  dx = rect->y2 - rect->y1;
4245  dy = rect->x2 - rect->x1;
4246  break;
4247  default: // assume rot == 0
4248  dx = rect->x2 - rect->x1;
4249  dy = rect->y2 - rect->y1;
4250  break;
4251  }
4252  appearBuf->append("BT\n");
4253  // multi-line text
4254  if (multiline) {
4255  // note: the comb flag is ignored in multiline mode
4256 
4257  wMax = dx - 2 * borderWidth - 4;
4258 
4259  // compute font autosize
4260  if (fontSize == 0) {
4261  for (fontSize = 20; fontSize > 1; --fontSize) {
4262  y = dy - 3;
4263  int i = 0;
4264  while (i < text->getLength()) {
4265  Annot::layoutText(text, &convertedText, &i, font, &w, wMax / fontSize, nullptr, forceZapfDingbats);
4266  y -= fontSize;
4267  }
4268  // approximate the descender for the last line
4269  if (y >= 0.33 * fontSize) {
4270  break;
4271  }
4272  }
4273  if (tfPos >= 0) {
4274  tok = (*daToks)[tfPos + 1];
4275  tok->clear();
4276  tok->appendf("{0:.2f}", fontSize);
4277  }
4278  }
4279 
4280  // starting y coordinate
4281  // (note: each line of text starts with a Td operator that moves
4282  // down a line)
4283  y = dy - 3;
4284 
4285  // set the font matrix
4286  if (tmPos >= 0) {
4287  tok = (*daToks)[tmPos + 4];
4288  tok->clear();
4289  tok->append('0');
4290  tok = (*daToks)[tmPos + 5];
4291  tok->clear();
4292  tok->appendf("{0:.2f}", y);
4293  }
4294 
4295  // write the DA string
4296  if (daToks) {
4297  for (const GooString *daTok : *daToks) {
4298  appearBuf->append(daTok)->append(' ');
4299  }
4300  }
4301 
4302  // write the font matrix (if not part of the DA string)
4303  if (tmPos < 0) {
4304  appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
4305  }
4306 
4307  // write a series of lines of text
4308  int i = 0;
4309  xPrev = 0;
4310  while (i < text->getLength()) {
4311  Annot::layoutText(text, &convertedText, &i, font, &w, wMax / fontSize, nullptr, forceZapfDingbats);
4312  w *= fontSize;
4313 
4314  // compute text start position
4315  switch (quadding) {
4316  case quaddingLeftJustified:
4317  default:
4318  x = borderWidth + 2;
4319  break;
4320  case quaddingCentered:
4321  x = (dx - w) / 2;
4322  break;
4324  x = dx - borderWidth - 2 - w;
4325  break;
4326  }
4327 
4328  // draw the line
4329  appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
4330  writeString(convertedText);
4331  appearBuf->append(" Tj\n");
4332 
4333  // next line
4334  xPrev = x;
4335  }
4336 
4337  // single-line text
4338  } else {
4339  //~ replace newlines with spaces? - what does Acrobat do?
4340 
4341  // comb formatting
4342  if (comb > 0) {
4343  int charCount;
4344 
4345  // compute comb spacing
4346  w = (dx - 2 * borderWidth) / comb;
4347 
4348  // compute font autosize
4349  if (fontSize == 0) {
4350  fontSize = dy - 2 * borderWidth;
4351  if (w < fontSize) {
4352  fontSize = w;
4353  }
4354  fontSize = floor(fontSize);
4355  if (tfPos >= 0) {
4356  tok = (*daToks)[tfPos + 1];
4357  tok->clear();
4358  tok->appendf("{0:.2f}", fontSize);
4359  }
4360  }
4361 
4362  int i = 0;
4363  Annot::layoutText(text, &convertedText, &i, font, nullptr, 0.0, &charCount, forceZapfDingbats);
4364  if (charCount > comb)
4365  charCount = comb;
4366 
4367  // compute starting text cell
4368  switch (quadding) {
4369  case quaddingLeftJustified:
4370  default:
4371  x = borderWidth;
4372  break;
4373  case quaddingCentered:
4374  x = borderWidth + (comb - charCount) / 2.0 * w;
4375  break;
4377  x = borderWidth + (comb - charCount) * w;
4378  break;
4379  }
4380  y = 0.5 * dy - 0.4 * fontSize;
4381 
4382  // set the font matrix
4383  if (tmPos >= 0) {
4384  tok = (*daToks)[tmPos + 4];
4385  tok->clear();
4386  tok->appendf("{0:.2f}", x);
4387  tok = (*daToks)[tmPos + 5];
4388  tok->clear();
4389  tok->appendf("{0:.2f}", y);
4390  }
4391 
4392  // write the DA string
4393  if (daToks) {
4394  for (i = 0; i < (int)daToks->size(); ++i) {
4395  appearBuf->append((*daToks)[i])->append(' ');
4396  }
4397  }
4398 
4399  // write the font matrix (if not part of the DA string)
4400  if (tmPos < 0) {
4401  appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
4402  }
4403 
4404  // write the text string
4405  const char *s = convertedText.c_str();
4406  int len = convertedText.getLength();
4407  i = 0;
4408  xPrev = w; // so that first character is placed properly
4409  while (i < comb && len > 0) {
4410  CharCode code;
4411  const Unicode *uAux;
4412  int uLen, n;
4413  double char_dx, char_dy, ox, oy;
4414 
4415  char_dx = 0.0;
4416  n = font->getNextChar(s, len, &code, &uAux, &uLen, &char_dx, &char_dy, &ox, &oy);
4417  char_dx *= fontSize;
4418 
4419  // center each character within its cell, by advancing the text
4420  // position the appropriate amount relative to the start of the
4421  // previous character
4422  x = 0.5 * (w - char_dx);
4423  appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
4424 
4425  GooString charBuf(s, n);
4427  appearBuf->append(" Tj\n");
4428 
4429  i++;
4430  s += n;
4431  len -= n;
4432  xPrev = x;
4433  }
4434 
4435  // regular (non-comb) formatting
4436  } else {
4437  int ii = 0;
4438  Annot::layoutText(text, &convertedText, &ii, font, &w, 0.0, nullptr, forceZapfDingbats);
4439 
4440  // compute font autosize
4441  if (fontSize == 0) {
4442  fontSize = dy - 2 * borderWidth;
4443  if (w > 0) {
4444  const double fontSize2 = (dx - 4 - 2 * borderWidth) / w;
4445  if (fontSize2 < fontSize) {
4446