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

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

PDF plugin: Poppler library updated to version 0.8.6

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