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

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

PDF plugin: poppler library updated to version 0.8.3

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