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

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

PDF plugin: Poppler library updated to version 0.10.0

File size: 103.2 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  if (dict->lookup("OC", &obj1)->isDict()) {
962    optionalContent = new OCGs(&obj1, xrefA);
963  } else {
964    optionalContent = NULL;
965  }
966  obj1.free();
967}
968
969double Annot::getXMin() {
970  return rect->x1;
971}
972
973double Annot::getYMin() {
974  return rect->y1;
975}
976
977void Annot::readArrayNum(Object *pdfArray, int key, double *value) {
978  Object valueObject;
979
980  pdfArray->arrayGet(key, &valueObject);
981  if (valueObject.isNum()) {
982    *value = valueObject.getNum();
983  } else {
984    *value = 0;
985    ok = gFalse;
986  }
987  valueObject.free();
988}
989
990Annot::~Annot() {
991  delete rect;
992
993  if (contents)
994    delete contents;
995
996  if (pageDict)
997    delete pageDict;
998
999  if (name)
1000    delete name;
1001
1002  if (modified)
1003    delete modified;
1004
1005  appearance.free();
1006
1007  if (appearState)
1008    delete appearState;
1009
1010  if (border)
1011    delete border;
1012
1013  if (color)
1014    delete color;
1015
1016  if (optionalContent)
1017    delete optionalContent;
1018}
1019
1020// Set the current fill or stroke color, based on <a> (which should
1021// have 1, 3, or 4 elements).  If <adjust> is +1, color is brightened;
1022// if <adjust> is -1, color is darkened; otherwise color is not
1023// modified.
1024void Annot::setColor(Array *a, GBool fill, int adjust) {
1025  Object obj1;
1026  double color[4];
1027  int nComps, i;
1028
1029  nComps = a->getLength();
1030  if (nComps > 4) {
1031    nComps = 4;
1032  }
1033  for (i = 0; i < nComps && i < 4; ++i) {
1034    if (a->get(i, &obj1)->isNum()) {
1035      color[i] = obj1.getNum();
1036    } else {
1037      color[i] = 0;
1038    }
1039    obj1.free();
1040  }
1041  if (nComps == 4) {
1042    adjust = -adjust;
1043  }
1044  if (adjust > 0) {
1045    for (i = 0; i < nComps; ++i) {
1046      color[i] = 0.5 * color[i] + 0.5;
1047    }
1048  } else if (adjust < 0) {
1049    for (i = 0; i < nComps; ++i) {
1050      color[i] = 0.5 * color[i];
1051    }
1052  }
1053  if (nComps == 4) {
1054    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
1055        color[0], color[1], color[2], color[3],
1056        fill ? 'k' : 'K');
1057  } else if (nComps == 3) {
1058    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
1059        color[0], color[1], color[2],
1060        fill ? "rg" : "RG");
1061  } else {
1062    appearBuf->appendf("{0:.2f} {1:c}\n",
1063        color[0],
1064        fill ? 'g' : 'G');
1065  }
1066}
1067
1068// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1069// If <fill> is true, the circle is filled; otherwise it is stroked.
1070void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
1071  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1072      cx + r, cy);
1073  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1074      cx + r, cy + bezierCircle * r,
1075      cx + bezierCircle * r, cy + r,
1076      cx, cy + r);
1077  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1078      cx - bezierCircle * r, cy + r,
1079      cx - r, cy + bezierCircle * r,
1080      cx - r, cy);
1081  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1082      cx - r, cy - bezierCircle * r,
1083      cx - bezierCircle * r, cy - r,
1084      cx, cy - r);
1085  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1086      cx + bezierCircle * r, cy - r,
1087      cx + r, cy - bezierCircle * r,
1088      cx + r, cy);
1089  appearBuf->append(fill ? "f\n" : "s\n");
1090}
1091
1092// Draw the top-left half of an (approximate) circle of radius <r>
1093// centered at (<cx>, <cy>).
1094void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1095  double r2;
1096
1097  r2 = r / sqrt(2.0);
1098  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1099      cx + r2, cy + r2);
1100  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1101      cx + (1 - bezierCircle) * r2,
1102      cy + (1 + bezierCircle) * r2,
1103      cx - (1 - bezierCircle) * r2,
1104      cy + (1 + bezierCircle) * r2,
1105      cx - r2,
1106      cy + r2);
1107  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1108      cx - (1 + bezierCircle) * r2,
1109      cy + (1 - bezierCircle) * r2,
1110      cx - (1 + bezierCircle) * r2,
1111      cy - (1 - bezierCircle) * r2,
1112      cx - r2,
1113      cy - r2);
1114  appearBuf->append("S\n");
1115}
1116
1117// Draw the bottom-right half of an (approximate) circle of radius <r>
1118// centered at (<cx>, <cy>).
1119void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1120  double r2;
1121
1122  r2 = r / sqrt(2.0);
1123  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1124      cx - r2, cy - r2);
1125  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1126      cx - (1 - bezierCircle) * r2,
1127      cy - (1 + bezierCircle) * r2,
1128      cx + (1 - bezierCircle) * r2,
1129      cy - (1 + bezierCircle) * r2,
1130      cx + r2,
1131      cy - r2);
1132  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1133      cx + (1 + bezierCircle) * r2,
1134      cy - (1 - bezierCircle) * r2,
1135      cx + (1 + bezierCircle) * r2,
1136      cy + (1 - bezierCircle) * r2,
1137      cx + r2,
1138      cy + r2);
1139  appearBuf->append("S\n");
1140}
1141
1142void Annot::draw(Gfx *gfx, GBool printing) {
1143  Object obj;
1144
1145  // check the flags
1146  if ((flags & annotFlagHidden) ||
1147      (printing && !(flags & annotFlagPrint)) ||
1148      (!printing && (flags & annotFlagNoView))) {
1149    return;
1150  }
1151
1152  // draw the appearance stream
1153  appearance.fetch(xref, &obj);
1154  gfx->drawAnnot(&obj, (type == typeLink) ? border : (AnnotBorder *)NULL, color,
1155      rect->x1, rect->y1, rect->x2, rect->y2);
1156  obj.free();
1157}
1158
1159//------------------------------------------------------------------------
1160// AnnotPopup
1161//------------------------------------------------------------------------
1162
1163AnnotPopup::AnnotPopup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1164    Annot(xrefA, dict, catalog, obj) {
1165  type = typePopup;
1166  initialize(xrefA, dict, catalog);
1167}
1168
1169AnnotPopup::~AnnotPopup() {
1170  /*
1171  if (parent)
1172    delete parent;
1173  */
1174}
1175
1176void AnnotPopup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
1177  Object obj1;
1178  /*
1179  if (dict->lookup("Parent", &obj1)->isDict()) {
1180    parent = NULL;
1181  } else {
1182    parent = NULL;
1183  }
1184  obj1.free();
1185  */
1186  if (dict->lookup("Open", &obj1)->isBool()) {
1187    open = obj1.getBool();
1188  } else {
1189    open = gFalse;
1190  }
1191  obj1.free();
1192}
1193
1194//------------------------------------------------------------------------
1195// AnnotMarkup
1196//------------------------------------------------------------------------
1197 
1198AnnotMarkup::AnnotMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1199    Annot(xrefA, dict, catalog, obj) {
1200  initialize(xrefA, dict, catalog, obj);
1201}
1202
1203AnnotMarkup::~AnnotMarkup() {
1204  if (label)
1205    delete label;
1206
1207  if (popup)
1208    delete popup;
1209
1210  if (date)
1211    delete date;
1212
1213  if (subject)
1214    delete subject;
1215}
1216
1217void AnnotMarkup::initialize(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) {
1218  Object obj1;
1219
1220  if (dict->lookup("T", &obj1)->isString()) {
1221    label = obj1.getString()->copy();
1222  } else {
1223    label = NULL;
1224  }
1225  obj1.free();
1226
1227  if (dict->lookup("Popup", &obj1)->isDict()) {
1228    popup = new AnnotPopup(xrefA, obj1.getDict(), catalog, obj);
1229  } else {
1230    popup = NULL;
1231  }
1232  obj1.free();
1233
1234  if (dict->lookup("CA", &obj1)->isNum()) {
1235    opacity = obj1.getNum();
1236  } else {
1237    opacity = 1.0;
1238  }
1239  obj1.free();
1240
1241  if (dict->lookup("CreationDate", &obj1)->isString()) {
1242    date = obj1.getString()->copy();
1243  } else {
1244    date = NULL;
1245  }
1246  obj1.free();
1247
1248  if (dict->lookupNF("IRT", &obj1)->isRef()) {
1249    inReplyTo = obj1.getRef();
1250  } else {
1251    inReplyTo.num = 0;
1252    inReplyTo.gen = 0;
1253  }
1254  obj1.free();
1255
1256  if (dict->lookup("Subj", &obj1)->isString()) {
1257    subject = obj1.getString()->copy();
1258  } else {
1259    subject = NULL;
1260  }
1261  obj1.free();
1262
1263  if (dict->lookup("RT", &obj1)->isName()) {
1264    GooString *replyName = new GooString(obj1.getName());
1265
1266    if (!replyName->cmp("R")) {
1267      replyTo = replyTypeR;
1268    } else if (!replyName->cmp("Group")) {
1269      replyTo = replyTypeGroup;
1270    } else {
1271      replyTo = replyTypeR;
1272    }
1273    delete replyName;
1274  } else {
1275    replyTo = replyTypeR;
1276  }
1277  obj1.free();
1278
1279  if (dict->lookup("ExData", &obj1)->isDict()) {
1280    exData = parseAnnotExternalData(obj1.getDict());
1281  } else {
1282    exData = annotExternalDataMarkupUnknown;
1283  }
1284  obj1.free();
1285}
1286
1287//------------------------------------------------------------------------
1288// AnnotText
1289//------------------------------------------------------------------------
1290
1291AnnotText::AnnotText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1292    AnnotMarkup(xrefA, dict, catalog, obj) {
1293
1294  type = typeText;
1295  flags |= flagNoZoom | flagNoRotate;
1296  initialize (xrefA, catalog, dict);
1297}
1298
1299AnnotText::~AnnotText() {
1300  delete icon;
1301}
1302
1303void AnnotText::setModified(GooString *date) {
1304  if (date) {
1305    delete modified;
1306    modified = new GooString(date);
1307  }
1308}
1309
1310void AnnotText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1311  Object obj1;
1312
1313  if (dict->lookup("Open", &obj1)->isBool())
1314    open = obj1.getBool();
1315  else
1316    open = gFalse;
1317  obj1.free();
1318
1319  if (dict->lookup("Name", &obj1)->isName()) {
1320    icon = new GooString(obj1.getName());
1321  } else {
1322    icon = new GooString("Note");
1323  }
1324  obj1.free();
1325
1326  if (dict->lookup("StateModel", &obj1)->isString()) {
1327    Object obj2;
1328    GooString *modelName = obj1.getString();
1329
1330    if (dict->lookup("State", &obj2)->isString()) {
1331      GooString *stateName = obj2.getString();
1332
1333      if (!stateName->cmp("Marked")) {
1334        state = stateMarked;
1335      } else if (!stateName->cmp("Unmarked")) {
1336        state = stateUnmarked;
1337      } else if (!stateName->cmp("Accepted")) {
1338        state = stateAccepted;
1339      } else if (!stateName->cmp("Rejected")) {
1340        state = stateRejected;
1341      } else if (!stateName->cmp("Cancelled")) {
1342        state = stateCancelled;
1343      } else if (!stateName->cmp("Completed")) {
1344        state = stateCompleted;
1345      } else if (!stateName->cmp("None")) {
1346        state = stateNone;
1347      } else {
1348        state = stateUnknown;
1349      }
1350    } else {
1351      state = stateUnknown;
1352    }
1353    obj2.free();
1354
1355    if (!modelName->cmp("Marked")) {
1356      switch (state) {
1357        case stateUnknown:
1358          state = stateMarked;
1359          break;
1360        case stateAccepted:
1361        case stateRejected:
1362        case stateCancelled:
1363        case stateCompleted:
1364        case stateNone:
1365          state = stateUnknown;
1366          break;
1367        default:
1368          break;
1369      }
1370    } else if (!modelName->cmp("Review")) {
1371      switch (state) {
1372        case stateUnknown:
1373          state = stateNone;
1374          break;
1375        case stateMarked:
1376        case stateUnmarked:
1377          state = stateUnknown;
1378          break;
1379        default:
1380          break;
1381      }
1382    } else {
1383      state = stateUnknown;
1384    }
1385  } else {
1386    state = stateUnknown;
1387  }
1388  obj1.free();
1389}
1390
1391//------------------------------------------------------------------------
1392// AnnotLink
1393//------------------------------------------------------------------------
1394
1395AnnotLink::AnnotLink(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1396    Annot(xrefA, dict, catalog, obj) {
1397
1398  type = typeLink;
1399  initialize (xrefA, catalog, dict);
1400}
1401
1402AnnotLink::~AnnotLink() {
1403  /*
1404  if (actionDict)
1405    delete actionDict;
1406  */
1407  dest.free();
1408  /*
1409  if (uriAction)
1410    delete uriAction;
1411  */
1412  if (quadrilaterals)
1413    delete quadrilaterals;
1414}
1415
1416void AnnotLink::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1417  Object obj1;
1418  /*
1419  if (dict->lookup("A", &obj1)->isDict()) {
1420    actionDict = NULL;
1421  } else {
1422    actionDict = NULL;
1423  }
1424  obj1.free();
1425  */
1426  dict->lookup("Dest", &dest);
1427  if (dict->lookup("H", &obj1)->isName()) {
1428    GooString *effect = new GooString(obj1.getName());
1429
1430    if (!effect->cmp("N")) {
1431      linkEffect = effectNone;
1432    } else if (!effect->cmp("I")) {
1433      linkEffect = effectInvert;
1434    } else if (!effect->cmp("O")) {
1435      linkEffect = effectOutline;
1436    } else if (!effect->cmp("P")) {
1437      linkEffect = effectPush;
1438    } else {
1439      linkEffect = effectInvert;
1440    }
1441    delete effect;
1442  } else {
1443    linkEffect = effectInvert;
1444  }
1445  obj1.free();
1446  /*
1447  if (dict->lookup("PA", &obj1)->isDict()) {
1448    uriAction = NULL;
1449  } else {
1450    uriAction = NULL;
1451  }
1452  obj1.free();
1453  */
1454  if (dict->lookup("QuadPoints", &obj1)->isArray()) {
1455    quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
1456  } else {
1457    quadrilaterals = NULL;
1458  }
1459  obj1.free();
1460}
1461
1462void AnnotLink::draw(Gfx *gfx, GBool printing) {
1463  Object obj;
1464
1465  // check the flags
1466  if ((flags & annotFlagHidden) ||
1467      (printing && !(flags & annotFlagPrint)) ||
1468      (!printing && (flags & annotFlagNoView))) {
1469    return;
1470  }
1471
1472  // draw the appearance stream
1473  appearance.fetch(xref, &obj);
1474  gfx->drawAnnot(&obj, border, color,
1475                 rect->x1, rect->y1, rect->x2, rect->y2);
1476  obj.free();
1477}
1478
1479//------------------------------------------------------------------------
1480// AnnotFreeText
1481//------------------------------------------------------------------------
1482
1483AnnotFreeText::AnnotFreeText(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1484    AnnotMarkup(xrefA, dict, catalog, obj) {
1485  type = typeFreeText;
1486  initialize(xrefA, catalog, dict);
1487}
1488
1489AnnotFreeText::~AnnotFreeText() {
1490  delete appearanceString;
1491
1492  if (styleString)
1493    delete styleString;
1494
1495  if (calloutLine)
1496    delete calloutLine;
1497
1498  if (borderEffect)
1499    delete borderEffect;
1500
1501  if (rectangle)
1502    delete rectangle;
1503}
1504
1505void AnnotFreeText::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1506  Object obj1;
1507
1508  if (dict->lookup("DA", &obj1)->isString()) {
1509    appearanceString = obj1.getString()->copy();
1510  } else {
1511    appearanceString = new GooString();
1512    error(-1, "Bad appearance for annotation");
1513    ok = gFalse;
1514  }
1515  obj1.free();
1516
1517  if (dict->lookup("Q", &obj1)->isInt()) {
1518    quadding = (AnnotFreeTextQuadding) obj1.getInt();
1519  } else {
1520    quadding = quaddingLeftJustified;
1521  }
1522  obj1.free();
1523
1524  if (dict->lookup("DS", &obj1)->isString()) {
1525    styleString = obj1.getString()->copy();
1526  } else {
1527    styleString = NULL;
1528  }
1529  obj1.free();
1530
1531  if (dict->lookup("CL", &obj1)->isArray() && obj1.arrayGetLength() >= 4) {
1532    double x1, y1, x2, y2;
1533    Object obj2;
1534
1535    (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
1536    obj2.free();
1537    (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
1538    obj2.free();
1539    (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
1540    obj2.free();
1541    (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
1542    obj2.free();
1543
1544    if (obj1.arrayGetLength() == 6) {
1545      double x3, y3;
1546      (obj1.arrayGet(4, &obj2)->isNum() ? x3 = obj2.getNum() : x3 = 0);
1547      obj2.free();
1548      (obj1.arrayGet(5, &obj2)->isNum() ? y3 = obj2.getNum() : y3 = 0);
1549      obj2.free();
1550      calloutLine = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
1551    } else {
1552      calloutLine = new AnnotCalloutLine(x1, y1, x2, y2);
1553    }
1554  } else {
1555    calloutLine = NULL;
1556  }
1557  obj1.free();
1558
1559  if (dict->lookup("IT", &obj1)->isName()) {
1560    GooString *intentName = new GooString(obj1.getName());
1561
1562    if (!intentName->cmp("FreeText")) {
1563      intent = intentFreeText;
1564    } else if (!intentName->cmp("FreeTextCallout")) {
1565      intent = intentFreeTextCallout;
1566    } else if (!intentName->cmp("FreeTextTypeWriter")) {
1567      intent = intentFreeTextTypeWriter;
1568    } else {
1569      intent = intentFreeText;
1570    }
1571    delete intentName;
1572  } else {
1573    intent = intentFreeText;
1574  }
1575  obj1.free();
1576
1577  if (dict->lookup("BE", &obj1)->isDict()) {
1578    borderEffect = new AnnotBorderEffect(obj1.getDict());
1579  } else {
1580    borderEffect = NULL;
1581  }
1582  obj1.free();
1583
1584  if (dict->lookup("RD", &obj1)->isArray()) {
1585    rectangle = parseDiffRectangle(obj1.getArray(), rect);
1586  } else {
1587    rectangle = NULL;
1588  }
1589  obj1.free();
1590
1591  if (dict->lookup("LE", &obj1)->isName()) {
1592    GooString *styleName = new GooString(obj1.getName());
1593    endStyle = parseAnnotLineEndingStyle(styleName);
1594    delete styleName;
1595  } else {
1596    endStyle = annotLineEndingNone;
1597  }
1598  obj1.free();
1599}
1600
1601//------------------------------------------------------------------------
1602// AnnotLine
1603//------------------------------------------------------------------------
1604
1605AnnotLine::AnnotLine(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1606    AnnotMarkup(xrefA, dict, catalog, obj) {
1607  type = typeLine;
1608  initialize(xrefA, catalog, dict);
1609}
1610
1611AnnotLine::~AnnotLine() {
1612  delete coord1;
1613  delete coord2;
1614
1615  if (interiorColor)
1616    delete interiorColor;
1617
1618  if (measure)
1619    delete measure;
1620}
1621
1622void AnnotLine::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1623  Object obj1;
1624
1625  if (dict->lookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) {
1626    Object obj2;
1627    double x1, y1, x2, y2;
1628
1629    (obj1.arrayGet(0, &obj2)->isNum() ? x1 = obj2.getNum() : x1 = 0);
1630    obj2.free();
1631    (obj1.arrayGet(1, &obj2)->isNum() ? y1 = obj2.getNum() : y1 = 0);
1632    obj2.free();
1633    (obj1.arrayGet(2, &obj2)->isNum() ? x2 = obj2.getNum() : x2 = 0);
1634    obj2.free();
1635    (obj1.arrayGet(3, &obj2)->isNum() ? y2 = obj2.getNum() : y2 = 0);
1636    obj2.free();
1637
1638    coord1 = new AnnotCoord(x1, y1);
1639    coord2 = new AnnotCoord(x2, y2);
1640  } else {
1641    coord1 = new AnnotCoord();
1642    coord2 = new AnnotCoord();
1643  }
1644  obj1.free();
1645
1646  if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
1647    Object obj2;
1648
1649    if(obj1.arrayGet(0, &obj2)->isString())
1650      startStyle = parseAnnotLineEndingStyle(obj2.getString());
1651    else
1652      startStyle = annotLineEndingNone;
1653    obj2.free();
1654
1655    if(obj1.arrayGet(1, &obj2)->isString())
1656      endStyle = parseAnnotLineEndingStyle(obj2.getString());
1657    else
1658      endStyle = annotLineEndingNone;
1659    obj2.free();
1660
1661  } else {
1662    startStyle = endStyle = annotLineEndingNone;
1663  }
1664  obj1.free();
1665
1666  if (dict->lookup("IC", &obj1)->isArray()) {
1667    interiorColor = new AnnotColor(obj1.getArray());
1668  } else {
1669    interiorColor = NULL;
1670  }
1671  obj1.free();
1672
1673  if (dict->lookup("LL", &obj1)->isNum()) {
1674    leaderLineLength = obj1.getNum();
1675  } else {
1676    leaderLineLength = 0;
1677  }
1678  obj1.free();
1679
1680  if (dict->lookup("LLE", &obj1)->isNum()) {
1681    leaderLineExtension = obj1.getNum();
1682
1683    if (leaderLineExtension < 0)
1684      leaderLineExtension = 0;
1685  } else {
1686    leaderLineExtension = 0;
1687  }
1688  obj1.free();
1689
1690  if (dict->lookup("Cap", &obj1)->isBool()) {
1691    caption = obj1.getBool();
1692  } else {
1693    caption = gFalse;
1694  }
1695  obj1.free();
1696
1697  if (dict->lookup("IT", &obj1)->isName()) {
1698    GooString *intentName = new GooString(obj1.getName());
1699
1700    if(!intentName->cmp("LineArrow")) {
1701      intent = intentLineArrow;
1702    } else if(!intentName->cmp("LineDimension")) {
1703      intent = intentLineDimension;
1704    } else {
1705      intent = intentLineArrow;
1706    }
1707    delete intentName;
1708  } else {
1709    intent = intentLineArrow;
1710  }
1711  obj1.free();
1712
1713  if (dict->lookup("LLO", &obj1)->isNum()) {
1714    leaderLineOffset = obj1.getNum();
1715
1716    if (leaderLineOffset < 0)
1717      leaderLineOffset = 0;
1718  } else {
1719    leaderLineOffset = 0;
1720  }
1721  obj1.free();
1722
1723  if (dict->lookup("CP", &obj1)->isName()) {
1724    GooString *captionName = new GooString(obj1.getName());
1725
1726    if(!captionName->cmp("Inline")) {
1727      captionPos = captionPosInline;
1728    } else if(!captionName->cmp("Top")) {
1729      captionPos = captionPosTop;
1730    } else {
1731      captionPos = captionPosInline;
1732    }
1733    delete captionName;
1734  } else {
1735    captionPos = captionPosInline;
1736  }
1737  obj1.free();
1738
1739  if (dict->lookup("Measure", &obj1)->isDict()) {
1740    measure = NULL;
1741  } else {
1742    measure = NULL;
1743  }
1744  obj1.free();
1745
1746  if ((dict->lookup("CO", &obj1)->isArray()) && (obj1.arrayGetLength() == 2)) {
1747    Object obj2;
1748
1749    (obj1.arrayGet(0, &obj2)->isNum() ? captionTextHorizontal = obj2.getNum() :
1750      captionTextHorizontal = 0);
1751    obj2.free();
1752    (obj1.arrayGet(1, &obj2)->isNum() ? captionTextVertical = obj2.getNum() :
1753      captionTextVertical = 0);
1754    obj2.free();
1755  } else {
1756    captionTextHorizontal = captionTextVertical = 0;
1757  }
1758  obj1.free();
1759}
1760
1761//------------------------------------------------------------------------
1762// AnnotTextMarkup
1763//------------------------------------------------------------------------
1764
1765AnnotTextMarkup::AnnotTextMarkup(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1766  AnnotMarkup(xrefA, dict, catalog, obj) {
1767  // the real type will be read in initialize()
1768  type = typeHighlight;
1769  initialize(xrefA, catalog, dict);
1770}
1771
1772void AnnotTextMarkup::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1773  Object obj1;
1774
1775  if (dict->lookup("Subtype", &obj1)->isName()) {
1776    GooString typeName(obj1.getName());
1777    if (!typeName.cmp("Highlight")) {
1778      type = typeHighlight;
1779    } else if (!typeName.cmp("Underline")) {
1780      type = typeUnderline;
1781    } else if (!typeName.cmp("Squiggly")) {
1782      type = typeSquiggly;
1783    } else if (!typeName.cmp("StrikeOut")) {
1784      type = typeStrikeOut;
1785    }
1786  }
1787  obj1.free();
1788
1789  if(dict->lookup("QuadPoints", &obj1)->isArray()) {
1790    quadrilaterals = new AnnotQuadrilaterals(obj1.getArray(), rect);
1791  } else {
1792    quadrilaterals = NULL;
1793  }
1794  obj1.free();
1795}
1796
1797AnnotTextMarkup::~AnnotTextMarkup() {
1798  if(quadrilaterals) {
1799    delete quadrilaterals;
1800  }
1801}
1802
1803//------------------------------------------------------------------------
1804// AnnotWidget
1805//------------------------------------------------------------------------
1806
1807AnnotWidget::AnnotWidget(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
1808    Annot(xrefA, dict, catalog, obj) {
1809  type = typeWidget;
1810  widget = NULL;
1811  initialize(xrefA, catalog, dict);
1812}
1813
1814AnnotWidget::~AnnotWidget() {
1815  if (appearCharacs)
1816    delete appearCharacs;
1817 
1818  if (action)
1819    delete action;
1820   
1821  if (additionActions)
1822    delete additionActions;
1823   
1824  if (parent)
1825    delete parent;
1826}
1827
1828void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1829  Object obj1;
1830
1831  if ((form = catalog->getForm ())) {
1832    widget = form->findWidgetByRef (ref);
1833
1834    // check if field apperances need to be regenerated
1835    // Only text or choice fields needs to have appearance regenerated
1836    // see section 8.6.2 "Variable Text" of PDFReference
1837    regen = gFalse;
1838    if (widget != NULL && (widget->getType () == formText || widget->getType () == formChoice)) {
1839      regen = form->getNeedAppearances ();
1840    }
1841  }
1842
1843  // If field doesn't have an AP we'll have to generate it
1844  if (appearance.isNone () || appearance.isNull ())
1845    regen = gTrue;
1846
1847  if(dict->lookup("H", &obj1)->isName()) {
1848    GooString *modeName = new GooString(obj1.getName());
1849
1850    if(!modeName->cmp("N")) {
1851      mode = highlightModeNone;
1852    } else if(!modeName->cmp("O")) {
1853      mode = highlightModeOutline;
1854    } else if(!modeName->cmp("P") || !modeName->cmp("T")) {
1855      mode = highlightModePush;
1856    } else {
1857      mode = highlightModeInvert;
1858    }
1859    delete modeName;
1860  } else {
1861    mode = highlightModeInvert;
1862  }
1863  obj1.free();
1864
1865  if(dict->lookup("MK", &obj1)->isDict()) {
1866    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
1867  } else {
1868    appearCharacs = NULL;
1869  }
1870  obj1.free();
1871
1872  if(dict->lookup("A", &obj1)->isDict()) {
1873    action = NULL;
1874  } else {
1875    action = NULL;
1876  }
1877  obj1.free();
1878
1879  if(dict->lookup("AA", &obj1)->isDict()) {
1880    additionActions = NULL;
1881  } else {
1882    additionActions = NULL;
1883  }
1884  obj1.free();
1885
1886  if(dict->lookup("Parent", &obj1)->isDict()) {
1887    parent = NULL;
1888  } else {
1889    parent = NULL;
1890  }
1891  obj1.free();
1892}
1893
1894// Grand unified handler for preparing text strings to be drawn into form
1895// fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
1896// Converts some or all of this string to the appropriate encoding for the
1897// specified font, and computes the width of the text.  Can optionally stop
1898// converting when a specified width has been reached, to perform line-breaking
1899// for multi-line fields.
1900//
1901// Parameters:
1902//   text: input text string to convert
1903//   outBuf: buffer for writing re-encoded string
1904//   i: index at which to start converting; will be updated to point just after
1905//      last character processed
1906//   font: the font which will be used for display
1907//   width: computed width (unscaled by font size) will be stored here
1908//   widthLimit: if non-zero, stop converting to keep width under this value
1909//      (should be scaled down by font size)
1910//   charCount: count of number of characters will be stored here
1911//   noReencode: if set, do not try to translate the character encoding
1912//      (useful for Zapf Dingbats or other unusual encodings)
1913//      can only be used with simple fonts, not CID-keyed fonts
1914//
1915// TODO: Handle surrogate pairs in UTF-16.
1916//       Should be able to generate output for any CID-keyed font.
1917//       Doesn't handle vertical fonts--should it?
1918void AnnotWidget::layoutText(GooString *text, GooString *outBuf, int *i,
1919                             GfxFont *font, double *width, double widthLimit,
1920                             int *charCount, GBool noReencode)
1921{
1922  CharCode c;
1923  Unicode uChar, *uAux;
1924  double w = 0.0;
1925  int uLen, n;
1926  double dx, dy, ox, oy;
1927  GBool unicode = text->hasUnicodeMarker();
1928  CharCodeToUnicode *ccToUnicode = font->getToUnicode();
1929  ccToUnicode->decRefCnt();
1930  GBool spacePrev;              // previous character was a space
1931
1932  // State for backtracking when more text has been processed than fits within
1933  // widthLimit.  We track for both input (text) and output (outBuf) the offset
1934  // to the first character to discard.
1935  //
1936  // We keep track of several points:
1937  //   1 - end of previous completed word which fits
1938  //   2 - previous character which fit
1939  int last_i1, last_i2, last_o1, last_o2;
1940
1941  if (unicode && text->getLength() % 2 != 0) {
1942    error(-1, "AnnotWidget::layoutText, bad unicode string");
1943    return;
1944  }
1945
1946  // skip Unicode marker on string if needed
1947  if (unicode && *i == 0)
1948    *i = 2;
1949
1950  // Start decoding and copying characters, until either:
1951  //   we reach the end of the string
1952  //   we reach the maximum width
1953  //   we reach a newline character
1954  // As we copy characters, keep track of the last full word to fit, so that we
1955  // can backtrack if we exceed the maximum width.
1956  last_i1 = last_i2 = *i;
1957  last_o1 = last_o2 = 0;
1958  spacePrev = gFalse;
1959  outBuf->clear();
1960
1961  while (*i < text->getLength()) {
1962    last_i2 = *i;
1963    last_o2 = outBuf->getLength();
1964
1965    if (unicode) {
1966      uChar = (unsigned char)(text->getChar(*i)) << 8;
1967      uChar += (unsigned char)(text->getChar(*i + 1));
1968      *i += 2;
1969    } else {
1970      if (noReencode)
1971        uChar = text->getChar(*i) & 0xff;
1972      else
1973        uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
1974      *i += 1;
1975    }
1976
1977    // Explicit line break?
1978    if (uChar == '\r' || uChar == '\n') {
1979      // Treat a <CR><LF> sequence as a single line break
1980      if (uChar == '\r' && *i < text->getLength()) {
1981        if (unicode && text->getChar(*i) == '\0'
1982            && text->getChar(*i + 1) == '\n')
1983          *i += 2;
1984        else if (!unicode && text->getChar(*i) == '\n')
1985          *i += 1;
1986      }
1987
1988      break;
1989    }
1990
1991    if (noReencode) {
1992      outBuf->append(uChar);
1993    } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
1994      if (font->isCIDFont()) {
1995        // TODO: This assumes an identity CMap.  It should be extended to
1996        // handle the general case.
1997        outBuf->append((c >> 8) & 0xff);
1998        outBuf->append(c & 0xff);
1999      } else {
2000        // 8-bit font
2001        outBuf->append(c);
2002      }
2003    } else {
2004      fprintf(stderr,
2005              "warning: layoutText: cannot convert U+%04X\n", uChar);
2006    }
2007
2008    // If we see a space, then we have a linebreak opportunity.
2009    if (uChar == ' ') {
2010      last_i1 = *i;
2011      if (!spacePrev)
2012        last_o1 = last_o2;
2013      spacePrev = gTrue;
2014    } else {
2015      spacePrev = gFalse;
2016    }
2017
2018    // Compute width of character just output
2019    if (outBuf->getLength() > last_o2) {
2020      dx = 0.0;
2021      font->getNextChar(outBuf->getCString() + last_o2,
2022                        outBuf->getLength() - last_o2,
2023                        &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2024      w += dx;
2025    }
2026
2027    // Current line over-full now?
2028    if (widthLimit > 0.0 && w > widthLimit) {
2029      if (last_o1 > 0) {
2030        // Back up to the previous word which fit, if there was a previous
2031        // word.
2032        *i = last_i1;
2033        outBuf->del(last_o1, outBuf->getLength() - last_o1);
2034      } else if (last_o2 > 0) {
2035        // Otherwise, back up to the previous character (in the only word on
2036        // this line)
2037        *i = last_i2;
2038        outBuf->del(last_o2, outBuf->getLength() - last_o2);
2039      } else {
2040        // Otherwise, we were trying to fit the first character; include it
2041        // anyway even if it overflows the space--no updates to make.
2042      }
2043      break;
2044    }
2045  }
2046
2047  // If splitting input into lines because we reached the width limit, then
2048  // consume any remaining trailing spaces that would go on this line from the
2049  // input.  If in doing so we reach a newline, consume that also.  This code
2050  // won't run if we stopped at a newline above, since in that case w <=
2051  // widthLimit still.
2052  if (widthLimit > 0.0 && w > widthLimit) {
2053    if (unicode) {
2054      while (*i < text->getLength()
2055             && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
2056        *i += 2;
2057      if (*i < text->getLength()
2058          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
2059        *i += 2;
2060      if (*i < text->getLength()
2061          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
2062        *i += 2;
2063    } else {
2064      while (*i < text->getLength() && text->getChar(*i) == ' ')
2065        *i += 1;
2066      if (*i < text->getLength() && text->getChar(*i) == '\r')
2067        *i += 1;
2068      if (*i < text->getLength() && text->getChar(*i) == '\n')
2069        *i += 1;
2070    }
2071  }
2072
2073  // Compute the actual width and character count of the final string, based on
2074  // breakpoint, if this information is requested by the caller.
2075  if (width != NULL || charCount != NULL) {
2076    char *s = outBuf->getCString();
2077    int len = outBuf->getLength();
2078
2079    if (width != NULL)
2080      *width = 0.0;
2081    if (charCount != NULL)
2082      *charCount = 0;
2083
2084    while (len > 0) {
2085      dx = 0.0;
2086      n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
2087
2088      if (n == 0) {
2089        break;
2090      }
2091
2092      if (width != NULL)
2093        *width += dx;
2094      if (charCount != NULL)
2095        *charCount += 1;
2096
2097      s += n;
2098      len -= n;
2099    }
2100  }
2101}
2102
2103// Copy the given string to appearBuf, adding parentheses around it and
2104// escaping characters as appropriate.
2105void AnnotWidget::writeString(GooString *str, GooString *appearBuf)
2106{
2107  char c;
2108  int i;
2109
2110  appearBuf->append('(');
2111
2112  for (i = 0; i < str->getLength(); ++i) {
2113    c = str->getChar(i);
2114    if (c == '(' || c == ')' || c == '\\') {
2115      appearBuf->append('\\');
2116      appearBuf->append(c);
2117    } else if (c < 0x20) {
2118      appearBuf->appendf("\\{0:03o}", (unsigned char)c);
2119    } else {
2120      appearBuf->append(c);
2121    }
2122  }
2123
2124  appearBuf->append(')');
2125}
2126
2127// Draw the variable text or caption for a field.
2128void AnnotWidget::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
2129    GBool multiline, int comb, int quadding,
2130    GBool txField, GBool forceZapfDingbats,
2131    GBool password) {
2132  GooList *daToks;
2133  GooString *tok, *convertedText;
2134  GfxFont *font;
2135  double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
2136  int tfPos, tmPos, i, j;
2137  GBool freeText = gFalse;      // true if text should be freed before return
2138
2139  //~ if there is no MK entry, this should use the existing content stream,
2140  //~ and only replace the marked content portion of it
2141  //~ (this is only relevant for Tx fields)
2142 
2143  // parse the default appearance string
2144  tfPos = tmPos = -1;
2145  if (da) {
2146    daToks = new GooList();
2147    i = 0;
2148    while (i < da->getLength()) {
2149      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2150        ++i;
2151      }
2152      if (i < da->getLength()) {
2153        for (j = i + 1;
2154            j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2155            ++j) ;
2156        daToks->append(new GooString(da, i, j - i));
2157        i = j;
2158      }
2159    }
2160    for (i = 2; i < daToks->getLength(); ++i) {
2161      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2162        tfPos = i - 2;
2163      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2164        tmPos = i - 6;
2165      }
2166    }
2167  } else {
2168    daToks = NULL;
2169  }
2170
2171  // force ZapfDingbats
2172  //~ this should create the font if needed (?)
2173  if (forceZapfDingbats) {
2174    if (tfPos >= 0) {
2175      tok = (GooString *)daToks->get(tfPos);
2176      if (tok->cmp("/ZaDb")) {
2177        tok->clear();
2178        tok->append("/ZaDb");
2179      }
2180    }
2181  }
2182  // get the font and font size
2183  font = NULL;
2184  fontSize = 0;
2185  if (tfPos >= 0) {
2186    tok = (GooString *)daToks->get(tfPos);
2187    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2188      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2189        error(-1, "Unknown font in field's DA string");
2190      }
2191    } else {
2192      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2193    }
2194    tok = (GooString *)daToks->get(tfPos + 1);
2195    fontSize = atof(tok->getCString());
2196  } else {
2197    error(-1, "Missing 'Tf' operator in field's DA string");
2198  }
2199  if (!font) {
2200    if (daToks) {
2201      deleteGooList(daToks, GooString);
2202    }
2203    return;
2204  }
2205
2206  // get the border width
2207  borderWidth = border ? border->getWidth() : 0;
2208
2209  // for a password field, replace all characters with asterisks
2210  if (password) {
2211    int len;
2212    if (text->hasUnicodeMarker())
2213      len = (text->getLength() - 2) / 2;
2214    else
2215      len = text->getLength();
2216
2217    text = new GooString;
2218    for (i = 0; i < len; ++i)
2219      text->append('*');
2220    freeText = gTrue;
2221  }
2222
2223  convertedText = new GooString;
2224
2225  // setup
2226  if (txField) {
2227    appearBuf->append("/Tx BMC\n");
2228  }
2229  appearBuf->append("q\n");
2230  appearBuf->append("BT\n");
2231  // multi-line text
2232  if (multiline) {
2233    // note: the comb flag is ignored in multiline mode
2234
2235    wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
2236
2237    // compute font autosize
2238    if (fontSize == 0) {
2239      for (fontSize = 20; fontSize > 1; --fontSize) {
2240        y = rect->y2 - rect->y1;
2241        i = 0;
2242        while (i < text->getLength()) {
2243          layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2244                     forceZapfDingbats);
2245          y -= fontSize;
2246        }
2247        // approximate the descender for the last line
2248        if (y >= 0.33 * fontSize) {
2249          break;
2250        }
2251      }
2252      if (tfPos >= 0) {
2253        tok = (GooString *)daToks->get(tfPos + 1);
2254        tok->clear();
2255        tok->appendf("{0:.2f}", fontSize);
2256      }
2257    }
2258
2259    // starting y coordinate
2260    // (note: each line of text starts with a Td operator that moves
2261    // down a line)
2262    y = rect->y2 - rect->y1;
2263
2264    // set the font matrix
2265    if (tmPos >= 0) {
2266      tok = (GooString *)daToks->get(tmPos + 4);
2267      tok->clear();
2268      tok->append('0');
2269      tok = (GooString *)daToks->get(tmPos + 5);
2270      tok->clear();
2271      tok->appendf("{0:.2f}", y);
2272    }
2273
2274    // write the DA string
2275    if (daToks) {
2276      for (i = 0; i < daToks->getLength(); ++i) {
2277        appearBuf->append((GooString *)daToks->get(i))->append(' ');
2278      }
2279    }
2280
2281    // write the font matrix (if not part of the DA string)
2282    if (tmPos < 0) {
2283      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
2284    }
2285
2286    // write a series of lines of text
2287    i = 0;
2288    xPrev = 0;
2289    while (i < text->getLength()) {
2290      layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2291                 forceZapfDingbats);
2292      w *= fontSize;
2293
2294      // compute text start position
2295      switch (quadding) {
2296        case fieldQuadLeft:
2297        default:
2298          x = borderWidth + 2;
2299          break;
2300        case fieldQuadCenter:
2301          x = (rect->x2 - rect->x1 - w) / 2;
2302          break;
2303        case fieldQuadRight:
2304          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2305          break;
2306      }
2307
2308      // draw the line
2309      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
2310      writeString(convertedText, appearBuf);
2311      appearBuf->append(" Tj\n");
2312
2313      // next line
2314      xPrev = x;
2315    }
2316
2317    // single-line text
2318  } else {
2319    //~ replace newlines with spaces? - what does Acrobat do?
2320
2321    // comb formatting
2322    if (comb > 0) {
2323      int charCount;
2324
2325      // compute comb spacing
2326      w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
2327
2328      // compute font autosize
2329      if (fontSize == 0) {
2330        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2331        if (w < fontSize) {
2332          fontSize = w;
2333        }
2334        fontSize = floor(fontSize);
2335        if (tfPos >= 0) {
2336          tok = (GooString *)daToks->get(tfPos + 1);
2337          tok->clear();
2338          tok->appendf("{0:.2f}", fontSize);
2339        }
2340      }
2341
2342      i = 0;
2343      layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
2344                 forceZapfDingbats);
2345      if (charCount > comb)
2346        charCount = comb;
2347
2348      // compute starting text cell
2349      switch (quadding) {
2350        case fieldQuadLeft:
2351        default:
2352          x = borderWidth;
2353          break;
2354        case fieldQuadCenter:
2355          x = borderWidth + (comb - charCount) / 2 * w;
2356          break;
2357        case fieldQuadRight:
2358          x = borderWidth + (comb - charCount) * w;
2359          break;
2360      }
2361      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2362
2363      // set the font matrix
2364      if (tmPos >= 0) {
2365        tok = (GooString *)daToks->get(tmPos + 4);
2366        tok->clear();
2367        tok->appendf("{0:.2f}", x);
2368        tok = (GooString *)daToks->get(tmPos + 5);
2369        tok->clear();
2370        tok->appendf("{0:.2f}", y);
2371      }
2372
2373      // write the DA string
2374      if (daToks) {
2375        for (i = 0; i < daToks->getLength(); ++i) {
2376          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2377        }
2378      }
2379
2380      // write the font matrix (if not part of the DA string)
2381      if (tmPos < 0) {
2382        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2383      }
2384
2385      // write the text string
2386      char *s = convertedText->getCString();
2387      int len = convertedText->getLength();
2388      i = 0;
2389      xPrev = w;                // so that first character is placed properly
2390      while (i < comb && len > 0) {
2391        CharCode code;
2392        Unicode *uAux;
2393        int uLen, n;
2394        double dx, dy, ox, oy;
2395
2396        dx = 0.0;
2397        n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
2398        dx *= fontSize;
2399
2400        // center each character within its cell, by advancing the text
2401        // position the appropriate amount relative to the start of the
2402        // previous character
2403        x = 0.5 * (w - dx);
2404        appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
2405
2406        GooString *charBuf = new GooString(s, n);
2407        writeString(charBuf, appearBuf);
2408        appearBuf->append(" Tj\n");
2409        delete charBuf;
2410
2411        i++;
2412        s += n;
2413        len -= n;
2414        xPrev = x;
2415      }
2416
2417      // regular (non-comb) formatting
2418    } else {
2419      i = 0;
2420      layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
2421                 forceZapfDingbats);
2422
2423      // compute font autosize
2424      if (fontSize == 0) {
2425        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2426        fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
2427        if (fontSize2 < fontSize) {
2428          fontSize = fontSize2;
2429        }
2430        fontSize = floor(fontSize);
2431        if (tfPos >= 0) {
2432          tok = (GooString *)daToks->get(tfPos + 1);
2433          tok->clear();
2434          tok->appendf("{0:.2f}", fontSize);
2435        }
2436      }
2437
2438      // compute text start position
2439      w *= fontSize;
2440      switch (quadding) {
2441        case fieldQuadLeft:
2442        default:
2443          x = borderWidth + 2;
2444          break;
2445        case fieldQuadCenter:
2446          x = (rect->x2 - rect->x1 - w) / 2;
2447          break;
2448        case fieldQuadRight:
2449          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2450          break;
2451      }
2452      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2453
2454      // set the font matrix
2455      if (tmPos >= 0) {
2456        tok = (GooString *)daToks->get(tmPos + 4);
2457        tok->clear();
2458        tok->appendf("{0:.2f}", x);
2459        tok = (GooString *)daToks->get(tmPos + 5);
2460        tok->clear();
2461        tok->appendf("{0:.2f}", y);
2462      }
2463
2464      // write the DA string
2465      if (daToks) {
2466        for (i = 0; i < daToks->getLength(); ++i) {
2467          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2468        }
2469      }
2470
2471      // write the font matrix (if not part of the DA string)
2472      if (tmPos < 0) {
2473        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2474      }
2475
2476      // write the text string
2477      writeString(convertedText, appearBuf);
2478      appearBuf->append(" Tj\n");
2479    }
2480  }
2481  // cleanup
2482  appearBuf->append("ET\n");
2483  appearBuf->append("Q\n");
2484  if (txField) {
2485    appearBuf->append("EMC\n");
2486  }
2487  if (daToks) {
2488    deleteGooList(daToks, GooString);
2489  }
2490  if (freeText) {
2491    delete text;
2492  }
2493  delete convertedText;
2494}
2495
2496// Draw the variable text or caption for a field.
2497void AnnotWidget::drawListBox(GooString **text, GBool *selection,
2498                              int nOptions, int topIdx,
2499                              GooString *da, GfxFontDict *fontDict, GBool quadding) {
2500  GooList *daToks;
2501  GooString *tok, *convertedText;
2502  GfxFont *font;
2503  double fontSize, fontSize2, borderWidth, x, y, w, wMax;
2504  int tfPos, tmPos, i, j;
2505
2506  //~ if there is no MK entry, this should use the existing content stream,
2507  //~ and only replace the marked content portion of it
2508  //~ (this is only relevant for Tx fields)
2509
2510  // parse the default appearance string
2511  tfPos = tmPos = -1;
2512  if (da) {
2513    daToks = new GooList();
2514    i = 0;
2515    while (i < da->getLength()) {
2516      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2517        ++i;
2518       }
2519      if (i < da->getLength()) {
2520        for (j = i + 1;
2521             j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2522             ++j) ;
2523        daToks->append(new GooString(da, i, j - i));
2524        i = j;
2525      }
2526    }
2527    for (i = 2; i < daToks->getLength(); ++i) {
2528      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2529        tfPos = i - 2;
2530      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2531        tmPos = i - 6;
2532      }
2533    }
2534  } else {
2535    daToks = NULL;
2536  }
2537
2538  // get the font and font size
2539  font = NULL;
2540  fontSize = 0;
2541  if (tfPos >= 0) {
2542    tok = (GooString *)daToks->get(tfPos);
2543    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2544      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2545        error(-1, "Unknown font in field's DA string");
2546      }
2547    } else {
2548      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2549    }
2550    tok = (GooString *)daToks->get(tfPos + 1);
2551    fontSize = atof(tok->getCString());
2552  } else {
2553    error(-1, "Missing 'Tf' operator in field's DA string");
2554  }
2555  if (!font) {
2556    if (daToks) {
2557      deleteGooList(daToks, GooString);
2558    }
2559    return;
2560  }
2561
2562  convertedText = new GooString;
2563
2564  // get the border width
2565  borderWidth = border ? border->getWidth() : 0;
2566
2567  // compute font autosize
2568  if (fontSize == 0) {
2569    wMax = 0;
2570    for (i = 0; i < nOptions; ++i) {
2571      j = 0;
2572      layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2573      if (w > wMax) {
2574        wMax = w;
2575      }
2576    }
2577    fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2578    fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
2579    if (fontSize2 < fontSize) {
2580      fontSize = fontSize2;
2581    }
2582    fontSize = floor(fontSize);
2583    if (tfPos >= 0) {
2584      tok = (GooString *)daToks->get(tfPos + 1);
2585      tok->clear();
2586      tok->appendf("{0:.2f}", fontSize);
2587    }
2588  }
2589  // draw the text
2590  y = rect->y2 - rect->y1 - 1.1 * fontSize;
2591  for (i = topIdx; i < nOptions; ++i) {
2592    // setup
2593    appearBuf->append("q\n");
2594
2595    // draw the background if selected
2596    if (selection[i]) {
2597      appearBuf->append("0 g f\n");
2598      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
2599          borderWidth,
2600          y - 0.2 * fontSize,
2601          rect->x2 - rect->x1 - 2 * borderWidth,
2602          1.1 * fontSize);
2603    }
2604
2605    // setup
2606    appearBuf->append("BT\n");
2607
2608    // compute text width and start position
2609    j = 0;
2610    layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2611    w *= fontSize;
2612    switch (quadding) {
2613      case fieldQuadLeft:
2614      default:
2615        x = borderWidth + 2;
2616        break;
2617      case fieldQuadCenter:
2618        x = (rect->x2 - rect->x1 - w) / 2;
2619        break;
2620      case fieldQuadRight:
2621        x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2622        break;
2623    }
2624
2625    // set the font matrix
2626    if (tmPos >= 0) {
2627      tok = (GooString *)daToks->get(tmPos + 4);
2628      tok->clear();
2629      tok->appendf("{0:.2f}", x);
2630      tok = (GooString *)daToks->get(tmPos + 5);
2631      tok->clear();
2632      tok->appendf("{0:.2f}", y);
2633    }
2634
2635    // write the DA string
2636    if (daToks) {
2637      for (j = 0; j < daToks->getLength(); ++j) {
2638        appearBuf->append((GooString *)daToks->get(j))->append(' ');
2639      }
2640    }
2641
2642    // write the font matrix (if not part of the DA string)
2643    if (tmPos < 0) {
2644      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2645    }
2646
2647    // change the text color if selected
2648    if (selection[i]) {
2649      appearBuf->append("1 g\n");
2650    }
2651
2652    // write the text string
2653    writeString(convertedText, appearBuf);
2654    appearBuf->append(" Tj\n");
2655
2656    // cleanup
2657    appearBuf->append("ET\n");
2658    appearBuf->append("Q\n");
2659
2660    // next line
2661    y -= 1.1 * fontSize;
2662  }
2663
2664  if (daToks) {
2665    deleteGooList(daToks, GooString);
2666  }
2667
2668  delete convertedText;
2669}
2670
2671void AnnotWidget::generateFieldAppearance() {
2672  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
2673  Dict *field;
2674  Dict *annot;
2675  Dict *acroForm;
2676  Dict *mkDict;
2677  MemStream *appearStream;
2678  GfxFontDict *fontDict;
2679  GBool hasCaption;
2680  double w, dx, dy, r;
2681  double *dash;
2682  GooString *caption, *da;
2683  GooString **text;
2684  GBool *selection;
2685  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
2686  GBool modified;
2687
2688  if (widget == NULL || !widget->getField () || !widget->getField ()->getObj ()->isDict ())
2689    return;
2690
2691  field = widget->getField ()->getObj ()->getDict ();
2692  annot = widget->getObj ()->getDict ();
2693  acroForm = form->getObj ()->getDict ();
2694 
2695  // do not regenerate appearence if widget has not changed
2696  modified = widget->isModified ();
2697
2698  // only regenerate when it doesn't have an AP or
2699  // it already has an AP but widget has been modified
2700  if (!regen && !modified) {
2701    return;
2702  }
2703
2704  appearBuf = new GooString ();
2705  // get the appearance characteristics (MK) dictionary
2706  if (annot->lookup("MK", &mkObj)->isDict()) {
2707    mkDict = mkObj.getDict();
2708  } else {
2709    mkDict = NULL;
2710  }
2711  // draw the background
2712  if (mkDict) {
2713    if (mkDict->lookup("BG", &obj1)->isArray() &&
2714        obj1.arrayGetLength() > 0) {
2715      setColor(obj1.getArray(), gTrue, 0);
2716      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
2717          rect->x2 - rect->x1, rect->y2 - rect->y1);
2718    }
2719    obj1.free();
2720  }
2721
2722  // get the field type
2723  Form::fieldLookup(field, "FT", &ftObj);
2724
2725  // get the field flags (Ff) value
2726  if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
2727    ff = obj1.getInt();
2728  } else {
2729    ff = 0;
2730  }
2731  obj1.free();
2732
2733  // draw the border
2734  if (mkDict && border) {
2735    w = border->getWidth();
2736    if (w > 0) {
2737      mkDict->lookup("BC", &obj1);
2738      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
2739        mkDict->lookup("BG", &obj1);
2740      }
2741      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
2742        dx = rect->x2 - rect->x1;
2743        dy = rect->y2 - rect->y1;
2744
2745        // radio buttons with no caption have a round border
2746        hasCaption = mkDict->lookup("CA", &obj2)->isString();
2747        obj2.free();
2748        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
2749          r = 0.5 * (dx < dy ? dx : dy);
2750          switch (border->getStyle()) {
2751            case AnnotBorder::borderDashed:
2752              appearBuf->append("[");
2753              dashLength = border->getDashLength();
2754              dash = border->getDash();
2755              for (i = 0; i < dashLength; ++i) {
2756                appearBuf->appendf(" {0:.2f}", dash[i]);
2757              }
2758              appearBuf->append("] 0 d\n");
2759              // fall through to the solid case
2760            case AnnotBorder::borderSolid:
2761            case AnnotBorder::borderUnderlined:
2762              appearBuf->appendf("{0:.2f} w\n", w);
2763              setColor(obj1.getArray(), gFalse, 0);
2764              drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
2765              break;
2766            case AnnotBorder::borderBeveled:
2767            case AnnotBorder::borderInset:
2768              appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
2769              setColor(obj1.getArray(), gFalse, 0);
2770              drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
2771              setColor(obj1.getArray(), gFalse,
2772                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2773              drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2774              setColor(obj1.getArray(), gFalse,
2775                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2776              drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2777              break;
2778          }
2779
2780        } else {
2781          switch (border->getStyle()) {
2782            case AnnotBorder::borderDashed:
2783              appearBuf->append("[");
2784              dashLength = border->getDashLength();
2785              dash = border->getDash();
2786              for (i = 0; i < dashLength; ++i) {
2787                appearBuf->appendf(" {0:.2f}", dash[i]);
2788              }
2789              appearBuf->append("] 0 d\n");
2790              // fall through to the solid case
2791            case AnnotBorder::borderSolid:
2792              appearBuf->appendf("{0:.2f} w\n", w);
2793              setColor(obj1.getArray(), gFalse, 0);
2794              appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
2795                  0.5 * w, dx - w, dy - w);
2796              break;
2797            case AnnotBorder::borderBeveled:
2798            case AnnotBorder::borderInset:
2799              setColor(obj1.getArray(), gTrue,
2800                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2801              appearBuf->append("0 0 m\n");
2802              appearBuf->appendf("0 {0:.2f} l\n", dy);
2803              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2804              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2805              appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
2806              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2807              appearBuf->append("f\n");
2808              setColor(obj1.getArray(), gTrue,
2809                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2810              appearBuf->append("0 0 m\n");
2811              appearBuf->appendf("{0:.2f} 0 l\n", dx);
2812              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2813              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2814              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
2815              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2816              appearBuf->append("f\n");
2817              break;
2818            case AnnotBorder::borderUnderlined:
2819              appearBuf->appendf("{0:.2f} w\n", w);
2820              setColor(obj1.getArray(), gFalse, 0);
2821              appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
2822              break;
2823          }
2824
2825          // clip to the inside of the border
2826          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
2827              w, dx - 2 * w, dy - 2 * w);
2828        }
2829      }
2830      obj1.free();
2831    }
2832  }
2833
2834  // get the resource dictionary
2835  acroForm->lookup("DR", &drObj);
2836
2837  // build the font dictionary
2838  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
2839    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
2840  } else {
2841    fontDict = NULL;
2842  }
2843  obj1.free();
2844
2845  // get the default appearance string
2846  if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
2847    obj1.free();
2848    acroForm->lookup("DA", &obj1);
2849  }
2850  if (obj1.isString()) {
2851    da = obj1.getString()->copy();
2852    //TODO: look for a font size / name HERE
2853    // => create a function
2854  } else {
2855    da = NULL;
2856  }
2857  obj1.free();
2858
2859  // draw the field contents
2860  if (ftObj.isName("Btn")) {
2861    caption = NULL;
2862    if (mkDict) {
2863      if (mkDict->lookup("CA", &obj1)->isString()) {
2864        caption = obj1.getString()->copy();
2865      }
2866      obj1.free();
2867    }
2868    // radio button
2869    if (ff & fieldFlagRadio) {
2870      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
2871      if (Form::fieldLookup(field, "V", &obj1)->isName()) {
2872        if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
2873            strcmp (obj1.getName(), "Off") != 0) {
2874          if (caption) {
2875            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2876                gFalse, gTrue);
2877          } else {
2878            if (mkDict) {
2879              if (mkDict->lookup("BC", &obj3)->isArray() &&
2880                  obj3.arrayGetLength() > 0) {
2881                dx = rect->x2 - rect->x1;
2882                dy = rect->y2 - rect->y1;
2883                setColor(obj3.getArray(), gTrue, 0);
2884                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
2885                    gTrue);
2886              }
2887              obj3.free();
2888            }
2889          }
2890        }
2891        obj2.free();
2892      }
2893      obj1.free();
2894      // pushbutton
2895    } else if (ff & fieldFlagPushbutton) {
2896      if (caption) {
2897        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2898            gFalse, gFalse);
2899      }
2900      // checkbox
2901    } else {
2902      if (annot->lookup("AS", &obj1)->isName() &&
2903          strcmp(obj1.getName(), "Off") != 0) {
2904        if (!caption) {
2905          caption = new GooString("3"); // ZapfDingbats checkmark
2906        }
2907        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2908            gFalse, gTrue);
2909      }
2910      obj1.free();
2911    }
2912    if (caption) {
2913      delete caption;
2914    }
2915  } else if (ftObj.isName("Tx")) {
2916    if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2917      if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
2918        quadding = obj2.getInt();
2919      } else {
2920        quadding = fieldQuadLeft;
2921      }
2922      obj2.free();
2923      comb = 0;
2924      if (ff & fieldFlagComb) {
2925        if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
2926          comb = obj2.getInt();
2927        }
2928        obj2.free();
2929      }
2930      drawText(obj1.getString(), da, fontDict,
2931          ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
2932    }
2933    obj1.free();
2934  } else if (ftObj.isName("Ch")) {
2935    if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
2936      quadding = obj1.getInt();
2937    } else {
2938      quadding = fieldQuadLeft;
2939    }
2940    obj1.free();
2941    // combo box
2942    if (ff & fieldFlagCombo) {
2943      if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2944        drawText(obj1.getString(), da, fontDict,
2945            gFalse, 0, quadding, gTrue, gFalse);
2946        //~ Acrobat draws a popup icon on the right side
2947      }
2948      obj1.free();
2949      // list box
2950    } else {
2951      if (field->lookup("Opt", &obj1)->isArray()) {
2952        nOptions = obj1.arrayGetLength();
2953        // get the option text
2954        text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
2955        for (i = 0; i < nOptions; ++i) {
2956          text[i] = NULL;
2957          obj1.arrayGet(i, &obj2);
2958          if (obj2.isString()) {
2959            text[i] = obj2.getString()->copy();
2960          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
2961            if (obj2.arrayGet(1, &obj3)->isString()) {
2962              text[i] = obj3.getString()->copy();
2963            }
2964            obj3.free();
2965          }
2966          obj2.free();
2967          if (!text[i]) {
2968            text[i] = new GooString();
2969          }
2970        }
2971        // get the selected option(s)
2972        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
2973        //~ need to use the I field in addition to the V field
2974        Form::fieldLookup(field, "V", &obj2);
2975        for (i = 0; i < nOptions; ++i) {
2976          selection[i] = gFalse;
2977          if (obj2.isString()) {
2978            if (!obj2.getString()->cmp(text[i])) {
2979              selection[i] = gTrue;
2980            }
2981          } else if (obj2.isArray()) {
2982            for (j = 0; j < obj2.arrayGetLength(); ++j) {
2983              if (obj2.arrayGet(j, &obj3)->isString() &&
2984                  !obj3.getString()->cmp(text[i])) {
2985                selection[i] = gTrue;
2986              }
2987              obj3.free();
2988            }
2989          }
2990        }
2991        obj2.free();
2992        // get the top index
2993        if (field->lookup("TI", &obj2)->isInt()) {
2994          topIdx = obj2.getInt();
2995        } else {
2996          topIdx = 0;
2997        }
2998        obj2.free();
2999        // draw the text
3000        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
3001        for (i = 0; i < nOptions; ++i) {
3002          delete text[i];
3003        }
3004        gfree(text);
3005        gfree(selection);
3006      }
3007      obj1.free();
3008    }
3009  } else if (ftObj.isName("Sig")) {
3010    //~unimp
3011  } else {
3012    error(-1, "Unknown field type");
3013  }
3014
3015  if (da) {
3016    delete da;
3017  }
3018
3019  // build the appearance stream dictionary
3020  appearDict.initDict(xref);
3021  appearDict.dictAdd(copyString("Length"),
3022      obj1.initInt(appearBuf->getLength()));
3023  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
3024  obj1.initArray(xref);
3025  obj1.arrayAdd(obj2.initReal(0));
3026  obj1.arrayAdd(obj2.initReal(0));
3027  obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
3028  obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
3029  appearDict.dictAdd(copyString("BBox"), &obj1);
3030
3031  // set the resource dictionary
3032  if (drObj.isDict()) {
3033    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
3034  }
3035  drObj.free();
3036
3037  // build the appearance stream
3038  appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
3039      appearBuf->getLength(), &appearDict);
3040  appearance.free();
3041  appearance.initStream(appearStream);
3042  delete appearBuf;
3043
3044  appearStream->setNeedFree(gTrue);
3045
3046  if (widget->isModified()) {
3047    //create a new object that will contains the new appearance
3048   
3049    //if we already have a N entry in our AP dict, reuse it
3050    if (annot->lookup("AP", &obj1)->isDict() &&
3051        obj1.dictLookupNF("N", &obj2)->isRef()) {
3052      appRef = obj2.getRef();
3053    }
3054
3055    // this annot doesn't have an AP yet, create one
3056    if (appRef.num == 0)
3057      appRef = xref->addIndirectObject(&appearance);
3058    else // since we reuse the already existing AP, we have to notify the xref about this update
3059      xref->setModifiedObject(&appearance, appRef);
3060
3061    // update object's AP and AS
3062    Object apObj;
3063    apObj.initDict(xref);
3064
3065    Object oaRef;
3066    oaRef.initRef(appRef.num, appRef.gen);
3067
3068    apObj.dictSet("N", &oaRef);
3069    annot->set("AP", &apObj);
3070    Dict* d = new Dict(annot);
3071    d->decRef();
3072    Object dictObj;
3073    dictObj.initDict(d);
3074
3075    xref->setModifiedObject(&dictObj, ref);
3076    dictObj.free();
3077  }
3078
3079  if (fontDict) {
3080    delete fontDict;
3081  }
3082  ftObj.free();
3083  mkObj.free();
3084}
3085
3086
3087void AnnotWidget::draw(Gfx *gfx, GBool printing) {
3088  Object obj;
3089
3090  // check the flags
3091  if ((flags & annotFlagHidden) ||
3092      (printing && !(flags & annotFlagPrint)) ||
3093      (!printing && (flags & annotFlagNoView))) {
3094    return;
3095  }
3096
3097  generateFieldAppearance ();
3098
3099  // draw the appearance stream
3100  appearance.fetch(xref, &obj);
3101  gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3102                 rect->x1, rect->y1, rect->x2, rect->y2);
3103  obj.free();
3104}
3105
3106
3107//------------------------------------------------------------------------
3108// AnnotMovie
3109//------------------------------------------------------------------------
3110 
3111AnnotMovie::AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3112  Annot(xrefA, dict, catalog, obj) {
3113  type = typeMovie;
3114  initialize(xrefA, catalog, dict);
3115
3116  movie = new Movie();
3117  movie->parseAnnotMovie(this);
3118}
3119
3120AnnotMovie::~AnnotMovie() {
3121  if (title)
3122    delete title;
3123  if (fileName)
3124    delete fileName;
3125  delete movie;
3126
3127  if (posterStream && (!posterStream->decRef())) {
3128    delete posterStream;
3129  }
3130}
3131
3132void AnnotMovie::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3133  Object obj1;
3134
3135  if (dict->lookup("T", &obj1)->isString()) {
3136    title = obj1.getString()->copy();
3137  } else {
3138    title = NULL;
3139  }
3140  obj1.free();
3141
3142  Object movieDict;
3143  Object aDict;
3144
3145  // default values
3146  fileName = NULL;
3147  width = 0;
3148  height = 0;
3149  rotationAngle = 0;
3150  rate = 1.0;
3151  volume = 1.0;
3152  showControls = false;
3153  repeatMode = repeatModeOnce;
3154  synchronousPlay = false;
3155 
3156  hasFloatingWindow = false;
3157  isFullscreen = false;
3158  FWScaleNum = 1;
3159  FWScaleDenum = 1;
3160  FWPosX = 0.5;
3161  FWPosY = 0.5;
3162
3163  if (dict->lookup("Movie", &movieDict)->isDict()) {
3164    Object obj2;
3165    getFileSpecNameForPlatform(movieDict.dictLookup("F", &obj1), &obj2);
3166    fileName = obj2.getString()->copy();
3167    obj2.free();
3168    obj1.free();
3169
3170    if (movieDict.dictLookup("Aspect", &obj1)->isArray()) {
3171      Array* aspect = obj1.getArray();
3172      if (aspect->getLength() >= 2) {
3173        Object tmp;
3174        width = aspect->get(0, &tmp)->getInt();
3175        tmp.free();
3176        height = aspect->get(1, &tmp)->getInt();
3177        tmp.free();
3178      }
3179    }
3180    obj1.free();
3181
3182    if (movieDict.dictLookup("Rotate", &obj1)->isInt()) {
3183      // round up to 90°
3184      rotationAngle = (((obj1.getInt() + 360) % 360) % 90) * 90;
3185    }
3186    obj1.free();
3187
3188    //
3189    // movie poster
3190    //
3191    posterType = posterTypeNone;
3192    posterStream = NULL;
3193    if (!movieDict.dictLookup("Poster", &obj1)->isNone()) {
3194      if (obj1.isBool()) {
3195        GBool v = obj1.getBool();
3196        if (v)
3197          posterType = posterTypeFromMovie;
3198      }
3199     
3200      if (obj1.isStream()) {
3201        posterType = posterTypeStream;
3202       
3203        // "copy" stream
3204        posterStream = obj1.getStream();
3205        posterStream->incRef();
3206      }
3207
3208      obj1.free();
3209    }
3210
3211  }
3212  movieDict.free();
3213
3214
3215  // activation dictionary parsing ...
3216
3217  if (dict->lookup("A", &aDict)->isDict()) {
3218    if (!aDict.dictLookup("Start", &obj1)->isNone()) {
3219      if (obj1.isInt()) {
3220        // If it is representable as an integer (subject to the implementation limit for
3221        // integers, as described in Appendix C), it should be specified as such.
3222
3223        start.units = obj1.getInt();
3224      }
3225      if (obj1.isString()) {
3226        // If it is not representable as an integer, it should be specified as an 8-byte
3227        // string representing a 64-bit twos-complement integer, most significant
3228        // byte first.
3229
3230        // UNSUPPORTED
3231      }
3232
3233      if (obj1.isArray()) {
3234        Array* a = obj1.getArray();
3235
3236        Object tmp;
3237        a->get(0, &tmp);
3238        if (tmp.isInt()) {
3239          start.units = tmp.getInt();
3240        }
3241        if (tmp.isString()) {
3242          // UNSUPPORTED
3243        }
3244        tmp.free();
3245
3246        a->get(1, &tmp);
3247        if (tmp.isInt()) {
3248          start.units_per_second = tmp.getInt();
3249        }
3250        tmp.free();
3251      }
3252    }
3253    obj1.free();
3254
3255    if (!aDict.dictLookup("Duration", &obj1)->isNone()) {
3256      if (obj1.isInt()) {
3257        duration.units = obj1.getInt();
3258      }
3259      if (obj1.isString()) {
3260        // UNSUPPORTED
3261      }
3262
3263      if (obj1.isArray()) {
3264        Array* a = obj1.getArray();
3265
3266        Object tmp;
3267        a->get(0, &tmp);
3268        if (tmp.isInt()) {
3269          duration.units = tmp.getInt();
3270        }
3271        if (tmp.isString()) {
3272          // UNSUPPORTED
3273        }
3274        tmp.free();
3275
3276        a->get(1, &tmp);
3277        if (tmp.isInt()) {
3278          duration.units_per_second = tmp.getInt();
3279        }
3280        tmp.free();
3281      }
3282    }
3283    obj1.free();
3284
3285    if (aDict.dictLookup("Rate", &obj1)->isNum()) {
3286      rate = obj1.getNum();
3287    }
3288    obj1.free();
3289
3290    if (aDict.dictLookup("Volume", &obj1)->isNum()) {
3291      volume = obj1.getNum();
3292    }
3293    obj1.free();
3294
3295    if (aDict.dictLookup("ShowControls", &obj1)->isBool()) {
3296      showControls = obj1.getBool();
3297    }
3298    obj1.free();
3299
3300    if (aDict.dictLookup("Synchronous", &obj1)->isBool()) {
3301      synchronousPlay = obj1.getBool();
3302    }
3303    obj1.free();
3304
3305    if (aDict.dictLookup("Mode", &obj1)->isName()) {
3306      char* name = obj1.getName();
3307      if (!strcmp(name, "Once"))
3308        repeatMode = repeatModeOnce;
3309      if (!strcmp(name, "Open"))
3310        repeatMode = repeatModeOpen;
3311      if (!strcmp(name, "Repeat"))
3312        repeatMode = repeatModeRepeat;
3313      if (!strcmp(name,"Palindrome"))
3314        repeatMode = repeatModePalindrome;
3315    }
3316    obj1.free();
3317
3318    if (aDict.dictLookup("FWScale", &obj1)->isArray()) {
3319      // the presence of that entry implies that the movie is to be played
3320      // in a floating window
3321      hasFloatingWindow = true;
3322
3323      Array* scale = obj1.getArray();
3324      if (scale->getLength() >= 2) {
3325        Object tmp;
3326        if (scale->get(0, &tmp)->isInt()) {
3327          FWScaleNum = tmp.getInt();
3328        }
3329        tmp.free();
3330        if (scale->get(1, &tmp)->isInt()) {
3331          FWScaleDenum = tmp.getInt();
3332        }
3333        tmp.free();
3334      }
3335
3336      // detect fullscreen mode
3337      if ((FWScaleNum == 999) && (FWScaleDenum == 1)) {
3338        isFullscreen = true;
3339      }
3340    }
3341    obj1.free();
3342
3343    if (aDict.dictLookup("FWPosition", &obj1)->isArray()) {
3344      Array* pos = obj1.getArray();
3345      if (pos->getLength() >= 2) {
3346        Object tmp;
3347        if (pos->get(0, &tmp)->isNum()) {
3348          FWPosX = tmp.getNum();
3349        }
3350        tmp.free();
3351        if (pos->get(1, &tmp)->isNum()) {
3352          FWPosY = tmp.getNum();
3353        }
3354        tmp.free();
3355      }
3356    }
3357  }
3358  aDict.free();
3359}
3360
3361void AnnotMovie::getMovieSize(int& width, int& height) {
3362  width = this->width;
3363  height = this->height;
3364}
3365
3366void AnnotMovie::getZoomFactor(int& num, int& denum) {
3367  num = FWScaleNum;
3368  denum = FWScaleDenum;
3369}
3370
3371
3372//------------------------------------------------------------------------
3373// AnnotScreen
3374//------------------------------------------------------------------------
3375 
3376AnnotScreen::AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3377  Annot(xrefA, dict, catalog, obj) {
3378  type = typeScreen;
3379  initialize(xrefA, catalog, dict);
3380}
3381
3382AnnotScreen::~AnnotScreen() {
3383  if (title)
3384    delete title;
3385  if (appearCharacs)
3386    delete appearCharacs;
3387}
3388
3389void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3390  Object obj1;
3391
3392  title = NULL;
3393  if (dict->lookup("T", &obj1)->isString()) {
3394    title = obj1.getString()->copy();
3395  }
3396  obj1.free();
3397
3398  dict->lookup("A", &action);
3399
3400  dict->lookup("AA", &additionAction);
3401
3402  appearCharacs = NULL;
3403  if(dict->lookup("MK", &obj1)->isDict()) {
3404    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
3405  }
3406  obj1.free();
3407
3408}
3409
3410//------------------------------------------------------------------------
3411// AnnotStamp
3412//------------------------------------------------------------------------
3413 
3414AnnotStamp::AnnotStamp(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3415  AnnotMarkup(xrefA, dict, catalog, obj) {
3416  type = typeStamp;
3417  initialize(xrefA, catalog, dict);
3418}
3419
3420AnnotStamp::~AnnotStamp() {
3421  delete icon;
3422}
3423
3424void AnnotStamp::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3425  Object obj1;
3426
3427  if (dict->lookup("Name", &obj1)->isName()) {
3428    icon = new GooString(obj1.getName());
3429  } else {
3430    icon = new GooString("Draft");
3431  }
3432  obj1.free();
3433
3434}
3435
3436//------------------------------------------------------------------------
3437// AnnotGeometry
3438//------------------------------------------------------------------------
3439
3440AnnotGeometry::AnnotGeometry(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3441  AnnotMarkup(xrefA, dict, catalog, obj) {
3442  // the real type will be read in initialize()
3443  type = typeSquare;
3444  initialize(xrefA, catalog, dict);
3445}
3446
3447AnnotGeometry::~AnnotGeometry() {
3448  delete interiorColor;
3449  delete borderEffect;
3450  delete geometryRect;
3451}
3452
3453void AnnotGeometry::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3454  Object obj1;
3455
3456  if (dict->lookup("Subtype", &obj1)->isName()) {
3457    GooString typeName(obj1.getName());
3458    if (!typeName.cmp("Square")) {
3459      type = typeSquare;
3460    } else if (!typeName.cmp("Circle")) {
3461      type = typeCircle;
3462    }
3463  }
3464  obj1.free();
3465
3466  if (dict->lookup("IC", &obj1)->isArray()) {
3467    interiorColor = new AnnotColor(obj1.getArray());
3468  } else {
3469    interiorColor = NULL;
3470  }
3471  obj1.free();
3472
3473  if (dict->lookup("BE", &obj1)->isDict()) {
3474    borderEffect = new AnnotBorderEffect(obj1.getDict());
3475  } else {
3476    borderEffect = NULL;
3477  }
3478  obj1.free();
3479
3480  geometryRect = NULL;
3481  if (dict->lookup("RD", &obj1)->isArray()) {
3482    geometryRect = parseDiffRectangle(obj1.getArray(), rect);
3483  }
3484  obj1.free();
3485
3486}
3487
3488//------------------------------------------------------------------------
3489// AnnotPolygon
3490//------------------------------------------------------------------------
3491
3492AnnotPolygon::AnnotPolygon(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3493  AnnotMarkup(xrefA, dict, catalog, obj) {
3494  // the real type will be read in initialize()
3495  type = typePolygon;
3496  initialize(xrefA, catalog, dict);
3497}
3498
3499AnnotPolygon::~AnnotPolygon() {
3500  delete vertices;
3501
3502  if (interiorColor)
3503    delete interiorColor;
3504
3505  if (borderEffect)
3506    delete borderEffect;
3507}
3508
3509void AnnotPolygon::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3510  Object obj1;
3511
3512  if (dict->lookup("Subtype", &obj1)->isName()) {
3513    GooString typeName(obj1.getName());
3514    if (!typeName.cmp("Polygon")) {
3515      type = typePolygon;
3516    } else if (!typeName.cmp("PolyLine")) {
3517      type = typePolyLine;
3518    }
3519  }
3520  obj1.free();
3521
3522  if (dict->lookup("Vertices", &obj1)->isArray()) {
3523    vertices = new AnnotPath(obj1.getArray());
3524  } else {
3525    vertices = new AnnotPath();
3526    error(-1, "Bad Annot Polygon Vertices");
3527    ok = gFalse;
3528  }
3529  obj1.free();
3530
3531  if (dict->lookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) {
3532    Object obj2;
3533
3534    if(obj1.arrayGet(0, &obj2)->isString())
3535      startStyle = parseAnnotLineEndingStyle(obj2.getString());
3536    else
3537      startStyle = annotLineEndingNone;
3538    obj2.free();
3539
3540    if(obj1.arrayGet(1, &obj2)->isString())
3541      endStyle = parseAnnotLineEndingStyle(obj2.getString());
3542    else
3543      endStyle = annotLineEndingNone;
3544    obj2.free();
3545
3546  } else {
3547    startStyle = endStyle = annotLineEndingNone;
3548  }
3549  obj1.free();
3550
3551  if (dict->lookup("IC", &obj1)->isArray()) {
3552    interiorColor = new AnnotColor(obj1.getArray());
3553  } else {
3554    interiorColor = NULL;
3555  }
3556  obj1.free();
3557
3558  if (dict->lookup("BE", &obj1)->isDict()) {
3559    borderEffect = new AnnotBorderEffect(obj1.getDict());
3560  } else {
3561    borderEffect = NULL;
3562  }
3563  obj1.free();
3564
3565  if (dict->lookup("IT", &obj1)->isName()) {
3566    GooString *intentName = new GooString(obj1.getName());
3567
3568    if(!intentName->cmp("PolygonCloud")) {
3569      intent = polygonCloud;
3570    } else if(!intentName->cmp("PolyLineDimension")) {
3571      intent = polylineDimension;
3572    } else {
3573      intent = polygonDimension;
3574    }
3575    delete intentName;
3576  } else {
3577    intent = polygonCloud;
3578  }
3579  obj1.free();
3580}
3581
3582//------------------------------------------------------------------------
3583// AnnotCaret
3584//------------------------------------------------------------------------
3585
3586AnnotCaret::AnnotCaret(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3587  AnnotMarkup(xrefA, dict, catalog, obj) {
3588  type = typeCaret;
3589  initialize(xrefA, catalog, dict);
3590}
3591
3592AnnotCaret::~AnnotCaret() {
3593  delete caretRect;
3594}
3595
3596void AnnotCaret::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3597  Object obj1;
3598
3599  symbol = symbolNone;
3600  if (dict->lookup("Sy", &obj1)->isName()) {
3601    GooString typeName(obj1.getName());
3602    if (!typeName.cmp("P")) {
3603      symbol = symbolP;
3604    } else if (!typeName.cmp("None")) {
3605      symbol = symbolNone;
3606    }
3607  }
3608  obj1.free();
3609
3610  if (dict->lookup("RD", &obj1)->isArray()) {
3611    caretRect = parseDiffRectangle(obj1.getArray(), rect);
3612  } else {
3613    caretRect = NULL;
3614  }
3615  obj1.free();
3616
3617}
3618
3619//------------------------------------------------------------------------
3620// AnnotInk
3621//------------------------------------------------------------------------
3622
3623AnnotInk::AnnotInk(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3624  AnnotMarkup(xrefA, dict, catalog, obj) {
3625  type = typeInk;
3626  initialize(xrefA, catalog, dict);
3627}
3628
3629AnnotInk::~AnnotInk() {
3630  if (inkList) {
3631    for (int i = 0; i < inkListLength; ++i)
3632      delete inkList[i];
3633    gfree(inkList);
3634  }
3635}
3636
3637void AnnotInk::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3638  Object obj1;
3639
3640  if (dict->lookup("InkList", &obj1)->isArray()) {
3641    Array *array = obj1.getArray();
3642    inkListLength = array->getLength();
3643    inkList = (AnnotPath **) gmallocn ((inkListLength), sizeof(AnnotPath *));
3644    memset(inkList, 0, inkListLength * sizeof(AnnotPath *));
3645    for (int i = 0; i < inkListLength; i++) {
3646      Object obj2;
3647      if (array->get(i, &obj2)->isArray())
3648        inkList[i] = new AnnotPath(obj2.getArray());
3649      obj2.free();
3650    }
3651  } else {
3652    inkListLength = 0;
3653    inkList = NULL;
3654    error(-1, "Bad Annot Ink List");
3655    ok = gFalse;
3656  }
3657  obj1.free();
3658}
3659
3660//------------------------------------------------------------------------
3661// AnnotFileAttachment
3662//------------------------------------------------------------------------
3663
3664AnnotFileAttachment::AnnotFileAttachment(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3665  AnnotMarkup(xrefA, dict, catalog, obj) {
3666  type = typeFileAttachment;
3667  initialize(xrefA, catalog, dict);
3668}
3669
3670AnnotFileAttachment::~AnnotFileAttachment() {
3671  file.free();
3672
3673  if (name)
3674    delete name;
3675}
3676
3677void AnnotFileAttachment::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3678  Object obj1;
3679
3680  if (dict->lookup("FS", &obj1)->isDict()) {
3681    obj1.copy(&file);
3682  } else {
3683    error(-1, "Bad Annot File Attachment");
3684    ok = gFalse;
3685  }
3686  obj1.free();
3687
3688  if (dict->lookup("Name", &obj1)->isName()) {
3689    name = new GooString(obj1.getName());
3690  } else {
3691    name = new GooString("PushPin");
3692  }
3693  obj1.free();
3694}
3695
3696//------------------------------------------------------------------------
3697// AnnotSound
3698//------------------------------------------------------------------------
3699
3700AnnotSound::AnnotSound(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3701  AnnotMarkup(xrefA, dict, catalog, obj) {
3702  type = typeSound;
3703  initialize(xrefA, catalog, dict);
3704}
3705
3706AnnotSound::~AnnotSound() {
3707  delete sound;
3708
3709  delete name;
3710}
3711
3712void AnnotSound::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3713  Object obj1;
3714
3715  sound = Sound::parseSound(dict->lookup("Sound", &obj1));
3716  if (!sound) {
3717    error(-1, "Bad Annot Sound");
3718    ok = gFalse;
3719  }
3720  obj1.free();
3721
3722  if (dict->lookup("Name", &obj1)->isName()) {
3723    name = new GooString(obj1.getName());
3724  } else {
3725    name = new GooString("Speaker");
3726  }
3727  obj1.free();
3728}
3729
3730//------------------------------------------------------------------------
3731// Annot3D
3732//------------------------------------------------------------------------
3733
3734Annot3D::Annot3D(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3735  Annot(xrefA, dict, catalog, obj) {
3736  type = type3D;
3737  initialize(xrefA, catalog, dict);
3738}
3739
3740Annot3D::~Annot3D() {
3741  if (activation)
3742    delete activation;
3743}
3744
3745void Annot3D::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3746  Object obj1;
3747
3748  if (dict->lookup("3DA", &obj1)->isDict()) {
3749    activation = new Activation(obj1.getDict());
3750  } else {
3751    activation = NULL;
3752  }
3753  obj1.free();
3754}
3755
3756Annot3D::Activation::Activation(Dict *dict) {
3757  Object obj1;
3758
3759  if (dict->lookup("A", &obj1)->isName()) {
3760    GooString *name = new GooString(obj1.getName());
3761
3762    if(!name->cmp("PO")) {
3763      aTrigger = aTriggerPageOpened;
3764    } else if(!name->cmp("PV")) {
3765      aTrigger = aTriggerPageVisible;
3766    } else if(!name->cmp("XA")) {
3767      aTrigger = aTriggerUserAction;
3768    } else {
3769      aTrigger = aTriggerUnknown;
3770    }
3771    delete name;
3772  } else {
3773    aTrigger = aTriggerUnknown;
3774  }
3775  obj1.free();
3776
3777  if(dict->lookup("AIS", &obj1)->isName()) {
3778    GooString *name = new GooString(obj1.getName());
3779
3780    if(!name->cmp("I")) {
3781      aState = aStateEnabled;
3782    } else if(!name->cmp("L")) {
3783      aState = aStateDisabled;
3784    } else {
3785      aState = aStateUnknown;
3786    }
3787    delete name;
3788  } else {
3789    aState = aStateUnknown;
3790  }
3791  obj1.free();
3792
3793  if(dict->lookup("D", &obj1)->isName()) {
3794    GooString *name = new GooString(obj1.getName());
3795
3796    if(!name->cmp("PC")) {
3797      dTrigger = dTriggerPageClosed;
3798    } else if(!name->cmp("PI")) {
3799      dTrigger = dTriggerPageInvisible;
3800    } else if(!name->cmp("XD")) {
3801      dTrigger = dTriggerUserAction;
3802    } else {
3803      dTrigger = dTriggerUnknown;
3804    }
3805    delete name;
3806  } else {
3807    dTrigger = dTriggerUnknown;
3808  }
3809  obj1.free();
3810
3811  if(dict->lookup("DIS", &obj1)->isName()) {
3812    GooString *name = new GooString(obj1.getName());
3813
3814    if(!name->cmp("U")) {
3815      dState = dStateUninstantiaded;
3816    } else if(!name->cmp("I")) {
3817      dState = dStateInstantiated;
3818    } else if(!name->cmp("L")) {
3819      dState = dStateLive;
3820    } else {
3821      dState = dStateUnknown;
3822    }
3823    delete name;
3824  } else {
3825    dState = dStateUnknown;
3826  }
3827  obj1.free();
3828
3829  if (dict->lookup("TB", &obj1)->isBool()) {
3830    displayToolbar = obj1.getBool();
3831  } else {
3832    displayToolbar = gTrue;
3833  }
3834  obj1.free();
3835
3836  if (dict->lookup("NP", &obj1)->isBool()) {
3837    displayNavigation = obj1.getBool();
3838  } else {
3839    displayNavigation = gFalse;
3840  }
3841  obj1.free();
3842}
3843
3844//------------------------------------------------------------------------
3845// Annots
3846//------------------------------------------------------------------------
3847
3848Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
3849  Annot *annot;
3850  Object obj1;
3851  int size;
3852  int i;
3853
3854  annots = NULL;
3855  size = 0;
3856  nAnnots = 0;
3857
3858  if (annotsObj->isArray()) {
3859    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
3860      //get the Ref to this annot and pass it to Annot constructor
3861      //this way, it'll be possible for the annot to retrieve the corresponding
3862      //form widget
3863      Object obj2;
3864      if (annotsObj->arrayGet(i, &obj1)->isDict()) {
3865        annotsObj->arrayGetNF(i, &obj2);
3866        annot = createAnnot (xref, obj1.getDict(), catalog, &obj2);
3867        if (annot && annot->isOk()) {
3868          if (nAnnots >= size) {
3869            size += 16;
3870            annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
3871          }
3872          annots[nAnnots++] = annot;
3873        } else {
3874          delete annot;
3875        }
3876      }
3877      obj2.free();
3878      obj1.free();
3879    }
3880  }
3881}
3882
3883Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj) {
3884  Annot *annot;
3885  Object obj1;
3886
3887  if (dict->lookup("Subtype", &obj1)->isName()) {
3888    GooString *typeName = new GooString(obj1.getName());
3889
3890    if (!typeName->cmp("Text")) {
3891      annot = new AnnotText(xref, dict, catalog, obj);
3892    } else if (!typeName->cmp("Link")) {
3893      annot = new AnnotLink(xref, dict, catalog, obj);
3894    } else if (!typeName->cmp("FreeText")) {
3895      annot = new AnnotFreeText(xref, dict, catalog, obj);
3896    } else if (!typeName->cmp("Line")) {
3897      annot = new AnnotLine(xref, dict, catalog, obj);
3898    } else if (!typeName->cmp("Square")) {
3899      annot = new AnnotGeometry(xref, dict, catalog, obj);
3900    } else if (!typeName->cmp("Circle")) {
3901      annot = new AnnotGeometry(xref, dict, catalog, obj);
3902    } else if (!typeName->cmp("Polygon")) {
3903      annot = new AnnotPolygon(xref, dict, catalog, obj);
3904    } else if (!typeName->cmp("PolyLine")) {
3905      annot = new AnnotPolygon(xref, dict, catalog, obj);
3906    } else if (!typeName->cmp("Highlight")) {
3907      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3908    } else if (!typeName->cmp("Underline")) {
3909      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3910    } else if (!typeName->cmp("Squiggly")) {
3911      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3912    } else if (!typeName->cmp("StrikeOut")) {
3913      annot = new AnnotTextMarkup(xref, dict, catalog, obj);
3914    } else if (!typeName->cmp("Stamp")) {
3915      annot = new AnnotStamp(xref, dict, catalog, obj);
3916    } else if (!typeName->cmp("Caret")) {
3917      annot = new AnnotCaret(xref, dict, catalog, obj);
3918    } else if (!typeName->cmp("Ink")) {
3919      annot = new AnnotInk(xref, dict, catalog, obj);
3920    } else if (!typeName->cmp("FileAttachment")) {
3921      annot = new AnnotFileAttachment(xref, dict, catalog, obj);
3922    } else if (!typeName->cmp("Sound")) {
3923      annot = new AnnotSound(xref, dict, catalog, obj);
3924    } else if(!typeName->cmp("Movie")) {
3925      annot = new AnnotMovie(xref, dict, catalog, obj);
3926    } else if(!typeName->cmp("Widget")) {
3927      annot = new AnnotWidget(xref, dict, catalog, obj);
3928    } else if(!typeName->cmp("Screen")) {
3929      annot = new AnnotScreen(xref, dict, catalog, obj);
3930    } else if(!typeName->cmp("PrinterMark")) {
3931      annot = new Annot(xref, dict, catalog, obj);
3932    } else if (!typeName->cmp("TrapNet")) {
3933      annot = new Annot(xref, dict, catalog, obj);
3934    } else if (!typeName->cmp("Watermark")) {
3935      annot = new Annot(xref, dict, catalog, obj);
3936    } else if (!typeName->cmp("3D")) {
3937      annot = new Annot3D(xref, dict, catalog, obj);
3938    } else {
3939      annot = new Annot(xref, dict, catalog, obj);
3940    }
3941
3942    delete typeName;
3943  } else {
3944    annot = NULL;
3945  }
3946  obj1.free();
3947
3948  return annot;
3949}
3950
3951Annot *Annots::findAnnot(Ref *ref) {
3952  int i;
3953
3954  for (i = 0; i < nAnnots; ++i) {
3955    if (annots[i]->match(ref)) {
3956      return annots[i];
3957    }
3958  }
3959  return NULL;
3960}
3961
3962
3963Annots::~Annots() {
3964  int i;
3965
3966  for (i = 0; i < nAnnots; ++i) {
3967    delete annots[i];
3968  }
3969  gfree(annots);
3970}
Note: See TracBrowser for help on using the repository browser.