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

Last change on this file since 277 was 277, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.3

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