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

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

PDF plugin: Poppler library updated to version 0.8.7

File size: 89.7 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 = new Dict(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  widget = NULL;
1714  initialize(xrefA, catalog, dict);
1715  widget = NULL;
1716}
1717
1718AnnotWidget::~AnnotWidget() {
1719  if (appearCharacs)
1720    delete appearCharacs;
1721 
1722  if (action)
1723    delete action;
1724   
1725  if (additionActions)
1726    delete additionActions;
1727   
1728  if (parent)
1729    delete parent;
1730}
1731
1732void AnnotWidget::initialize(XRef *xrefA, Catalog *catalog, Dict *dict) {
1733  Object obj1;
1734
1735  form = catalog->getForm ();
1736  widget = form->findWidgetByRef (ref);
1737
1738  // check if field apperances need to be regenerated
1739  // Only text or choice fields needs to have appearance regenerated
1740  // see section 8.6.2 "Variable Text" of PDFReference
1741  regen = gFalse;
1742  if (widget != NULL && (widget->getType () == formText || widget->getType () == formChoice)) {
1743    regen = form->getNeedAppearances ();
1744  }
1745
1746  // If field doesn't have an AP we'll have to generate it
1747  if (appearance.isNone () || appearance.isNull ())
1748    regen = gTrue;
1749
1750  if(dict->lookup("H", &obj1)->isName()) {
1751    GooString *modeName = new GooString(obj1.getName());
1752
1753    if(!modeName->cmp("N")) {
1754      mode = highlightModeNone;
1755    } else if(!modeName->cmp("O")) {
1756      mode = highlightModeOutline;
1757    } else if(!modeName->cmp("P") || !modeName->cmp("T")) {
1758      mode = highlightModePush;
1759    } else {
1760      mode = highlightModeInvert;
1761    }
1762    delete modeName;
1763  } else {
1764    mode = highlightModeInvert;
1765  }
1766  obj1.free();
1767
1768  if(dict->lookup("MK", &obj1)->isDict()) {
1769    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
1770  } else {
1771    appearCharacs = NULL;
1772  }
1773  obj1.free();
1774
1775  if(dict->lookup("A", &obj1)->isDict()) {
1776    action = NULL;
1777  } else {
1778    action = NULL;
1779  }
1780  obj1.free();
1781
1782  if(dict->lookup("AA", &obj1)->isDict()) {
1783    additionActions = NULL;
1784  } else {
1785    additionActions = NULL;
1786  }
1787  obj1.free();
1788
1789  if(dict->lookup("Parent", &obj1)->isDict()) {
1790    parent = NULL;
1791  } else {
1792    parent = NULL;
1793  }
1794  obj1.free();
1795}
1796
1797// Grand unified handler for preparing text strings to be drawn into form
1798// fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
1799// Converts some or all of this string to the appropriate encoding for the
1800// specified font, and computes the width of the text.  Can optionally stop
1801// converting when a specified width has been reached, to perform line-breaking
1802// for multi-line fields.
1803//
1804// Parameters:
1805//   text: input text string to convert
1806//   outBuf: buffer for writing re-encoded string
1807//   i: index at which to start converting; will be updated to point just after
1808//      last character processed
1809//   font: the font which will be used for display
1810//   width: computed width (unscaled by font size) will be stored here
1811//   widthLimit: if non-zero, stop converting to keep width under this value
1812//      (should be scaled down by font size)
1813//   charCount: count of number of characters will be stored here
1814//   noReencode: if set, do not try to translate the character encoding
1815//      (useful for Zapf Dingbats or other unusual encodings)
1816//      can only be used with simple fonts, not CID-keyed fonts
1817//
1818// TODO: Handle surrogate pairs in UTF-16.
1819//       Should be able to generate output for any CID-keyed font.
1820//       Doesn't handle vertical fonts--should it?
1821void AnnotWidget::layoutText(GooString *text, GooString *outBuf, int *i,
1822                             GfxFont *font, double *width, double widthLimit,
1823                             int *charCount, GBool noReencode)
1824{
1825  CharCode c;
1826  Unicode uChar, *uAux;
1827  double w = 0.0;
1828  int uLen, n;
1829  double dx, dy, ox, oy;
1830  GBool unicode = text->hasUnicodeMarker();
1831  CharCodeToUnicode *ccToUnicode = font->getToUnicode();
1832  ccToUnicode->decRefCnt();
1833  GBool spacePrev;              // previous character was a space
1834
1835  // State for backtracking when more text has been processed than fits within
1836  // widthLimit.  We track for both input (text) and output (outBuf) the offset
1837  // to the first character to discard.
1838  //
1839  // We keep track of several points:
1840  //   1 - end of previous completed word which fits
1841  //   2 - previous character which fit
1842  int last_i1, last_i2, last_o1, last_o2;
1843
1844  if (unicode && text->getLength() % 2 != 0) {
1845    error(-1, "AnnotWidget::layoutText, bad unicode string");
1846    return;
1847  }
1848
1849  // skip Unicode marker on string if needed
1850  if (unicode && *i == 0)
1851    *i = 2;
1852
1853  // Start decoding and copying characters, until either:
1854  //   we reach the end of the string
1855  //   we reach the maximum width
1856  //   we reach a newline character
1857  // As we copy characters, keep track of the last full word to fit, so that we
1858  // can backtrack if we exceed the maximum width.
1859  last_i1 = last_i2 = *i;
1860  last_o1 = last_o2 = 0;
1861  spacePrev = gFalse;
1862  outBuf->clear();
1863
1864  while (*i < text->getLength()) {
1865    last_i2 = *i;
1866    last_o2 = outBuf->getLength();
1867
1868    if (unicode) {
1869      uChar = (unsigned char)(text->getChar(*i)) << 8;
1870      uChar += (unsigned char)(text->getChar(*i + 1));
1871      *i += 2;
1872    } else {
1873      if (noReencode)
1874        uChar = text->getChar(*i) & 0xff;
1875      else
1876        uChar = pdfDocEncoding[text->getChar(*i) & 0xff];
1877      *i += 1;
1878    }
1879
1880    // Explicit line break?
1881    if (uChar == '\r' || uChar == '\n') {
1882      // Treat a <CR><LF> sequence as a single line break
1883      if (uChar == '\r' && *i < text->getLength()) {
1884        if (unicode && text->getChar(*i) == '\0'
1885            && text->getChar(*i + 1) == '\n')
1886          *i += 2;
1887        else if (!unicode && text->getChar(*i) == '\n')
1888          *i += 1;
1889      }
1890
1891      break;
1892    }
1893
1894    if (noReencode) {
1895      outBuf->append(uChar);
1896    } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
1897      if (font->isCIDFont()) {
1898        // TODO: This assumes an identity CMap.  It should be extended to
1899        // handle the general case.
1900        outBuf->append((c >> 8) & 0xff);
1901        outBuf->append(c & 0xff);
1902      } else {
1903        // 8-bit font
1904        outBuf->append(c);
1905      }
1906    } else {
1907      fprintf(stderr,
1908              "warning: layoutText: cannot convert U+%04X\n", uChar);
1909    }
1910
1911    // If we see a space, then we have a linebreak opportunity.
1912    if (uChar == ' ') {
1913      last_i1 = *i;
1914      if (!spacePrev)
1915        last_o1 = last_o2;
1916      spacePrev = gTrue;
1917    } else {
1918      spacePrev = gFalse;
1919    }
1920
1921    // Compute width of character just output
1922    if (outBuf->getLength() > last_o2) {
1923      dx = 0.0;
1924      font->getNextChar(outBuf->getCString() + last_o2,
1925                        outBuf->getLength() - last_o2,
1926                        &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
1927      w += dx;
1928    }
1929
1930    // Current line over-full now?
1931    if (widthLimit > 0.0 && w > widthLimit) {
1932      if (last_o1 > 0) {
1933        // Back up to the previous word which fit, if there was a previous
1934        // word.
1935        *i = last_i1;
1936        outBuf->del(last_o1, outBuf->getLength() - last_o1);
1937      } else if (last_o2 > 0) {
1938        // Otherwise, back up to the previous character (in the only word on
1939        // this line)
1940        *i = last_i2;
1941        outBuf->del(last_o2, outBuf->getLength() - last_o2);
1942      } else {
1943        // Otherwise, we were trying to fit the first character; include it
1944        // anyway even if it overflows the space--no updates to make.
1945      }
1946      break;
1947    }
1948  }
1949
1950  // If splitting input into lines because we reached the width limit, then
1951  // consume any remaining trailing spaces that would go on this line from the
1952  // input.  If in doing so we reach a newline, consume that also.  This code
1953  // won't run if we stopped at a newline above, since in that case w <=
1954  // widthLimit still.
1955  if (widthLimit > 0.0 && w > widthLimit) {
1956    if (unicode) {
1957      while (*i < text->getLength()
1958             && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ')
1959        *i += 2;
1960      if (*i < text->getLength()
1961          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r')
1962        *i += 2;
1963      if (*i < text->getLength()
1964          && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n')
1965        *i += 2;
1966    } else {
1967      while (*i < text->getLength() && text->getChar(*i) == ' ')
1968        *i += 1;
1969      if (*i < text->getLength() && text->getChar(*i) == '\r')
1970        *i += 1;
1971      if (*i < text->getLength() && text->getChar(*i) == '\n')
1972        *i += 1;
1973    }
1974  }
1975
1976  // Compute the actual width and character count of the final string, based on
1977  // breakpoint, if this information is requested by the caller.
1978  if (width != NULL || charCount != NULL) {
1979    char *s = outBuf->getCString();
1980    int len = outBuf->getLength();
1981
1982    if (width != NULL)
1983      *width = 0.0;
1984    if (charCount != NULL)
1985      *charCount = 0;
1986
1987    while (len > 0) {
1988      dx = 0.0;
1989      n = font->getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
1990
1991      if (n == 0) {
1992        break;
1993      }
1994
1995      if (width != NULL)
1996        *width += dx;
1997      if (charCount != NULL)
1998        *charCount += 1;
1999
2000      s += n;
2001      len -= n;
2002    }
2003  }
2004}
2005
2006// Copy the given string to appearBuf, adding parentheses around it and
2007// escaping characters as appropriate.
2008void AnnotWidget::writeString(GooString *str, GooString *appearBuf)
2009{
2010  char c;
2011  int i;
2012
2013  appearBuf->append('(');
2014
2015  for (i = 0; i < str->getLength(); ++i) {
2016    c = str->getChar(i);
2017    if (c == '(' || c == ')' || c == '\\') {
2018      appearBuf->append('\\');
2019      appearBuf->append(c);
2020    } else if (c < 0x20) {
2021      appearBuf->appendf("\\{0:03o}", (unsigned char)c);
2022    } else {
2023      appearBuf->append(c);
2024    }
2025  }
2026
2027  appearBuf->append(')');
2028}
2029
2030// Draw the variable text or caption for a field.
2031void AnnotWidget::drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
2032    GBool multiline, int comb, int quadding,
2033    GBool txField, GBool forceZapfDingbats,
2034    GBool password) {
2035  GooList *daToks;
2036  GooString *tok, *convertedText;
2037  GfxFont *font;
2038  double fontSize, fontSize2, borderWidth, x, xPrev, y, w, wMax;
2039  int tfPos, tmPos, i, j;
2040  GBool freeText = gFalse;      // true if text should be freed before return
2041
2042  //~ if there is no MK entry, this should use the existing content stream,
2043  //~ and only replace the marked content portion of it
2044  //~ (this is only relevant for Tx fields)
2045 
2046  // parse the default appearance string
2047  tfPos = tmPos = -1;
2048  if (da) {
2049    daToks = new GooList();
2050    i = 0;
2051    while (i < da->getLength()) {
2052      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2053        ++i;
2054      }
2055      if (i < da->getLength()) {
2056        for (j = i + 1;
2057            j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2058            ++j) ;
2059        daToks->append(new GooString(da, i, j - i));
2060        i = j;
2061      }
2062    }
2063    for (i = 2; i < daToks->getLength(); ++i) {
2064      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2065        tfPos = i - 2;
2066      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2067        tmPos = i - 6;
2068      }
2069    }
2070  } else {
2071    daToks = NULL;
2072  }
2073
2074  // force ZapfDingbats
2075  //~ this should create the font if needed (?)
2076  if (forceZapfDingbats) {
2077    if (tfPos >= 0) {
2078      tok = (GooString *)daToks->get(tfPos);
2079      if (tok->cmp("/ZaDb")) {
2080        tok->clear();
2081        tok->append("/ZaDb");
2082      }
2083    }
2084  }
2085  // get the font and font size
2086  font = NULL;
2087  fontSize = 0;
2088  if (tfPos >= 0) {
2089    tok = (GooString *)daToks->get(tfPos);
2090    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2091      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2092        error(-1, "Unknown font in field's DA string");
2093      }
2094    } else {
2095      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2096    }
2097    tok = (GooString *)daToks->get(tfPos + 1);
2098    fontSize = atof(tok->getCString());
2099  } else {
2100    error(-1, "Missing 'Tf' operator in field's DA string");
2101  }
2102  if (!font) {
2103    if (daToks) {
2104      deleteGooList(daToks, GooString);
2105    }
2106    return;
2107  }
2108
2109  // get the border width
2110  borderWidth = border ? border->getWidth() : 0;
2111
2112  // for a password field, replace all characters with asterisks
2113  if (password) {
2114    int len;
2115    if (text->hasUnicodeMarker())
2116      len = (text->getLength() - 2) / 2;
2117    else
2118      len = text->getLength();
2119
2120    text = new GooString;
2121    for (i = 0; i < len; ++i)
2122      text->append('*');
2123    freeText = gTrue;
2124  }
2125
2126  convertedText = new GooString;
2127
2128  // setup
2129  if (txField) {
2130    appearBuf->append("/Tx BMC\n");
2131  }
2132  appearBuf->append("q\n");
2133  appearBuf->append("BT\n");
2134  // multi-line text
2135  if (multiline) {
2136    // note: the comb flag is ignored in multiline mode
2137
2138    wMax = rect->x2 - rect->x1 - 2 * borderWidth - 4;
2139
2140    // compute font autosize
2141    if (fontSize == 0) {
2142      for (fontSize = 20; fontSize > 1; --fontSize) {
2143        y = rect->y2 - rect->y1;
2144        i = 0;
2145        while (i < text->getLength()) {
2146          layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2147                     forceZapfDingbats);
2148          y -= fontSize;
2149        }
2150        // approximate the descender for the last line
2151        if (y >= 0.33 * fontSize) {
2152          break;
2153        }
2154      }
2155      if (tfPos >= 0) {
2156        tok = (GooString *)daToks->get(tfPos + 1);
2157        tok->clear();
2158        tok->appendf("{0:.2f}", fontSize);
2159      }
2160    }
2161
2162    // starting y coordinate
2163    // (note: each line of text starts with a Td operator that moves
2164    // down a line)
2165    y = rect->y2 - rect->y1;
2166
2167    // set the font matrix
2168    if (tmPos >= 0) {
2169      tok = (GooString *)daToks->get(tmPos + 4);
2170      tok->clear();
2171      tok->append('0');
2172      tok = (GooString *)daToks->get(tmPos + 5);
2173      tok->clear();
2174      tok->appendf("{0:.2f}", y);
2175    }
2176
2177    // write the DA string
2178    if (daToks) {
2179      for (i = 0; i < daToks->getLength(); ++i) {
2180        appearBuf->append((GooString *)daToks->get(i))->append(' ');
2181      }
2182    }
2183
2184    // write the font matrix (if not part of the DA string)
2185    if (tmPos < 0) {
2186      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
2187    }
2188
2189    // write a series of lines of text
2190    i = 0;
2191    xPrev = 0;
2192    while (i < text->getLength()) {
2193      layoutText(text, convertedText, &i, font, &w, wMax / fontSize, NULL,
2194                 forceZapfDingbats);
2195      w *= fontSize;
2196
2197      // compute text start position
2198      switch (quadding) {
2199        case fieldQuadLeft:
2200        default:
2201          x = borderWidth + 2;
2202          break;
2203        case fieldQuadCenter:
2204          x = (rect->x2 - rect->x1 - w) / 2;
2205          break;
2206        case fieldQuadRight:
2207          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2208          break;
2209      }
2210
2211      // draw the line
2212      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
2213      writeString(convertedText, appearBuf);
2214      appearBuf->append(" Tj\n");
2215
2216      // next line
2217      xPrev = x;
2218    }
2219
2220    // single-line text
2221  } else {
2222    //~ replace newlines with spaces? - what does Acrobat do?
2223
2224    // comb formatting
2225    if (comb > 0) {
2226      int charCount;
2227
2228      // compute comb spacing
2229      w = (rect->x2 - rect->x1 - 2 * borderWidth) / comb;
2230
2231      // compute font autosize
2232      if (fontSize == 0) {
2233        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2234        if (w < fontSize) {
2235          fontSize = w;
2236        }
2237        fontSize = floor(fontSize);
2238        if (tfPos >= 0) {
2239          tok = (GooString *)daToks->get(tfPos + 1);
2240          tok->clear();
2241          tok->appendf("{0:.2f}", fontSize);
2242        }
2243      }
2244
2245      i = 0;
2246      layoutText(text, convertedText, &i, font, NULL, 0.0, &charCount,
2247                 forceZapfDingbats);
2248      if (charCount > comb)
2249        charCount = comb;
2250
2251      // compute starting text cell
2252      switch (quadding) {
2253        case fieldQuadLeft:
2254        default:
2255          x = borderWidth;
2256          break;
2257        case fieldQuadCenter:
2258          x = borderWidth + (comb - charCount) / 2 * w;
2259          break;
2260        case fieldQuadRight:
2261          x = borderWidth + (comb - charCount) * w;
2262          break;
2263      }
2264      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2265
2266      // set the font matrix
2267      if (tmPos >= 0) {
2268        tok = (GooString *)daToks->get(tmPos + 4);
2269        tok->clear();
2270        tok->appendf("{0:.2f}", x);
2271        tok = (GooString *)daToks->get(tmPos + 5);
2272        tok->clear();
2273        tok->appendf("{0:.2f}", y);
2274      }
2275
2276      // write the DA string
2277      if (daToks) {
2278        for (i = 0; i < daToks->getLength(); ++i) {
2279          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2280        }
2281      }
2282
2283      // write the font matrix (if not part of the DA string)
2284      if (tmPos < 0) {
2285        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2286      }
2287
2288      // write the text string
2289      char *s = convertedText->getCString();
2290      int len = convertedText->getLength();
2291      i = 0;
2292      xPrev = w;                // so that first character is placed properly
2293      while (i < comb && len > 0) {
2294        CharCode code;
2295        Unicode *uAux;
2296        int uLen, n;
2297        double dx, dy, ox, oy;
2298
2299        dx = 0.0;
2300        n = font->getNextChar(s, len, &code, &uAux, &uLen, &dx, &dy, &ox, &oy);
2301        dx *= fontSize;
2302
2303        // center each character within its cell, by advancing the text
2304        // position the appropriate amount relative to the start of the
2305        // previous character
2306        x = 0.5 * (w - dx);
2307        appearBuf->appendf("{0:.2f} 0 Td\n", x - xPrev + w);
2308
2309        GooString *charBuf = new GooString(s, n);
2310        writeString(charBuf, appearBuf);
2311        appearBuf->append(" Tj\n");
2312        delete charBuf;
2313
2314        i++;
2315        s += n;
2316        len -= n;
2317        xPrev = x;
2318      }
2319
2320      // regular (non-comb) formatting
2321    } else {
2322      i = 0;
2323      layoutText(text, convertedText, &i, font, &w, 0.0, NULL,
2324                 forceZapfDingbats);
2325
2326      // compute font autosize
2327      if (fontSize == 0) {
2328        fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2329        fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / w;
2330        if (fontSize2 < fontSize) {
2331          fontSize = fontSize2;
2332        }
2333        fontSize = floor(fontSize);
2334        if (tfPos >= 0) {
2335          tok = (GooString *)daToks->get(tfPos + 1);
2336          tok->clear();
2337          tok->appendf("{0:.2f}", fontSize);
2338        }
2339      }
2340
2341      // compute text start position
2342      w *= fontSize;
2343      switch (quadding) {
2344        case fieldQuadLeft:
2345        default:
2346          x = borderWidth + 2;
2347          break;
2348        case fieldQuadCenter:
2349          x = (rect->x2 - rect->x1 - w) / 2;
2350          break;
2351        case fieldQuadRight:
2352          x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2353          break;
2354      }
2355      y = 0.5 * (rect->y2 - rect->y1) - 0.4 * fontSize;
2356
2357      // set the font matrix
2358      if (tmPos >= 0) {
2359        tok = (GooString *)daToks->get(tmPos + 4);
2360        tok->clear();
2361        tok->appendf("{0:.2f}", x);
2362        tok = (GooString *)daToks->get(tmPos + 5);
2363        tok->clear();
2364        tok->appendf("{0:.2f}", y);
2365      }
2366
2367      // write the DA string
2368      if (daToks) {
2369        for (i = 0; i < daToks->getLength(); ++i) {
2370          appearBuf->append((GooString *)daToks->get(i))->append(' ');
2371        }
2372      }
2373
2374      // write the font matrix (if not part of the DA string)
2375      if (tmPos < 0) {
2376        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2377      }
2378
2379      // write the text string
2380      writeString(convertedText, appearBuf);
2381      appearBuf->append(" Tj\n");
2382    }
2383  }
2384  // cleanup
2385  appearBuf->append("ET\n");
2386  appearBuf->append("Q\n");
2387  if (txField) {
2388    appearBuf->append("EMC\n");
2389  }
2390  if (daToks) {
2391    deleteGooList(daToks, GooString);
2392  }
2393  if (freeText) {
2394    delete text;
2395  }
2396  delete convertedText;
2397}
2398
2399// Draw the variable text or caption for a field.
2400void AnnotWidget::drawListBox(GooString **text, GBool *selection,
2401                              int nOptions, int topIdx,
2402                              GooString *da, GfxFontDict *fontDict, GBool quadding) {
2403  GooList *daToks;
2404  GooString *tok, *convertedText;
2405  GfxFont *font;
2406  double fontSize, fontSize2, borderWidth, x, y, w, wMax;
2407  int tfPos, tmPos, i, j;
2408
2409  //~ if there is no MK entry, this should use the existing content stream,
2410  //~ and only replace the marked content portion of it
2411  //~ (this is only relevant for Tx fields)
2412
2413  // parse the default appearance string
2414  tfPos = tmPos = -1;
2415  if (da) {
2416    daToks = new GooList();
2417    i = 0;
2418    while (i < da->getLength()) {
2419      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
2420        ++i;
2421       }
2422      if (i < da->getLength()) {
2423        for (j = i + 1;
2424             j < da->getLength() && !Lexer::isSpace(da->getChar(j));
2425             ++j) ;
2426        daToks->append(new GooString(da, i, j - i));
2427        i = j;
2428      }
2429    }
2430    for (i = 2; i < daToks->getLength(); ++i) {
2431      if (i >= 2 && !((GooString *)daToks->get(i))->cmp("Tf")) {
2432        tfPos = i - 2;
2433      } else if (i >= 6 && !((GooString *)daToks->get(i))->cmp("Tm")) {
2434        tmPos = i - 6;
2435      }
2436    }
2437  } else {
2438    daToks = NULL;
2439  }
2440
2441  // get the font and font size
2442  font = NULL;
2443  fontSize = 0;
2444  if (tfPos >= 0) {
2445    tok = (GooString *)daToks->get(tfPos);
2446    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
2447      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
2448        error(-1, "Unknown font in field's DA string");
2449      }
2450    } else {
2451      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
2452    }
2453    tok = (GooString *)daToks->get(tfPos + 1);
2454    fontSize = atof(tok->getCString());
2455  } else {
2456    error(-1, "Missing 'Tf' operator in field's DA string");
2457  }
2458  if (!font) {
2459    if (daToks) {
2460      deleteGooList(daToks, GooString);
2461    }
2462    return;
2463  }
2464
2465  convertedText = new GooString;
2466
2467  // get the border width
2468  borderWidth = border ? border->getWidth() : 0;
2469
2470  // compute font autosize
2471  if (fontSize == 0) {
2472    wMax = 0;
2473    for (i = 0; i < nOptions; ++i) {
2474      j = 0;
2475      layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2476      if (w > wMax) {
2477        wMax = w;
2478      }
2479    }
2480    fontSize = rect->y2 - rect->y1 - 2 * borderWidth;
2481    fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax;
2482    if (fontSize2 < fontSize) {
2483      fontSize = fontSize2;
2484    }
2485    fontSize = floor(fontSize);
2486    if (tfPos >= 0) {
2487      tok = (GooString *)daToks->get(tfPos + 1);
2488      tok->clear();
2489      tok->appendf("{0:.2f}", fontSize);
2490    }
2491  }
2492  // draw the text
2493  y = rect->y2 - rect->y1 - 1.1 * fontSize;
2494  for (i = topIdx; i < nOptions; ++i) {
2495    // setup
2496    appearBuf->append("q\n");
2497
2498    // draw the background if selected
2499    if (selection[i]) {
2500      appearBuf->append("0 g f\n");
2501      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
2502          borderWidth,
2503          y - 0.2 * fontSize,
2504          rect->x2 - rect->x1 - 2 * borderWidth,
2505          1.1 * fontSize);
2506    }
2507
2508    // setup
2509    appearBuf->append("BT\n");
2510
2511    // compute text width and start position
2512    j = 0;
2513    layoutText(text[i], convertedText, &j, font, &w, 0.0, NULL, gFalse);
2514    w *= fontSize;
2515    switch (quadding) {
2516      case fieldQuadLeft:
2517      default:
2518        x = borderWidth + 2;
2519        break;
2520      case fieldQuadCenter:
2521        x = (rect->x2 - rect->x1 - w) / 2;
2522        break;
2523      case fieldQuadRight:
2524        x = rect->x2 - rect->x1 - borderWidth - 2 - w;
2525        break;
2526    }
2527
2528    // set the font matrix
2529    if (tmPos >= 0) {
2530      tok = (GooString *)daToks->get(tmPos + 4);
2531      tok->clear();
2532      tok->appendf("{0:.2f}", x);
2533      tok = (GooString *)daToks->get(tmPos + 5);
2534      tok->clear();
2535      tok->appendf("{0:.2f}", y);
2536    }
2537
2538    // write the DA string
2539    if (daToks) {
2540      for (j = 0; j < daToks->getLength(); ++j) {
2541        appearBuf->append((GooString *)daToks->get(j))->append(' ');
2542      }
2543    }
2544
2545    // write the font matrix (if not part of the DA string)
2546    if (tmPos < 0) {
2547      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
2548    }
2549
2550    // change the text color if selected
2551    if (selection[i]) {
2552      appearBuf->append("1 g\n");
2553    }
2554
2555    // write the text string
2556    writeString(convertedText, appearBuf);
2557    appearBuf->append(" Tj\n");
2558
2559    // cleanup
2560    appearBuf->append("ET\n");
2561    appearBuf->append("Q\n");
2562
2563    // next line
2564    y -= 1.1 * fontSize;
2565  }
2566
2567  if (daToks) {
2568    deleteGooList(daToks, GooString);
2569  }
2570
2571  delete convertedText;
2572}
2573
2574void AnnotWidget::generateFieldAppearance() {
2575  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
2576  Dict *field;
2577  Dict *annot;
2578  Dict *acroForm;
2579  Dict *mkDict;
2580  MemStream *appearStream;
2581  GfxFontDict *fontDict;
2582  GBool hasCaption;
2583  double w, dx, dy, r;
2584  double *dash;
2585  GooString *caption, *da;
2586  GooString **text;
2587  GBool *selection;
2588  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
2589  GBool modified;
2590
2591  if (widget == NULL || !widget->getField () || !widget->getField ()->getObj ()->isDict ())
2592    return;
2593
2594  field = widget->getField ()->getObj ()->getDict ();
2595  annot = widget->getObj ()->getDict ();
2596  acroForm = form->getObj ()->getDict ();
2597 
2598  // do not regenerate appearence if widget has not changed
2599  modified = widget->isModified ();
2600
2601  // only regenerate when it doesn't have an AP or
2602  // it already has an AP but widget has been modified
2603  if (!regen && !modified) {
2604    return;
2605  }
2606
2607  appearBuf = new GooString ();
2608  // get the appearance characteristics (MK) dictionary
2609  if (annot->lookup("MK", &mkObj)->isDict()) {
2610    mkDict = mkObj.getDict();
2611  } else {
2612    mkDict = NULL;
2613  }
2614  // draw the background
2615  if (mkDict) {
2616    if (mkDict->lookup("BG", &obj1)->isArray() &&
2617        obj1.arrayGetLength() > 0) {
2618      setColor(obj1.getArray(), gTrue, 0);
2619      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
2620          rect->x2 - rect->x1, rect->y2 - rect->y1);
2621    }
2622    obj1.free();
2623  }
2624
2625  // get the field type
2626  Form::fieldLookup(field, "FT", &ftObj);
2627
2628  // get the field flags (Ff) value
2629  if (Form::fieldLookup(field, "Ff", &obj1)->isInt()) {
2630    ff = obj1.getInt();
2631  } else {
2632    ff = 0;
2633  }
2634  obj1.free();
2635
2636  // draw the border
2637  if (mkDict && border) {
2638    w = border->getWidth();
2639    if (w > 0) {
2640      mkDict->lookup("BC", &obj1);
2641      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
2642        mkDict->lookup("BG", &obj1);
2643      }
2644      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
2645        dx = rect->x2 - rect->x1;
2646        dy = rect->y2 - rect->y1;
2647
2648        // radio buttons with no caption have a round border
2649        hasCaption = mkDict->lookup("CA", &obj2)->isString();
2650        obj2.free();
2651        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
2652          r = 0.5 * (dx < dy ? dx : dy);
2653          switch (border->getStyle()) {
2654            case AnnotBorder::borderDashed:
2655              appearBuf->append("[");
2656              dashLength = border->getDashLength();
2657              dash = border->getDash();
2658              for (i = 0; i < dashLength; ++i) {
2659                appearBuf->appendf(" {0:.2f}", dash[i]);
2660              }
2661              appearBuf->append("] 0 d\n");
2662              // fall through to the solid case
2663            case AnnotBorder::borderSolid:
2664            case AnnotBorder::borderUnderlined:
2665              appearBuf->appendf("{0:.2f} w\n", w);
2666              setColor(obj1.getArray(), gFalse, 0);
2667              drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
2668              break;
2669            case AnnotBorder::borderBeveled:
2670            case AnnotBorder::borderInset:
2671              appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
2672              setColor(obj1.getArray(), gFalse, 0);
2673              drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
2674              setColor(obj1.getArray(), gFalse,
2675                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2676              drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2677              setColor(obj1.getArray(), gFalse,
2678                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2679              drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
2680              break;
2681          }
2682
2683        } else {
2684          switch (border->getStyle()) {
2685            case AnnotBorder::borderDashed:
2686              appearBuf->append("[");
2687              dashLength = border->getDashLength();
2688              dash = border->getDash();
2689              for (i = 0; i < dashLength; ++i) {
2690                appearBuf->appendf(" {0:.2f}", dash[i]);
2691              }
2692              appearBuf->append("] 0 d\n");
2693              // fall through to the solid case
2694            case AnnotBorder::borderSolid:
2695              appearBuf->appendf("{0:.2f} w\n", w);
2696              setColor(obj1.getArray(), gFalse, 0);
2697              appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
2698                  0.5 * w, dx - w, dy - w);
2699              break;
2700            case AnnotBorder::borderBeveled:
2701            case AnnotBorder::borderInset:
2702              setColor(obj1.getArray(), gTrue,
2703                  border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
2704              appearBuf->append("0 0 m\n");
2705              appearBuf->appendf("0 {0:.2f} l\n", dy);
2706              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2707              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2708              appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
2709              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2710              appearBuf->append("f\n");
2711              setColor(obj1.getArray(), gTrue,
2712                  border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
2713              appearBuf->append("0 0 m\n");
2714              appearBuf->appendf("{0:.2f} 0 l\n", dx);
2715              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
2716              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
2717              appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
2718              appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
2719              appearBuf->append("f\n");
2720              break;
2721            case AnnotBorder::borderUnderlined:
2722              appearBuf->appendf("{0:.2f} w\n", w);
2723              setColor(obj1.getArray(), gFalse, 0);
2724              appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
2725              break;
2726          }
2727
2728          // clip to the inside of the border
2729          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
2730              w, dx - 2 * w, dy - 2 * w);
2731        }
2732      }
2733      obj1.free();
2734    }
2735  }
2736
2737  // get the resource dictionary
2738  acroForm->lookup("DR", &drObj);
2739
2740  // build the font dictionary
2741  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
2742    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
2743  } else {
2744    fontDict = NULL;
2745  }
2746  obj1.free();
2747
2748  // get the default appearance string
2749  if (Form::fieldLookup(field, "DA", &obj1)->isNull()) {
2750    obj1.free();
2751    acroForm->lookup("DA", &obj1);
2752  }
2753  if (obj1.isString()) {
2754    da = obj1.getString()->copy();
2755    //TODO: look for a font size / name HERE
2756    // => create a function
2757  } else {
2758    da = NULL;
2759  }
2760  obj1.free();
2761
2762  // draw the field contents
2763  if (ftObj.isName("Btn")) {
2764    caption = NULL;
2765    if (mkDict) {
2766      if (mkDict->lookup("CA", &obj1)->isString()) {
2767        caption = obj1.getString()->copy();
2768      }
2769      obj1.free();
2770    }
2771    // radio button
2772    if (ff & fieldFlagRadio) {
2773      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
2774      if (Form::fieldLookup(field, "V", &obj1)->isName()) {
2775        if (annot->lookup("AS", &obj2)->isName(obj1.getName()) &&
2776            strcmp (obj1.getName(), "Off") != 0) {
2777          if (caption) {
2778            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2779                gFalse, gTrue);
2780          } else {
2781            if (mkDict) {
2782              if (mkDict->lookup("BC", &obj3)->isArray() &&
2783                  obj3.arrayGetLength() > 0) {
2784                dx = rect->x2 - rect->x1;
2785                dy = rect->y2 - rect->y1;
2786                setColor(obj3.getArray(), gTrue, 0);
2787                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
2788                    gTrue);
2789              }
2790              obj3.free();
2791            }
2792          }
2793        }
2794        obj2.free();
2795      }
2796      obj1.free();
2797      // pushbutton
2798    } else if (ff & fieldFlagPushbutton) {
2799      if (caption) {
2800        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2801            gFalse, gFalse);
2802      }
2803      // checkbox
2804    } else {
2805      if (annot->lookup("AS", &obj1)->isName() &&
2806          strcmp(obj1.getName(), "Off") != 0) {
2807        if (!caption) {
2808          caption = new GooString("3"); // ZapfDingbats checkmark
2809        }
2810        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
2811            gFalse, gTrue);
2812      }
2813      obj1.free();
2814    }
2815    if (caption) {
2816      delete caption;
2817    }
2818  } else if (ftObj.isName("Tx")) {
2819    if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2820      if (Form::fieldLookup(field, "Q", &obj2)->isInt()) {
2821        quadding = obj2.getInt();
2822      } else {
2823        quadding = fieldQuadLeft;
2824      }
2825      obj2.free();
2826      comb = 0;
2827      if (ff & fieldFlagComb) {
2828        if (Form::fieldLookup(field, "MaxLen", &obj2)->isInt()) {
2829          comb = obj2.getInt();
2830        }
2831        obj2.free();
2832      }
2833      drawText(obj1.getString(), da, fontDict,
2834          ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, ff & fieldFlagPassword);
2835    }
2836    obj1.free();
2837  } else if (ftObj.isName("Ch")) {
2838    if (Form::fieldLookup(field, "Q", &obj1)->isInt()) {
2839      quadding = obj1.getInt();
2840    } else {
2841      quadding = fieldQuadLeft;
2842    }
2843    obj1.free();
2844    // combo box
2845    if (ff & fieldFlagCombo) {
2846      if (Form::fieldLookup(field, "V", &obj1)->isString()) {
2847        drawText(obj1.getString(), da, fontDict,
2848            gFalse, 0, quadding, gTrue, gFalse);
2849        //~ Acrobat draws a popup icon on the right side
2850      }
2851      obj1.free();
2852      // list box
2853    } else {
2854      if (field->lookup("Opt", &obj1)->isArray()) {
2855        nOptions = obj1.arrayGetLength();
2856        // get the option text
2857        text = (GooString **)gmallocn(nOptions, sizeof(GooString *));
2858        for (i = 0; i < nOptions; ++i) {
2859          text[i] = NULL;
2860          obj1.arrayGet(i, &obj2);
2861          if (obj2.isString()) {
2862            text[i] = obj2.getString()->copy();
2863          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
2864            if (obj2.arrayGet(1, &obj3)->isString()) {
2865              text[i] = obj3.getString()->copy();
2866            }
2867            obj3.free();
2868          }
2869          obj2.free();
2870          if (!text[i]) {
2871            text[i] = new GooString();
2872          }
2873        }
2874        // get the selected option(s)
2875        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
2876        //~ need to use the I field in addition to the V field
2877        Form::fieldLookup(field, "V", &obj2);
2878        for (i = 0; i < nOptions; ++i) {
2879          selection[i] = gFalse;
2880          if (obj2.isString()) {
2881            if (!obj2.getString()->cmp(text[i])) {
2882              selection[i] = gTrue;
2883            }
2884          } else if (obj2.isArray()) {
2885            for (j = 0; j < obj2.arrayGetLength(); ++j) {
2886              if (obj2.arrayGet(j, &obj3)->isString() &&
2887                  !obj3.getString()->cmp(text[i])) {
2888                selection[i] = gTrue;
2889              }
2890              obj3.free();
2891            }
2892          }
2893        }
2894        obj2.free();
2895        // get the top index
2896        if (field->lookup("TI", &obj2)->isInt()) {
2897          topIdx = obj2.getInt();
2898        } else {
2899          topIdx = 0;
2900        }
2901        obj2.free();
2902        // draw the text
2903        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
2904        for (i = 0; i < nOptions; ++i) {
2905          delete text[i];
2906        }
2907        gfree(text);
2908        gfree(selection);
2909      }
2910      obj1.free();
2911    }
2912  } else if (ftObj.isName("Sig")) {
2913    //~unimp
2914  } else {
2915    error(-1, "Unknown field type");
2916  }
2917
2918  if (da) {
2919    delete da;
2920  }
2921
2922  // build the appearance stream dictionary
2923  appearDict.initDict(xref);
2924  appearDict.dictAdd(copyString("Length"),
2925      obj1.initInt(appearBuf->getLength()));
2926  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
2927  obj1.initArray(xref);
2928  obj1.arrayAdd(obj2.initReal(0));
2929  obj1.arrayAdd(obj2.initReal(0));
2930  obj1.arrayAdd(obj2.initReal(rect->x2 - rect->x1));
2931  obj1.arrayAdd(obj2.initReal(rect->y2 - rect->y1));
2932  appearDict.dictAdd(copyString("BBox"), &obj1);
2933
2934  // set the resource dictionary
2935  if (drObj.isDict()) {
2936    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
2937  }
2938  drObj.free();
2939
2940  // build the appearance stream
2941  appearStream = new MemStream(strdup(appearBuf->getCString()), 0,
2942      appearBuf->getLength(), &appearDict);
2943  appearance.free();
2944  appearance.initStream(appearStream);
2945  delete appearBuf;
2946
2947  appearStream->setNeedFree(gTrue);
2948
2949  if (widget->isModified()) {
2950    //create a new object that will contains the new appearance
2951   
2952    //if we already have a N entry in our AP dict, reuse it
2953    if (annot->lookup("AP", &obj1)->isDict() &&
2954        obj1.dictLookupNF("N", &obj2)->isRef()) {
2955      appRef = obj2.getRef();
2956    }
2957
2958    // this annot doesn't have an AP yet, create one
2959    if (appRef.num == 0)
2960      appRef = xref->addIndirectObject(&appearance);
2961    else // since we reuse the already existing AP, we have to notify the xref about this update
2962      xref->setModifiedObject(&appearance, appRef);
2963
2964    // update object's AP and AS
2965    Object apObj;
2966    apObj.initDict(xref);
2967
2968    Object oaRef;
2969    oaRef.initRef(appRef.num, appRef.gen);
2970
2971    apObj.dictSet("N", &oaRef);
2972    annot->set("AP", &apObj);
2973    Dict* d = new Dict(annot);
2974    d->decRef();
2975    Object dictObj;
2976    dictObj.initDict(d);
2977
2978    xref->setModifiedObject(&dictObj, ref);
2979    dictObj.free();
2980  }
2981
2982  if (fontDict) {
2983    delete fontDict;
2984  }
2985  ftObj.free();
2986  mkObj.free();
2987}
2988
2989
2990void AnnotWidget::draw(Gfx *gfx, GBool printing) {
2991  Object obj;
2992
2993  // check the flags
2994  if ((flags & annotFlagHidden) ||
2995      (printing && !(flags & annotFlagPrint)) ||
2996      (!printing && (flags & annotFlagNoView))) {
2997    return;
2998  }
2999
3000  generateFieldAppearance ();
3001
3002  // draw the appearance stream
3003  appearance.fetch(xref, &obj);
3004  gfx->drawAnnot(&obj, (AnnotBorder *)NULL, color,
3005                 rect->x1, rect->y1, rect->x2, rect->y2);
3006  obj.free();
3007}
3008
3009
3010//------------------------------------------------------------------------
3011// AnnotMovie
3012//------------------------------------------------------------------------
3013 
3014AnnotMovie::AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3015  Annot(xrefA, dict, catalog, obj) {
3016  type = typeMovie;
3017  initialize(xrefA, catalog, dict);
3018
3019  movie = new Movie();
3020  movie->parseAnnotMovie(this);
3021}
3022
3023AnnotMovie::~AnnotMovie() {
3024  if (title)
3025    delete title;
3026  if (fileName)
3027    delete fileName;
3028  delete movie;
3029
3030  if (posterStream && (!posterStream->decRef())) {
3031    delete posterStream;
3032  }
3033}
3034
3035void AnnotMovie::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3036  Object obj1;
3037
3038  if (dict->lookup("T", &obj1)->isString()) {
3039    title = obj1.getString()->copy();
3040  } else {
3041    title = NULL;
3042  }
3043  obj1.free();
3044
3045  Object movieDict;
3046  Object aDict;
3047
3048  // default values
3049  fileName = NULL;
3050  width = 0;
3051  height = 0;
3052  rotationAngle = 0;
3053  rate = 1.0;
3054  volume = 1.0;
3055  showControls = false;
3056  repeatMode = repeatModeOnce;
3057  synchronousPlay = false;
3058 
3059  hasFloatingWindow = false;
3060  isFullscreen = false;
3061  FWScaleNum = 1;
3062  FWScaleDenum = 1;
3063  FWPosX = 0.5;
3064  FWPosY = 0.5;
3065
3066  if (dict->lookup("Movie", &movieDict)->isDict()) {
3067    if (movieDict.dictLookup("F", &obj1)->isString()) {
3068      fileName = obj1.getString()->copy();
3069    }
3070    obj1.free();
3071
3072    if (movieDict.dictLookup("Aspect", &obj1)->isArray()) {
3073      Array* aspect = obj1.getArray();
3074      if (aspect->getLength() >= 2) {
3075        Object tmp;
3076        width = aspect->get(0, &tmp)->getInt();
3077        tmp.free();
3078        height = aspect->get(1, &tmp)->getInt();
3079        tmp.free();
3080      }
3081    }
3082    obj1.free();
3083
3084    if (movieDict.dictLookup("Rotate", &obj1)->isInt()) {
3085      // round up to 90°
3086      rotationAngle = (((obj1.getInt() + 360) % 360) % 90) * 90;
3087    }
3088    obj1.free();
3089
3090    //
3091    // movie poster
3092    //
3093    posterType = posterTypeNone;
3094    posterStream = NULL;
3095    if (!movieDict.dictLookup("Poster", &obj1)->isNone()) {
3096      if (obj1.isBool()) {
3097        GBool v = obj1.getBool();
3098        if (v)
3099          posterType = posterTypeFromMovie;
3100      }
3101     
3102      if (obj1.isStream()) {
3103        posterType = posterTypeStream;
3104       
3105        // "copy" stream
3106        posterStream = obj1.getStream();
3107        posterStream->incRef();
3108      }
3109
3110      obj1.free();
3111    }
3112
3113  }
3114  movieDict.free();
3115
3116
3117  // activation dictionary parsing ...
3118
3119  if (dict->lookup("A", &aDict)->isDict()) {
3120    if (!aDict.dictLookup("Start", &obj1)->isNone()) {
3121      if (obj1.isInt()) {
3122        // If it is representable as an integer (subject to the implementation limit for
3123        // integers, as described in Appendix C), it should be specified as such.
3124
3125        start.units = obj1.getInt();
3126      }
3127      if (obj1.isString()) {
3128        // If it is not representable as an integer, it should be specified as an 8-byte
3129        // string representing a 64-bit twos-complement integer, most significant
3130        // byte first.
3131
3132        // UNSUPPORTED
3133      }
3134
3135      if (obj1.isArray()) {
3136        Array* a = obj1.getArray();
3137
3138        Object tmp;
3139        a->get(0, &tmp);
3140        if (tmp.isInt()) {
3141          start.units = tmp.getInt();
3142        }
3143        if (tmp.isString()) {
3144          // UNSUPPORTED
3145        }
3146        tmp.free();
3147
3148        a->get(1, &tmp);
3149        if (tmp.isInt()) {
3150          start.units_per_second = tmp.getInt();
3151        }
3152        tmp.free();
3153      }
3154    }
3155    obj1.free();
3156
3157    if (!aDict.dictLookup("Duration", &obj1)->isNone()) {
3158      if (obj1.isInt()) {
3159        duration.units = obj1.getInt();
3160      }
3161      if (obj1.isString()) {
3162        // UNSUPPORTED
3163      }
3164
3165      if (obj1.isArray()) {
3166        Array* a = obj1.getArray();
3167
3168        Object tmp;
3169        a->get(0, &tmp);
3170        if (tmp.isInt()) {
3171          duration.units = tmp.getInt();
3172        }
3173        if (tmp.isString()) {
3174          // UNSUPPORTED
3175        }
3176        tmp.free();
3177
3178        a->get(1, &tmp);
3179        if (tmp.isInt()) {
3180          duration.units_per_second = tmp.getInt();
3181        }
3182        tmp.free();
3183      }
3184    }
3185    obj1.free();
3186
3187    if (aDict.dictLookup("Rate", &obj1)->isNum()) {
3188      rate = obj1.getNum();
3189    }
3190    obj1.free();
3191
3192    if (aDict.dictLookup("Volume", &obj1)->isNum()) {
3193      volume = obj1.getNum();
3194    }
3195    obj1.free();
3196
3197    if (aDict.dictLookup("ShowControls", &obj1)->isBool()) {
3198      showControls = obj1.getBool();
3199    }
3200    obj1.free();
3201
3202    if (aDict.dictLookup("Synchronous", &obj1)->isBool()) {
3203      synchronousPlay = obj1.getBool();
3204    }
3205    obj1.free();
3206
3207    if (aDict.dictLookup("Mode", &obj1)->isName()) {
3208      char* name = obj1.getName();
3209      if (!strcmp(name, "Once"))
3210        repeatMode = repeatModeOnce;
3211      if (!strcmp(name, "Open"))
3212        repeatMode = repeatModeOpen;
3213      if (!strcmp(name, "Repeat"))
3214        repeatMode = repeatModeRepeat;
3215      if (!strcmp(name,"Palindrome"))
3216        repeatMode = repeatModePalindrome;
3217    }
3218    obj1.free();
3219
3220    if (aDict.dictLookup("FWScale", &obj1)->isArray()) {
3221      // the presence of that entry implies that the movie is to be played
3222      // in a floating window
3223      hasFloatingWindow = true;
3224
3225      Array* scale = obj1.getArray();
3226      if (scale->getLength() >= 2) {
3227        Object tmp;
3228        if (scale->get(0, &tmp)->isInt()) {
3229          FWScaleNum = tmp.getInt();
3230        }
3231        tmp.free();
3232        if (scale->get(1, &tmp)->isInt()) {
3233          FWScaleDenum = tmp.getInt();
3234        }
3235        tmp.free();
3236      }
3237
3238      // detect fullscreen mode
3239      if ((FWScaleNum == 999) && (FWScaleDenum == 1)) {
3240        isFullscreen = true;
3241      }
3242    }
3243    obj1.free();
3244
3245    if (aDict.dictLookup("FWPosition", &obj1)->isArray()) {
3246      Array* pos = obj1.getArray();
3247      if (pos->getLength() >= 2) {
3248        Object tmp;
3249        if (pos->get(0, &tmp)->isNum()) {
3250          FWPosX = tmp.getNum();
3251        }
3252        tmp.free();
3253        if (pos->get(1, &tmp)->isNum()) {
3254          FWPosY = tmp.getNum();
3255        }
3256        tmp.free();
3257      }
3258    }
3259  }
3260  aDict.free();
3261}
3262
3263void AnnotMovie::getMovieSize(int& width, int& height) {
3264  width = this->width;
3265  height = this->height;
3266}
3267
3268void AnnotMovie::getZoomFactor(int& num, int& denum) {
3269  num = FWScaleNum;
3270  denum = FWScaleDenum;
3271}
3272
3273
3274//------------------------------------------------------------------------
3275// AnnotScreen
3276//------------------------------------------------------------------------
3277 
3278AnnotScreen::AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
3279  Annot(xrefA, dict, catalog, obj) {
3280  type = typeScreen;
3281  initialize(xrefA, catalog, dict);
3282}
3283
3284AnnotScreen::~AnnotScreen() {
3285  if (title)
3286    delete title;
3287  if (appearCharacs)
3288    delete appearCharacs;
3289}
3290
3291void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
3292  Object obj1;
3293
3294  title = NULL;
3295  if (dict->lookup("T", &obj1)->isString()) {
3296    title = obj1.getString()->copy();
3297  }
3298  obj1.free();
3299
3300  dict->lookup("A", &action);
3301
3302  dict->lookup("AA", &additionAction);
3303
3304  appearCharacs = NULL;
3305  if(dict->lookup("MK", &obj1)->isDict()) {
3306    appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
3307  }
3308  obj1.free();
3309
3310}
3311
3312//------------------------------------------------------------------------
3313// Annots
3314//------------------------------------------------------------------------
3315
3316Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
3317  Annot *annot;
3318  Object obj1;
3319  int size;
3320  int i;
3321
3322  annots = NULL;
3323  size = 0;
3324  nAnnots = 0;
3325
3326  if (annotsObj->isArray()) {
3327    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
3328      //get the Ref to this annot and pass it to Annot constructor
3329      //this way, it'll be possible for the annot to retrieve the corresponding
3330      //form widget
3331      Object obj2;
3332      if (annotsObj->arrayGet(i, &obj1)->isDict()) {
3333        annotsObj->arrayGetNF(i, &obj2);
3334        annot = createAnnot (xref, obj1.getDict(), catalog, &obj2);
3335        if (annot && annot->isOk()) {
3336          if (nAnnots >= size) {
3337            size += 16;
3338            annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
3339          }
3340          annots[nAnnots++] = annot;
3341        } else {
3342          delete annot;
3343        }
3344      }
3345      obj2.free();
3346      obj1.free();
3347    }
3348  }
3349}
3350
3351Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj) {
3352  Annot *annot;
3353  Object obj1;
3354
3355  if (dict->lookup("Subtype", &obj1)->isName()) {
3356    GooString *typeName = new GooString(obj1.getName());
3357
3358    if (!typeName->cmp("Text")) {
3359      annot = new AnnotText(xref, dict, catalog, obj);
3360    } else if (!typeName->cmp("Link")) {
3361      annot = new AnnotLink(xref, dict, catalog, obj);
3362    } else if (!typeName->cmp("FreeText")) {
3363      annot = new AnnotFreeText(xref, dict, catalog, obj);
3364    } else if (!typeName->cmp("Line")) {
3365      annot = new AnnotLine(xref, dict, catalog, obj);
3366    } else if (!typeName->cmp("Square")) {
3367      annot = new Annot(xref, dict, catalog, obj);
3368    } else if (!typeName->cmp("Circle")) {
3369      annot = new Annot(xref, dict, catalog, obj);
3370    } else if (!typeName->cmp("Polygon")) {
3371      annot = new Annot(xref, dict, catalog, obj);
3372    } else if (!typeName->cmp("PolyLine")) {
3373      annot = new Annot(xref, dict, catalog, obj);
3374    } else if (!typeName->cmp("Highlight")) {
3375      annot = new Annot(xref, dict, catalog, obj);
3376    } else if (!typeName->cmp("Underline")) {
3377      annot = new Annot(xref, dict, catalog, obj);
3378    } else if (!typeName->cmp("Squiggly")) {
3379      annot = new Annot(xref, dict, catalog, obj);
3380    } else if (!typeName->cmp("StrikeOut")) {
3381      annot = new Annot(xref, dict, catalog, obj);
3382    } else if (!typeName->cmp("Stamp")) {
3383      annot = new Annot(xref, dict, catalog, obj);
3384    } else if (!typeName->cmp("Caret")) {
3385      annot = new Annot(xref, dict, catalog, obj);
3386    } else if (!typeName->cmp("Ink")) {
3387      annot = new Annot(xref, dict, catalog, obj);
3388    } else if (!typeName->cmp("FileAttachment")) {
3389      annot = new Annot(xref, dict, catalog, obj);
3390    } else if (!typeName->cmp("Sound")) {
3391      annot = new Annot(xref, dict, catalog, obj);
3392    } else if(!typeName->cmp("Movie")) {
3393      annot = new AnnotMovie(xref, dict, catalog, obj);
3394    } else if(!typeName->cmp("Widget")) {
3395      annot = new AnnotWidget(xref, dict, catalog, obj);
3396    } else if(!typeName->cmp("Screen")) {
3397      annot = new AnnotScreen(xref, dict, catalog, obj);
3398    } else if(!typeName->cmp("PrinterMark")) {
3399      annot = new Annot(xref, dict, catalog, obj);
3400    } else if (!typeName->cmp("TrapNet")) {
3401      annot = new Annot(xref, dict, catalog, obj);
3402    } else if (!typeName->cmp("Watermark")) {
3403      annot = new Annot(xref, dict, catalog, obj);
3404    } else if (!typeName->cmp("3D")) {
3405      annot = new Annot(xref, dict, catalog, obj);
3406    } else {
3407      annot = new Annot(xref, dict, catalog, obj);
3408    }
3409
3410    delete typeName;
3411  } else {
3412    annot = NULL;
3413  }
3414  obj1.free();
3415
3416  return annot;
3417}
3418
3419Annot *Annots::findAnnot(Ref *ref) {
3420  int i;
3421
3422  for (i = 0; i < nAnnots; ++i) {
3423    if (annots[i]->match(ref)) {
3424      return annots[i];
3425    }
3426  }
3427  return NULL;
3428}
3429
3430
3431Annots::~Annots() {
3432  int i;
3433
3434  for (i = 0; i < nAnnots; ++i) {
3435    delete annots[i];
3436  }
3437  gfree(annots);
3438}
Note: See TracBrowser for help on using the repository browser.