Ignore:
Timestamp:
Jun 30, 2008, 10:13:07 AM (13 years ago)
Author:
Eugene Romanenko
Message:

PDF plugin: poppler library updated to version 0.8.3

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/poppler/mypoppler/poppler/Annot.cc

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