source: trunk/poppler/mypoppler/poppler/Annot.cc @ 261

Last change on this file since 261 was 261, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.2

File size: 103.3 KB
Line 
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, 2008 Albert Astals Cid <aacid@kde.org>
19// Copyright (C) 2007, 2008 Carlos Garcia Campos <carlosgc@gnome.org>
20// Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
21// Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
22// Copyright (C) 2008 Pino Toscano <pino@kde.org>
23// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
24// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
25//
26// To see a description of the changes please see the Changelog file that
27// came with your tarball or type make ChangeLog if you are building from git
28//
29//========================================================================
30
31#include <config.h>
32
33#ifdef USE_GCC_PRAGMAS
34#pragma implementation
35#endif
36
37#include <stdlib.h>
38#include <math.h>
39#include "goo/gmem.h"
40#include "GooList.h"
41#include "Error.h"
42#include "Object.h"
43#include "Catalog.h"
44#include "Gfx.h"
45#include "Lexer.h"
46#include "Annot.h"
47#include "GfxFont.h"
48#include "CharCodeToUnicode.h"
49#include "PDFDocEncoding.h"
50#include "Form.h"
51#include "Error.h"
52#include "Page.h"
53#include "XRef.h"
54#include "Movie.h"
55#include "OptionalContent.h"
56#include "Sound.h"
57#include "FileSpec.h"
58#include <string.h>
59
60#define annotFlagHidden    0x0002
61#define annotFlagPrint     0x0004
62#define annotFlagNoView    0x0020
63
64#define fieldFlagReadOnly           0x00000001
65#define fieldFlagRequired           0x00000002
66#define fieldFlagNoExport           0x00000004
67#define fieldFlagMultiline          0x00001000
68#define fieldFlagPassword           0x00002000
69#define fieldFlagNoToggleToOff      0x00004000
70#define fieldFlagRadio              0x00008000
71#define fieldFlagPushbutton         0x00010000
72#define fieldFlagCombo              0x00020000
73#define fieldFlagEdit               0x00040000
74#define fieldFlagSort               0x00080000
75#define fieldFlagFileSelect         0x00100000
76#define fieldFlagMultiSelect        0x00200000
77#define fieldFlagDoNotSpellCheck    0x00400000
78#define fieldFlagDoNotScroll        0x00800000
79#define fieldFlagComb               0x01000000
80#define fieldFlagRichText           0x02000000
81#define fieldFlagRadiosInUnison     0x02000000
82#define fieldFlagCommitOnSelChange  0x04000000
83
84#define fieldQuadLeft   0
85#define fieldQuadCenter 1
86#define fieldQuadRight  2
87
88// distance of Bezier control point from center for circle approximation
89// = (4 * (sqrt(2) - 1) / 3) * r
90#define bezierCircle 0.55228475
91
92AnnotLineEndingStyle parseAnnotLineEndingStyle(GooString *string) {
93  if (string != NULL) {
94    if (!string->cmp("Square")) {
95      return annotLineEndingSquare;
96    } else if (!string->cmp("Circle")) {
97      return annotLineEndingCircle;
98    } else if (!string->cmp("Diamond")) {
99      return annotLineEndingDiamond;
100    } else if (!string->cmp("OpenArrow")) {
101      return annotLineEndingOpenArrow;
102    } else if (!string->cmp("ClosedArrow")) {
103      return annotLineEndingClosedArrow;
104    } else if (!string->cmp("Butt")) {
105      return annotLineEndingButt;
106    } else if (!string->cmp("ROpenArrow")) {
107      return annotLineEndingROpenArrow;
108    } else if (!string->cmp("RClosedArrow")) {
109      return annotLineEndingRClosedArrow;
110    } else if (!string->cmp("Slash")) {
111      return annotLineEndingSlash;
112    } else {
113      return annotLineEndingNone;
114    }
115  } else {
116    return annotLineEndingNone;
117  } 
118}
119
120static AnnotExternalDataType parseAnnotExternalData(Dict* dict) {
121  Object obj1;
122  AnnotExternalDataType type;
123
124  if (dict->lookup("Subtype", &obj1)->isName()) {
125    GooString *typeName = new GooString(obj1.getName());
126
127    if (!typeName->cmp("Markup3D")) {
128      type = annotExternalDataMarkup3D;
129    } else {
130      type = annotExternalDataMarkupUnknown;
131    }
132    delete typeName;
133  } else {
134    type = annotExternalDataMarkupUnknown;
135  }
136  obj1.free();
137
138  return type;
139}
140
141PDFRectangle *parseDiffRectangle(Array *array, PDFRectangle *rect) {
142  PDFRectangle *newRect = NULL;
143  if (array->getLength() == 4) {
144    // deltas
145    Object obj1;
146    double dx1 = (array->get(0, &obj1)->isNum() ? obj1.getNum() : 0);
147    obj1.free();
148    double dy1 = (array->get(1, &obj1)->isNum() ? obj1.getNum() : 0);
149    obj1.free();
150    double dx2 = (array->get(2, &obj1)->isNum() ? obj1.getNum() : 0);
151    obj1.free();
152    double dy2 = (array->get(3, &obj1)->isNum() ? obj1.getNum() : 0);
153    obj1.free();
154
155    // checking that the numbers are valid (i.e. >= 0),
156    // and that applying the differences still give us a valid rect
157    if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2
158        && (rect->x2 - rect->x1 - dx1 - dx2) >= 0
159        && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
160      newRect = new PDFRectangle();
161      newRect->x1 = rect->x1 + dx1;
162      newRect->y1 = rect->y1 + dy1;
163      newRect->x2 = rect->x2 - dx2;
164      newRect->y2 = rect->y2 - dy2;
165    }
166  }
167  return newRect;
168}
169
170//------------------------------------------------------------------------
171// AnnotBorderEffect
172//------------------------------------------------------------------------
173
174AnnotBorderEffect::AnnotBorderEffect(Dict *dict) {
175  Object obj1;
176
177  if (dict->lookup("S", &obj1)->isName()) {
178    GooString *effectName = new GooString(obj1.getName());
179
180    if (!effectName->cmp("C"))
181      effectType = borderEffectCloudy;
182    else
183      effectType = borderEffectNoEffect;
184    delete effectName;
185  } else {
186    effectType = borderEffectNoEffect;
187  }
188  obj1.free();
189
190  if ((dict->lookup("I", &obj1)->isNum()) && effectType == borderEffectCloudy) {
191    intensity = obj1.getNum();
192  } else {
193    intensity = 0;
194  }
195  obj1.free();
196}
197
198//------------------------------------------------------------------------
199// AnnotPath
200//------------------------------------------------------------------------
201
202AnnotPath::AnnotPath() {
203  coords = NULL;
204  coordsLength = 0;
205}
206
207AnnotPath::AnnotPath(Array *array) {
208  coords = NULL;
209  coordsLength = 0;
210  parsePathArray(array);
211}
212
213AnnotPath::AnnotPath(AnnotCoord **coords, int coordsLength) {
214  this->coords = coords;
215  this->coordsLength = coordsLength;
216}
217
218AnnotPath::~AnnotPath() {
219  if (coords) {
220    for (int i = 0; i < coordsLength; ++i)
221      delete coords[i];
222    gfree(coords);
223  }
224}
225
226double AnnotPath::getX(int coord) const {
227  if (coord >= 0 && coord < coordsLength)
228    return coords[coord]->getX();
229  return 0;
230}
231
232double AnnotPath::getY(int coord) const {
233  if (coord >= 0 && coord < coordsLength)
234    return coords[coord]->getY();
235  return 0;
236}
237
238AnnotCoord *AnnotPath::getCoord(int coord) const {
239  if (coord >= 0 && coord < coordsLength)
240    return coords[coord];
241  return NULL;
242}
243
244void AnnotPath::parsePathArray(Array *array) {
245  int tempLength;
246  AnnotCoord **tempCoords;
247  GBool correct = gTrue;
248
249  if (array->getLength() % 2) {
250    error(-1, "Bad Annot Path");
251    return;
252  }
253
254  tempLength = array->getLength() / 2;
255  tempCoords = (AnnotCoord **) gmallocn (tempLength, sizeof(AnnotCoord *));
256  memset(tempCoords, 0, tempLength * sizeof(AnnotCoord *));
257  for (int i = 0; i < tempLength && correct; i++) {
258    Object obj1;
259    double x = 0, y = 0;
260
261    if (array->get(i * 2, &obj1)->isNum()) {
262      x = obj1.getNum();
263    } else {
264      correct = gFalse;
265    }
266    obj1.free();
267
268    if (array->get((i * 2) + 1, &obj1)->isNum()) {
269      y = obj1.getNum();
270    } else {
271      correct = gFalse;
272    }
273    obj1.free();
274
275    if (!correct) {
276      for (int j = i - 1; j >= 0; j--)
277        delete tempCoords[j];
278      gfree (tempCoords);
279      return;
280    }
281
282    tempCoords[i] = new AnnotCoord(x, y);
283  }
284
285  coords = tempCoords;
286  coordsLength = tempLength;
287}
288
289//------------------------------------------------------------------------
290// AnnotCalloutLine
291//------------------------------------------------------------------------
292
293AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2)
294    :  coord1(x1, y1), coord2(x2, y2) {
295}
296
297//------------------------------------------------------------------------
298// AnnotCalloutMultiLine
299//------------------------------------------------------------------------
300
301AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2,
302                                             double y2, double x3, double y3)
303    : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) {
304}
305
306//------------------------------------------------------------------------
307// AnnotQuadrilateral
308//------------------------------------------------------------------------
309
310AnnotQuadrilaterals::AnnotQuadrilaterals(Array *array, PDFRectangle *rect) {
311  int arrayLength = array->getLength();
312  GBool correct = gTrue;
313  int quadsLength = 0;
314  AnnotQuadrilateral **quads;
315  double quadArray[8];
316
317  // default values
318  quadrilaterals = NULL;
319  quadrilateralsLength = 0;
320
321  if ((arrayLength % 8) == 0) {
322    int i = 0;
323
324    quadsLength = arrayLength / 8;
325    quads = (AnnotQuadrilateral **) gmallocn
326        ((quadsLength), sizeof(AnnotQuadrilateral *));
327    memset(quads, 0, quadsLength * sizeof(AnnotQuadrilateral *));
328
329    while (i < (quadsLength) && correct) {
330      for (int j = 0; j < 8 && correct; j++) {
331        Object obj;
332        if (array->get(i * 8 + j, &obj)->isNum()) {
333          quadArray[j] = obj.getNum();
334          if (j % 2 == 1) {
335              if (quadArray[j] < rect->y1 || quadArray[j] > rect->y2)
336                  correct = gFalse;
337          } else {
338              if (quadArray[j] < rect->x1 || quadArray[j] > rect->x2)
339                  correct = gFalse;
340          }
341        } else {
342            correct = gFalse;
343        }
344        obj.free();
345      }
346
347      if (correct)
348        quads[i] = new AnnotQuadrilateral(quadArray[0], quadArray[1],
349                                          quadArray[2], quadArray[3],
350                                          quadArray[4], quadArray[5],
351                                          quadArray[6], quadArray[7]);
352      i++;
353    }
354
355    if (correct) {
356      quadrilateralsLength = quadsLength;
357      quadrilaterals = quads;
358    } else {
359      for (int j = 0; j < i; j++)
360        delete quads[j];
361      gfree (quads);
362    }
363  }
364}
365
366AnnotQuadrilaterals::~AnnotQuadrilaterals() {
367  if (quadrilaterals) {
368    for(int i = 0; i < quadrilateralsLength; i++)
369      delete quadrilaterals[i];
370
371    gfree (quadrilaterals);
372  }
373}
374
375double AnnotQuadrilaterals::getX1(int quadrilateral) {
376  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
377    return quadrilaterals[quadrilateral]->coord1.getX();
378  return 0;
379}
380
381double AnnotQuadrilaterals::getY1(int quadrilateral) {
382  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
383    return quadrilaterals[quadrilateral]->coord1.getY();
384  return 0;
385}
386
387double AnnotQuadrilaterals::getX2(int quadrilateral) {
388  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
389    return quadrilaterals[quadrilateral]->coord2.getX();
390  return 0;
391}
392
393double AnnotQuadrilaterals::getY2(int quadrilateral) {
394  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
395    return quadrilaterals[quadrilateral]->coord2.getY();
396  return 0;
397}
398
399double AnnotQuadrilaterals::getX3(int quadrilateral) {
400  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
401    return quadrilaterals[quadrilateral]->coord3.getX();
402  return 0;
403}
404
405double AnnotQuadrilaterals::getY3(int quadrilateral) {
406  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
407    return quadrilaterals[quadrilateral]->coord3.getY();
408  return 0;
409}
410
411double AnnotQuadrilaterals::getX4(int quadrilateral) {
412  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
413    return quadrilaterals[quadrilateral]->coord4.getX();
414  return 0;
415}
416
417double AnnotQuadrilaterals::getY4(int quadrilateral) {
418  if (quadrilateral >= 0  && quadrilateral < quadrilateralsLength)
419    return quadrilaterals[quadrilateral]->coord4.getY();
420  return 0;
421}
422
423AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1,
424    double x2, double y2, double x3, double y3, double x4, double y4)
425    : coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) {
426}
427
428//------------------------------------------------------------------------
429// AnnotBorder
430//------------------------------------------------------------------------
431AnnotBorder::AnnotBorder() {
432  type = typeUnknown;
433  width = 1;
434  dashLength = 0;
435  dash = NULL;
436  style = borderSolid;
437}
438
439AnnotBorder::~AnnotBorder() {
440  if (dash)
441    gfree (dash); 
442}
443 
444//------------------------------------------------------------------------
445// AnnotBorderArray
446//------------------------------------------------------------------------
447
448AnnotBorderArray::AnnotBorderArray() {
449  type = typeArray;
450  horizontalCorner = 0;
451  verticalCorner = 0;
452}
453
454AnnotBorderArray::AnnotBorderArray(Array *array) {
455  Object obj1;
456  int arrayLength = array->getLength();
457
458  if (arrayLength >= 3) {
459    // implementation note 81 in Appendix H.
460
461    if (array->get(0, &obj1)->isNum())
462      horizontalCorner = obj1.getNum();
463    obj1.free();
464
465    if (array->get(1, &obj1)->isNum())
466      verticalCorner = obj1.getNum();
467    obj1.free();
468
469    if (array->get(2, &obj1)->isNum())
470      width = obj1.getNum();
471    obj1.free();
472
473    // TODO: check not all zero ? (Line Dash Pattern Page 217 PDF 8.1)
474    if (arrayLength > 3) {
475      GBool correct = gTrue;
476      int tempLength = array->getLength() - 3;
477      double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
478
479      for(int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
480
481        if (array->get((i + 3), &obj1)->isNum()) {
482          tempDash[i] = obj1.getNum();
483
484          if (tempDash[i] < 0)
485            correct = gFalse;
486
487        } else {
488          correct = gFalse;
489        }
490        obj1.free();
491      }
492
493      if (correct) {
494        dashLength = tempLength;
495        dash = tempDash;
496        style = borderDashed;
497      } else {
498        gfree (tempDash);
499      }
500    }
501  }
502}
503
504//------------------------------------------------------------------------
505// AnnotBorderBS
506//------------------------------------------------------------------------
507
508AnnotBorderBS::AnnotBorderBS() {
509  type = typeBS;
510}
511
512AnnotBorderBS::AnnotBorderBS(Dict *dict) {
513  Object obj1, obj2;
514
515  // acroread 8 seems to need both W and S entries for
516  // any border to be drawn, even though the spec
517  // doesn't claim anything of that sort. We follow
518  // that behaviour by veryifying both entries exist
519  // otherwise we set the borderWidth to 0
520  // --jrmuizel
521  dict->lookup("W", &obj1);
522  dict->lookup("S", &obj2);
523  if (obj1.isNum() && obj2.isName()) {
524    GooString *styleName = new GooString(obj2.getName());
525
526    width = obj1.getNum();
527
528    if (!styleName->cmp("S")) {
529      style = borderSolid;
530    } else if (!styleName->cmp("D")) {
531      style = borderDashed;
532    } else if (!styleName->cmp("B")) {
533      style = borderBeveled;
534    } else if (!styleName->cmp("I")) {
535      style = borderInset;
536    } else if (!styleName->cmp("U")) {
537      style = borderUnderlined;
538    } else {
539      style = borderSolid;
540    }
541    delete styleName;
542  } else {
543    width = 0;
544  }
545  obj2.free();
546  obj1.free();
547
548  // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
549  if (dict->lookup("D", &obj1)->isArray()) {
550    GBool correct = gTrue;
551    int tempLength = obj1.arrayGetLength();
552    double *tempDash = (double *) gmallocn (tempLength, sizeof (double));
553
554    for(int i = 0; i < tempLength && correct; i++) {
555      Object obj2;
556
557      if (obj1.arrayGet(i, &obj2)->isNum()) {
558        tempDash[i] = obj2.getNum();
559
560        if (tempDash[i] < 0)
561          correct = gFalse;
562      } else {
563        correct = gFalse;
564      }
565      obj2.free();
566    }
567
568    if (correct) {
569      dashLength = tempLength;
570      dash = tempDash;
571      style = borderDashed;
572    } else {
573      gfree (tempDash);
574    }
575
576  }
577
578  if (!dash) {
579    dashLength = 1;
580    dash = (double *) gmallocn (dashLength, sizeof (double));
581    dash[0] = 3;
582  }
583  obj1.free();
584}
585
586//------------------------------------------------------------------------
587// AnnotColor
588//------------------------------------------------------------------------
589
590AnnotColor::AnnotColor() {
591  length = 0;
592  values = NULL;
593}
594
595AnnotColor::AnnotColor(Array *array) {
596  // TODO: check what Acrobat does in the case of having more than 5 numbers.
597  if (array->getLength() < 5) {
598    length = array->getLength();
599    values = (double *) gmallocn (length, sizeof(double));
600
601    for(int i = 0; i < length; i++) { 
602      Object obj1;
603
604      if (array->get(i, &obj1)->isNum()) {
605        values[i] = obj1.getNum();
606
607        if (values[i] < 0 || values[i] > 1)
608          values[i] = 0;
609      } else {
610        values[i] = 0;
611      }
612      obj1.free();
613    }
614  }
615}
616
617AnnotColor::~AnnotColor() {
618  if (values)
619    gfree (values);
620}
621
622//------------------------------------------------------------------------
623// AnnotBorderStyle
624//------------------------------------------------------------------------
625
626AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
627                                   double *dashA, int dashLengthA,
628                                   double rA, double gA, double bA) {
629  type = typeA;
630  width = widthA;
631  dash = dashA;
632  dashLength = dashLengthA;
633  r = rA;
634  g = gA;
635  b = bA;
636}
637
638AnnotBorderStyle::~AnnotBorderStyle() {
639  if (dash) {
640    gfree(dash);
641  }
642}
643
644//------------------------------------------------------------------------
645// AnnotIconFit
646//------------------------------------------------------------------------
647
648AnnotIconFit::AnnotIconFit(Dict* dict) {
649  Object obj1;
650
651  if (dict->lookup("SW", &obj1)->isName()) {
652    GooString *scaleName = new GooString(obj1.getName());
653
654    if(!scaleName->cmp("B")) {
655      scaleWhen = scaleBigger;
656    } else if(!scaleName->cmp("S")) {
657      scaleWhen = scaleSmaller;
658    } else if(!scaleName->cmp("N")) {
659      scaleWhen = scaleNever;
660    } else {
661      scaleWhen = scaleAlways;
662    }
663    delete scaleName;
664  } else {
665    scaleWhen = scaleAlways;
666  }
667  obj1.free();
668
669  if (dict->lookup("S", &obj1)->isName()) {
670    GooString *scaleName = new GooString(obj1.getName());
671
672    if(!scaleName->cmp("A")) {
673      scale = scaleAnamorphic;
674    } else {
675      scale = scaleProportional;
676    }
677    delete scaleName;
678  } else {
679    scale = scaleProportional;
680  }
681  obj1.free();
682
683  if (dict->lookup("A", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
684    Object obj2;
685    (obj1.arrayGet(0, &obj2)->isNum() ? left = obj2.getNum() : left = 0);
686    obj2.free();
687    (obj1.arrayGet(1, &obj2)->isNum() ? bottom = obj2.getNum() : bottom = 0);
688    obj2.free();
689
690    if (left < 0 || left > 1)
691      left = 0.5;
692
693    if (bottom < 0 || bottom > 1)
694      bottom = 0.5;
695
696  } else {
697    left = bottom = 0.5;
698  }
699  obj1.free();
700
701  if (dict->lookup("FB", &obj1)->isBool()) {
702    fullyBounds = obj1.getBool();
703  } else {
704    fullyBounds = gFalse;
705  }
706  obj1.free();
707}
708
709//------------------------------------------------------------------------
710// AnnotAppearanceCharacs
711//------------------------------------------------------------------------
712
713AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) {
714  Object obj1;
715
716  if (dict->lookup("R", &obj1)->isInt()) {
717    rotation = obj1.getInt();
718  } else {
719    rotation = 0;
720  }
721  obj1.free();
722
723  if (dict->lookup("BC", &obj1)->isArray()) {
724    borderColor = new AnnotColor(obj1.getArray());
725  } else {
726    borderColor = NULL;
727  }
728  obj1.free();
729
730  if (dict->lookup("BG", &obj1)->isArray()) {
731    backColor = new AnnotColor(obj1.getArray());
732  } else {
733    backColor = NULL;
734  }
735  obj1.free();
736
737  if (dict->lookup("CA", &obj1)->isName()) {
738    normalCaption = new GooString(obj1.getName());
739  } else {
740    normalCaption = NULL;
741  }
742  obj1.free();
743
744  if (dict->lookup("RC", &obj1)->isName()) {
745    rolloverCaption = new GooString(obj1.getName());
746  } else {
747    rolloverCaption = NULL;
748  }
749  obj1.free();
750
751  if (dict->lookup("AC", &obj1)->isName()) {
752    alternateCaption = new GooString(obj1.getName());
753  } else {
754    alternateCaption = NULL;
755  }
756  obj1.free();
757
758  if (dict->lookup("IF", &obj1)->isDict()) {
759    iconFit = new AnnotIconFit(obj1.getDict());
760  } else {
761    iconFit = NULL;
762  }
763  obj1.free();
764
765  if (dict->lookup("TP", &obj1)->isInt()) {
766    position = (AnnotAppearanceCharacsTextPos) obj1.getInt();
767  } else {
768    position = captionNoIcon;
769  }
770  obj1.free();
771}
772
773AnnotAppearanceCharacs::~AnnotAppearanceCharacs() {
774  if (borderColor)
775    delete borderColor;
776
777  if (backColor)
778    delete backColor;
779
780  if (normalCaption)
781    delete normalCaption;
782
783  if (rolloverCaption)
784    delete rolloverCaption;
785
786  if (alternateCaption)
787    delete alternateCaption;
788
789  if (iconFit)
790    delete iconFit;
791}
792
793//------------------------------------------------------------------------
794// Annot
795//------------------------------------------------------------------------
796
797Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog) {
798  hasRef = false;
799  flags = flagUnknown;
800  type = typeUnknown;
801  initialize (xrefA, dict, catalog);
802}
803
804Annot::Annot(XRef *xrefA, Dict *dict, Catalog* catalog, Object *obj) {
805  if (obj->isRef()) {
806    hasRef = gTrue;
807    ref = obj->getRef();
808  } else {
809    hasRef = gFalse;
810  }
811  flags = flagUnknown;
812  type = typeUnknown;
813  initialize (xrefA, dict, catalog);
814}
815
816void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
817  Object asObj, obj1, obj2, obj3;
818
819  appRef.num = 0;
820  appRef.gen = 65535;
821  ok = gTrue;
822  xref = xrefA;
823  appearBuf = NULL;
824  fontSize = 0;
825
826  //----- parse the rectangle
827  rect = new PDFRectangle();
828  if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
829    Object obj2;
830    (obj1.arrayGet(0, &obj2)->isNum() ? rect->x1 = obj2.getNum() : rect->x1 = 0);
831    obj2.free();
832    (obj1.arrayGet(1, &obj2)->isNum() ? rect->y1 = obj2.getNum() : rect->y1 = 0);
833    obj2.free();
834    (obj1.arrayGet(2, &obj2)->isNum() ? rect->x2 = obj2.getNum() : rect->x2 = 1);
835    obj2.free();
836    (obj1.arrayGet(3, &obj2)->isNum() ? rect->y2 = obj2.getNum() : rect->y2 = 1);
837    obj2.free();
838
839    if (rect->x1 > rect->x2) {
840      double t = rect->x1;
841      rect->x1 = rect->x2;
842      rect->x2 = t;
843    }
844
845    if (rect->y1 > rect->y2) {
846      double t = rect->y1;
847      rect->y1 = rect->y2;
848      rect->y2 = t;
849    }
850  } else {
851    rect->x1 = rect->y1 = 0;
852    rect->x2 = rect->y2 = 1;
853    error(-1, "Bad bounding box for annotation");
854    ok = gFalse;
855  }
856  obj1.free();
857
858  if (dict->lookup("Contents", &obj1)->isString()) {
859    contents = obj1.getString()->copy();
860  } else {
861    contents = NULL;
862  }
863  obj1.free();
864
865  /* TODO: Page Object indirect reference (should be parsed ?) */
866  pageDict = NULL;
867  /*if (dict->lookup("P", &obj1)->isDict()) {
868    pageDict = NULL;
869  } else {
870    pageDict = NULL;
871  }
872  obj1.free();
873  */
874
875  if (dict->lookup("NM", &obj1)->isString()) {
876    name = obj1.getString()->copy();
877  } else {
878    name = NULL;
879  }
880  obj1.free();
881
882  if (dict->lookup("M", &obj1)->isString()) {
883    modified = obj1.getString()->copy();
884  } else {
885    modified = NULL;
886  }
887  obj1.free();
888
889  //----- get the flags
890  if (dict->lookup("F", &obj1)->isInt()) {
891    flags |= obj1.getInt();
892  } else {
893    flags = flagUnknown;
894  }
895  obj1.free();
896
897  if (dict->lookup("AP", &obj1)->isDict()) {
898    Object obj2;
899
900    if (dict->lookup("AS", &obj2)->isName()) {
901      Object obj3;
902
903      appearState = new GooString(obj2.getName());
904      if (obj1.dictLookup("N", &obj3)->isDict()) {
905        Object obj4;
906
907        if (obj3.dictLookupNF(appearState->getCString(), &obj4)->isRef()) {
908          obj4.copy(&appearance);
909        } else {
910          obj4.free();
911          if (obj3.dictLookupNF("Off", &obj4)->isRef()) {
912            obj4.copy(&appearance);
913          }
914        } 
915        obj4.free();
916      }
917      obj3.free();
918    } else {
919      obj2.free();
920
921      appearState = NULL;
922      if (obj1.dictLookupNF("N", &obj2)->isRef()) {
923        obj2.copy(&appearance);
924      }
925    }
926    obj2.free();
927  } else {
928    appearState = NULL;
929  }
930  obj1.free();
931
932  //----- parse the border style
933  if (dict->lookup("BS", &obj1)->isDict()) {
934    border = new AnnotBorderBS(obj1.getDict());
935  } else {
936    obj1.free();
937
938    if (dict->lookup("Border", &obj1)->isArray())
939      border = new AnnotBorderArray(obj1.getArray());
940    else
941      // Adobe draws no border at all if the last element is of
942      // the wrong type.
943      border = NULL;
944  }
945  obj1.free();
946
947  if (dict->lookup("C", &obj1)->isArray()) {
948    color = new AnnotColor(obj1.getArray());
949  } else {
950    color = NULL;
951  }
952  obj1.free();
953
954  if (dict->lookup("StructParent", &obj1)->isInt()) {
955    treeKey = obj1.getInt();
956  } else {
957    treeKey = 0;
958  }
959  obj1.free();
960
961  optContentConfig = catalog ? catalog->getOptContentConfig() : NULL;
962  dict->lookupNF("OC", &oc);
963  if (!oc.isRef() && !oc.isNull()) {
964    error (-1, "Annotation OC value not null or dict: %i", oc.getType());
965  }
966}
967
968double Annot::getXMin() {
969  return rect->x1;
970}
971
972double Annot::getYMin() {
973  return rect->y1;
974}
975
976void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
977  Object valueObject;
978
979  pdfArray->arrayGet(key, &valueObject);
980  if (valueObject.isNum()) {
981    *value = valueObject.getNum();
982  } else {
983    *value = 0;
984    ok = gFalse;
985  }
986  valueObject.free();
987}
988
989Annot::~Annot() {
990  delete rect;
991
992  if (contents)
993    delete contents;
994
995  if (pageDict)
996    delete pageDict;
997
998  if (name)
999    delete name;
1000
1001  if (modified)
1002    delete modified;
1003
1004  appearance.free();
1005
1006  if (appearState)
1007    delete appearState;
1008
1009  if (border)
1010    delete border;
1011
1012  if (color)
1013    delete color;
1014
1015  oc.free();
1016}
1017
1018// Set the current fill or stroke color, based on <a> (which should
1019// have 1, 3, or 4 elements).  If <adjust> is +1, color is brightened;
1020// if <adjust> is -1, color is darkened; otherwise color is not
1021// modified.
1022void Annot::setColor(Array *a, GBool fill, int adjust) {
1023  Object obj1;
1024  double color[4];
1025  int nComps, i;
1026
1027  nComps = a->getLength();
1028  if (nComps > 4) {
1029    nComps = 4;
1030  }
1031  for (i = 0; i < nComps && i < 4; ++i) {
1032    if (a->get(i, &obj1)->isNum()) {
1033      color[i] = obj1.getNum();
1034    } else {
1035      color[i] = 0;
1036    }
1037    obj1.free();
1038  }
1039  if (nComps == 4) {
1040    adjust = -adjust;
1041  }
1042  if (adjust > 0) {
1043    for (i = 0; i < nComps; ++i) {
1044      color[i] = 0.5 * color[i] + 0.5;
1045    }
1046  } else if (adjust < 0) {
1047    for (i = 0; i < nComps; ++i) {
1048      color[i] = 0.5 * color[i];
1049    }
1050  }
1051  if (nComps == 4) {
1052    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1053        color[0], color[1], color[2], color[3],
1054        fill ? 'k' : 'K');
1055  } else if (nComps == 3) {
1056    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1057        color[0], color[1], color[2],
1058        fill ? "rg" : "RG");
1059  } else {
1060    appearBuf->appendf("{0:.2f} {1:c}\n",
1061        color[0],
1062        fill ? 'g' : 'G');
1063  }
1064}
1065
1066// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1067// If <fill> is true, the circle is filled; otherwise it is stroked.
1068void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
1069  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1070      cx + r, cy);
1071  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1072      cx + r, cy + bezierCircle * r,
1073      cx + bezierCircle * r, cy + r,
1074      cx, cy + r);
1075  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1076      cx - bezierCircle * r, cy + r,
1077      cx - r, cy + bezierCircle * r,
1078      cx - r, cy);
1079  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1080      cx - r, cy - bezierCircle * r,
1081      cx - bezierCircle * r, cy - r,
1082      cx, cy - r);
1083  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1084      cx + bezierCircle * r, cy - r,
1085      cx + r, cy - bezierCircle * r,
1086      cx + r, cy);
1087  appearBuf->append(fill ? "f\n" : "s\n");
1088}
1089
1090// Draw the top-left half of an (approximate) circle of radius <r>
1091// centered at (<cx>, <cy>).
1092void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1093  double r2;
1094
1095  r2 = r / sqrt(2.0);
1096  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1097      cx + r2, cy + r2);
1098  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1099      cx + (1 - bezierCircle) * r2,
1100      cy + (1 + bezierCircle) * r2,
1101      cx - (1 - bezierCircle) * r2,
1102      cy + (1 + bezierCircle) * r2,
1103      cx - r2,
1104      cy + r2);
1105  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1106      cx - (1 + bezierCircle) * r2,
1107      cy + (1 - bezierCircle) * r2,
1108      cx - (1 + bezierCircle) * r2,
1109      cy - (1 - bezierCircle) * r2,
1110      cx - r2,
1111      cy - r2);
1112  appearBuf->append("S\n");
1113}
1114
1115// Draw the bottom-right half of an (approximate) circle of radius <r>
1116// centered at (<cx>, <cy>).
1117void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1118  double r2;
1119
1120  r2 = r / sqrt(2.0);
1121  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1122      cx - r2, cy - r2);
1123  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1124      cx - (1 - bezierCircle) * r2,
1125      cy - (1 + bezierCircle) * r2,
1126      cx + (1 - bezierCircle) * r2,
1127      cy - (1 + bezierCircle) * r2,
1128      cx + r2,
1129      cy - r2);
1130  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1131      cx + (1 + bezierCircle) * r2,
1132      cy - (1 - bezierCircle) * r2,
1133      cx + (1 + bezierCircle) * r2,
1134      cy + (1 - bezierCircle) * r2,
1135      cx + r2,
1136      cy + r2);
1137  appearBuf->append("S\n");
1138}
1139
1140void Annot::draw(Gfx *gfx, GBool printing) {
1141  Object obj;
1142
1143  // check the flags
1144  if ((flags & annotFlagHidden) ||
1145      (printing && !(flags & annotFlagPrint)) ||
1146      (!printing && (flags & annotFlagNoView))) {
1147    return;
1148  }
1149
1150  // check the OC
1151  if (optContentConfig && oc.isRef()) {
1152    if (! optContentConfig->optContentIsVisible(&oc))
1153      return;
1154  }
1155
1156  // draw the appearance stream
1157  appearance.fetch(xref, &obj);
1158  gfx->drawAnnot(&obj, (type == typeLink) ? border : (AnnotBorder *)NULL, color,
1159      rect->x1, rect->y1, rect->x2, rect->y2);
1160  obj.free();
1161}
1162
1163//------------------------------------------------------------------------
1164// AnnotPopup
1165//------------------------------------------------------------------------
1166
1167AnnotPopup::AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1168    Annot(xrefA, dict, catalog, obj) {
1169  type = typePopup;
1170  initialize(xrefA, dict, catalog);
1171}
1172
1173AnnotPopup::~AnnotPopup() {
1174  /*
1175  if (parent)
1176    delete parent;
1177  */
1178}
1179
1180void AnnotPopup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
1181  Object obj1;
1182  /*
1183  if (dict->lookup("Parent", &obj1)->isDict()) {
1184    parent = NULL;
1185  } else {
1186    parent = NULL;
1187  }
1188  obj1.free();
1189  */
1190  if (dict->lookup("Open", &obj1)->isBool()) {
1191    open = obj1.getBool();
1192  } else {
1193    open = gFalse;
1194  }
1195  obj1.free();
1196}
1197
1198//------------------------------------------------------------------------
1199// AnnotMarkup
1200//------------------------------------------------------------------------
1201 
1202AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1203    Annot(xrefA, dict, catalog, obj) {
1204  initialize(xrefA, dict, catalog, obj);
1205}
1206
1207AnnotMarkup::~AnnotMarkup() {
1208  if (label)
1209    delete label;
1210
1211  if (popup)
1212    delete popup;
1213
1214  if (date)
1215    delete date;
1216
1217  if (subject)
1218    delete subject;
1219}
1220
1221void AnnotMarkup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
1222  Object obj1;
1223
1224  if (dict->lookup("T", &obj1)->isString()) {
1225    label = obj1.getString()->copy();
1226  } else {
1227    label = NULL;
1228  }
1229  obj1.free();
1230
1231  if (dict->lookup("Popup", &obj1)->isDict()) {
1232    popup = new AnnotPopup(xrefA, obj1.getDict(), catalog, obj);
1233  } else {
1234    popup = NULL;
1235  }
1236  obj1.free();
1237
1238  if (dict->lookup("CA", &obj1)->isNum()) {
1239    opacity = obj1.getNum();
1240  } else {
1241    opacity = 1.0;
1242  }
1243  obj1.free();
1244
1245  if (dict->lookup("CreationDate", &obj1)->isString()) {
1246    date = obj1.getString()->copy();
1247  } else {
1248    date = NULL;
1249  }
1250  obj1.free();
1251
1252  if (dict->lookupNF("IRT", &obj1)->isRef()) {
1253    inReplyTo = obj1.getRef();
1254  } else {
1255    inReplyTo.num = 0;
1256    inReplyTo.gen = 0;
1257  }
1258  obj1.free();
1259
1260  if (dict->lookup("Subj", &obj1)->isString()) {
1261    subject = obj1.getString()->copy();
1262  } else {
1263    subject = NULL;
1264  }
1265  obj1.free();
1266
1267  if (dict->lookup("RT", &obj1)->isName()) {
1268    GooString *replyName = new GooString(obj1.getName());
1269
1270    if (!replyName->cmp("R")) {
1271      replyTo = replyTypeR;
1272    } else if (!replyName->cmp("Group")) {
1273      replyTo = replyTypeGroup;
1274    } else {
1275      replyTo = replyTypeR;
1276    }
1277    delete replyName;
1278  } else {
1279    replyTo = replyTypeR;
1280  }
1281  obj1.free();
1282
1283  if (dict->lookup("ExData", &obj1)->isDict()) {
1284    exData = parseAnnotExternalData(obj1.getDict());
1285  } else {
1286    exData = annotExternalDataMarkupUnknown;
1287  }
1288  obj1.free();
1289}
1290
1291//------------------------------------------------------------------------
1292// AnnotText
1293//------------------------------------------------------------------------
1294
1295AnnotText::AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1296    AnnotMarkup(xrefA, dict, catalog, obj) {
1297
1298  type = typeText;
1299  flags |= flagNoZoom | flagNoRotate;
1300  initialize (xrefA, catalog, dict);
1301}
1302
1303AnnotText::~AnnotText() {
1304  delete icon;
1305}
1306
1307void AnnotText::setModified(GooString *date) {
1308  if (date) {
1309    delete modified;
1310    modified = new GooString(date);
1311  }
1312}
1313
1314void AnnotText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1315  Object obj1;
1316
1317  if (dict->lookup("Open", &obj1)->isBool())
1318    open = obj1.getBool();
1319  else
1320    open = gFalse;
1321  obj1.free();
1322
1323  if (dict->lookup("Name", &obj1)->isName()) {
1324    icon = new GooString(obj1.getName());
1325  } else {
1326    icon = new GooString("Note");
1327  }
1328  obj1.free();
1329
1330  if (dict->lookup("StateModel", &obj1)->isString()) {
1331    Object obj2;
1332    GooString *modelName = obj1.getString();
1333
1334    if (dict->lookup("State", &obj2)->isString()) {
1335      GooString *stateName = obj2.getString();
1336
1337      if (!stateName->cmp("Marked")) {
1338        state = stateMarked;
1339      } else if (!stateName->cmp("Unmarked")) {
1340        state = stateUnmarked;
1341      } else if (!stateName->cmp("Accepted")) {
1342        state = stateAccepted;
1343      } else if (!stateName->cmp("Rejected")) {
1344        state = stateRejected;
1345      } else if (!stateName->cmp("Cancelled")) {
1346        state = stateCancelled;
1347      } else if (!stateName->cmp("Completed")) {
1348        state = stateCompleted;
1349      } else if (!stateName->cmp("None")) {
1350        state = stateNone;
1351      } else {
1352        state = stateUnknown;
1353      }
1354    } else {
1355      state = stateUnknown;
1356    }
1357    obj2.free();
1358
1359    if (!modelName->cmp("Marked")) {
1360      switch (state) {
1361        case stateUnknown:
1362          state = stateMarked;
1363          break;
1364        case stateAccepted:
1365        case stateRejected:
1366        case stateCancelled:
1367        case stateCompleted:
1368        case stateNone:
1369          state = stateUnknown;
1370          break;
1371        default:
1372          break;
1373      }
1374    } else if (!modelName->cmp("Review")) {
1375      switch (state) {
1376        case stateUnknown:
1377          state = stateNone;
1378          break;
1379        case stateMarked:
1380        case stateUnmarked:
1381          state = stateUnknown;
1382          break;
1383        default:
1384          break;
1385      }
1386    } else {
1387      state = stateUnknown;
1388    }
1389  } else {
1390    state = stateUnknown;
1391  }
1392  obj1.free();
1393}
1394
1395//------------------------------------------------------------------------
1396// AnnotLink
1397//------------------------------------------------------------------------
1398
1399AnnotLink::AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1400    Annot(xrefA, dict, catalog, obj) {
1401
1402  type = typeLink;
1403  initialize (xrefA, catalog, dict);
1404}
1405
1406AnnotLink::~AnnotLink() {
1407  /*
1408  if (actionDict)
1409    delete actionDict;
1410  */
1411  dest.free();
1412  /*
1413  if (uriAction)
1414    delete uriAction;
1415  */
1416  if (quadrilaterals)
1417    delete quadrilaterals;
1418}
1419
1420void AnnotLink::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1421  Object obj1;
1422  /*
1423  if (dict->lookup("A", &obj1)->isDict()) {
1424    actionDict = NULL;
1425  } else {
1426    actionDict = NULL;
1427  }
1428  obj1.free();
1429  */
1430  dict->lookup("Dest", &dest);
1431  if (dict->lookup("H", &obj1)->isName()) {
1432    GooString *effect = new GooString(obj1.getName());
1433
1434    if (!effect->cmp("N")) {
1435      linkEffect = effectNone;
1436    } else if (!effect->cmp("I")) {
1437      linkEffect = effectInvert;
1438    } else if (!effect->cmp("O")) {
1439      linkEffect = effectOutline;
1440    } else if (!effect->cmp("P")) {
1441      linkEffect = effectPush;
1442    } else {
1443      linkEffect = effectInvert;
1444    }
1445    delete effect;
1446  } else {
1447    linkEffect = effectInvert;
1448  }
1449  obj1.free();
1450  /*
1451  if (dict->lookup("PA", &obj1)->isDict()) {
1452    uriAction = NULL;
1453  } else {
1454    uriAction = NULL;
1455  }
1456  obj1.free();
1457  */
1458  if (dict->lookup("QuadPoints", &obj1)->isArray()) {
1459    quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
1460  } else {
1461    quadrilaterals = NULL;
1462  }
1463  obj1.free();
1464}
1465
1466void AnnotLink::draw(Gfx *gfx, GBool printing) {
1467  Object obj;
1468
1469  // check the flags
1470  if ((flags & annotFlagHidden) ||
1471      (printing && !(flags & annotFlagPrint)) ||
1472      (!printing && (flags & annotFlagNoView))) {
1473    return;
1474  }
1475
1476  // draw the appearance stream
1477  appearance.fetch(xref, &obj);
1478  gfx->drawAnnot(&obj, border, color,
1479                 rect->x1, rect->y1, rect->x2, rect->y2);
1480  obj.free();
1481}
1482
1483//------------------------------------------------------------------------
1484// AnnotFreeText
1485//------------------------------------------------------------------------
1486
1487AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1488    AnnotMarkup(xrefA, dict, catalog, obj) {
1489  type = typeFreeText;
1490  initialize(xrefA, catalog, dict);
1491}
1492
1493AnnotFreeText::~AnnotFreeText() {
1494  delete appearanceString;
1495
1496  if (styleString)
1497    delete styleString;
1498
1499  if (calloutLine)
1500    delete calloutLine;
1501
1502  if (borderEffect)
1503    delete borderEffect;
1504
1505  if (rectangle)
1506    delete rectangle;
1507}
1508
1509void AnnotFreeText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1510  Object obj1;
1511
1512  if (dict->lookup("DA", &obj1)->isString()) {
1513    appearanceString = obj1.getString()->copy();
1514  } else {
1515    appearanceString = new GooString();
1516    error(-1, "Bad appearance for annotation");
1517    ok = gFalse;
1518  }
1519  obj1.free();
1520
1521  if (dict->lookup("Q", &obj1)->isInt()) {
1522    quadding = (AnnotFreeTextQuadding) obj1.getInt();
1523  } else {
1524    quadding = quaddingLeftJustified;
1525  }
1526  obj1.free();
1527
1528  if (dict->lookup("DS", &obj1)->isString()) {
1529    styleString = obj1.getString()->copy();
1530  } else {
1531    styleString = NULL;
1532  }
1533  obj1.free();
1534
1535  if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
1536    double x1, y1, x2, y2;
1537    Object obj2;
1538
1539    (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
1540    obj2.free();
1541    (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
1542    obj2.free();
1543    (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
1544    obj2.free();
1545    (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
1546    obj2.free();
1547
1548    if (obj1.arrayGetLength() == 6) {
1549      double x3, y3;
1550      (obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
1551      obj2.free();
1552      (obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
1553      obj2.free();
1554      calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
1555    } else {
1556      calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
1557    }
1558  } else {
1559    calloutLine = NULL;
1560  }
1561  obj1.free();
1562
1563  if (dict->lookup("IT", &obj1)->isName()) {
1564    GooString *intentName = new GooString(obj1.getName());
1565
1566    if (!intentName->cmp("FreeText")) {
1567      intent = intentFreeText;
1568    } else if (!intentName->cmp("FreeTextCallout")) {
1569      intent = intentFreeTextCallout;
1570    } else if (!intentName->cmp("FreeTextTypeWriter")) {
1571      intent = intentFreeTextTypeWriter;
1572    } else {
1573      intent = intentFreeText;
1574    }
1575    delete intentName;
1576  } else {
1577    intent = intentFreeText;
1578  }
1579  obj1.free();
1580
1581  if (dict->lookup("BE", &obj1)->isDict()) {
1582    borderEffect = new AnnotBorderEffect(obj1.getDict());
1583  } else {
1584    borderEffect = NULL;
1585  }
1586  obj1.free();
1587
1588  if (dict->lookup("RD", &obj1)->isArray()) {
1589    rectangle = parseDiffRectangle(obj1.getArray(), rect);
1590  } else {
1591    rectangle = NULL;
1592  }
1593  obj1.free();
1594
1595  if (dict->lookup("LE", &obj1)->isName()) {
1596    GooString *styleName = new GooString(obj1.getName());
1597    endStyle = parseAnnotLineEndingStyle(styleName);
1598    delete styleName;
1599  } else {
1600    endStyle = annotLineEndingNone;
1601  }
1602  obj1.free();
1603}
1604
1605//------------------------------------------------------------------------
1606// AnnotLine
1607//------------------------------------------------------------------------
1608
1609AnnotLine::AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1610    AnnotMarkup(xrefA, dict, catalog, obj) {
1611  type = typeLine;
1612  initialize(xrefA, catalog, dict);
1613}
1614
1615AnnotLine::~AnnotLine() {
1616  delete coord1;
1617  delete coord2;
1618
1619  if (interiorColor)
1620    delete interiorColor;
1621
1622  if (measure)
1623    delete measure;
1624}
1625
1626void AnnotLine::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1627  Object obj1;
1628
1629  if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
1630    Object obj2;
1631    double x1, y1, x2, y2;
1632
1633    (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
1634    obj2.free();
1635    (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
1636    obj2.free();
1637    (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
1638    obj2.free();
1639    (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
1640    obj2.free();
1641
1642    coord1 = new AnnotCoord(x1, y1);
1643    coord2 = new AnnotCoord(x2, y2);
1644  } else {
1645    coord1 = new AnnotCoord();
1646    coord2 = new AnnotCoord();
1647  }
1648  obj1.free();
1649
1650  if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
1651    Object obj2;
1652
1653    if(obj1.arrayGet(0, &obj2)->isString())
1654      startStyle = parseAnnotLineEndingStyle(obj2.getString());
1655    else
1656      startStyle = annotLineEndingNone;
1657    obj2.free();
1658
1659    if(obj1.arrayGet(1, &obj2)->isString())
1660      endStyle = parseAnnotLineEndingStyle(obj2.getString());
1661    else
1662      endStyle = annotLineEndingNone;
1663    obj2.free();
1664
1665  } else {
1666    startStyle = endStyle = annotLineEndingNone;
1667  }
1668  obj1.free();
1669
1670  if (dict->lookup("IC", &obj1)->isArray()) {
1671    interiorColor = new AnnotColor(obj1.getArray());
1672  } else {
1673    interiorColor = NULL;
1674  }
1675  obj1.free();
1676
1677  if (dict->lookup("LL", &obj1)->isNum()) {
1678    leaderLineLength = obj1.getNum();
1679  } else {
1680    leaderLineLength = 0;
1681  }
1682  obj1.free();
1683
1684  if (dict->lookup("LLE", &obj1)->isNum()) {
1685    leaderLineExtension = obj1.getNum();
1686
1687    if (leaderLineExtension < 0)
1688      leaderLineExtension = 0;
1689  } else {
1690    leaderLineExtension = 0;
1691  }
1692  obj1.free();
1693
1694  if (dict->lookup("Cap", &obj1)->isBool()) {
1695    caption = obj1.getBool();
1696  } else {
1697    caption = gFalse;
1698  }
1699  obj1.free();
1700
1701  if (dict->lookup("IT", &obj1)->isName()) {
1702    GooString *intentName = new GooString(obj1.getName());
1703
1704    if(!intentName->cmp("LineArrow")) {
1705      intent = intentLineArrow;
1706    } else if(!intentName->cmp("LineDimension")) {
1707      intent = intentLineDimension;
1708    } else {
1709      intent = intentLineArrow;
1710    }
1711    delete intentName;
1712  } else {
1713    intent = intentLineArrow;
1714  }
1715  obj1.free();
1716
1717  if (dict->lookup("LLO", &obj1)->isNum()) {
1718    leaderLineOffset = obj1.getNum();
1719
1720    if (leaderLineOffset < 0)
1721      leaderLineOffset = 0;
1722  } else {
1723    leaderLineOffset = 0;
1724  }
1725  obj1.free();
1726
1727  if (dict->lookup("CP", &obj1)->isName()) {
1728    GooString *captionName = new GooString(obj1.getName());
1729
1730    if(!captionName->cmp("Inline")) {
1731      captionPos = captionPosInline;
1732    } else if(!captionName->cmp("Top")) {
1733      captionPos = captionPosTop;
1734    } else {
1735      captionPos = captionPosInline;
1736    }
1737    delete captionName;
1738  } else {
1739    captionPos = captionPosInline;
1740  }
1741  obj1.free();
1742
1743  if (dict->lookup("Measure", &obj1)->isDict()) {
1744    measure = NULL;
1745  } else {
1746    measure = NULL;
1747  }
1748  obj1.free();
1749
1750  if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
1751    Object obj2;
1752
1753    (obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
1754      captionTextHorizontal = 0);
1755    obj2.free();
1756    (obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
1757      captionTextVertical = 0);
1758    obj2.free();
1759  } else {
1760    captionTextHorizontal = captionTextVertical = 0;
1761  }
1762  obj1.free();
1763}
1764
1765//------------------------------------------------------------------------
1766// AnnotTextMarkup
1767//------------------------------------------------------------------------
1768
1769AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1770  AnnotMarkup(xrefA, dict, catalog, obj) {
1771  // the real type will be read in initialize()
1772  type = typeHighlight;
1773  initialize(xrefA, catalog, dict);
1774}
1775
1776void AnnotTextMarkup::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1777  Object obj1;
1778
1779  if (dict->lookup("Subtype", &obj1)->isName()) {
1780    GooString typeName(obj1.getName());
1781    if (!typeName.cmp("Highlight")) {
1782      type = typeHighlight;
1783    } else if (!typeName.cmp("Underline")) {
1784      type = typeUnderline;
1785    } else if (!typeName.cmp("Squiggly")) {
1786      type = typeSquiggly;
1787    } else if (!typeName.cmp("StrikeOut")) {
1788      type = typeStrikeOut;
1789    }
1790  }
1791  obj1.free();
1792
1793  if(dict->lookup("QuadPoints", &obj1)->isArray()) {
1794    quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
1795  } else {
1796    quadrilaterals = NULL;
1797  }
1798  obj1.free();
1799}
1800
1801AnnotTextMarkup::~AnnotTextMarkup() {
1802  if(quadrilaterals) {
1803    delete quadrilaterals;
1804  }
1805}
1806
1807//------------------------------------------------------------------------
1808// AnnotWidget
1809//------------------------------------------------------------------------
1810
1811AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1812    Annot(xrefA, dict, catalog, obj) {
1813  type = typeWidget;
1814  widget = NULL;
1815  initialize(xrefA, catalog, dict);
1816}
1817
1818AnnotWidget::~AnnotWidget() {
1819  if (appearCharacs)
1820    delete appearCharacs;
1821 
1822  if (action)
1823    delete action;
1824   
1825  if (additionActions)
1826    delete additionActions;
1827   
1828  if (parent)
1829    delete parent;
1830}
1831
1832void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1833  Object obj1;
1834
1835  if ((form = catalog->getForm ())) {
1836    widget = form->findWidgetByRef (ref);
1837
1838    // check if field apperances need to be regenerated
1839    // Only text or choice fields needs to have appearance regenerated
1840    // see section 8.6.2 "Variable Text" of PDFReference
1841    regen = gFalse;
1842    if (widget != NULL && (widget->getType () == formText || widget->getType () == formChoice)) {
1843      regen = form->getNeedAppearances ();
1844    }
1845  }
1846
1847  // If field doesn't have an AP we'll have to generate it
1848  if (appearance.isNone () || appearance.isNull ())
1849    regen = gTrue;
1850
1851  if(dict->lookup("H", &obj1)->isName()) {
1852    GooString *modeName = new GooString(obj1.getName());
1853
1854    if(!modeName->cmp("N")) {
1855      mode = highlightModeNone;
1856    } else if(!modeName->cmp("O")) {
1857      mode = highlightModeOutline;
1858    } else if(!modeName->cmp("P") || !modeName->cmp("T")) {
1859      mode = highlightModePush;
1860    } else {
1861      mode = highlightModeInvert;
1862    }
1863    delete modeName;
1864  } else {
1865    mode = highlightModeInvert;
1866  }
1867  obj1.free();
1868
1869  if(dict->lookup("MK", &obj1)->isDict()) {
1870    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
1871  } else {
1872    appearCharacs = NULL;
1873  }
1874  obj1.free();
1875
1876  if(dict->lookup("A", &obj1)->isDict()) {
1877    action = NULL;
1878  } else {
1879    action = NULL;
1880  }
1881  obj1.free();
1882
1883  if(dict->lookup("AA", &obj1)->isDict()) {
1884    additionActions = NULL;
1885  } else {
1886    additionActions = NULL;
1887  }
1888  obj1.free();
1889
1890  if(dict->lookup("Parent", &obj1)->isDict()) {
1891    parent = NULL;
1892  } else {
1893    parent = NULL;
1894  }
1895  obj1.free();
1896}
1897
1898// Grand unified handler for preparing text strings to be drawn into form
1899// fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
1900// Converts some or all of this string to the appropriate encoding for the
1901// specified font, and computes the width of the text.  Can optionally stop
1902// converting when a specified width has been reached, to perform line-breaking
1903// for multi-line fields.
1904//
1905// Parameters:
1906//   text: input text string to convert
1907//   outBuf: buffer for writing re-encoded string
1908//   i: index at which to start converting; will be updated to point just after
1909//      last character processed
1910//   font: the font which will be used for display
1911//   width: computed width (unscaled by font size) will be stored here
1912//   widthLimit: if non-zero, stop converting to keep width under this value
1913//      (should be scaled down by font size)
1914//   charCount: count of number of characters will be stored here
1915//   noReencode: if set, do not try to translate the character encoding
1916//      (useful for Zapf Dingbats or other unusual encodings)
1917//      can only be used with simple fonts, not CID-keyed fonts
1918//
1919// TODO: Handle surrogate pairs in UTF-16.
1920//       Should be able to generate output for any CID-keyed font.
1921//       Doesn't handle vertical fonts--should it?
1922void AnnotWidget::layoutText(GooString *text, GooString *outBuf, int *i,
1923                             GfxFont *font, double *width, double widthLimit,
1924                             int *charCount, GBool noReencode)
1925{
1926  CharCode c;
1927  Unicode uChar, *uAux;
1928  double w = 0.0;
1929  int uLen, n;
1930  double dx, dy, ox, oy;
1931  GBool unicode = text->hasUnicodeMarker();
1932  CharCodeToUnicode *ccToUnicode = font->getToUnicode();
1933  ccToUnicode->decRefCnt();
1934  GBool spacePrev;              // previous character was a space
1935
1936  // State for backtracking when more text has been processed than fits within
1937  // widthLimit.  We track for both input (text) and output (outBuf) the offset
1938  // to the first character to discard.
1939  //
1940  // We keep track of several points:
1941  //   1 - end of previous completed word which fits
1942  //   2 - previous character which fit
1943  int last_i1, last_i2, last_o1, last_o2;
1944
1945  if (unicode && text->getLength() % 2 != 0) {
1946    error(-1, "AnnotWidget::layoutText, bad unicode string");
1947    return;
1948  }
1949
1950  // skip Unicode marker on string if needed
1951  if (unicode && *i == 0)
1952    *i = 2;
1953
1954  // Start decoding and copying characters, until either:
1955  //   we reach the end of the string
1956  //   we reach the maximum width
1957  //   we reach a newline character
1958  // As we copy characters, keep track of the last full word to fit, so that we
1959  // can backtrack if we exceed the maximum width.
1960  last_i1 = last_i2 = *i;
1961  last_o1 = last_o2 = 0;
1962  spacePrev = gFalse;
1963  outBuf->clear();
1964
1965  while (*i < text->getLength()) {
1966    last_i2 = *i;
1967    last_o2 = outBuf->getLength();
1968
1969    if (unicode) {
1970      uChar = (unsigned char)(text->getChar(*i)) << 8;
1971      uChar += (unsigned char)(text->getChar(*i + 1));
1972      *i += 2;
1973    } else {
1974      if (noReencode)
1975        uChar = text->getChar(*i) & 0xff;
1976      else
1977        uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
1978      *i += 1;
1979    }
1980
1981    // Explicit line break?
1982    if (uChar == '\r' || uChar == '\n') {
1983      // Treat a <CR><LF> sequence as a single line break
1984      if (uChar == '\r' && *i < text->getLength()) {
1985        if (unicode && text->getChar(*i) == '\0'
1986            && text->getChar(*i + 1) == '\n')
1987          *i += 2;
1988        else if (!unicode && text->getChar(*i) == '\n')
1989          *i += 1;
1990      }
1991
1992      break;
1993    }
1994
1995    if (noReencode) {
1996      outBuf->append(uChar);
1997    } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
1998      if (font->isCIDFont()) {
1999        // TODO: This assumes an identity CMap.  It should be extended to
2000        // handle the general case.
2001        outBuf->append((c >> 8) & 0xff);
2002        outBuf->append(c & 0xff);
2003      } else {
2004        // 8-bit font
2005        outBuf->append(c);
2006      }
2007    } else {
2008      fprintf(stderr,
2009              "warning: layoutText: cannot convert U+%04X\n", uChar);
2010    }
2011
2012    // If we see a space, then we have a linebreak opportunity.
2013    if (uChar == ' ') {
2014      last_i1 = *i;
2015      if (!spacePrev)
2016        last_o1 = last_o2;
2017      spacePrev = gTrue;
2018    } else {
2019      spacePrev = gFalse;
2020    }
2021
2022    // Compute width of character just output
2023    if (outBuf->getLength() > last_o2) {
2024      dx = 0.0;
2025      font->getNextChar(outBuf->getCString() + last_o2,
2026                        outBuf->getLength() - last_o2,
2027                        &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2028      w += dx;
2029    }
2030
2031    // Current line over-full now?
2032    if (widthLimit > 0.0 && w > widthLimit) {
2033      if (last_o1 > 0) {
2034        // Back up to the previous word which fit, if there was a previous
2035        // word.
2036        *i = last_i1;
2037        outBuf->del(last_o1, outBuf->getLength() - last_o1);
2038      } else if (last_o2 > 0) {
2039        // Otherwise, back up to the previous character (in the only word on
2040        // this line)
2041        *i = last_i2;
2042        outBuf->del(last_o2, outBuf->getLength() - last_o2);
2043      } else {
2044        // Otherwise, we were trying to fit the first character; include it
2045        // anyway even if it overflows the space--no updates to make.
2046      }
2047      break;
2048    }
2049  }
2050
2051  // If splitting input into lines because we reached the width limit, then
2052  // consume any remaining trailing spaces that would go on this line from the
2053  // input.  If in doing so we reach a newline, consume that also.  This code
2054  // won't run if we stopped at a newline above, since in that case w <=
2055  // widthLimit still.
2056  if (widthLimit > 0.0 && w > widthLimit) {
2057    if (unicode) {
2058      while (*i < text->getLength()
2059             && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
2060        *i += 2;
2061      if (*i < text->getLength()
2062          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
2063        *i += 2;
2064      if (*i < text->getLength()
2065          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
2066        *i += 2;
2067    } else {
2068      while (*i < text->getLength() && text->getChar(*i) == ' ')
2069        *i += 1;
2070      if (*i < text->getLength() && text->getChar(*i) == '\r')
2071        *i += 1;
2072      if (*i < text->getLength() && text->getChar(*i) == '\n')
2073        *i += 1;
2074    }
2075  }
2076
2077  // Compute the actual width and character count of the final string, based on
2078  // breakpoint, if this information is requested by the caller.
2079  if (width != NULL || charCount != NULL) {
2080    char *s = outBuf->getCString();
2081    int len = outBuf->getLength();
2082
2083    if (width != NULL)
2084      *width = 0.0;
2085    if (charCount != NULL)
2086      *charCount = 0;
2087
2088    while (len > 0) {
2089      dx = 0.0;
2090      n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2091
2092      if (n == 0) {
2093        break;
2094      }
2095
2096      if (width != NULL)
2097        *width += dx;
2098      if (charCount != NULL)
2099        *charCount += 1;
2100
2101      s += n;
2102      len -= n;
2103    }
2104  }
2105}
2106
2107// Copy the given string to appearBuf, adding parentheses around it and
2108// escaping characters as appropriate.
2109void AnnotWidget::writeString(GooString *str, GooString *appearBuf)
2110{
2111  char c;
2112  int i;
2113
2114  appearBuf->append('(');
2115
2116  for (i = 0; i < str->getLength(); ++i) {
2117    c = str->getChar(i);
2118    if (c == '(' || c == ')' || c == '\\') {
2119      appearBuf->append('\\');
2120      appearBuf->append(c);
2121    } else if (c < 0x20) {
2122      appearBuf->appendf("\\{0:03o}", (unsigned char)c);
2123    } else {
2124      appearBuf->append(c);
2125    }
2126  }
2127
2128  appearBuf->append(')');
2129}
2130
2131// Draw the variable text or caption for a field.
2132void AnnotWidget::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
2133    GBool multiline, int comb, int quadding,
2134    GBool txField, GBool forceZapfDingbats,
2135    GBool password) {
2136  GooList *daToks;
2137  GooString *tok, *convertedText;
2138  GfxFont *font;
2139  double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
2140  int tfPos, tmPos, i, j;
2141  GBool freeText = gFalse;      // true if text should be freed before return
2142
2143  //~ if there is no MK entry, this should use the existing content stream,
2144  //~ and only replace the marked content portion of it
2145  //~ (this is only relevant for Tx fields)
2146 
2147  // parse the default appearance string
2148  tfPos = tmPos = -1;
2149  if (da) {
2150    daToks = new GooList();
2151    i = 0;
2152    while (i < da->getLength()) {
2153      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2154        ++i;
2155      }
2156      if (i < da->getLength()) {
2157        for (j = i + 1;
2158            j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2159            ++j) ;
2160        daToks->append(new GooString(da, i, j - i));
2161        i = j;
2162      }
2163    }
2164    for (i = 2; i < daToks->getLength(); ++i) {
2165      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2166        tfPos = i - 2;
2167      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2168        tmPos = i - 6;
2169      }
2170    }
2171  } else {
2172    daToks = NULL;
2173  }
2174
2175  // force ZapfDingbats
2176  //~ this should create the font if needed (?)
2177  if (forceZapfDingbats) {
2178    if (tfPos >= 0) {
2179      tok = (GooString *)daToks->get(tfPos);
2180      if (tok->cmp("/ZaDb")) {
2181        tok->clear();
2182        tok->append("/ZaDb");
2183      }
2184    }
2185  }
2186  // get the font and font size
2187  font = NULL;
2188  fontSize = 0;
2189  if (tfPos >= 0) {
2190    tok = (GooString *)daToks->get(tfPos);
2191    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2192      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2193        error(-1, "Unknown font in field's DA string");
2194      }
2195    } else {
2196      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2197    }
2198    tok = (GooString *)daToks->get(tfPos + 1);
2199    fontSize = atof(tok->getCString());
2200  } else {
2201    error(-1, "Missing 'Tf' operator in field's DA string");
2202  }
2203  if (!font) {
2204    if (daToks) {
2205      deleteGooList(daToks, GooString);
2206    }
2207    return;
2208  }
2209
2210  // get the border width
2211  borderWidth = border ? border->getWidth() : 0;
2212
2213  // for a password field, replace all characters with asterisks
2214  if (password) {
2215    int len;
2216    if (text->hasUnicodeMarker())
2217      len = (text->getLength() - 2) / 2;
2218    else
2219      len = text->getLength();
2220
2221    text = new GooString;
2222    for (i = 0; i < len; ++i)
2223      text->append('*');
2224    freeText = gTrue;
2225  }
2226
2227  convertedText = new GooString;
2228
2229  // setup
2230  if (txField) {
2231    appearBuf->append("/Tx BMC\n");
2232  }
2233  appearBuf->append("q\n");
2234  appearBuf->append("BT\n");
2235  // multi-line text
2236  if (multiline) {
2237    // note: the comb flag is ignored in multiline mode
2238
2239    wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
2240
2241    // compute font autosize
2242    if (fontSize == 0) {
2243      for (fontSize = 20; fontSize > 1; --fontSize) {
2244        y = rect->y2 - rect->y1;
2245        i = 0;
2246        while (i < text->getLength()) {
2247          layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2248                     forceZapfDingbats);
2249          y -= fontSize;
2250        }
2251        // approximate the descender for the last line
2252        if (y >= 0.33 * fontSize) {
2253          break;
2254        }
2255      }
2256      if (tfPos >= 0) {
2257        tok = (GooString *)daToks->get(tfPos + 1);
2258        tok->clear();
2259        tok->appendf("{0:.2f}", fontSize);
2260      }
2261    }
2262
2263    // starting y coordinate
2264    // (note: each line of text starts with a Td operator that moves
2265    // down a line)
2266    y = rect->y2 - rect->y1;
2267
2268    // set the font matrix
2269    if (tmPos >= 0) {
2270      tok = (GooString *)daToks->get(tmPos + 4);
2271      tok->clear();
2272      tok->append('0');
2273      tok = (GooString *)daToks->get(tmPos + 5);
2274      tok->clear();
2275      tok->appendf("{0:.2f}", y);
2276    }
2277
2278    // write the DA string
2279    if (daToks) {
2280      for (i = 0; i < daToks->getLength(); ++i) {
2281        appearBuf->append((GooString *)daToks->get(i))->append(' ');
2282      }
2283    }
2284
2285    // write the font matrix (if not part of the DA string)
2286    if (tmPos < 0) {
2287      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
2288    }
2289
2290    // write a series of lines of text
2291    i = 0;
2292    xPrev = 0;
2293    while (i < text->getLength()) {
2294      layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2295                 forceZapfDingbats);
2296      w *= fontSize;
2297
2298      // compute text start position
2299      switch (quadding) {
2300        case fieldQuadLeft:
2301        default:
2302          x = borderWidth + 2;
2303          break;
2304        case fieldQuadCenter:
2305          x = (rect->x2 - rect->x1 - w) / 2;
2306          break;
2307        case fieldQuadRight:
2308          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2309          break;
2310      }
2311
2312      // draw the line
2313      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
2314      writeString(convertedText, appearBuf);
2315      appearBuf->append(" Tj\n");
2316
2317      // next line
2318      xPrev = x;
2319    }
2320
2321    // single-line text
2322  } else {
2323    //~ replace newlines with spaces? - what does Acrobat do?
2324
2325    // comb formatting
2326    if (comb > 0) {
2327      int charCount;
2328
2329      // compute comb spacing
2330      w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
2331
2332      // compute font autosize
2333      if (fontSize == 0) {
2334        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2335        if (w < fontSize) {
2336          fontSize = w;
2337        }
2338        fontSize = floor(fontSize);
2339        if (tfPos >= 0) {
2340          tok = (GooString *)daToks->get(tfPos + 1);
2341          tok->clear();
2342          tok->appendf("{0:.2f}", fontSize);
2343        }
2344      }
2345
2346      i = 0;
2347      layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
2348                 forceZapfDingbats);
2349      if (charCount > comb)
2350        charCount = comb;
2351
2352      // compute starting text cell
2353      switch (quadding) {
2354        case fieldQuadLeft:
2355        default:
2356          x = borderWidth;
2357          break;
2358        case fieldQuadCenter:
2359          x = borderWidth + (comb - charCount) / 2 * w;
2360          break;
2361        case fieldQuadRight:
2362          x = borderWidth + (comb - charCount) * w;
2363          break;
2364      }
2365      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2366
2367      // set the font matrix
2368      if (tmPos >= 0) {
2369        tok = (GooString *)daToks->get(tmPos + 4);
2370        tok->clear();
2371        tok->appendf("{0:.2f}", x);
2372        tok = (GooString *)daToks->get(tmPos + 5);
2373        tok->clear();
2374        tok->appendf("{0:.2f}", y);
2375      }
2376
2377      // write the DA string
2378      if (daToks) {
2379        for (i = 0; i < daToks->getLength(); ++i) {
2380          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2381        }
2382      }
2383
2384      // write the font matrix (if not part of the DA string)
2385      if (tmPos < 0) {
2386        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2387      }
2388
2389      // write the text string
2390      char *s = convertedText->getCString();
2391      int len = convertedText->getLength();
2392      i = 0;
2393      xPrev = w;                // so that first character is placed properly
2394      while (i < comb && len > 0) {
2395        CharCode code;
2396        Unicode *uAux;
2397        int uLen, n;
2398        double dx, dy, ox, oy;
2399
2400        dx = 0.0;
2401        n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
2402        dx *= fontSize;
2403
2404        // center each character within its cell, by advancing the text
2405        // position the appropriate amount relative to the start of the
2406        // previous character
2407        x = 0.5 * (w - dx);
2408        appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
2409
2410        GooString *charBuf = new GooString(s, n);
2411        writeString(charBuf, appearBuf);
2412        appearBuf->append(" Tj\n");
2413        delete charBuf;
2414
2415        i++;
2416        s += n;
2417        len -= n;
2418        xPrev = x;
2419      }
2420
2421      // regular (non-comb) formatting
2422    } else {
2423      i = 0;
2424      layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
2425                 forceZapfDingbats);
2426
2427      // compute font autosize
2428      if (fontSize == 0) {
2429        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2430        fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
2431        if (fontSize2 < fontSize) {
2432          fontSize = fontSize2;
2433        }
2434        fontSize = floor(fontSize);
2435        if (tfPos >= 0) {
2436          tok = (GooString *)daToks->get(tfPos + 1);
2437          tok->clear();
2438          tok->appendf("{0:.2f}", fontSize);
2439        }
2440      }
2441
2442      // compute text start position
2443      w *= fontSize;
2444      switch (quadding) {
2445        case fieldQuadLeft:
2446        default:
2447          x = borderWidth + 2;
2448          break;
2449        case fieldQuadCenter:
2450          x = (rect->x2 - rect->x1 - w) / 2;
2451          break;
2452        case fieldQuadRight:
2453          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2454          break;
2455      }
2456      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2457
2458      // set the font matrix
2459      if (tmPos >= 0) {
2460        tok = (GooString *)daToks->get(tmPos + 4);
2461        tok->clear();
2462        tok->appendf("{0:.2f}", x);
2463        tok = (GooString *)daToks->get(tmPos + 5);
2464        tok->clear();
2465        tok->appendf("{0:.2f}", y);
2466      }
2467
2468      // write the DA string
2469      if (daToks) {
2470        for (i = 0; i < daToks->getLength(); ++i) {
2471          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2472        }
2473      }
2474
2475      // write the font matrix (if not part of the DA string)
2476      if (tmPos < 0) {
2477        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2478      }
2479
2480      // write the text string
2481      writeString(convertedText, appearBuf);
2482      appearBuf->append(" Tj\n");
2483    }
2484  }
2485  // cleanup
2486  appearBuf->append("ET\n");
2487  appearBuf->append("Q\n");
2488  if (txField) {
2489    appearBuf->append("EMC\n");
2490  }
2491  if (daToks) {
2492    deleteGooList(daToks, GooString);
2493  }
2494  if (freeText) {
2495    delete text;
2496  }
2497  delete convertedText;
2498}
2499
2500// Draw the variable text or caption for a field.
2501void AnnotWidget::drawListBox(GooString **text, GBool *selection,
2502                              int nOptions, int topIdx,
2503                              GooString *da, GfxFontDict *fontDict, GBool quadding) {
2504  GooList *daToks;
2505  GooString *tok, *convertedText;
2506  GfxFont *font;
2507  double fontSize, fontSize2, borderWidth, x, y, w, wMax;
2508  int tfPos, tmPos, i, j;
2509
2510  //~ if there is no MK entry, this should use the existing content stream,
2511  //~ and only replace the marked content portion of it
2512  //~ (this is only relevant for Tx fields)
2513
2514  // parse the default appearance string
2515  tfPos = tmPos = -1;
2516  if (da) {
2517    daToks = new GooList();
2518    i = 0;
2519    while (i < da->getLength()) {
2520      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2521        ++i;
2522       }
2523      if (i < da->getLength()) {
2524        for (j = i + 1;
2525             j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2526             ++j) ;
2527        daToks->append(new GooString(da, i, j - i));
2528        i = j;
2529      }
2530    }
2531    for (i = 2; i < daToks->getLength(); ++i) {
2532      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2533        tfPos = i - 2;
2534      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2535        tmPos = i - 6;
2536      }
2537    }
2538  } else {
2539    daToks = NULL;
2540  }
2541
2542  // get the font and font size
2543  font = NULL;
2544  fontSize = 0;
2545  if (tfPos >= 0) {
2546    tok = (GooString *)daToks->get(tfPos);
2547    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2548      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2549        error(-1, "Unknown font in field's DA string");
2550      }
2551    } else {
2552      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2553    }
2554    tok = (GooString *)daToks->get(tfPos + 1);
2555    fontSize = atof(tok->getCString());
2556  } else {
2557    error(-1, "Missing 'Tf' operator in field's DA string");
2558  }
2559  if (!font) {
2560    if (daToks) {
2561      deleteGooList(daToks, GooString);
2562    }
2563    return;
2564  }
2565
2566  convertedText = new GooString;
2567
2568  // get the border width
2569  borderWidth = border ? border->getWidth() : 0;
2570
2571  // compute font autosize
2572  if (fontSize == 0) {
2573    wMax = 0;
2574    for (i = 0; i < nOptions; ++i) {
2575      j = 0;
2576      layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2577      if (w > wMax) {
2578        wMax = w;
2579      }
2580    }
2581    fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2582    fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
2583    if (fontSize2 < fontSize) {
2584      fontSize = fontSize2;
2585    }
2586    fontSize = floor(fontSize);
2587    if (tfPos >= 0) {
2588      tok = (GooString *)daToks->get(tfPos + 1);
2589      tok->clear();
2590      tok->appendf("{0:.2f}", fontSize);
2591    }
2592  }
2593  // draw the text
2594  y = rect->y2 - rect->y1 - 1.1 * fontSize;
2595  for (i = topIdx; i < nOptions; ++i) {
2596    // setup
2597    appearBuf->append("q\n");
2598
2599    // draw the background if selected
2600    if (selection[i]) {
2601      appearBuf->append("0 g f\n");
2602      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
2603          borderWidth,
2604          y - 0.2 * fontSize,
2605          rect->x2 - rect->x1 - 2 * borderWidth,
2606          1.1 * fontSize);
2607    }
2608
2609    // setup
2610    appearBuf->append("BT\n");
2611
2612    // compute text width and start position
2613    j = 0;
2614    layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2615    w *= fontSize;
2616    switch (quadding) {
2617      case fieldQuadLeft:
2618      default:
2619        x = borderWidth + 2;
2620        break;
2621      case fieldQuadCenter:
2622        x = (rect->x2 - rect->x1 - w) / 2;
2623        break;
2624      case fieldQuadRight:
2625        x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2626        break;
2627    }
2628
2629    // set the font matrix
2630    if (tmPos >= 0) {
2631      tok = (GooString *)daToks->get(tmPos + 4);
2632      tok->clear();
2633      tok->appendf("{0:.2f}", x);
2634      tok = (GooString *)daToks->get(tmPos + 5);
2635      tok->clear();
2636      tok->appendf("{0:.2f}", y);
2637    }
2638
2639    // write the DA string
2640    if (daToks) {
2641      for (j = 0; j < daToks->getLength(); ++j) {
2642        appearBuf->append((GooString *)daToks->get(j))->append(' ');
2643      }
2644    }
2645
2646    // write the font matrix (if not part of the DA string)
2647    if (tmPos < 0) {
2648      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2649    }
2650
2651    // change the text color if selected
2652    if (selection[i]) {
2653      appearBuf->append("1 g\n");
2654    }
2655
2656    // write the text string
2657    writeString(convertedText, appearBuf);
2658    appearBuf->append(" Tj\n");
2659
2660    // cleanup
2661    appearBuf->append("ET\n");
2662    appearBuf->append("Q\n");
2663
2664    // next line
2665    y -= 1.1 * fontSize;
2666  }
2667
2668  if (daToks) {
2669    deleteGooList(daToks, GooString);
2670  }
2671
2672  delete convertedText;
2673}
2674
2675void AnnotWidget::generateFieldAppearance() {
2676  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
2677  Dict *field;
2678  Dict *annot;
2679  Dict *acroForm;
2680  Dict *mkDict;
2681  MemStream *appearStream;
2682  GfxFontDict *fontDict;
2683  GBool hasCaption;
2684  double w, dx, dy, r;
2685  double *dash;
2686  GooString *caption, *da;
2687  GooString **text;
2688  GBool *selection;
2689  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
2690  GBool modified;
2691
2692  if (widget == NULL || !widget->getField () || !widget->getField ()->getObj ()->isDict ())
2693    return;
2694
2695  field = widget->getField ()->getObj ()->getDict ();
2696  annot = widget->getObj ()->getDict ();
2697  acroForm = form->getObj ()->getDict ();
2698 
2699  // do not regenerate appearence if widget has not changed
2700  modified = widget->isModified ();
2701
2702  // only regenerate when it doesn't have an AP or
2703  // it already has an AP but widget has been modified
2704  if (!regen && !modified) {
2705    return;
2706  }
2707
2708  appearBuf = new GooString ();
2709  // get the appearance characteristics (MK) dictionary
2710  if (annot->lookup("MK", &mkObj)->isDict()) {
2711    mkDict = mkObj.getDict();
2712  } else {
2713    mkDict = NULL;
2714  }
2715  // draw the background
2716  if (mkDict) {
2717    if (mkDict->lookup("BG", &obj1)->isArray() &&
2718        obj1.arrayGetLength() > 0) {
2719      setColor(obj1.getArray(), gTrue, 0);
2720      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
2721          rect->x2 - rect->x1, rect->y2 - rect->y1);
2722    }
2723    obj1.free();
2724  }
2725
2726  // get the field type
2727  Form::fieldLookup(field, "FT", &ftObj);
2728
2729  // get the field flags (Ff) value
2730  if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
2731    ff = obj1.getInt();
2732  } else {
2733    ff = 0;
2734  }
2735  obj1.free();
2736
2737  // draw the border
2738  if (mkDict && border) {
2739    w = border->getWidth();
2740    if (w > 0) {
2741      mkDict->lookup("BC", &obj1);
2742      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
2743        mkDict->lookup("BG", &obj1);
2744      }
2745      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
2746        dx = rect->x2 - rect->x1;
2747        dy = rect->y2 - rect->y1;
2748
2749        // radio buttons with no caption have a round border
2750        hasCaption = mkDict->lookup("CA", &obj2)->isString();
2751        obj2.free();
2752        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
2753          r = 0.5 * (dx < dy ? dx : dy);
2754          switch (border->getStyle()) {
2755            case AnnotBorder::borderDashed:
2756              appearBuf->append("[");
2757              dashLength = border->getDashLength();
2758              dash = border->getDash();
2759              for (i = 0; i < dashLength; ++i) {
2760                appearBuf->appendf(" {0:.2f}", dash[i]);
2761              }
2762              appearBuf->append("] 0 d\n");
2763              // fall through to the solid case
2764            case AnnotBorder::borderSolid:
2765            case AnnotBorder::borderUnderlined:
2766              appearBuf->appendf("{0:.2f} w\n", w);
2767              setColor(obj1.getArray(), gFalse, 0);
2768              drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
2769              break;
2770            case AnnotBorder::borderBeveled:
2771            case AnnotBorder::borderInset:
2772              appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
2773              setColor(obj1.getArray(), gFalse, 0);
2774              drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
2775              setColor(obj1.getArray(), gFalse,
2776                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2777              drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2778              setColor(obj1.getArray(), gFalse,
2779                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2780              drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2781              break;
2782          }
2783
2784        } else {
2785          switch (border->getStyle()) {
2786            case AnnotBorder::borderDashed:
2787              appearBuf->append("[");
2788              dashLength = border->getDashLength();
2789              dash = border->getDash();
2790              for (i = 0; i < dashLength; ++i) {
2791                appearBuf->appendf(" {0:.2f}", dash[i]);
2792              }
2793              appearBuf->append("] 0 d\n");
2794              // fall through to the solid case
2795            case AnnotBorder::borderSolid:
2796              appearBuf->appendf("{0:.2f} w\n", w);
2797              setColor(obj1.getArray(), gFalse, 0);
2798              appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
2799                  0.5 * w, dx - w, dy - w);
2800              break;
2801            case AnnotBorder::borderBeveled:
2802            case AnnotBorder::borderInset:
2803              setColor(obj1.getArray(), gTrue,
2804                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2805              appearBuf->append("0 0 m\n");
2806              appearBuf->appendf("0 {0:.2f} l\n", dy);
2807              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2808              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2809              appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
2810              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2811              appearBuf->append("f\n");
2812              setColor(obj1.getArray(), gTrue,
2813                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2814              appearBuf->append("0 0 m\n");
2815              appearBuf->appendf("{0:.2f} 0 l\n", dx);
2816              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2817              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2818              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
2819              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2820              appearBuf->append("f\n");
2821              break;
2822            case AnnotBorder::borderUnderlined:
2823              appearBuf->appendf("{0:.2f} w\n", w);
2824              setColor(obj1.getArray(), gFalse, 0);
2825              appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
2826              break;
2827          }
2828
2829          // clip to the inside of the border
2830          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
2831              w, dx - 2 * w, dy - 2 * w);
2832        }
2833      }
2834      obj1.free();
2835    }
2836  }
2837
2838  // get the resource dictionary
2839  acroForm->lookup("DR", &drObj);
2840
2841  // build the font dictionary
2842  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
2843    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
2844  } else {
2845    fontDict = NULL;
2846  }
2847  obj1.free();
2848
2849  // get the default appearance string
2850  if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
2851    obj1.free();
2852    acroForm->lookup("DA", &obj1);
2853  }
2854  if (obj1.isString()) {
2855    da = obj1.getString()->copy();
2856    //TODO: look for a font size / name HERE
2857    // => create a function
2858  } else {
2859    da = NULL;
2860  }
2861  obj1.free();
2862
2863  // draw the field contents
2864  if (ftObj.isName("Btn")) {
2865    caption = NULL;
2866    if (mkDict) {
2867      if (mkDict->lookup("CA", &obj1)->isString()) {
2868        caption = obj1.getString()->copy();
2869      }
2870      obj1.free();
2871    }
2872    // radio button
2873    if (ff & fieldFlagRadio) {
2874      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
2875      if (Form::fieldLookup(field, "V", &obj1)->isName()) {
2876        if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
2877            strcmp (obj1.getName(), "Off") != 0) {
2878          if (caption) {
2879            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2880                gFalse, gTrue);
2881          } else {
2882            if (mkDict) {
2883              if (mkDict->lookup("BC", &obj3)->isArray() &&
2884                  obj3.arrayGetLength() > 0) {
2885                dx = rect->x2 - rect->x1;
2886                dy = rect->y2 - rect->y1;
2887                setColor(obj3.getArray(), gTrue, 0);
2888                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
2889                    gTrue);
2890              }
2891              obj3.free();
2892            }
2893          }
2894        }
2895        obj2.free();
2896      }
2897      obj1.free();
2898      // pushbutton
2899    } else if (ff & fieldFlagPushbutton) {
2900      if (caption) {
2901        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2902            gFalse, gFalse);
2903      }
2904      // checkbox
2905    } else {
2906      if (annot->lookup("AS", &obj1)->isName() &&
2907          strcmp(obj1.getName(), "Off") != 0) {
2908        if (!caption) {
2909          caption = new GooString("3"); // ZapfDingbats checkmark
2910        }
2911        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2912            gFalse, gTrue);
2913      }
2914      obj1.free();
2915    }
2916    if (caption) {
2917      delete caption;
2918    }
2919  } else if (ftObj.isName("Tx")) {
2920    if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2921      if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
2922        quadding = obj2.getInt();
2923      } else {
2924        quadding = fieldQuadLeft;
2925      }
2926      obj2.free();
2927      comb = 0;
2928      if (ff & fieldFlagComb) {
2929        if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
2930          comb = obj2.getInt();
2931        }
2932        obj2.free();
2933      }
2934      drawText(obj1.getString(), da, fontDict,
2935          ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
2936    }
2937    obj1.free();
2938  } else if (ftObj.isName("Ch")) {
2939    if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
2940      quadding = obj1.getInt();
2941    } else {
2942      quadding = fieldQuadLeft;
2943    }
2944    obj1.free();
2945    // combo box
2946    if (ff & fieldFlagCombo) {
2947      if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2948        drawText(obj1.getString(), da, fontDict,
2949            gFalse, 0, quadding, gTrue, gFalse);
2950        //~ Acrobat draws a popup icon on the right side
2951      }
2952      obj1.free();
2953      // list box
2954    } else {
2955      if (field->lookup("Opt", &obj1)->isArray()) {
2956        nOptions = obj1.arrayGetLength();
2957        // get the option text
2958        text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
2959        for (i = 0; i < nOptions; ++i) {
2960          text[i] = NULL;
2961          obj1.arrayGet(i, &obj2);
2962          if (obj2.isString()) {
2963            text[i] = obj2.getString()->copy();
2964          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
2965            if (obj2.arrayGet(1, &obj3)->isString()) {
2966              text[i] = obj3.getString()->copy();
2967            }
2968            obj3.free();
2969          }
2970          obj2.free();
2971          if (!text[i]) {
2972            text[i] = new GooString();
2973          }
2974        }
2975        // get the selected option(s)
2976        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
2977        //~ need to use the I field in addition to the V field
2978        Form::fieldLookup(field, "V", &obj2);
2979        for (i = 0; i < nOptions; ++i) {
2980          selection[i] = gFalse;
2981          if (obj2.isString()) {
2982            if (!obj2.getString()->cmp(text[i])) {
2983              selection[i] = gTrue;
2984            }
2985          } else if (obj2.isArray()) {
2986            for (j = 0; j < obj2.arrayGetLength(); ++j) {
2987              if (obj2.arrayGet(j, &obj3)->isString() &&
2988                  !obj3.getString()->cmp(text[i])) {
2989                selection[i] = gTrue;
2990              }
2991              obj3.free();
2992            }
2993          }
2994        }
2995        obj2.free();
2996        // get the top index
2997        if (field->lookup("TI", &obj2)->isInt()) {
2998          topIdx = obj2.getInt();
2999        } else {
3000          topIdx = 0;
3001        }
3002        obj2.free();
3003        // draw the text
3004        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
3005        for (i = 0; i < nOptions; ++i) {
3006          delete text[i];
3007        }
3008        gfree(text);
3009        gfree(selection);
3010      }
3011      obj1.free();
3012    }
3013  } else if (ftObj.isName("Sig")) {
3014    //~unimp
3015  } else {
3016    error(-1, "Unknown field type");
3017  }
3018
3019  if (da) {
3020    delete da;
3021  }
3022
3023  // build the appearance stream dictionary
3024  appearDict.initDict(xref);
3025  appearDict.dictAdd(copyString("Length"),
3026      obj1.initInt(appearBuf->getLength()));
3027  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
3028  obj1.initArray(xref);
3029  obj1.arrayAdd(obj2.initReal(0));
3030  obj1.arrayAdd(obj2.initReal(0));
3031  obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
3032  obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
3033  appearDict.dictAdd(copyString("BBox"), &obj1);
3034
3035  // set the resource dictionary
3036  if (drObj.isDict()) {
3037    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
3038  }
3039  drObj.free();
3040
3041  // build the appearance stream
3042  appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
3043      appearBuf->getLength(), &appearDict);
3044  appearance.free();
3045  appearance.initStream(appearStream);
3046  delete appearBuf;
3047
3048  appearStream->setNeedFree(gTrue);
3049
3050  if (widget->isModified()) {
3051    //create a new object that will contains the new appearance
3052   
3053    //if we already have a N entry in our AP dict, reuse it
3054    if (annot->lookup("AP", &obj1)->isDict() &&
3055        obj1.dictLookupNF("N", &obj2)->isRef()) {
3056      appRef = obj2.getRef();
3057    }
3058
3059    // this annot doesn't have an AP yet, create one
3060    if (appRef.num == 0)
3061      appRef = xref->addIndirectObject(&appearance);
3062    else // since we reuse the already existing AP, we have to notify the xref about this update
3063      xref->setModifiedObject(&appearance, appRef);
3064
3065    // update object's AP and AS
3066    Object apObj;
3067    apObj.initDict(xref);
3068
3069    Object oaRef;
3070    oaRef.initRef(appRef.num, appRef.gen);
3071
3072    apObj.dictSet("N", &oaRef);
3073    annot->set("AP", &apObj);
3074    Dict* d = new Dict(annot);
3075    d->decRef();
3076    Object dictObj;
3077    dictObj.initDict(d);
3078
3079    xref->setModifiedObject(&dictObj, ref);
3080    dictObj.free();
3081  }
3082
3083  if (fontDict) {
3084    delete fontDict;
3085  }
3086  ftObj.free();
3087  mkObj.free();
3088}
3089
3090
3091void AnnotWidget::draw(Gfx *gfx, GBool printing) {
3092  Object obj;
3093
3094  // check the flags
3095  if ((flags & annotFlagHidden) ||
3096      (printing && !(flags & annotFlagPrint)) ||
3097      (!printing && (flags & annotFlagNoView))) {
3098    return;
3099  }
3100
3101  generateFieldAppearance ();
3102
3103  // draw the appearance stream
3104  appearance.fetch(xref, &obj);
3105  gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3106                 rect->x1, rect->y1, rect->x2, rect->y2);
3107  obj.free();
3108}
3109
3110
3111//------------------------------------------------------------------------
3112// AnnotMovie
3113//------------------------------------------------------------------------
3114 
3115AnnotMovie::AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3116  Annot(xrefA, dict, catalog, obj) {
3117  type = typeMovie;
3118  initialize(xrefA, catalog, dict);
3119
3120  movie = new Movie();
3121  movie->parseAnnotMovie(this);
3122}
3123
3124AnnotMovie::~AnnotMovie() {
3125  if (title)
3126    delete title;
3127  if (fileName)
3128    delete fileName;
3129  delete movie;
3130
3131  if (posterStream && (!posterStream->decRef())) {
3132    delete posterStream;
3133  }
3134}
3135
3136void AnnotMovie::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3137  Object obj1;
3138
3139  if (dict->lookup("T", &obj1)->isString()) {
3140    title = obj1.getString()->copy();
3141  } else {
3142    title = NULL;
3143  }
3144  obj1.free();
3145
3146  Object movieDict;
3147  Object aDict;
3148
3149  // default values
3150  fileName = NULL;
3151  width = 0;
3152  height = 0;
3153  rotationAngle = 0;
3154  rate = 1.0;
3155  volume = 1.0;
3156  showControls = false;
3157  repeatMode = repeatModeOnce;
3158  synchronousPlay = false;
3159 
3160  hasFloatingWindow = false;
3161  isFullscreen = false;
3162  FWScaleNum = 1;
3163  FWScaleDenum = 1;
3164  FWPosX = 0.5;
3165  FWPosY = 0.5;
3166
3167  if (dict->lookup("Movie", &movieDict)->isDict()) {
3168    Object obj2;
3169    getFileSpecNameForPlatform(movieDict.dictLookup("F", &obj1), &obj2);
3170    fileName = obj2.getString()->copy();
3171    obj2.free();
3172    obj1.free();
3173
3174    if (movieDict.dictLookup("Aspect", &obj1)->isArray()) {
3175      Array* aspect = obj1.getArray();
3176      if (aspect->getLength() >= 2) {
3177        Object tmp;
3178        width = aspect->get(0, &tmp)->getInt();
3179        tmp.free();
3180        height = aspect->get(1, &tmp)->getInt();
3181        tmp.free();
3182      }
3183    }
3184    obj1.free();
3185
3186    if (movieDict.dictLookup("Rotate", &obj1)->isInt()) {
3187      // round up to 90°
3188      rotationAngle = (((obj1.getInt() + 360) % 360) % 90) * 90;
3189    }
3190    obj1.free();
3191
3192    //
3193    // movie poster
3194    //
3195    posterType = posterTypeNone;
3196    posterStream = NULL;
3197    if (!movieDict.dictLookup("Poster", &obj1)->isNone()) {
3198      if (obj1.isBool()) {
3199        GBool v = obj1.getBool();
3200        if (v)
3201          posterType = posterTypeFromMovie;
3202      }
3203     
3204      if (obj1.isStream()) {
3205        posterType = posterTypeStream;
3206       
3207        // "copy" stream
3208        posterStream = obj1.getStream();
3209        posterStream->incRef();
3210      }
3211
3212      obj1.free();
3213    }
3214
3215  }
3216  movieDict.free();
3217
3218
3219  // activation dictionary parsing ...
3220
3221  if (dict->lookup("A", &aDict)->isDict()) {
3222    if (!aDict.dictLookup("Start", &obj1)->isNone()) {
3223      if (obj1.isInt()) {
3224        // If it is representable as an integer (subject to the implementation limit for
3225        // integers, as described in Appendix C), it should be specified as such.
3226
3227        start.units = obj1.getInt();
3228      }
3229      if (obj1.isString()) {
3230        // If it is not representable as an integer, it should be specified as an 8-byte
3231        // string representing a 64-bit twos-complement integer, most significant
3232        // byte first.
3233
3234        // UNSUPPORTED
3235      }
3236
3237      if (obj1.isArray()) {
3238        Array* a = obj1.getArray();
3239
3240        Object tmp;
3241        a->get(0, &tmp);
3242        if (tmp.isInt()) {
3243          start.units = tmp.getInt();
3244        }
3245        if (tmp.isString()) {
3246          // UNSUPPORTED
3247        }
3248        tmp.free();
3249
3250        a->get(1, &tmp);
3251        if (tmp.isInt()) {
3252          start.units_per_second = tmp.getInt();
3253        }
3254        tmp.free();
3255      }
3256    }
3257    obj1.free();
3258
3259    if (!aDict.dictLookup("Duration", &obj1)->isNone()) {
3260      if (obj1.isInt()) {
3261        duration.units = obj1.getInt();
3262      }
3263      if (obj1.isString()) {
3264        // UNSUPPORTED
3265      }
3266
3267      if (obj1.isArray()) {
3268        Array* a = obj1.getArray();
3269
3270        Object tmp;
3271        a->get(0, &tmp);
3272        if (tmp.isInt()) {
3273          duration.units = tmp.getInt();
3274        }
3275        if (tmp.isString()) {
3276          // UNSUPPORTED
3277        }
3278        tmp.free();
3279
3280        a->get(1, &tmp);
3281        if (tmp.isInt()) {
3282          duration.units_per_second = tmp.getInt();
3283        }
3284        tmp.free();
3285      }
3286    }
3287    obj1.free();
3288
3289    if (aDict.dictLookup("Rate", &obj1)->isNum()) {
3290      rate = obj1.getNum();
3291    }
3292    obj1.free();
3293
3294    if (aDict.dictLookup("Volume", &obj1)->isNum()) {
3295      volume = obj1.getNum();
3296    }
3297    obj1.free();
3298
3299    if (aDict.dictLookup("ShowControls", &obj1)->isBool()) {
3300      showControls = obj1.getBool();
3301    }
3302    obj1.free();
3303
3304    if (aDict.dictLookup("Synchronous", &obj1)->isBool()) {
3305      synchronousPlay = obj1.getBool();
3306    }
3307    obj1.free();
3308
3309    if (aDict.dictLookup("Mode", &obj1)->isName()) {
3310      char* name = obj1.getName();
3311      if (!strcmp(name, "Once"))
3312        repeatMode = repeatModeOnce;
3313      if (!strcmp(name, "Open"))
3314        repeatMode = repeatModeOpen;
3315      if (!strcmp(name, "Repeat"))
3316        repeatMode = repeatModeRepeat;
3317      if (!strcmp(name,"Palindrome"))
3318        repeatMode = repeatModePalindrome;
3319    }
3320    obj1.free();
3321
3322    if (aDict.dictLookup("FWScale", &obj1)->isArray()) {
3323      // the presence of that entry implies that the movie is to be played
3324      // in a floating window
3325      hasFloatingWindow = true;
3326
3327      Array* scale = obj1.getArray();
3328      if (scale->getLength() >= 2) {
3329        Object tmp;
3330        if (scale->get(0, &tmp)->isInt()) {
3331          FWScaleNum = tmp.getInt();
3332        }
3333        tmp.free();
3334        if (scale->get(1, &tmp)->isInt()) {
3335          FWScaleDenum = tmp.getInt();
3336        }
3337        tmp.free();
3338      }
3339
3340      // detect fullscreen mode
3341      if ((FWScaleNum == 999) && (FWScaleDenum == 1)) {
3342        isFullscreen = true;
3343      }
3344    }
3345    obj1.free();
3346
3347    if (aDict.dictLookup("FWPosition", &obj1)->isArray()) {
3348      Array* pos = obj1.getArray();
3349      if (pos->getLength() >= 2) {
3350        Object tmp;
3351        if (pos->get(0, &tmp)->isNum()) {
3352          FWPosX = tmp.getNum();
3353        }
3354        tmp.free();
3355        if (pos->get(1, &tmp)->isNum()) {
3356          FWPosY = tmp.getNum();
3357        }
3358        tmp.free();
3359      }
3360    }
3361  }
3362  aDict.free();
3363}
3364
3365void AnnotMovie::getMovieSize(int& width, int& height) {
3366  width = this->width;
3367  height = this->height;
3368}
3369
3370void AnnotMovie::getZoomFactor(int& num, int& denum) {
3371  num = FWScaleNum;
3372  denum = FWScaleDenum;
3373}
3374
3375
3376//------------------------------------------------------------------------
3377// AnnotScreen
3378//------------------------------------------------------------------------
3379 
3380AnnotScreen::AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3381  Annot(xrefA, dict, catalog, obj) {
3382  type = typeScreen;
3383  initialize(xrefA, catalog, dict);
3384}
3385
3386AnnotScreen::~AnnotScreen() {
3387  if (title)
3388    delete title;
3389  if (appearCharacs)
3390    delete appearCharacs;
3391}
3392
3393void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3394  Object obj1;
3395
3396  title = NULL;
3397  if (dict->lookup("T", &obj1)->isString()) {
3398    title = obj1.getString()->copy();
3399  }
3400  obj1.free();
3401
3402  dict->lookup("A", &action);
3403
3404  dict->lookup("AA", &additionAction);
3405
3406  appearCharacs = NULL;
3407  if(dict->lookup("MK", &obj1)->isDict()) {
3408    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
3409  }
3410  obj1.free();
3411
3412}
3413
3414//------------------------------------------------------------------------
3415// AnnotStamp
3416//------------------------------------------------------------------------
3417 
3418AnnotStamp::AnnotStamp(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3419  AnnotMarkup(xrefA, dict, catalog, obj) {
3420  type = typeStamp;
3421  initialize(xrefA, catalog, dict);
3422}
3423
3424AnnotStamp::~AnnotStamp() {
3425  delete icon;
3426}
3427
3428void AnnotStamp::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3429  Object obj1;
3430
3431  if (dict->lookup("Name", &obj1)->isName()) {
3432    icon = new GooString(obj1.getName());
3433  } else {
3434    icon = new GooString("Draft");
3435  }
3436  obj1.free();
3437
3438}
3439
3440//------------------------------------------------------------------------
3441// AnnotGeometry
3442//------------------------------------------------------------------------
3443
3444AnnotGeometry::AnnotGeometry(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3445  AnnotMarkup(xrefA, dict, catalog, obj) {
3446  // the real type will be read in initialize()
3447  type = typeSquare;
3448  initialize(xrefA, catalog, dict);
3449}
3450
3451AnnotGeometry::~AnnotGeometry() {
3452  delete interiorColor;
3453  delete borderEffect;
3454  delete geometryRect;
3455}
3456
3457void AnnotGeometry::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3458  Object obj1;
3459
3460  if (dict->lookup("Subtype", &obj1)->isName()) {
3461    GooString typeName(obj1.getName());
3462    if (!typeName.cmp("Square")) {
3463      type = typeSquare;
3464    } else if (!typeName.cmp("Circle")) {
3465      type = typeCircle;
3466    }
3467  }
3468  obj1.free();
3469
3470  if (dict->lookup("IC", &obj1)->isArray()) {
3471    interiorColor = new AnnotColor(obj1.getArray());
3472  } else {
3473    interiorColor = NULL;
3474  }
3475  obj1.free();
3476
3477  if (dict->lookup("BE", &obj1)->isDict()) {
3478    borderEffect = new AnnotBorderEffect(obj1.getDict());
3479  } else {
3480    borderEffect = NULL;
3481  }
3482  obj1.free();
3483
3484  geometryRect = NULL;
3485  if (dict->lookup("RD", &obj1)->isArray()) {
3486    geometryRect = parseDiffRectangle(obj1.getArray(), rect);
3487  }
3488  obj1.free();
3489
3490}
3491
3492//------------------------------------------------------------------------
3493// AnnotPolygon
3494//------------------------------------------------------------------------
3495
3496AnnotPolygon::AnnotPolygon(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3497  AnnotMarkup(xrefA, dict, catalog, obj) {
3498  // the real type will be read in initialize()
3499  type = typePolygon;
3500  initialize(xrefA, catalog, dict);
3501}
3502
3503AnnotPolygon::~AnnotPolygon() {
3504  delete vertices;
3505
3506  if (interiorColor)
3507    delete interiorColor;
3508
3509  if (borderEffect)
3510    delete borderEffect;
3511}
3512
3513void AnnotPolygon::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3514  Object obj1;
3515
3516  if (dict->lookup("Subtype", &obj1)->isName()) {
3517    GooString typeName(obj1.getName());
3518    if (!typeName.cmp("Polygon")) {
3519      type = typePolygon;
3520    } else if (!typeName.cmp("PolyLine")) {
3521      type = typePolyLine;
3522    }
3523  }
3524  obj1.free();
3525
3526  if (dict->lookup("Vertices", &obj1)->isArray()) {
3527    vertices = new AnnotPath(obj1.getArray());
3528  } else {
3529    vertices = new AnnotPath();
3530    error(-1, "Bad Annot Polygon Vertices");
3531    ok = gFalse;
3532  }
3533  obj1.free();
3534
3535  if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
3536    Object obj2;
3537
3538    if(obj1.arrayGet(0, &obj2)->isString())
3539      startStyle = parseAnnotLineEndingStyle(obj2.getString());
3540    else
3541      startStyle = annotLineEndingNone;
3542    obj2.free();
3543
3544    if(obj1.arrayGet(1, &obj2)->isString())
3545      endStyle = parseAnnotLineEndingStyle(obj2.getString());
3546    else
3547      endStyle = annotLineEndingNone;
3548    obj2.free();
3549
3550  } else {
3551    startStyle = endStyle = annotLineEndingNone;
3552  }
3553  obj1.free();
3554
3555  if (dict->lookup("IC", &obj1)->isArray()) {
3556    interiorColor = new AnnotColor(obj1.getArray());
3557  } else {
3558    interiorColor = NULL;
3559  }
3560  obj1.free();
3561
3562  if (dict->lookup("BE", &obj1)->isDict()) {
3563    borderEffect = new AnnotBorderEffect(obj1.getDict());
3564  } else {
3565    borderEffect = NULL;
3566  }
3567  obj1.free();
3568
3569  if (dict->lookup("IT", &obj1)->isName()) {
3570    GooString *intentName = new GooString(obj1.getName());
3571
3572    if(!intentName->cmp("PolygonCloud")) {
3573      intent = polygonCloud;
3574    } else if(!intentName->cmp("PolyLineDimension")) {
3575      intent = polylineDimension;
3576    } else {
3577      intent = polygonDimension;
3578    }
3579    delete intentName;
3580  } else {
3581    intent = polygonCloud;
3582  }
3583  obj1.free();
3584}
3585
3586//------------------------------------------------------------------------
3587// AnnotCaret
3588//------------------------------------------------------------------------
3589
3590AnnotCaret::AnnotCaret(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3591  AnnotMarkup(xrefA, dict, catalog, obj) {
3592  type = typeCaret;
3593  initialize(xrefA, catalog, dict);
3594}
3595
3596AnnotCaret::~AnnotCaret() {
3597  delete caretRect;
3598}
3599
3600void AnnotCaret::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3601  Object obj1;
3602
3603  symbol = symbolNone;
3604  if (dict->lookup("Sy", &obj1)->isName()) {
3605    GooString typeName(obj1.getName());
3606    if (!typeName.cmp("P")) {
3607      symbol = symbolP;
3608    } else if (!typeName.cmp("None")) {
3609      symbol = symbolNone;
3610    }
3611  }
3612  obj1.free();
3613
3614  if (dict->lookup("RD", &obj1)->isArray()) {
3615    caretRect = parseDiffRectangle(obj1.getArray(), rect);
3616  } else {
3617    caretRect = NULL;
3618  }
3619  obj1.free();
3620
3621}
3622
3623//------------------------------------------------------------------------
3624// AnnotInk
3625//------------------------------------------------------------------------
3626
3627AnnotInk::AnnotInk(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3628  AnnotMarkup(xrefA, dict, catalog, obj) {
3629  type = typeInk;
3630  initialize(xrefA, catalog, dict);
3631}
3632
3633AnnotInk::~AnnotInk() {
3634  if (inkList) {
3635    for (int i = 0; i < inkListLength; ++i)
3636      delete inkList[i];
3637    gfree(inkList);
3638  }
3639}
3640
3641void AnnotInk::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3642  Object obj1;
3643
3644  if (dict->lookup("InkList", &obj1)->isArray()) {
3645    Array *array = obj1.getArray();
3646    inkListLength = array->getLength();
3647    inkList = (AnnotPath **) gmallocn ((inkListLength), sizeof(AnnotPath *));
3648    memset(inkList, 0, inkListLength * sizeof(AnnotPath *));
3649    for (int i = 0; i < inkListLength; i++) {
3650      Object obj2;
3651      if (array->get(i, &obj2)->isArray())
3652        inkList[i] = new AnnotPath(obj2.getArray());
3653      obj2.free();
3654    }
3655  } else {
3656    inkListLength = 0;
3657    inkList = NULL;
3658    error(-1, "Bad Annot Ink List");
3659    ok = gFalse;
3660  }
3661  obj1.free();
3662}
3663
3664//------------------------------------------------------------------------
3665// AnnotFileAttachment
3666//------------------------------------------------------------------------
3667
3668AnnotFileAttachment::AnnotFileAttachment(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3669  AnnotMarkup(xrefA, dict, catalog, obj) {
3670  type = typeFileAttachment;
3671  initialize(xrefA, catalog, dict);
3672}
3673
3674AnnotFileAttachment::~AnnotFileAttachment() {
3675  file.free();
3676
3677  if (name)
3678    delete name;
3679}
3680
3681void AnnotFileAttachment::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3682  Object obj1;
3683
3684  if (dict->lookup("FS", &obj1)->isDict()) {
3685    obj1.copy(&file);
3686  } else {
3687    error(-1, "Bad Annot File Attachment");
3688    ok = gFalse;
3689  }
3690  obj1.free();
3691
3692  if (dict->lookup("Name", &obj1)->isName()) {
3693    name = new GooString(obj1.getName());
3694  } else {
3695    name = new GooString("PushPin");
3696  }
3697  obj1.free();
3698}
3699
3700//------------------------------------------------------------------------
3701// AnnotSound
3702//------------------------------------------------------------------------
3703
3704AnnotSound::AnnotSound(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3705  AnnotMarkup(xrefA, dict, catalog, obj) {
3706  type = typeSound;
3707  initialize(xrefA, catalog, dict);
3708}
3709
3710AnnotSound::~AnnotSound() {
3711  delete sound;
3712
3713  delete name;
3714}
3715
3716void AnnotSound::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3717  Object obj1;
3718
3719  sound = Sound::parseSound(dict->lookup("Sound", &obj1));
3720  if (!sound) {
3721    error(-1, "Bad Annot Sound");
3722    ok = gFalse;
3723  }
3724  obj1.free();
3725
3726  if (dict->lookup("Name", &obj1)->isName()) {
3727    name = new GooString(obj1.getName());
3728  } else {
3729    name = new GooString("Speaker");
3730  }
3731  obj1.free();
3732}
3733
3734//------------------------------------------------------------------------
3735// Annot3D
3736//------------------------------------------------------------------------
3737
3738Annot3D::Annot3D(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3739  Annot(xrefA, dict, catalog, obj) {
3740  type = type3D;
3741  initialize(xrefA, catalog, dict);
3742}
3743
3744Annot3D::~Annot3D() {
3745  if (activation)
3746    delete activation;
3747}
3748
3749void Annot3D::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3750  Object obj1;
3751
3752  if (dict->lookup("3DA", &obj1)->isDict()) {
3753    activation = new Activation(obj1.getDict());
3754  } else {
3755    activation = NULL;
3756  }
3757  obj1.free();
3758}
3759
3760Annot3D::Activation::Activation(Dict *dict) {
3761  Object obj1;
3762
3763  if (dict->lookup("A", &obj1)->isName()) {
3764    GooString *name = new GooString(obj1.getName());
3765
3766    if(!name->cmp("PO")) {
3767      aTrigger = aTriggerPageOpened;
3768    } else if(!name->cmp("PV")) {
3769      aTrigger = aTriggerPageVisible;
3770    } else if(!name->cmp("XA")) {
3771      aTrigger = aTriggerUserAction;
3772    } else {
3773      aTrigger = aTriggerUnknown;
3774    }
3775    delete name;
3776  } else {
3777    aTrigger = aTriggerUnknown;
3778  }
3779  obj1.free();
3780
3781  if(dict->lookup("AIS", &obj1)->isName()) {
3782    GooString *name = new GooString(obj1.getName());
3783
3784    if(!name->cmp("I")) {
3785      aState = aStateEnabled;
3786    } else if(!name->cmp("L")) {
3787      aState = aStateDisabled;
3788    } else {
3789      aState = aStateUnknown;
3790    }
3791    delete name;
3792  } else {
3793    aState = aStateUnknown;
3794  }
3795  obj1.free();
3796
3797  if(dict->lookup("D", &obj1)->isName()) {
3798    GooString *name = new GooString(obj1.getName());
3799
3800    if(!name->cmp("PC")) {
3801      dTrigger = dTriggerPageClosed;
3802    } else if(!name->cmp("PI")) {
3803      dTrigger = dTriggerPageInvisible;
3804    } else if(!name->cmp("XD")) {
3805      dTrigger = dTriggerUserAction;
3806    } else {
3807      dTrigger = dTriggerUnknown;
3808    }
3809    delete name;
3810  } else {
3811    dTrigger = dTriggerUnknown;
3812  }
3813  obj1.free();
3814
3815  if(dict->lookup("DIS", &obj1)->isName()) {
3816    GooString *name = new GooString(obj1.getName());
3817
3818    if(!name->cmp("U")) {
3819      dState = dStateUninstantiaded;
3820    } else if(!name->cmp("I")) {
3821      dState = dStateInstantiated;
3822    } else if(!name->cmp("L")) {
3823      dState = dStateLive;
3824    } else {
3825      dState = dStateUnknown;
3826    }
3827    delete name;
3828  } else {
3829    dState = dStateUnknown;
3830  }
3831  obj1.free();
3832
3833  if (dict->lookup("TB", &obj1)->isBool()) {
3834    displayToolbar = obj1.getBool();
3835  } else {
3836    displayToolbar = gTrue;
3837  }
3838  obj1.free();
3839
3840  if (dict->lookup("NP", &obj1)->isBool()) {
3841    displayNavigation = obj1.getBool();
3842  } else {
3843    displayNavigation = gFalse;
3844  }
3845  obj1.free();
3846}
3847
3848//------------------------------------------------------------------------
3849// Annots
3850//------------------------------------------------------------------------
3851
3852Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
3853  Annot *annot;
3854  Object obj1;
3855  int size;
3856  int i;
3857
3858  annots = NULL;
3859  size = 0;
3860  nAnnots = 0;
3861
3862  if (annotsObj->isArray()) {
3863    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
3864      //get the Ref to this annot and pass it to Annot constructor
3865      //this way, it'll be possible for the annot to retrieve the corresponding
3866      //form widget
3867      Object obj2;
3868      if (annotsObj->arrayGet(i, &obj1)->isDict()) {
3869        annotsObj->arrayGetNF(i, &obj2);
3870        annot = createAnnot (xref, obj1.getDict(), catalog, &obj2);
3871        if (annot && annot->isOk()) {
3872          if (nAnnots >= size) {
3873            size += 16;
3874            annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
3875          }
3876          annots[nAnnots++] = annot;
3877        } else {
3878          delete annot;
3879        }
3880      }
3881      obj2.free();
3882      obj1.free();
3883    }
3884  }
3885}
3886
3887Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj) {
3888  Annot *annot;
3889  Object obj1;
3890
3891  if (dict->lookup("Subtype", &obj1)->isName()) {
3892    GooString *typeName = new GooString(obj1.getName());
3893
3894    if (!typeName->cmp("Text")) {
3895      annot = new AnnotText(xref, dict, catalog, obj);
3896    } else if (!typeName->cmp("Link")) {
3897      annot = new AnnotLink(xref, dict, catalog, obj);
3898    } else if (!typeName->cmp("FreeText")) {
3899      annot = new AnnotFreeText(xref, dict, catalog, obj);
3900    } else if (!typeName->cmp("Line")) {
3901      annot = new AnnotLine(xref, dict, catalog, obj);
3902    } else if (!typeName->cmp("Square")) {
3903      annot = new AnnotGeometry(xref, dict, catalog, obj);
3904    } else if (!typeName->cmp("Circle")) {
3905      annot = new AnnotGeometry(xref, dict, catalog, obj);
3906    } else if (!typeName->cmp("Polygon")) {
3907      annot = new AnnotPolygon(xref, dict, catalog, obj);
3908    } else if (!typeName->cmp("PolyLine")) {
3909      annot = new AnnotPolygon(xref, dict, catalog, obj);
3910    } else if (!typeName->cmp("Highlight")) {
3911      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3912    } else if (!typeName->cmp("Underline")) {
3913      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3914    } else if (!typeName->cmp("Squiggly")) {
3915      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3916    } else if (!typeName->cmp("StrikeOut")) {
3917      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3918    } else if (!typeName->cmp("Stamp")) {
3919      annot = new AnnotStamp(xref, dict, catalog, obj);
3920    } else if (!typeName->cmp("Caret")) {
3921      annot = new AnnotCaret(xref, dict, catalog, obj);
3922    } else if (!typeName->cmp("Ink")) {
3923      annot = new AnnotInk(xref, dict, catalog, obj);
3924    } else if (!typeName->cmp("FileAttachment")) {
3925      annot = new AnnotFileAttachment(xref, dict, catalog, obj);
3926    } else if (!typeName->cmp("Sound")) {
3927      annot = new AnnotSound(xref, dict, catalog, obj);
3928    } else if(!typeName->cmp("Movie")) {
3929      annot = new AnnotMovie(xref, dict, catalog, obj);
3930    } else if(!typeName->cmp("Widget")) {
3931      annot = new AnnotWidget(xref, dict, catalog, obj);
3932    } else if(!typeName->cmp("Screen")) {
3933      annot = new AnnotScreen(xref, dict, catalog, obj);
3934    } else if(!typeName->cmp("PrinterMark")) {
3935      annot = new Annot(xref, dict, catalog, obj);
3936    } else if (!typeName->cmp("TrapNet")) {
3937      annot = new Annot(xref, dict, catalog, obj);
3938    } else if (!typeName->cmp("Watermark")) {
3939      annot = new Annot(xref, dict, catalog, obj);
3940    } else if (!typeName->cmp("3D")) {
3941      annot = new Annot3D(xref, dict, catalog, obj);
3942    } else {
3943      annot = new Annot(xref, dict, catalog, obj);
3944    }
3945
3946    delete typeName;
3947  } else {
3948    annot = NULL;
3949  }
3950  obj1.free();
3951
3952  return annot;
3953}
3954
3955Annot *Annots::findAnnot(Ref *ref) {
3956  int i;
3957
3958  for (i = 0; i < nAnnots; ++i) {
3959    if (annots[i]->match(ref)) {
3960      return annots[i];
3961    }
3962  }
3963  return NULL;
3964}
3965
3966
3967Annots::~Annots() {
3968  int i;
3969
3970  for (i = 0; i < nAnnots; ++i) {
3971    delete annots[i];
3972  }
3973  gfree(annots);
3974}
Note: See TracBrowser for help on using the repository browser.