~ubuntu-branches/ubuntu/natty/luatex/natty

« back to all changes in this revision

Viewing changes to source/libs/xpdf/xpdf-3.02/xpdf/Annot.cc

  • Committer: Package Import Robot
  • Author(s): Norbert Preining
  • Date: 2010-12-13 23:22:59 UTC
  • mfrom: (0.2.1) (1.5.4) (4.3.12 experimental)
  • Revision ID: package-import@ubuntu.com-20101213232259-nqq2mq5z5x6qldw3
Tags: 0.65.0-1
* new upstream release
* ship two source packages as they are distributed by upstream, only
  renamed to match source package requirements. Fix debian/rules
  to install the manual pdf from the right place

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//========================================================================
2
 
//
3
 
// Annot.cc
4
 
//
5
 
// Copyright 2000-2003 Glyph & Cog, LLC
6
 
//
7
 
//========================================================================
8
 
 
9
 
#include <aconf.h>
10
 
 
11
 
#ifdef USE_GCC_PRAGMAS
12
 
#pragma implementation
13
 
#endif
14
 
 
15
 
#include <stdlib.h>
16
 
#include <math.h>
17
 
#include "gmem.h"
18
 
#include "GList.h"
19
 
#include "Error.h"
20
 
#include "Object.h"
21
 
#include "Catalog.h"
22
 
#include "Gfx.h"
23
 
#include "GfxFont.h"
24
 
#include "Lexer.h"
25
 
#include "Annot.h"
26
 
 
27
 
//------------------------------------------------------------------------
28
 
 
29
 
#define annotFlagHidden    0x0002
30
 
#define annotFlagPrint     0x0004
31
 
#define annotFlagNoView    0x0020
32
 
 
33
 
#define fieldFlagReadOnly           0x00000001
34
 
#define fieldFlagRequired           0x00000002
35
 
#define fieldFlagNoExport           0x00000004
36
 
#define fieldFlagMultiline          0x00001000
37
 
#define fieldFlagPassword           0x00002000
38
 
#define fieldFlagNoToggleToOff      0x00004000
39
 
#define fieldFlagRadio              0x00008000
40
 
#define fieldFlagPushbutton         0x00010000
41
 
#define fieldFlagCombo              0x00020000
42
 
#define fieldFlagEdit               0x00040000
43
 
#define fieldFlagSort               0x00080000
44
 
#define fieldFlagFileSelect         0x00100000
45
 
#define fieldFlagMultiSelect        0x00200000
46
 
#define fieldFlagDoNotSpellCheck    0x00400000
47
 
#define fieldFlagDoNotScroll        0x00800000
48
 
#define fieldFlagComb               0x01000000
49
 
#define fieldFlagRichText           0x02000000
50
 
#define fieldFlagRadiosInUnison     0x02000000
51
 
#define fieldFlagCommitOnSelChange  0x04000000
52
 
 
53
 
#define fieldQuadLeft   0
54
 
#define fieldQuadCenter 1
55
 
#define fieldQuadRight  2
56
 
 
57
 
// distance of Bezier control point from center for circle approximation
58
 
// = (4 * (sqrt(2) - 1) / 3) * r
59
 
#define bezierCircle 0.55228475
60
 
 
61
 
//------------------------------------------------------------------------
62
 
// AnnotBorderStyle
63
 
//------------------------------------------------------------------------
64
 
 
65
 
AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
66
 
                                   double *dashA, int dashLengthA,
67
 
                                   double rA, double gA, double bA) {
68
 
  type = typeA;
69
 
  width = widthA;
70
 
  dash = dashA;
71
 
  dashLength = dashLengthA;
72
 
  r = rA;
73
 
  g = gA;
74
 
  b = bA;
75
 
}
76
 
 
77
 
AnnotBorderStyle::~AnnotBorderStyle() {
78
 
  if (dash) {
79
 
    gfree(dash);
80
 
  }
81
 
}
82
 
 
83
 
//------------------------------------------------------------------------
84
 
// Annot
85
 
//------------------------------------------------------------------------
86
 
 
87
 
Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref *refA) {
88
 
  Object apObj, asObj, obj1, obj2, obj3;
89
 
  AnnotBorderType borderType;
90
 
  double borderWidth;
91
 
  double *borderDash;
92
 
  int borderDashLength;
93
 
  double borderR, borderG, borderB;
94
 
  double t;
95
 
  int i;
96
 
 
97
 
  ok = gTrue;
98
 
  xref = xrefA;
99
 
  ref = *refA;
100
 
  type = NULL;
101
 
  appearBuf = NULL;
102
 
  borderStyle = NULL;
103
 
 
104
 
  //----- parse the type
105
 
 
106
 
  if (dict->lookup("Subtype", &obj1)->isName()) {
107
 
    type = new GString(obj1.getName());
108
 
  }
109
 
  obj1.free();
110
 
 
111
 
  //----- parse the rectangle
112
 
 
113
 
  if (dict->lookup("Rect", &obj1)->isArray() &&
114
 
      obj1.arrayGetLength() == 4) {
115
 
    xMin = yMin = xMax = yMax = 0;
116
 
    if (obj1.arrayGet(0, &obj2)->isNum()) {
117
 
      xMin = obj2.getNum();
118
 
    }
119
 
    obj2.free();
120
 
    if (obj1.arrayGet(1, &obj2)->isNum()) {
121
 
      yMin = obj2.getNum();
122
 
    }
123
 
    obj2.free();
124
 
    if (obj1.arrayGet(2, &obj2)->isNum()) {
125
 
      xMax = obj2.getNum();
126
 
    }
127
 
    obj2.free();
128
 
    if (obj1.arrayGet(3, &obj2)->isNum()) {
129
 
      yMax = obj2.getNum();
130
 
    }
131
 
    obj2.free();
132
 
    if (xMin > xMax) {
133
 
      t = xMin; xMin = xMax; xMax = t;
134
 
    }
135
 
    if (yMin > yMax) {
136
 
      t = yMin; yMin = yMax; yMax = t;
137
 
    }
138
 
  } else {
139
 
    error(-1, "Bad bounding box for annotation");
140
 
    ok = gFalse;
141
 
  }
142
 
  obj1.free();
143
 
 
144
 
  //----- parse the flags
145
 
 
146
 
  if (dict->lookup("F", &obj1)->isInt()) {
147
 
    flags = obj1.getInt();
148
 
  } else {
149
 
    flags = 0;
150
 
  }
151
 
  obj1.free();
152
 
 
153
 
  //----- parse the border style
154
 
 
155
 
  borderType = annotBorderSolid;
156
 
  borderWidth = 1;
157
 
  borderDash = NULL;
158
 
  borderDashLength = 0;
159
 
  borderR = 0;
160
 
  borderG = 0;
161
 
  borderB = 1;
162
 
  if (dict->lookup("BS", &obj1)->isDict()) {
163
 
    if (obj1.dictLookup("S", &obj2)->isName()) {
164
 
      if (obj2.isName("S")) {
165
 
        borderType = annotBorderSolid;
166
 
      } else if (obj2.isName("D")) {
167
 
        borderType = annotBorderDashed;
168
 
      } else if (obj2.isName("B")) {
169
 
        borderType = annotBorderBeveled;
170
 
      } else if (obj2.isName("I")) {
171
 
        borderType = annotBorderInset;
172
 
      } else if (obj2.isName("U")) {
173
 
        borderType = annotBorderUnderlined;
174
 
      }
175
 
    }
176
 
    obj2.free();
177
 
    if (obj1.dictLookup("W", &obj2)->isNum()) {
178
 
      borderWidth = obj2.getNum();
179
 
    }
180
 
    obj2.free();
181
 
    if (obj1.dictLookup("D", &obj2)->isArray()) {
182
 
      borderDashLength = obj2.arrayGetLength();
183
 
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
184
 
      for (i = 0; i < borderDashLength; ++i) {
185
 
        if (obj2.arrayGet(i, &obj3)->isNum()) {
186
 
          borderDash[i] = obj3.getNum();
187
 
        } else {
188
 
          borderDash[i] = 1;
189
 
        }
190
 
        obj3.free();
191
 
      }
192
 
    }
193
 
    obj2.free();
194
 
  } else {
195
 
    obj1.free();
196
 
    if (dict->lookup("Border", &obj1)->isArray()) {
197
 
      if (obj1.arrayGetLength() >= 3) {
198
 
        if (obj1.arrayGet(2, &obj2)->isNum()) {
199
 
          borderWidth = obj2.getNum();
200
 
        }
201
 
        obj2.free();
202
 
        if (obj1.arrayGetLength() >= 4) {
203
 
          if (obj1.arrayGet(3, &obj2)->isArray()) {
204
 
            borderType = annotBorderDashed;
205
 
            borderDashLength = obj2.arrayGetLength();
206
 
            borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
207
 
            for (i = 0; i < borderDashLength; ++i) {
208
 
              if (obj2.arrayGet(i, &obj3)->isNum()) {
209
 
                borderDash[i] = obj3.getNum();
210
 
              } else {
211
 
                borderDash[i] = 1;
212
 
              }
213
 
              obj3.free();
214
 
            }
215
 
          } else {
216
 
            // Adobe draws no border at all if the last element is of
217
 
            // the wrong type.
218
 
            borderWidth = 0;
219
 
          }
220
 
          obj2.free();
221
 
        }
222
 
      }
223
 
    }
224
 
  }
225
 
  obj1.free();
226
 
  if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
227
 
    if (obj1.arrayGet(0, &obj2)->isNum()) {
228
 
      borderR = obj2.getNum();
229
 
    }
230
 
    obj1.free();
231
 
    if (obj1.arrayGet(1, &obj2)->isNum()) {
232
 
      borderG = obj2.getNum();
233
 
    }
234
 
    obj1.free();
235
 
    if (obj1.arrayGet(2, &obj2)->isNum()) {
236
 
      borderB = obj2.getNum();
237
 
    }
238
 
    obj1.free();
239
 
  }
240
 
  obj1.free();
241
 
  borderStyle = new AnnotBorderStyle(borderType, borderWidth,
242
 
                                     borderDash, borderDashLength,
243
 
                                     borderR, borderG, borderB);
244
 
 
245
 
  //----- get the annotation appearance
246
 
 
247
 
  if (dict->lookup("AP", &apObj)->isDict()) {
248
 
    if (dict->lookup("AS", &asObj)->isName()) {
249
 
      if (apObj.dictLookup("N", &obj1)->isDict()) {
250
 
        if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
251
 
          obj2.copy(&appearance);
252
 
          ok = gTrue;
253
 
        } else {
254
 
          obj2.free();
255
 
          if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
256
 
            obj2.copy(&appearance);
257
 
          }
258
 
        }
259
 
        obj2.free();
260
 
      }
261
 
      obj1.free();
262
 
    } else {
263
 
      if (apObj.dictLookupNF("N", &obj1)->isRef()) {
264
 
        obj1.copy(&appearance);
265
 
      }
266
 
      obj1.free();
267
 
    }
268
 
    asObj.free();
269
 
  }
270
 
  apObj.free();
271
 
}
272
 
 
273
 
Annot::~Annot() {
274
 
  if (type) {
275
 
    delete type;
276
 
  }
277
 
  appearance.free();
278
 
  if (appearBuf) {
279
 
    delete appearBuf;
280
 
  }
281
 
  if (borderStyle) {
282
 
    delete borderStyle;
283
 
  }
284
 
}
285
 
 
286
 
void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
287
 
  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
288
 
  Dict *mkDict;
289
 
  MemStream *appearStream;
290
 
  GfxFontDict *fontDict;
291
 
  GBool hasCaption;
292
 
  double w, dx, dy, r;
293
 
  double *dash;
294
 
  GString *caption, *da;
295
 
  GString **text;
296
 
  GBool *selection;
297
 
  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
298
 
 
299
 
  // must be a Widget annotation
300
 
  if (type->cmp("Widget")) {
301
 
    return;
302
 
  }
303
 
 
304
 
  appearBuf = new GString();
305
 
 
306
 
  // get the appearance characteristics (MK) dictionary
307
 
  if (annot->lookup("MK", &mkObj)->isDict()) {
308
 
    mkDict = mkObj.getDict();
309
 
  } else {
310
 
    mkDict = NULL;
311
 
  }
312
 
 
313
 
  // draw the background
314
 
  if (mkDict) {
315
 
    if (mkDict->lookup("BG", &obj1)->isArray() &&
316
 
        obj1.arrayGetLength() > 0) {
317
 
      setColor(obj1.getArray(), gTrue, 0);
318
 
      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
319
 
                         xMax - xMin, yMax - yMin);
320
 
    }
321
 
    obj1.free();
322
 
  }
323
 
 
324
 
  // get the field type
325
 
  fieldLookup(field, "FT", &ftObj);
326
 
 
327
 
  // get the field flags (Ff) value
328
 
  if (fieldLookup(field, "Ff", &obj1)->isInt()) {
329
 
    ff = obj1.getInt();
330
 
  } else {
331
 
    ff = 0;
332
 
  }
333
 
  obj1.free();
334
 
 
335
 
  // draw the border
336
 
  if (mkDict) {
337
 
    w = borderStyle->getWidth();
338
 
    if (w > 0) {
339
 
      mkDict->lookup("BC", &obj1);
340
 
      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
341
 
        mkDict->lookup("BG", &obj1);
342
 
      }
343
 
      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
344
 
        dx = xMax - xMin;
345
 
        dy = yMax - yMin;
346
 
 
347
 
        // radio buttons with no caption have a round border
348
 
        hasCaption = mkDict->lookup("CA", &obj2)->isString();
349
 
        obj2.free();
350
 
        if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
351
 
          r = 0.5 * (dx < dy ? dx : dy);
352
 
          switch (borderStyle->getType()) {
353
 
          case annotBorderDashed:
354
 
            appearBuf->append("[");
355
 
            borderStyle->getDash(&dash, &dashLength);
356
 
            for (i = 0; i < dashLength; ++i) {
357
 
              appearBuf->appendf(" {0:.2f}", dash[i]);
358
 
            }
359
 
            appearBuf->append("] 0 d\n");
360
 
            // fall through to the solid case
361
 
          case annotBorderSolid:
362
 
          case annotBorderUnderlined:
363
 
            appearBuf->appendf("{0:.2f} w\n", w);
364
 
            setColor(obj1.getArray(), gFalse, 0);
365
 
            drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
366
 
            break;
367
 
          case annotBorderBeveled:
368
 
          case annotBorderInset:
369
 
            appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
370
 
            setColor(obj1.getArray(), gFalse, 0);
371
 
            drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
372
 
            setColor(obj1.getArray(), gFalse,
373
 
                     borderStyle->getType() == annotBorderBeveled ? 1 : -1);
374
 
            drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
375
 
            setColor(obj1.getArray(), gFalse,
376
 
                     borderStyle->getType() == annotBorderBeveled ? -1 : 1);
377
 
            drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
378
 
            break;
379
 
          }
380
 
 
381
 
        } else {
382
 
          switch (borderStyle->getType()) {
383
 
          case annotBorderDashed:
384
 
            appearBuf->append("[");
385
 
            borderStyle->getDash(&dash, &dashLength);
386
 
            for (i = 0; i < dashLength; ++i) {
387
 
              appearBuf->appendf(" {0:.2f}", dash[i]);
388
 
            }
389
 
            appearBuf->append("] 0 d\n");
390
 
            // fall through to the solid case
391
 
          case annotBorderSolid:
392
 
            appearBuf->appendf("{0:.2f} w\n", w);
393
 
            setColor(obj1.getArray(), gFalse, 0);
394
 
            appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
395
 
                               0.5 * w, dx - w, dy - w);
396
 
            break;
397
 
          case annotBorderBeveled:
398
 
          case annotBorderInset:
399
 
            setColor(obj1.getArray(), gTrue,
400
 
                     borderStyle->getType() == annotBorderBeveled ? 1 : -1);
401
 
            appearBuf->append("0 0 m\n");
402
 
            appearBuf->appendf("0 {0:.2f} l\n", dy);
403
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
404
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
405
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
406
 
            appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
407
 
            appearBuf->append("f\n");
408
 
            setColor(obj1.getArray(), gTrue,
409
 
                     borderStyle->getType() == annotBorderBeveled ? -1 : 1);
410
 
            appearBuf->append("0 0 m\n");
411
 
            appearBuf->appendf("{0:.2f} 0 l\n", dx);
412
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
413
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
414
 
            appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
415
 
            appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
416
 
            appearBuf->append("f\n");
417
 
            break;
418
 
          case annotBorderUnderlined:
419
 
            appearBuf->appendf("{0:.2f} w\n", w);
420
 
            setColor(obj1.getArray(), gFalse, 0);
421
 
            appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
422
 
            break;
423
 
          }
424
 
 
425
 
          // clip to the inside of the border
426
 
          appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
427
 
                             w, dx - 2 * w, dy - 2 * w);
428
 
        }
429
 
      }
430
 
      obj1.free();
431
 
    }
432
 
  }
433
 
 
434
 
  // get the resource dictionary
435
 
  acroForm->lookup("DR", &drObj);
436
 
 
437
 
  // build the font dictionary
438
 
  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
439
 
    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
440
 
  } else {
441
 
    fontDict = NULL;
442
 
  }
443
 
  obj1.free();
444
 
 
445
 
  // get the default appearance string
446
 
  if (fieldLookup(field, "DA", &obj1)->isNull()) {
447
 
    obj1.free();
448
 
    acroForm->lookup("DA", &obj1);
449
 
  }
450
 
  if (obj1.isString()) {
451
 
    da = obj1.getString()->copy();
452
 
  } else {
453
 
    da = NULL;
454
 
  }
455
 
  obj1.free();
456
 
 
457
 
  // draw the field contents
458
 
  if (ftObj.isName("Btn")) {
459
 
    caption = NULL;
460
 
    if (mkDict) {
461
 
      if (mkDict->lookup("CA", &obj1)->isString()) {
462
 
        caption = obj1.getString()->copy();
463
 
      }
464
 
      obj1.free();
465
 
    }
466
 
    // radio button
467
 
    if (ff & fieldFlagRadio) {
468
 
      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
469
 
      if (fieldLookup(field, "V", &obj1)->isName()) {
470
 
        if (annot->lookup("AS", &obj2)->isName(obj1.getName())) {
471
 
          if (caption) {
472
 
            drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
473
 
                     gFalse, gTrue);
474
 
          } else {
475
 
            if (mkDict) {
476
 
              if (mkDict->lookup("BC", &obj3)->isArray() &&
477
 
                  obj3.arrayGetLength() > 0) {
478
 
                dx = xMax - xMin;
479
 
                dy = yMax - yMin;
480
 
                setColor(obj3.getArray(), gTrue, 0);
481
 
                drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
482
 
                           gTrue);
483
 
              }
484
 
              obj3.free();
485
 
            }
486
 
          }
487
 
        }
488
 
        obj2.free();
489
 
      }
490
 
      obj1.free();
491
 
    // pushbutton
492
 
    } else if (ff & fieldFlagPushbutton) {
493
 
      if (caption) {
494
 
        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
495
 
                 gFalse, gFalse);
496
 
      }
497
 
    // checkbox
498
 
    } else {
499
 
      // According to the PDF spec the off state must be named "Off",
500
 
      // and the on state can be named anything, but Acrobat apparently
501
 
      // looks for "Yes" and treats anything else as off.
502
 
      if (fieldLookup(field, "V", &obj1)->isName("Yes")) {
503
 
        if (!caption) {
504
 
          caption = new GString("3"); // ZapfDingbats checkmark
505
 
        }
506
 
        drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
507
 
                 gFalse, gTrue);
508
 
      }
509
 
      obj1.free();
510
 
    }
511
 
    if (caption) {
512
 
      delete caption;
513
 
    }
514
 
  } else if (ftObj.isName("Tx")) {
515
 
    //~ value strings can be Unicode
516
 
    if (fieldLookup(field, "V", &obj1)->isString()) {
517
 
      if (fieldLookup(field, "Q", &obj2)->isInt()) {
518
 
        quadding = obj2.getInt();
519
 
      } else {
520
 
        quadding = fieldQuadLeft;
521
 
      }
522
 
      obj2.free();
523
 
      comb = 0;
524
 
      if (ff & fieldFlagComb) {
525
 
        if (fieldLookup(field, "MaxLen", &obj2)->isInt()) {
526
 
          comb = obj2.getInt();
527
 
        }
528
 
        obj2.free();
529
 
      }
530
 
      drawText(obj1.getString(), da, fontDict,
531
 
               ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse);
532
 
    }
533
 
    obj1.free();
534
 
  } else if (ftObj.isName("Ch")) {
535
 
    //~ value/option strings can be Unicode
536
 
    if (fieldLookup(field, "Q", &obj1)->isInt()) {
537
 
      quadding = obj1.getInt();
538
 
    } else {
539
 
      quadding = fieldQuadLeft;
540
 
    }
541
 
    obj1.free();
542
 
    // combo box
543
 
    if (ff & fieldFlagCombo) {
544
 
      if (fieldLookup(field, "V", &obj1)->isString()) {
545
 
        drawText(obj1.getString(), da, fontDict,
546
 
                 gFalse, 0, quadding, gTrue, gFalse);
547
 
        //~ Acrobat draws a popup icon on the right side
548
 
      }
549
 
      obj1.free();
550
 
    // list box
551
 
    } else {
552
 
      if (field->lookup("Opt", &obj1)->isArray()) {
553
 
        nOptions = obj1.arrayGetLength();
554
 
        // get the option text
555
 
        text = (GString **)gmallocn(nOptions, sizeof(GString *));
556
 
        for (i = 0; i < nOptions; ++i) {
557
 
          text[i] = NULL;
558
 
          obj1.arrayGet(i, &obj2);
559
 
          if (obj2.isString()) {
560
 
            text[i] = obj2.getString()->copy();
561
 
          } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
562
 
            if (obj2.arrayGet(1, &obj3)->isString()) {
563
 
              text[i] = obj3.getString()->copy();
564
 
            }
565
 
            obj3.free();
566
 
          }
567
 
          obj2.free();
568
 
          if (!text[i]) {
569
 
            text[i] = new GString();
570
 
          }
571
 
        }
572
 
        // get the selected option(s)
573
 
        selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
574
 
        //~ need to use the I field in addition to the V field
575
 
        fieldLookup(field, "V", &obj2);
576
 
        for (i = 0; i < nOptions; ++i) {
577
 
          selection[i] = gFalse;
578
 
          if (obj2.isString()) {
579
 
            if (!obj2.getString()->cmp(text[i])) {
580
 
              selection[i] = gTrue;
581
 
            }
582
 
          } else if (obj2.isArray()) {
583
 
            for (j = 0; j < obj2.arrayGetLength(); ++j) {
584
 
              if (obj2.arrayGet(j, &obj3)->isString() &&
585
 
                  !obj3.getString()->cmp(text[i])) {
586
 
                selection[i] = gTrue;
587
 
              }
588
 
              obj3.free();
589
 
            }
590
 
          }
591
 
        }
592
 
        obj2.free();
593
 
        // get the top index
594
 
        if (field->lookup("TI", &obj2)->isInt()) {
595
 
          topIdx = obj2.getInt();
596
 
        } else {
597
 
          topIdx = 0;
598
 
        }
599
 
        obj2.free();
600
 
        // draw the text
601
 
        drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
602
 
        for (i = 0; i < nOptions; ++i) {
603
 
          delete text[i];
604
 
        }
605
 
        gfree(text);
606
 
        gfree(selection);
607
 
      }
608
 
      obj1.free();
609
 
    }
610
 
  } else if (ftObj.isName("Sig")) {
611
 
    //~unimp
612
 
  } else {
613
 
    error(-1, "Unknown field type");
614
 
  }
615
 
 
616
 
  if (da) {
617
 
    delete da;
618
 
  }
619
 
 
620
 
  // build the appearance stream dictionary
621
 
  appearDict.initDict(xref);
622
 
  appearDict.dictAdd(copyString("Length"),
623
 
                     obj1.initInt(appearBuf->getLength()));
624
 
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
625
 
  obj1.initArray(xref);
626
 
  obj1.arrayAdd(obj2.initReal(0));
627
 
  obj1.arrayAdd(obj2.initReal(0));
628
 
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
629
 
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
630
 
  appearDict.dictAdd(copyString("BBox"), &obj1);
631
 
 
632
 
  // set the resource dictionary
633
 
  if (drObj.isDict()) {
634
 
    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
635
 
  }
636
 
  drObj.free();
637
 
 
638
 
  // build the appearance stream
639
 
  appearStream = new MemStream(appearBuf->getCString(), 0,
640
 
                               appearBuf->getLength(), &appearDict);
641
 
  appearance.free();
642
 
  appearance.initStream(appearStream);
643
 
 
644
 
  if (fontDict) {
645
 
    delete fontDict;
646
 
  }
647
 
  ftObj.free();
648
 
  mkObj.free();
649
 
}
650
 
 
651
 
// Set the current fill or stroke color, based on <a> (which should
652
 
// have 1, 3, or 4 elements).  If <adjust> is +1, color is brightened;
653
 
// if <adjust> is -1, color is darkened; otherwise color is not
654
 
// modified.
655
 
void Annot::setColor(Array *a, GBool fill, int adjust) {
656
 
  Object obj1;
657
 
  double color[4];
658
 
  int nComps, i;
659
 
 
660
 
  nComps = a->getLength();
661
 
  if (nComps > 4) {
662
 
    nComps = 4;
663
 
  }
664
 
  for (i = 0; i < nComps && i < 4; ++i) {
665
 
    if (a->get(i, &obj1)->isNum()) {
666
 
      color[i] = obj1.getNum();
667
 
    } else {
668
 
      color[i] = 0;
669
 
    }
670
 
    obj1.free();
671
 
  }
672
 
  if (nComps == 4) {
673
 
    adjust = -adjust;
674
 
  }
675
 
  if (adjust > 0) {
676
 
    for (i = 0; i < nComps; ++i) {
677
 
      color[i] = 0.5 * color[i] + 0.5;
678
 
    }
679
 
  } else if (adjust < 0) {
680
 
    for (i = 0; i < nComps; ++i) {
681
 
      color[i] = 0.5 * color[i];
682
 
    }
683
 
  }
684
 
  if (nComps == 4) {
685
 
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
686
 
                       color[0], color[1], color[2], color[3],
687
 
                       fill ? 'k' : 'K');
688
 
  } else if (nComps == 3) {
689
 
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
690
 
                       color[0], color[1], color[2],
691
 
                       fill ? "rg" : "RG");
692
 
  } else {
693
 
    appearBuf->appendf("{0:.2f} {1:c}\n",
694
 
                       color[0],
695
 
                       fill ? 'g' : 'G');
696
 
  }
697
 
}
698
 
 
699
 
// Draw the variable text or caption for a field.
700
 
void Annot::drawText(GString *text, GString *da, GfxFontDict *fontDict,
701
 
                     GBool multiline, int comb, int quadding,
702
 
                     GBool txField, GBool forceZapfDingbats) {
703
 
  GList *daToks;
704
 
  GString *tok;
705
 
  GfxFont *font;
706
 
  double fontSize, fontSize2, border, x, xPrev, y, w, w2, wMax;
707
 
  int tfPos, tmPos, i, j, k, c;
708
 
 
709
 
  //~ if there is no MK entry, this should use the existing content stream,
710
 
  //~ and only replace the marked content portion of it
711
 
  //~ (this is only relevant for Tx fields)
712
 
 
713
 
  // parse the default appearance string
714
 
  tfPos = tmPos = -1;
715
 
  if (da) {
716
 
    daToks = new GList();
717
 
    i = 0;
718
 
    while (i < da->getLength()) {
719
 
      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
720
 
        ++i;
721
 
      }
722
 
      if (i < da->getLength()) {
723
 
        for (j = i + 1;
724
 
             j < da->getLength() && !Lexer::isSpace(da->getChar(j));
725
 
             ++j) ;
726
 
        daToks->append(new GString(da, i, j - i));
727
 
        i = j;
728
 
      }
729
 
    }
730
 
    for (i = 2; i < daToks->getLength(); ++i) {
731
 
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
732
 
        tfPos = i - 2;
733
 
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
734
 
        tmPos = i - 6;
735
 
      }
736
 
    }
737
 
  } else {
738
 
    daToks = NULL;
739
 
  }
740
 
 
741
 
  // force ZapfDingbats
742
 
  //~ this should create the font if needed (?)
743
 
  if (forceZapfDingbats) {
744
 
    if (tfPos >= 0) {
745
 
      tok = (GString *)daToks->get(tfPos);
746
 
      if (tok->cmp("/ZaDb")) {
747
 
        tok->clear();
748
 
        tok->append("/ZaDb");
749
 
      }
750
 
    }
751
 
  }
752
 
 
753
 
  // get the font and font size
754
 
  font = NULL;
755
 
  fontSize = 0;
756
 
  if (tfPos >= 0) {
757
 
    tok = (GString *)daToks->get(tfPos);
758
 
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
759
 
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
760
 
        error(-1, "Unknown font in field's DA string");
761
 
      }
762
 
    } else {
763
 
      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
764
 
    }
765
 
    tok = (GString *)daToks->get(tfPos + 1);
766
 
    fontSize = atof(tok->getCString());
767
 
  } else {
768
 
    error(-1, "Missing 'Tf' operator in field's DA string");
769
 
  }
770
 
 
771
 
  // get the border width
772
 
  border = borderStyle->getWidth();
773
 
 
774
 
  // setup
775
 
  if (txField) {
776
 
    appearBuf->append("/Tx BMC\n");
777
 
  }
778
 
  appearBuf->append("q\n");
779
 
  appearBuf->append("BT\n");
780
 
 
781
 
  // multi-line text
782
 
  if (multiline) {
783
 
    // note: the comb flag is ignored in multiline mode
784
 
 
785
 
    wMax = xMax - xMin - 2 * border - 4;
786
 
 
787
 
    // compute font autosize
788
 
    if (fontSize == 0) {
789
 
      for (fontSize = 20; fontSize > 1; --fontSize) {
790
 
        y = yMax - yMin;
791
 
        w2 = 0;
792
 
        i = 0;
793
 
        while (i < text->getLength()) {
794
 
          getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
795
 
          if (w > w2) {
796
 
            w2 = w;
797
 
          }
798
 
          i = k;
799
 
          y -= fontSize;
800
 
        }
801
 
        // approximate the descender for the last line
802
 
        if (y >= 0.33 * fontSize) {
803
 
          break;
804
 
        }
805
 
      }
806
 
      if (tfPos >= 0) {
807
 
        tok = (GString *)daToks->get(tfPos + 1);
808
 
        tok->clear();
809
 
        tok->appendf("{0:.2f}", fontSize);
810
 
      }
811
 
    }
812
 
 
813
 
    // starting y coordinate
814
 
    // (note: each line of text starts with a Td operator that moves
815
 
    // down a line)
816
 
    y = yMax - yMin;
817
 
 
818
 
    // set the font matrix
819
 
    if (tmPos >= 0) {
820
 
      tok = (GString *)daToks->get(tmPos + 4);
821
 
      tok->clear();
822
 
      tok->append('0');
823
 
      tok = (GString *)daToks->get(tmPos + 5);
824
 
      tok->clear();
825
 
      tok->appendf("{0:.2f}", y);
826
 
    }
827
 
 
828
 
    // write the DA string
829
 
    if (daToks) {
830
 
      for (i = 0; i < daToks->getLength(); ++i) {
831
 
        appearBuf->append((GString *)daToks->get(i))->append(' ');
832
 
      }
833
 
    }
834
 
 
835
 
    // write the font matrix (if not part of the DA string)
836
 
    if (tmPos < 0) {
837
 
      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
838
 
    }
839
 
 
840
 
    // write a series of lines of text
841
 
    i = 0;
842
 
    xPrev = 0;
843
 
    while (i < text->getLength()) {
844
 
 
845
 
      getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
846
 
 
847
 
      // compute text start position
848
 
      switch (quadding) {
849
 
      case fieldQuadLeft:
850
 
      default:
851
 
        x = border + 2;
852
 
        break;
853
 
      case fieldQuadCenter:
854
 
        x = (xMax - xMin - w) / 2;
855
 
        break;
856
 
      case fieldQuadRight:
857
 
        x = xMax - xMin - border - 2 - w;
858
 
        break;
859
 
      }
860
 
 
861
 
      // draw the line
862
 
      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
863
 
      appearBuf->append('(');
864
 
      for (; i < j; ++i) {
865
 
        c = text->getChar(i) & 0xff;
866
 
        if (c == '(' || c == ')' || c == '\\') {
867
 
          appearBuf->append('\\');
868
 
          appearBuf->append(c);
869
 
        } else if (c < 0x20 || c >= 0x80) {
870
 
          appearBuf->appendf("\\{0:03o}", c);
871
 
        } else {
872
 
          appearBuf->append(c);
873
 
        }
874
 
      }
875
 
      appearBuf->append(") Tj\n");
876
 
 
877
 
      // next line
878
 
      i = k;
879
 
      xPrev = x;
880
 
    }
881
 
 
882
 
  // single-line text
883
 
  } else {
884
 
    //~ replace newlines with spaces? - what does Acrobat do?
885
 
 
886
 
    // comb formatting
887
 
    if (comb > 0) {
888
 
 
889
 
      // compute comb spacing
890
 
      w = (xMax - xMin - 2 * border) / comb;
891
 
 
892
 
      // compute font autosize
893
 
      if (fontSize == 0) {
894
 
        fontSize = yMax - yMin - 2 * border;
895
 
        if (w < fontSize) {
896
 
          fontSize = w;
897
 
        }
898
 
        fontSize = floor(fontSize);
899
 
        if (tfPos >= 0) {
900
 
          tok = (GString *)daToks->get(tfPos + 1);
901
 
          tok->clear();
902
 
          tok->appendf("{0:.2f}", fontSize);
903
 
        }
904
 
      }
905
 
 
906
 
      // compute text start position
907
 
      switch (quadding) {
908
 
      case fieldQuadLeft:
909
 
      default:
910
 
        x = border + 2;
911
 
        break;
912
 
      case fieldQuadCenter:
913
 
        x = border + 2 + 0.5 * (comb - text->getLength()) * w;
914
 
        break;
915
 
      case fieldQuadRight:
916
 
        x = border + 2 + (comb - text->getLength()) * w;
917
 
        break;
918
 
      }
919
 
      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;
920
 
 
921
 
      // set the font matrix
922
 
      if (tmPos >= 0) {
923
 
        tok = (GString *)daToks->get(tmPos + 4);
924
 
        tok->clear();
925
 
        tok->appendf("{0:.2f}", x);
926
 
        tok = (GString *)daToks->get(tmPos + 5);
927
 
        tok->clear();
928
 
        tok->appendf("{0:.2f}", y);
929
 
      }
930
 
 
931
 
      // write the DA string
932
 
      if (daToks) {
933
 
        for (i = 0; i < daToks->getLength(); ++i) {
934
 
          appearBuf->append((GString *)daToks->get(i))->append(' ');
935
 
        }
936
 
      }
937
 
 
938
 
      // write the font matrix (if not part of the DA string)
939
 
      if (tmPos < 0) {
940
 
        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
941
 
      }
942
 
 
943
 
      // write the text string
944
 
      //~ this should center (instead of left-justify) each character within
945
 
      //~     its comb cell
946
 
      for (i = 0; i < text->getLength(); ++i) {
947
 
        if (i > 0) {
948
 
          appearBuf->appendf("{0:.2f} 0 Td\n", w);
949
 
        }
950
 
        appearBuf->append('(');
951
 
        c = text->getChar(i) & 0xff;
952
 
        if (c == '(' || c == ')' || c == '\\') {
953
 
          appearBuf->append('\\');
954
 
          appearBuf->append(c);
955
 
        } else if (c < 0x20 || c >= 0x80) {
956
 
          appearBuf->appendf("{0:.2f} 0 Td\n", w);
957
 
        } else {
958
 
          appearBuf->append(c);
959
 
        }
960
 
        appearBuf->append(") Tj\n");
961
 
      }
962
 
 
963
 
    // regular (non-comb) formatting
964
 
    } else {
965
 
 
966
 
      // compute string width
967
 
      if (font && !font->isCIDFont()) {
968
 
        w = 0;
969
 
        for (i = 0; i < text->getLength(); ++i) {
970
 
          w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
971
 
        }
972
 
      } else {
973
 
        // otherwise, make a crude estimate
974
 
        w = text->getLength() * 0.5;
975
 
      }
976
 
 
977
 
      // compute font autosize
978
 
      if (fontSize == 0) {
979
 
        fontSize = yMax - yMin - 2 * border;
980
 
        fontSize2 = (xMax - xMin - 4 - 2 * border) / w;
981
 
        if (fontSize2 < fontSize) {
982
 
          fontSize = fontSize2;
983
 
        }
984
 
        fontSize = floor(fontSize);
985
 
        if (tfPos >= 0) {
986
 
          tok = (GString *)daToks->get(tfPos + 1);
987
 
          tok->clear();
988
 
          tok->appendf("{0:.2f}", fontSize);
989
 
        }
990
 
      }
991
 
 
992
 
      // compute text start position
993
 
      w *= fontSize;
994
 
      switch (quadding) {
995
 
      case fieldQuadLeft:
996
 
      default:
997
 
        x = border + 2;
998
 
        break;
999
 
      case fieldQuadCenter:
1000
 
        x = (xMax - xMin - w) / 2;
1001
 
        break;
1002
 
      case fieldQuadRight:
1003
 
        x = xMax - xMin - border - 2 - w;
1004
 
        break;
1005
 
      }
1006
 
      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;
1007
 
 
1008
 
      // set the font matrix
1009
 
      if (tmPos >= 0) {
1010
 
        tok = (GString *)daToks->get(tmPos + 4);
1011
 
        tok->clear();
1012
 
        tok->appendf("{0:.2f}", x);
1013
 
        tok = (GString *)daToks->get(tmPos + 5);
1014
 
        tok->clear();
1015
 
        tok->appendf("{0:.2f}", y);
1016
 
      }
1017
 
 
1018
 
      // write the DA string
1019
 
      if (daToks) {
1020
 
        for (i = 0; i < daToks->getLength(); ++i) {
1021
 
          appearBuf->append((GString *)daToks->get(i))->append(' ');
1022
 
        }
1023
 
      }
1024
 
 
1025
 
      // write the font matrix (if not part of the DA string)
1026
 
      if (tmPos < 0) {
1027
 
        appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
1028
 
      }
1029
 
 
1030
 
      // write the text string
1031
 
      appearBuf->append('(');
1032
 
      for (i = 0; i < text->getLength(); ++i) {
1033
 
        c = text->getChar(i) & 0xff;
1034
 
        if (c == '(' || c == ')' || c == '\\') {
1035
 
          appearBuf->append('\\');
1036
 
          appearBuf->append(c);
1037
 
        } else if (c < 0x20 || c >= 0x80) {
1038
 
          appearBuf->appendf("\\{0:03o}", c);
1039
 
        } else {
1040
 
          appearBuf->append(c);
1041
 
        }
1042
 
      }
1043
 
      appearBuf->append(") Tj\n");
1044
 
    }
1045
 
  }
1046
 
 
1047
 
  // cleanup
1048
 
  appearBuf->append("ET\n");
1049
 
  appearBuf->append("Q\n");
1050
 
  if (txField) {
1051
 
    appearBuf->append("EMC\n");
1052
 
  }
1053
 
 
1054
 
  if (daToks) {
1055
 
    deleteGList(daToks, GString);
1056
 
  }
1057
 
}
1058
 
 
1059
 
// Draw the variable text or caption for a field.
1060
 
void Annot::drawListBox(GString **text, GBool *selection,
1061
 
                        int nOptions, int topIdx,
1062
 
                        GString *da, GfxFontDict *fontDict, GBool quadding) {
1063
 
  GList *daToks;
1064
 
  GString *tok;
1065
 
  GfxFont *font;
1066
 
  double fontSize, fontSize2, border, x, y, w, wMax;
1067
 
  int tfPos, tmPos, i, j, c;
1068
 
 
1069
 
  //~ if there is no MK entry, this should use the existing content stream,
1070
 
  //~ and only replace the marked content portion of it
1071
 
  //~ (this is only relevant for Tx fields)
1072
 
 
1073
 
  // parse the default appearance string
1074
 
  tfPos = tmPos = -1;
1075
 
  if (da) {
1076
 
    daToks = new GList();
1077
 
    i = 0;
1078
 
    while (i < da->getLength()) {
1079
 
      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
1080
 
        ++i;
1081
 
      }
1082
 
      if (i < da->getLength()) {
1083
 
        for (j = i + 1;
1084
 
             j < da->getLength() && !Lexer::isSpace(da->getChar(j));
1085
 
             ++j) ;
1086
 
        daToks->append(new GString(da, i, j - i));
1087
 
        i = j;
1088
 
      }
1089
 
    }
1090
 
    for (i = 2; i < daToks->getLength(); ++i) {
1091
 
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
1092
 
        tfPos = i - 2;
1093
 
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
1094
 
        tmPos = i - 6;
1095
 
      }
1096
 
    }
1097
 
  } else {
1098
 
    daToks = NULL;
1099
 
  }
1100
 
 
1101
 
  // get the font and font size
1102
 
  font = NULL;
1103
 
  fontSize = 0;
1104
 
  if (tfPos >= 0) {
1105
 
    tok = (GString *)daToks->get(tfPos);
1106
 
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
1107
 
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
1108
 
        error(-1, "Unknown font in field's DA string");
1109
 
      }
1110
 
    } else {
1111
 
      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
1112
 
    }
1113
 
    tok = (GString *)daToks->get(tfPos + 1);
1114
 
    fontSize = atof(tok->getCString());
1115
 
  } else {
1116
 
    error(-1, "Missing 'Tf' operator in field's DA string");
1117
 
  }
1118
 
 
1119
 
  // get the border width
1120
 
  border = borderStyle->getWidth();
1121
 
 
1122
 
  // compute font autosize
1123
 
  if (fontSize == 0) {
1124
 
    wMax = 0;
1125
 
    for (i = 0; i < nOptions; ++i) {
1126
 
      if (font && !font->isCIDFont()) {
1127
 
        w = 0;
1128
 
        for (j = 0; j < text[i]->getLength(); ++j) {
1129
 
          w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
1130
 
        }
1131
 
      } else {
1132
 
        // otherwise, make a crude estimate
1133
 
        w = text[i]->getLength() * 0.5;
1134
 
      }
1135
 
      if (w > wMax) {
1136
 
        wMax = w;
1137
 
      }
1138
 
    }
1139
 
    fontSize = yMax - yMin - 2 * border;
1140
 
    fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
1141
 
    if (fontSize2 < fontSize) {
1142
 
      fontSize = fontSize2;
1143
 
    }
1144
 
    fontSize = floor(fontSize);
1145
 
    if (tfPos >= 0) {
1146
 
      tok = (GString *)daToks->get(tfPos + 1);
1147
 
      tok->clear();
1148
 
      tok->appendf("{0:.2f}", fontSize);
1149
 
    }
1150
 
  }
1151
 
 
1152
 
  // draw the text
1153
 
  y = yMax - yMin - 1.1 * fontSize;
1154
 
  for (i = topIdx; i < nOptions; ++i) {
1155
 
 
1156
 
    // setup
1157
 
    appearBuf->append("q\n");
1158
 
 
1159
 
    // draw the background if selected
1160
 
    if (selection[i]) {
1161
 
      appearBuf->append("0 g f\n");
1162
 
      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
1163
 
              border,
1164
 
              y - 0.2 * fontSize,
1165
 
              xMax - xMin - 2 * border,
1166
 
              1.1 * fontSize);
1167
 
    }
1168
 
 
1169
 
    // setup
1170
 
    appearBuf->append("BT\n");
1171
 
 
1172
 
    // compute string width
1173
 
    if (font && !font->isCIDFont()) {
1174
 
      w = 0;
1175
 
      for (j = 0; j < text[i]->getLength(); ++j) {
1176
 
        w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
1177
 
      }
1178
 
    } else {
1179
 
      // otherwise, make a crude estimate
1180
 
      w = text[i]->getLength() * 0.5;
1181
 
    }
1182
 
 
1183
 
    // compute text start position
1184
 
    w *= fontSize;
1185
 
    switch (quadding) {
1186
 
    case fieldQuadLeft:
1187
 
    default:
1188
 
      x = border + 2;
1189
 
      break;
1190
 
    case fieldQuadCenter:
1191
 
      x = (xMax - xMin - w) / 2;
1192
 
      break;
1193
 
    case fieldQuadRight:
1194
 
      x = xMax - xMin - border - 2 - w;
1195
 
      break;
1196
 
    }
1197
 
 
1198
 
    // set the font matrix
1199
 
    if (tmPos >= 0) {
1200
 
      tok = (GString *)daToks->get(tmPos + 4);
1201
 
      tok->clear();
1202
 
      tok->appendf("{0:.2f}", x);
1203
 
      tok = (GString *)daToks->get(tmPos + 5);
1204
 
      tok->clear();
1205
 
      tok->appendf("{0:.2f}", y);
1206
 
    }
1207
 
 
1208
 
    // write the DA string
1209
 
    if (daToks) {
1210
 
      for (j = 0; j < daToks->getLength(); ++j) {
1211
 
        appearBuf->append((GString *)daToks->get(j))->append(' ');
1212
 
      }
1213
 
    }
1214
 
 
1215
 
    // write the font matrix (if not part of the DA string)
1216
 
    if (tmPos < 0) {
1217
 
      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
1218
 
    }
1219
 
 
1220
 
    // change the text color if selected
1221
 
    if (selection[i]) {
1222
 
      appearBuf->append("1 g\n");
1223
 
    }
1224
 
 
1225
 
    // write the text string
1226
 
    appearBuf->append('(');
1227
 
    for (j = 0; j < text[i]->getLength(); ++j) {
1228
 
      c = text[i]->getChar(j) & 0xff;
1229
 
      if (c == '(' || c == ')' || c == '\\') {
1230
 
        appearBuf->append('\\');
1231
 
        appearBuf->append(c);
1232
 
      } else if (c < 0x20 || c >= 0x80) {
1233
 
        appearBuf->appendf("\\{0:03o}", c);
1234
 
      } else {
1235
 
        appearBuf->append(c);
1236
 
      }
1237
 
    }
1238
 
    appearBuf->append(") Tj\n");
1239
 
 
1240
 
    // cleanup
1241
 
    appearBuf->append("ET\n");
1242
 
    appearBuf->append("Q\n");
1243
 
 
1244
 
    // next line
1245
 
    y -= 1.1 * fontSize;
1246
 
  }
1247
 
 
1248
 
  if (daToks) {
1249
 
    deleteGList(daToks, GString);
1250
 
  }
1251
 
}
1252
 
 
1253
 
// Figure out how much text will fit on the next line.  Returns:
1254
 
// *end = one past the last character to be included
1255
 
// *width = width of the characters start .. end-1
1256
 
// *next = index of first character on the following line
1257
 
void Annot::getNextLine(GString *text, int start,
1258
 
                        GfxFont *font, double fontSize, double wMax,
1259
 
                        int *end, double *width, int *next) {
1260
 
  double w, dw;
1261
 
  int j, k, c;
1262
 
 
1263
 
  // figure out how much text will fit on the line
1264
 
  //~ what does Adobe do with tabs?
1265
 
  w = 0;
1266
 
  for (j = start; j < text->getLength() && w <= wMax; ++j) {
1267
 
    c = text->getChar(j) & 0xff;
1268
 
    if (c == 0x0a || c == 0x0d) {
1269
 
      break;
1270
 
    }
1271
 
    if (font && !font->isCIDFont()) {
1272
 
      dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize;
1273
 
    } else {
1274
 
      // otherwise, make a crude estimate
1275
 
      dw = 0.5 * fontSize;
1276
 
    }
1277
 
    w += dw;
1278
 
  }
1279
 
  if (w > wMax) {
1280
 
    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
1281
 
    for (; k > start && text->getChar(k-1) == ' '; --k) ;
1282
 
    if (k > start) {
1283
 
      j = k;
1284
 
    }
1285
 
    if (j == start) {
1286
 
      // handle the pathological case where the first character is
1287
 
      // too wide to fit on the line all by itself
1288
 
      j = start + 1;
1289
 
    }
1290
 
  }
1291
 
  *end = j;
1292
 
 
1293
 
  // compute the width
1294
 
  w = 0;
1295
 
  for (k = start; k < j; ++k) {
1296
 
    if (font && !font->isCIDFont()) {
1297
 
      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
1298
 
    } else {
1299
 
      // otherwise, make a crude estimate
1300
 
      dw = 0.5 * fontSize;
1301
 
    }
1302
 
    w += dw;
1303
 
  }
1304
 
  *width = w;
1305
 
 
1306
 
  // next line
1307
 
  while (j < text->getLength() && text->getChar(j) == ' ') {
1308
 
    ++j;
1309
 
  }
1310
 
  if (j < text->getLength() && text->getChar(j) == 0x0d) {
1311
 
    ++j;
1312
 
  }
1313
 
  if (j < text->getLength() && text->getChar(j) == 0x0a) {
1314
 
    ++j;
1315
 
  }
1316
 
  *next = j;
1317
 
}
1318
 
 
1319
 
// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1320
 
// If <fill> is true, the circle is filled; otherwise it is stroked.
1321
 
void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
1322
 
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1323
 
                     cx + r, cy);
1324
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1325
 
                     cx + r, cy + bezierCircle * r,
1326
 
                     cx + bezierCircle * r, cy + r,
1327
 
                     cx, cy + r);
1328
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1329
 
                     cx - bezierCircle * r, cy + r,
1330
 
                     cx - r, cy + bezierCircle * r,
1331
 
                     cx - r, cy);
1332
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1333
 
                     cx - r, cy - bezierCircle * r,
1334
 
                     cx - bezierCircle * r, cy - r,
1335
 
                     cx, cy - r);
1336
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1337
 
                     cx + bezierCircle * r, cy - r,
1338
 
                     cx + r, cy - bezierCircle * r,
1339
 
                     cx + r, cy);
1340
 
  appearBuf->append(fill ? "f\n" : "s\n");
1341
 
}
1342
 
 
1343
 
// Draw the top-left half of an (approximate) circle of radius <r>
1344
 
// centered at (<cx>, <cy>).
1345
 
void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1346
 
  double r2;
1347
 
 
1348
 
  r2 = r / sqrt(2.0);
1349
 
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1350
 
                     cx + r2, cy + r2);
1351
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1352
 
                     cx + (1 - bezierCircle) * r2,
1353
 
                     cy + (1 + bezierCircle) * r2,
1354
 
                     cx - (1 - bezierCircle) * r2,
1355
 
                     cy + (1 + bezierCircle) * r2,
1356
 
                     cx - r2,
1357
 
                     cy + r2);
1358
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1359
 
                     cx - (1 + bezierCircle) * r2,
1360
 
                     cy + (1 - bezierCircle) * r2,
1361
 
                     cx - (1 + bezierCircle) * r2,
1362
 
                     cy - (1 - bezierCircle) * r2,
1363
 
                     cx - r2,
1364
 
                     cy - r2);
1365
 
  appearBuf->append("S\n");
1366
 
}
1367
 
 
1368
 
// Draw the bottom-right half of an (approximate) circle of radius <r>
1369
 
// centered at (<cx>, <cy>).
1370
 
void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1371
 
  double r2;
1372
 
 
1373
 
  r2 = r / sqrt(2.0);
1374
 
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
1375
 
                     cx - r2, cy - r2);
1376
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1377
 
                     cx - (1 - bezierCircle) * r2,
1378
 
                     cy - (1 + bezierCircle) * r2,
1379
 
                     cx + (1 - bezierCircle) * r2,
1380
 
                     cy - (1 + bezierCircle) * r2,
1381
 
                     cx + r2,
1382
 
                     cy - r2);
1383
 
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
1384
 
                     cx + (1 + bezierCircle) * r2,
1385
 
                     cy - (1 - bezierCircle) * r2,
1386
 
                     cx + (1 + bezierCircle) * r2,
1387
 
                     cy + (1 - bezierCircle) * r2,
1388
 
                     cx + r2,
1389
 
                     cy + r2);
1390
 
  appearBuf->append("S\n");
1391
 
}
1392
 
 
1393
 
// Look up an inheritable field dictionary entry.
1394
 
Object *Annot::fieldLookup(Dict *field, char *key, Object *obj) {
1395
 
  Dict *dict;
1396
 
  Object parent;
1397
 
 
1398
 
  dict = field;
1399
 
  if (!dict->lookup(key, obj)->isNull()) {
1400
 
    return obj;
1401
 
  }
1402
 
  obj->free();
1403
 
  if (dict->lookup("Parent", &parent)->isDict()) {
1404
 
    fieldLookup(parent.getDict(), key, obj);
1405
 
  } else {
1406
 
    obj->initNull();
1407
 
  }
1408
 
  parent.free();
1409
 
  return obj;
1410
 
}
1411
 
 
1412
 
void Annot::draw(Gfx *gfx, GBool printing) {
1413
 
  Object obj;
1414
 
  GBool isLink;
1415
 
 
1416
 
  // check the flags
1417
 
  if ((flags & annotFlagHidden) ||
1418
 
      (printing && !(flags & annotFlagPrint)) ||
1419
 
      (!printing && (flags & annotFlagNoView))) {
1420
 
    return;
1421
 
  }
1422
 
 
1423
 
  // draw the appearance stream
1424
 
  isLink = type && !type->cmp("Link");
1425
 
  appearance.fetch(xref, &obj);
1426
 
  gfx->drawAnnot(&obj, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
1427
 
                 xMin, yMin, xMax, yMax);
1428
 
  obj.free();
1429
 
}
1430
 
 
1431
 
//------------------------------------------------------------------------
1432
 
// Annots
1433
 
//------------------------------------------------------------------------
1434
 
 
1435
 
Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
1436
 
  Dict *acroForm;
1437
 
  Annot *annot;
1438
 
  Object obj1;
1439
 
  Ref ref;
1440
 
  int size;
1441
 
  int i;
1442
 
 
1443
 
  annots = NULL;
1444
 
  size = 0;
1445
 
  nAnnots = 0;
1446
 
 
1447
 
  acroForm = catalog->getAcroForm()->isDict() ?
1448
 
               catalog->getAcroForm()->getDict() : NULL;
1449
 
  if (annotsObj->isArray()) {
1450
 
    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
1451
 
      if (annotsObj->arrayGetNF(i, &obj1)->isRef()) {
1452
 
        ref = obj1.getRef();
1453
 
        obj1.free();
1454
 
        annotsObj->arrayGet(i, &obj1);
1455
 
      } else {
1456
 
        ref.num = ref.gen = -1;
1457
 
      }
1458
 
      if (obj1.isDict()) {
1459
 
        annot = new Annot(xref, acroForm, obj1.getDict(), &ref);
1460
 
        if (annot->isOk()) {
1461
 
          if (nAnnots >= size) {
1462
 
            size += 16;
1463
 
            annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
1464
 
          }
1465
 
          annots[nAnnots++] = annot;
1466
 
        } else {
1467
 
          delete annot;
1468
 
        }
1469
 
      }
1470
 
      obj1.free();
1471
 
    }
1472
 
  }
1473
 
}
1474
 
 
1475
 
Annots::~Annots() {
1476
 
  int i;
1477
 
 
1478
 
  for (i = 0; i < nAnnots; ++i) {
1479
 
    delete annots[i];
1480
 
  }
1481
 
  gfree(annots);
1482
 
}
1483
 
 
1484
 
void Annots::generateAppearances(Dict *acroForm) {
1485
 
  Object obj1, obj2;
1486
 
  Ref ref;
1487
 
  int i;
1488
 
 
1489
 
  if (acroForm->lookup("Fields", &obj1)->isArray()) {
1490
 
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
1491
 
      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
1492
 
        ref = obj2.getRef();
1493
 
        obj2.free();
1494
 
        obj1.arrayGet(i, &obj2);
1495
 
      } else {
1496
 
        ref.num = ref.gen = -1;
1497
 
      }
1498
 
      if (obj2.isDict()) {
1499
 
        scanFieldAppearances(obj2.getDict(), &ref, NULL, acroForm);
1500
 
      }
1501
 
      obj2.free();
1502
 
    }
1503
 
  }
1504
 
  obj1.free();
1505
 
}
1506
 
 
1507
 
void Annots::scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
1508
 
                                  Dict *acroForm) {
1509
 
  Annot *annot;
1510
 
  Object obj1, obj2;
1511
 
  Ref ref2;
1512
 
  int i;
1513
 
 
1514
 
  // non-terminal node: scan the children
1515
 
  if (node->lookup("Kids", &obj1)->isArray()) {
1516
 
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
1517
 
      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
1518
 
        ref2 = obj2.getRef();
1519
 
        obj2.free();
1520
 
        obj1.arrayGet(i, &obj2);
1521
 
      } else {
1522
 
        ref2.num = ref2.gen = -1;
1523
 
      }
1524
 
      if (obj2.isDict()) {
1525
 
        scanFieldAppearances(obj2.getDict(), &ref2, node, acroForm);
1526
 
      }
1527
 
      obj2.free();
1528
 
    }
1529
 
    obj1.free();
1530
 
    return;
1531
 
  }
1532
 
  obj1.free();
1533
 
 
1534
 
  // terminal node: this is either a combined annot/field dict, or an
1535
 
  // annot dict whose parent is a field
1536
 
  if ((annot = findAnnot(ref))) {
1537
 
    node->lookupNF("Parent", &obj1);
1538
 
    if (!parent || !obj1.isNull()) {
1539
 
      annot->generateFieldAppearance(node, node, acroForm);
1540
 
    } else {
1541
 
      annot->generateFieldAppearance(parent, node, acroForm);
1542
 
    }
1543
 
    obj1.free();
1544
 
  }
1545
 
}
1546
 
 
1547
 
Annot *Annots::findAnnot(Ref *ref) {
1548
 
  int i;
1549
 
 
1550
 
  for (i = 0; i < nAnnots; ++i) {
1551
 
    if (annots[i]->match(ref)) {
1552
 
      return annots[i];
1553
 
    }
1554
 
  }
1555
 
  return NULL;
1556
 
}