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

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

PDF plugin: Poppler library updated to version 0.10.3

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