Coverage Report

Created: 2026-06-22 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Annot.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Annot.cc
4
//
5
// Copyright 2000-2022 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <math.h>
13
#include <limits.h>
14
#include "gmem.h"
15
#include "gmempp.h"
16
#include "GList.h"
17
#include "Error.h"
18
#include "Object.h"
19
#include "Catalog.h"
20
#include "Gfx.h"
21
#include "GfxFont.h"
22
#include "Lexer.h"
23
#include "PDFDoc.h"
24
#include "OptionalContent.h"
25
#include "AcroForm.h"
26
#include "BuiltinFontTables.h"
27
#include "FontEncodingTables.h"
28
#include "Annot.h"
29
30
// the MSVC math.h doesn't define this
31
#ifndef M_PI
32
#define M_PI 3.14159265358979323846
33
#endif
34
35
//------------------------------------------------------------------------
36
37
9.32k
#define annotFlagHidden    0x0002
38
913
#define annotFlagPrint     0x0004
39
8.25k
#define annotFlagNoView    0x0020
40
41
// distance of Bezier control point from center for circle approximation
42
// = (4 * (sqrt(2) - 1) / 3) * r
43
0
#define bezierCircle 0.55228475
44
45
0
#define lineEndSize1    6
46
40
#define lineEndSize2   10
47
180
#define lineArrowAngle (M_PI / 6)
48
49
//------------------------------------------------------------------------
50
// AnnotBorderStyle
51
//------------------------------------------------------------------------
52
53
AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
54
           double *dashA, int dashLengthA,
55
29.1k
           double *colorA, int nColorCompsA) {
56
29.1k
  type = typeA;
57
29.1k
  width = widthA;
58
29.1k
  dash = dashA;
59
29.1k
  dashLength = dashLengthA;
60
29.1k
  color[0] = colorA[0];
61
29.1k
  color[1] = colorA[1];
62
29.1k
  color[2] = colorA[2];
63
29.1k
  color[3] = colorA[3];
64
29.1k
  nColorComps = nColorCompsA;
65
29.1k
}
66
67
29.1k
AnnotBorderStyle::~AnnotBorderStyle() {
68
29.1k
  if (dash) {
69
98
    gfree(dash);
70
98
  }
71
29.1k
}
72
73
//------------------------------------------------------------------------
74
// Annot
75
//------------------------------------------------------------------------
76
77
29.1k
Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
78
29.1k
  Object apObj, asObj, obj1, obj2, obj3;
79
29.1k
  AnnotBorderType borderType;
80
29.1k
  double borderWidth;
81
29.1k
  double *borderDash;
82
29.1k
  int borderDashLength;
83
29.1k
  double borderColor[4];
84
29.1k
  int nBorderColorComps;
85
29.1k
  double t;
86
29.1k
  int i;
87
88
29.1k
  ok = gTrue;
89
29.1k
  doc = docA;
90
29.1k
  xref = doc->getXRef();
91
29.1k
  ref = *refA;
92
29.1k
  type = NULL;
93
29.1k
  appearanceState = NULL;
94
29.1k
  appearBuf = NULL;
95
29.1k
  borderStyle = NULL;
96
97
  //----- parse the type
98
99
29.1k
  if (dict->lookup("Subtype", &obj1)->isName()) {
100
8.06k
    type = new GString(obj1.getName());
101
8.06k
  }
102
29.1k
  obj1.free();
103
104
  //----- parse the rectangle
105
106
29.1k
  if (dict->lookup("Rect", &obj1)->isArray() &&
107
10.1k
      obj1.arrayGetLength() == 4) {
108
9.32k
    xMin = yMin = xMax = yMax = 0;
109
9.32k
    if (obj1.arrayGet(0, &obj2)->isNum()) {
110
8.94k
      xMin = obj2.getNum();
111
8.94k
    }
112
9.32k
    obj2.free();
113
9.32k
    if (obj1.arrayGet(1, &obj2)->isNum()) {
114
9.06k
      yMin = obj2.getNum();
115
9.06k
    }
116
9.32k
    obj2.free();
117
9.32k
    if (obj1.arrayGet(2, &obj2)->isNum()) {
118
6.41k
      xMax = obj2.getNum();
119
6.41k
    }
120
9.32k
    obj2.free();
121
9.32k
    if (obj1.arrayGet(3, &obj2)->isNum()) {
122
8.92k
      yMax = obj2.getNum();
123
8.92k
    }
124
9.32k
    obj2.free();
125
9.32k
    if (xMin > xMax) {
126
3.28k
      t = xMin; xMin = xMax; xMax = t;
127
3.28k
    }
128
9.32k
    if (yMin > yMax) {
129
341
      t = yMin; yMin = yMax; yMax = t;
130
341
    }
131
19.8k
  } else {
132
19.8k
    error(errSyntaxError, -1, "Bad bounding box for annotation");
133
19.8k
    ok = gFalse;
134
19.8k
  }
135
29.1k
  obj1.free();
136
137
  //----- parse the flags
138
139
29.1k
  if (dict->lookup("F", &obj1)->isInt()) {
140
6.43k
    flags = obj1.getInt();
141
22.7k
  } else {
142
22.7k
    flags = 0;
143
22.7k
  }
144
29.1k
  obj1.free();
145
146
  //----- parse the border style
147
148
29.1k
  borderType = annotBorderSolid;
149
29.1k
  borderWidth = 1;
150
29.1k
  borderDash = NULL;
151
29.1k
  borderDashLength = 0;
152
29.1k
  nBorderColorComps = 3;
153
29.1k
  borderColor[0] = 0;
154
29.1k
  borderColor[1] = 0;
155
29.1k
  borderColor[2] = 1;
156
29.1k
  borderColor[3] = 0;
157
29.1k
  if (dict->lookup("BS", &obj1)->isDict()) {
158
504
    if (obj1.dictLookup("S", &obj2)->isName()) {
159
421
      if (obj2.isName("S")) {
160
310
  borderType = annotBorderSolid;
161
310
      } else if (obj2.isName("D")) {
162
15
  borderType = annotBorderDashed;
163
96
      } else if (obj2.isName("B")) {
164
28
  borderType = annotBorderBeveled;
165
68
      } else if (obj2.isName("I")) {
166
34
  borderType = annotBorderInset;
167
34
      } else if (obj2.isName("U")) {
168
11
  borderType = annotBorderUnderlined;
169
11
      }
170
421
    }
171
504
    obj2.free();
172
504
    if (obj1.dictLookup("W", &obj2)->isNum()) {
173
382
      borderWidth = obj2.getNum();
174
382
    }
175
504
    obj2.free();
176
504
    if (obj1.dictLookup("D", &obj2)->isArray()) {
177
29
      borderDashLength = obj2.arrayGetLength();
178
29
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
179
380
      for (i = 0; i < borderDashLength; ++i) {
180
351
  if (obj2.arrayGet(i, &obj3)->isNum()) {
181
136
    borderDash[i] = obj3.getNum();
182
215
  } else {
183
215
    borderDash[i] = 1;
184
215
  }
185
351
  obj3.free();
186
351
      }
187
29
    }
188
504
    obj2.free();
189
28.6k
  } else {
190
28.6k
    obj1.free();
191
28.6k
    if (dict->lookup("Border", &obj1)->isArray()) {
192
1.18k
      if (obj1.arrayGetLength() >= 3) {
193
848
  if (obj1.arrayGet(2, &obj2)->isNum()) {
194
707
    borderWidth = obj2.getNum();
195
707
  }
196
848
  obj2.free();
197
848
  if (obj1.arrayGetLength() >= 4) {
198
230
    if (obj1.arrayGet(3, &obj2)->isArray()) {
199
79
      borderType = annotBorderDashed;
200
79
      borderDashLength = obj2.arrayGetLength();
201
79
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
202
1.97k
      for (i = 0; i < borderDashLength; ++i) {
203
1.89k
        if (obj2.arrayGet(i, &obj3)->isNum()) {
204
664
    borderDash[i] = obj3.getNum();
205
1.23k
        } else {
206
1.23k
    borderDash[i] = 1;
207
1.23k
        }
208
1.89k
        obj3.free();
209
1.89k
      }
210
151
    } else {
211
      // Adobe draws no border at all if the last element is of
212
      // the wrong type.
213
151
      borderWidth = 0;
214
151
    }
215
230
    obj2.free();
216
230
  }
217
848
      } else {
218
  // an empty Border array also means "no border"
219
338
  borderWidth = 0;
220
338
      }
221
1.18k
    }
222
28.6k
  }
223
29.1k
  obj1.free();
224
  // Acrobat ignores borders with unreasonable widths
225
29.1k
  if (borderWidth > 1 && (borderWidth > xMax - xMin ||
226
276
        borderWidth > yMax - yMin)) {
227
66
    borderWidth = 0;
228
66
  }
229
29.1k
  if (dict->lookup("C", &obj1)->isArray() &&
230
1.33k
      (obj1.arrayGetLength() == 1 ||
231
1.28k
       obj1.arrayGetLength() == 3 ||
232
1.09k
       obj1.arrayGetLength() == 4)) {
233
1.09k
    nBorderColorComps = obj1.arrayGetLength();
234
4.29k
    for (i = 0; i < nBorderColorComps; ++i) {
235
3.20k
      if (obj1.arrayGet(i, &obj2)->isNum()) {
236
3.04k
  borderColor[i] = obj2.getNum();
237
3.04k
      } else {
238
162
  borderColor[i] = 0;
239
162
      }
240
3.20k
      obj2.free();
241
3.20k
    }
242
1.09k
  }
243
29.1k
  obj1.free();
244
29.1k
  borderStyle = new AnnotBorderStyle(borderType, borderWidth,
245
29.1k
             borderDash, borderDashLength,
246
29.1k
             borderColor, nBorderColorComps);
247
248
  //----- get the appearance state
249
250
29.1k
  dict->lookup("AP", &apObj);
251
29.1k
  dict->lookup("AS", &asObj);
252
29.1k
  if (asObj.isName()) {
253
257
    appearanceState = new GString(asObj.getName());
254
28.9k
  } else if (apObj.isDict()) {
255
1.00k
    apObj.dictLookup("N", &obj1);
256
1.00k
    if (obj1.isDict() && obj1.dictGetLength() == 1) {
257
40
      appearanceState = new GString(obj1.dictGetKey(0));
258
40
    }
259
1.00k
    obj1.free();
260
1.00k
  }
261
29.1k
  if (!appearanceState) {
262
28.8k
    appearanceState = new GString("Off");
263
28.8k
  }
264
29.1k
  asObj.free();
265
266
  //----- get the annotation appearance
267
268
29.1k
  if (apObj.isDict()) {
269
1.26k
    apObj.dictLookup("N", &obj1);
270
1.26k
    apObj.dictLookupNF("N", &obj2);
271
1.26k
    if (obj1.isDict()) {
272
468
      if (obj1.dictLookupNF(appearanceState->getCString(), &obj3)->isRef()) {
273
179
  obj3.copy(&appearance);
274
179
      }
275
468
      obj3.free();
276
798
    } else if (obj2.isRef()) {
277
681
      obj2.copy(&appearance);
278
681
    } else if (obj2.isStream()) {
279
0
      obj2.copy(&appearance);
280
0
    }
281
1.26k
    obj1.free();
282
1.26k
    obj2.free();
283
1.26k
  }
284
29.1k
  apObj.free();
285
286
  //----- get the optional content entry
287
288
29.1k
  dict->lookupNF("OC", &ocObj);
289
29.1k
}
290
291
29.1k
Annot::~Annot() {
292
29.1k
  if (type) {
293
8.06k
    delete type;
294
8.06k
  }
295
29.1k
  if (appearanceState) {
296
29.1k
    delete appearanceState;
297
29.1k
  }
298
29.1k
  appearance.free();
299
29.1k
  if (appearBuf) {
300
346
    delete appearBuf;
301
346
  }
302
29.1k
  if (borderStyle) {
303
29.1k
    delete borderStyle;
304
29.1k
  }
305
29.1k
  ocObj.free();
306
29.1k
}
307
308
9.32k
void Annot::generateAnnotAppearance(Object *annotObj) {
309
9.32k
  Object obj;
310
9.32k
  appearance.fetch(doc->getXRef(), &obj);
311
9.32k
  GBool alreadyHaveAppearance = obj.isStream();
312
9.32k
  obj.free();
313
9.32k
  if (alreadyHaveAppearance) {
314
297
    return;
315
297
  }
316
317
9.02k
  if (!type || (type->cmp("Line") &&
318
5.94k
    type->cmp("PolyLine") &&
319
5.80k
    type->cmp("Polygon") &&
320
8.65k
    type->cmp("FreeText"))) {
321
8.65k
    return;
322
8.65k
  }
323
324
372
  Object annotObj2;
325
372
  if (!annotObj) {
326
372
    getObject(&annotObj2);
327
372
    annotObj = &annotObj2;
328
372
  }
329
372
  if (!annotObj->isDict()) {
330
26
    annotObj2.free();
331
26
    return;
332
26
  }
333
334
346
  if (!type->cmp("Line")) {
335
141
    generateLineAppearance(annotObj);
336
205
  } else if (!type->cmp("PolyLine")) {
337
145
    generatePolyLineAppearance(annotObj);
338
145
  } else if (!type->cmp("Polygon")) {
339
0
    generatePolygonAppearance(annotObj);
340
60
  } else if (!type->cmp("FreeText")) {
341
60
    generateFreeTextAppearance(annotObj);
342
60
  }
343
344
346
  annotObj2.free();
345
346
}
346
347
//~ this doesn't draw the caption
348
141
void Annot::generateLineAppearance(Object *annotObj) {
349
141
  Object gfxStateDict, appearDict, obj1, obj2;
350
141
  MemStream *appearStream;
351
141
  double x1, y1, x2, y2, dx, dy, len, w;
352
141
  double lx1, ly1, lx2, ly2;
353
141
  double tx1, ty1, tx2, ty2;
354
141
  double ax1, ay1, ax2, ay2;
355
141
  double bx1, by1, bx2, by2;
356
141
  double leaderLen, leaderExtLen, leaderOffLen;
357
141
  AnnotLineEndType lineEnd1, lineEnd2;
358
141
  GBool fill;
359
360
141
  appearBuf = new GString();
361
362
  //----- check for transparency
363
141
  if (annotObj->dictLookup("CA", &obj1)->isNum()) {
364
18
    gfxStateDict.initDict(doc->getXRef());
365
18
    gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
366
18
    appearBuf->append("/GS1 gs\n");
367
18
  }
368
141
  obj1.free();
369
370
  //----- set line style, colors
371
141
  setLineStyle(borderStyle, &w);
372
141
  setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
373
141
  fill = gFalse;
374
141
  if (annotObj->dictLookup("IC", &obj1)->isArray()) {
375
61
    if (setFillColor(&obj1)) {
376
45
      fill = gTrue;
377
45
    }
378
61
  }
379
141
  obj1.free();
380
381
  //----- get line properties
382
141
  if (annotObj->dictLookup("L", &obj1)->isArray() &&
383
131
      obj1.arrayGetLength() == 4) {
384
115
    if (obj1.arrayGet(0, &obj2)->isNum()) {
385
104
      x1 = obj2.getNum();
386
104
    } else {
387
11
      obj2.free();
388
11
      obj1.free();
389
11
      return;
390
11
    }
391
104
    obj2.free();
392
104
    if (obj1.arrayGet(1, &obj2)->isNum()) {
393
97
      y1 = obj2.getNum();
394
97
    } else {
395
7
      obj2.free();
396
7
      obj1.free();
397
7
      return;
398
7
    }
399
97
    obj2.free();
400
97
    if (obj1.arrayGet(2, &obj2)->isNum()) {
401
87
      x2 = obj2.getNum();
402
87
    } else {
403
10
      obj2.free();
404
10
      obj1.free();
405
10
      return;
406
10
    }
407
87
    obj2.free();
408
87
    if (obj1.arrayGet(3, &obj2)->isNum()) {
409
75
      y2 = obj2.getNum();
410
75
    } else {
411
12
      obj2.free();
412
12
      obj1.free();
413
12
      return;
414
12
    }
415
75
    obj2.free();
416
75
  } else {
417
26
    obj1.free();
418
26
    return;
419
26
  }
420
75
  obj1.free();
421
75
  lineEnd1 = lineEnd2 = annotLineEndNone;
422
75
  if (annotObj->dictLookup("LE", &obj1)->isArray() &&
423
62
      obj1.arrayGetLength() == 2) {
424
52
    lineEnd1 = parseLineEndType(obj1.arrayGet(0, &obj2));
425
52
    obj2.free();
426
52
    lineEnd2 = parseLineEndType(obj1.arrayGet(1, &obj2));
427
52
    obj2.free();
428
52
  }
429
75
  obj1.free();
430
75
  if (annotObj->dictLookup("LL", &obj1)->isNum()) {
431
0
    leaderLen = obj1.getNum();
432
75
  } else {
433
75
    leaderLen = 0;
434
75
  }
435
75
  obj1.free();
436
75
  if (annotObj->dictLookup("LLE", &obj1)->isNum()) {
437
0
    leaderExtLen = obj1.getNum();
438
75
  } else {
439
75
    leaderExtLen = 0;
440
75
  }
441
75
  obj1.free();
442
75
  if (annotObj->dictLookup("LLO", &obj1)->isNum()) {
443
0
    leaderOffLen = obj1.getNum();
444
75
  } else {
445
75
    leaderOffLen = 0;
446
75
  }
447
75
  obj1.free();
448
449
  //----- compute positions
450
75
  x1 -= xMin;
451
75
  y1 -= yMin;
452
75
  x2 -= xMin;
453
75
  y2 -= yMin;
454
75
  dx = x2 - x1;
455
75
  dy = y2 - y1;
456
75
  len = sqrt(dx*dx + dy*dy);
457
75
  if (len > 0) {
458
75
    dx /= len;
459
75
    dy /= len;
460
75
  }
461
75
  if (leaderLen != 0) {
462
0
    ax1 = x1 + leaderOffLen * dy;
463
0
    ay1 = y1 - leaderOffLen * dx;
464
0
    lx1 = ax1 + leaderLen * dy;
465
0
    ly1 = ay1 - leaderLen * dx;
466
0
    bx1 = lx1 + leaderExtLen * dy;
467
0
    by1 = ly1 - leaderExtLen * dx;
468
0
    ax2 = x2 + leaderOffLen * dy;
469
0
    ay2 = y2 - leaderOffLen * dx;
470
0
    lx2 = ax2 + leaderLen * dy;
471
0
    ly2 = ay2 - leaderLen * dx;
472
0
    bx2 = lx2 + leaderExtLen * dy;
473
0
    by2 = ly2 - leaderExtLen * dx;
474
75
  } else {
475
75
    lx1 = x1;
476
75
    ly1 = y1;
477
75
    lx2 = x2;
478
75
    ly2 = y2;
479
75
    ax1 = ay1 = ax2 = ay2 = 0; // make gcc happy
480
75
    bx1 = by1 = bx2 = by2 = 0;
481
75
  }
482
75
  adjustLineEndpoint(lineEnd1, lx1, ly1, dx, dy, w, &tx1, &ty1);
483
75
  adjustLineEndpoint(lineEnd2, lx2, ly2, -dx, -dy, w, &tx2, &ty2);
484
485
  //----- draw leaders
486
75
  if (leaderLen != 0) {
487
0
    appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
488
0
           ax1, ay1, bx1, by1);
489
0
    appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
490
0
           ax2, ay2 , bx2, by2);
491
0
  }
492
493
  //----- draw the line
494
75
  appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
495
75
         tx1, ty1, tx2, ty2);
496
75
  appearBuf->append("S\n");
497
498
  //----- draw the arrows
499
75
  if (borderStyle->getType() == annotBorderDashed) {
500
5
    appearBuf->append("[] 0 d\n");
501
5
  }
502
75
  drawLineArrow(lineEnd1, lx1, ly1, dx, dy, w, fill);
503
75
  drawLineArrow(lineEnd2, lx2, ly2, -dx, -dy, w, fill);
504
505
  //----- build the appearance stream dictionary
506
75
  appearDict.initDict(doc->getXRef());
507
75
  appearDict.dictAdd(copyString("Length"),
508
75
         obj1.initInt(appearBuf->getLength()));
509
75
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
510
75
  obj1.initArray(doc->getXRef());
511
75
  obj1.arrayAdd(obj2.initReal(0));
512
75
  obj1.arrayAdd(obj2.initReal(0));
513
75
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
514
75
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
515
75
  appearDict.dictAdd(copyString("BBox"), &obj1);
516
75
  if (gfxStateDict.isDict()) {
517
16
    obj1.initDict(doc->getXRef());
518
16
    obj2.initDict(doc->getXRef());
519
16
    obj2.dictAdd(copyString("GS1"), &gfxStateDict);
520
16
    obj1.dictAdd(copyString("ExtGState"), &obj2);
521
16
    appearDict.dictAdd(copyString("Resources"), &obj1);
522
16
  }
523
524
  //----- build the appearance stream
525
75
  appearStream = new MemStream(appearBuf->getCString(), 0,
526
75
             appearBuf->getLength(), &appearDict);
527
75
  appearance.free();
528
75
  appearance.initStream(appearStream);
529
75
}
530
531
//~ this doesn't handle line ends (arrows)
532
145
void Annot::generatePolyLineAppearance(Object *annotObj) {
533
145
  Object gfxStateDict, appearDict, obj1, obj2;
534
145
  MemStream *appearStream;
535
145
  double x1, y1, w;
536
145
  int i;
537
538
145
  appearBuf = new GString();
539
540
  //----- check for transparency
541
145
  if (annotObj->dictLookup("CA", &obj1)->isNum()) {
542
108
    gfxStateDict.initDict(doc->getXRef());
543
108
    gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
544
108
    appearBuf->append("/GS1 gs\n");
545
108
  }
546
145
  obj1.free();
547
548
  //----- set line style, colors
549
145
  setLineStyle(borderStyle, &w);
550
145
  setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
551
  // fill = gFalse;
552
  // if (annotObj->dictLookup("IC", &obj1)->isArray()) {
553
  //   if (setFillColor(&obj1)) {
554
  //     fill = gTrue;
555
  //   }
556
  // }
557
  // obj1.free();
558
559
  //----- draw line
560
145
  if (!annotObj->dictLookup("Vertices", &obj1)->isArray()) {
561
22
    obj1.free();
562
22
    return;
563
22
  }
564
357
  for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
565
263
    if (!obj1.arrayGet(i, &obj2)->isNum()) {
566
6
      obj2.free();
567
6
      obj1.free();
568
6
      return;
569
6
    }
570
257
    x1 = obj2.getNum();
571
257
    obj2.free();
572
257
    if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
573
23
      obj2.free();
574
23
      obj1.free();
575
23
      return;
576
23
    }
577
234
    y1 = obj2.getNum();
578
234
    obj2.free();
579
234
    x1 -= xMin;
580
234
    y1 -= yMin;
581
234
    if (i == 0) {
582
102
      appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
583
132
    } else {
584
132
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
585
132
    }
586
234
  }
587
94
  appearBuf->append("S\n");
588
94
  obj1.free();
589
590
  //----- build the appearance stream dictionary
591
94
  appearDict.initDict(doc->getXRef());
592
94
  appearDict.dictAdd(copyString("Length"),
593
94
         obj1.initInt(appearBuf->getLength()));
594
94
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
595
94
  obj1.initArray(doc->getXRef());
596
94
  obj1.arrayAdd(obj2.initReal(0));
597
94
  obj1.arrayAdd(obj2.initReal(0));
598
94
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
599
94
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
600
94
  appearDict.dictAdd(copyString("BBox"), &obj1);
601
94
  if (gfxStateDict.isDict()) {
602
63
    obj1.initDict(doc->getXRef());
603
63
    obj2.initDict(doc->getXRef());
604
63
    obj2.dictAdd(copyString("GS1"), &gfxStateDict);
605
63
    obj1.dictAdd(copyString("ExtGState"), &obj2);
606
63
    appearDict.dictAdd(copyString("Resources"), &obj1);
607
63
  }
608
609
  //----- build the appearance stream
610
94
  appearStream = new MemStream(appearBuf->getCString(), 0,
611
94
             appearBuf->getLength(), &appearDict);
612
94
  appearance.free();
613
94
  appearance.initStream(appearStream);
614
94
}
615
616
0
void Annot::generatePolygonAppearance(Object *annotObj) {
617
0
  Object gfxStateDict, appearDict, obj1, obj2;
618
0
  MemStream *appearStream;
619
0
  double x1, y1;
620
0
  int i;
621
622
0
  appearBuf = new GString();
623
624
  //----- check for transparency
625
0
  if (annotObj->dictLookup("CA", &obj1)->isNum()) {
626
0
    gfxStateDict.initDict(doc->getXRef());
627
0
    gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
628
0
    appearBuf->append("/GS1 gs\n");
629
0
  }
630
0
  obj1.free();
631
632
  //----- set fill color
633
0
  if (!annotObj->dictLookup("IC", &obj1)->isArray()  ||
634
0
      !setFillColor(&obj1)) {
635
0
    obj1.free();
636
0
    return;
637
0
  }
638
0
  obj1.free();
639
640
  //----- fill polygon
641
0
  if (!annotObj->dictLookup("Vertices", &obj1)->isArray()) {
642
0
    obj1.free();
643
0
    return;
644
0
  }
645
0
  for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
646
0
    if (!obj1.arrayGet(i, &obj2)->isNum()) {
647
0
      obj2.free();
648
0
      obj1.free();
649
0
      return;
650
0
    }
651
0
    x1 = obj2.getNum();
652
0
    obj2.free();
653
0
    if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
654
0
      obj2.free();
655
0
      obj1.free();
656
0
      return;
657
0
    }
658
0
    y1 = obj2.getNum();
659
0
    obj2.free();
660
0
    x1 -= xMin;
661
0
    y1 -= yMin;
662
0
    if (i == 0) {
663
0
      appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
664
0
    } else {
665
0
      appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
666
0
    }
667
0
  }
668
0
  appearBuf->append("f\n");
669
0
  obj1.free();
670
671
  //----- build the appearance stream dictionary
672
0
  appearDict.initDict(doc->getXRef());
673
0
  appearDict.dictAdd(copyString("Length"),
674
0
         obj1.initInt(appearBuf->getLength()));
675
0
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
676
0
  obj1.initArray(doc->getXRef());
677
0
  obj1.arrayAdd(obj2.initReal(0));
678
0
  obj1.arrayAdd(obj2.initReal(0));
679
0
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
680
0
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
681
0
  appearDict.dictAdd(copyString("BBox"), &obj1);
682
0
  if (gfxStateDict.isDict()) {
683
0
    obj1.initDict(doc->getXRef());
684
0
    obj2.initDict(doc->getXRef());
685
0
    obj2.dictAdd(copyString("GS1"), &gfxStateDict);
686
0
    obj1.dictAdd(copyString("ExtGState"), &obj2);
687
0
    appearDict.dictAdd(copyString("Resources"), &obj1);
688
0
  }
689
690
  //----- build the appearance stream
691
0
  appearStream = new MemStream(appearBuf->getCString(), 0,
692
0
             appearBuf->getLength(), &appearDict);
693
0
  appearance.free();
694
0
  appearance.initStream(appearStream);
695
0
}
696
697
//~ this doesn't handle rich text
698
//~ this doesn't handle the callout
699
//~ this doesn't handle the RD field
700
60
void Annot::generateFreeTextAppearance(Object *annotObj) {
701
60
  Object gfxStateDict, appearDict, obj1, obj2;
702
60
  Object resources, gsResources, fontResources, defaultFont;
703
60
  GString *text, *da;
704
60
  double lineWidth;
705
60
  int quadding, rot;
706
60
  MemStream *appearStream;
707
708
60
  appearBuf = new GString();
709
710
  //----- check for transparency
711
60
  if (annotObj->dictLookup("CA", &obj1)->isNum()) {
712
10
    gfxStateDict.initDict(doc->getXRef());
713
10
    gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
714
10
    appearBuf->append("/GS1 gs\n");
715
10
  }
716
60
  obj1.free();
717
718
  //----- draw the text
719
60
  if (annotObj->dictLookup("Contents", &obj1)->isString()) {
720
51
    text = obj1.getString()->copy();
721
51
  } else {
722
9
    text = new GString();
723
9
  }
724
60
  obj1.free();
725
60
  if (annotObj->dictLookup("Q", &obj1)->isInt()) {
726
13
    quadding = obj1.getInt();
727
47
  } else {
728
47
    quadding = 0;
729
47
  }
730
60
  obj1.free();
731
60
  if (annotObj->dictLookup("DA", &obj1)->isString()) {
732
29
    da = obj1.getString()->copy();
733
31
  } else {
734
31
    da = new GString();
735
31
  }
736
60
  obj1.free();
737
  // the "Rotate" field is not defined in the PDF spec, but Acrobat
738
  // looks at it
739
60
  if (annotObj->dictLookup("Rotate", &obj1)->isInt()) {
740
1
    rot = obj1.getInt();
741
59
  } else {
742
59
    rot = 0;
743
59
  }
744
60
  obj1.free();
745
60
  drawText(text, da, quadding, 0, rot);
746
60
  delete text;
747
60
  delete da;
748
749
  //----- draw the border
750
60
  if (borderStyle->getWidth() != 0) {
751
37
    setLineStyle(borderStyle, &lineWidth);
752
37
    appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re s\n",
753
37
           0.5 * lineWidth, 0.5 * lineWidth,
754
37
           xMax - xMin - lineWidth, yMax - yMin - lineWidth);
755
37
  }
756
757
  //----- build the appearance stream dictionary
758
60
  appearDict.initDict(doc->getXRef());
759
60
  appearDict.dictAdd(copyString("Length"),
760
60
         obj1.initInt(appearBuf->getLength()));
761
60
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
762
60
  obj1.initArray(doc->getXRef());
763
60
  obj1.arrayAdd(obj2.initReal(0));
764
60
  obj1.arrayAdd(obj2.initReal(0));
765
60
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
766
60
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
767
60
  appearDict.dictAdd(copyString("BBox"), &obj1);
768
60
  resources.initDict(doc->getXRef());
769
60
  defaultFont.initDict(doc->getXRef());
770
60
  defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
771
60
  defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
772
60
  defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
773
60
  defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
774
60
  fontResources.initDict(doc->getXRef());
775
60
  fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
776
60
  resources.dictAdd(copyString("Font"), &fontResources);
777
60
  if (gfxStateDict.isDict()) {
778
10
    gsResources.initDict(doc->getXRef());
779
10
    gsResources.dictAdd(copyString("GS1"), &gfxStateDict);
780
10
    resources.dictAdd(copyString("ExtGState"), &gsResources);
781
10
  }
782
60
  appearDict.dictAdd(copyString("Resources"), &resources);
783
784
  //----- build the appearance stream
785
60
  appearStream = new MemStream(appearBuf->getCString(), 0,
786
60
             appearBuf->getLength(), &appearDict);
787
60
  appearance.free();
788
60
  appearance.initStream(appearStream);
789
60
}
790
791
323
void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
792
323
  double *dash;
793
323
  double w;
794
323
  int dashLength, i;
795
796
323
  if ((w = borderStyle->getWidth()) <= 0) {
797
7
    w = 0.1;
798
7
  }
799
323
  *lineWidth = w;
800
323
  appearBuf->appendf("{0:.4f} w\n", w);
801
  // this treats beveled/inset/underline as solid
802
323
  if (borderStyle->getType() == annotBorderDashed) {
803
8
    borderStyle->getDash(&dash, &dashLength);
804
8
    appearBuf->append("[");
805
8
    for (i = 0; i < dashLength; ++i) {
806
0
      appearBuf->appendf(" {0:.4f}", dash[i]);
807
0
    }
808
8
    appearBuf->append("] 0 d\n");
809
8
  }
810
323
  appearBuf->append("0 j\n0 J\n");
811
323
}
812
813
286
void Annot::setStrokeColor(double *color, int nComps) {
814
286
  switch (nComps) {
815
0
  case 0:
816
0
    appearBuf->append("0 G\n");
817
0
    break;
818
47
  case 1:
819
47
    appearBuf->appendf("{0:.2f} G\n", color[0]);
820
47
    break;
821
221
  case 3:
822
221
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} RG\n",
823
221
           color[0], color[1], color[2]);
824
221
    break;
825
18
  case 4:
826
18
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} K\n",
827
18
           color[0], color[1], color[2], color[3]);
828
18
    break;
829
286
  }
830
286
}
831
832
61
GBool Annot::setFillColor(Object *colorObj) {
833
61
  Object obj;
834
61
  double color[4];
835
61
  int i;
836
837
61
  if (!colorObj->isArray()) {
838
0
    return gFalse;
839
0
  }
840
233
  for (i = 0; i < colorObj->arrayGetLength() && i < 4; ++i) {
841
172
    if (colorObj->arrayGet(i, &obj)->isNum()) {
842
126
      color[i] = obj.getNum();
843
126
    } else {
844
46
      color[i] = 0;
845
46
    }
846
172
    obj.free();
847
172
  }
848
61
  switch (colorObj->arrayGetLength()) {
849
14
  case 1:
850
14
    appearBuf->appendf("{0:.2f} g\n", color[0]);
851
14
    return gTrue;
852
22
  case 3:
853
22
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} rg\n",
854
22
           color[0], color[1], color[2]);
855
22
    return gTrue;
856
9
  case 4:
857
9
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.3f} k\n",
858
9
           color[0], color[1],
859
9
           color[2], color[3]);
860
9
    return gTrue;
861
61
  }
862
16
  return gFalse;
863
61
}
864
865
104
AnnotLineEndType Annot::parseLineEndType(Object *obj) {
866
104
  if (obj->isName("None")) {
867
31
    return annotLineEndNone;
868
73
  } else if (obj->isName("Square")) {
869
0
    return annotLineEndSquare;
870
73
  } else if (obj->isName("Circle")) {
871
0
    return annotLineEndCircle;
872
73
  } else if (obj->isName("Diamond")) {
873
0
    return annotLineEndDiamond;
874
73
  } else if (obj->isName("OpenArrow")) {
875
0
    return annotLineEndOpenArrow;
876
73
  } else if (obj->isName("ClosedArrow")) {
877
20
    return annotLineEndClosedArrow;
878
53
  } else if (obj->isName("Butt")) {
879
0
    return annotLineEndButt;
880
53
  } else if (obj->isName("ROpenArrow")) {
881
0
    return annotLineEndROpenArrow;
882
53
  } else if (obj->isName("RClosedArrow")) {
883
0
    return annotLineEndRClosedArrow;
884
53
  } else if (obj->isName("Slash")) {
885
0
    return annotLineEndSlash;
886
53
  } else {
887
53
    return annotLineEndNone;
888
53
  }
889
104
}
890
891
void Annot::adjustLineEndpoint(AnnotLineEndType lineEnd,
892
             double x, double y, double dx, double dy,
893
150
             double w, double *tx, double *ty) {
894
150
  switch (lineEnd) {
895
130
  case annotLineEndNone:
896
130
    w = 0;
897
130
    break;
898
0
  case annotLineEndSquare:
899
0
    w *= lineEndSize1;
900
0
    break;
901
0
  case annotLineEndCircle:
902
0
    w *= lineEndSize1;
903
0
    break;
904
0
  case annotLineEndDiamond:
905
0
    w *= lineEndSize1;
906
0
    break;
907
0
  case annotLineEndOpenArrow:
908
0
    w = 0;
909
0
    break;
910
20
  case annotLineEndClosedArrow:
911
20
    w *= lineEndSize2 * cos(lineArrowAngle);
912
20
    break;
913
0
  case annotLineEndButt:
914
0
    w = 0;
915
0
    break;
916
0
  case annotLineEndROpenArrow:
917
0
    w *= lineEndSize2 * cos(lineArrowAngle);
918
0
    break;
919
0
  case annotLineEndRClosedArrow:
920
0
    w *= lineEndSize2 * cos(lineArrowAngle);
921
0
    break;
922
0
  case annotLineEndSlash:
923
0
    w = 0;
924
0
    break;
925
150
  }
926
150
  *tx = x + w * dx;
927
150
  *ty = y + w * dy;
928
150
}
929
930
void Annot::drawLineArrow(AnnotLineEndType lineEnd,
931
        double x, double y, double dx, double dy,
932
150
        double w, GBool fill) {
933
150
  switch (lineEnd) {
934
130
  case annotLineEndNone:
935
130
    break;
936
0
  case annotLineEndSquare:
937
0
    w *= lineEndSize1;
938
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
939
0
           x + w*dx + 0.5*w*dy,
940
0
           y + w*dy - 0.5*w*dx);
941
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
942
0
           x + 0.5*w*dy,
943
0
           y - 0.5*w*dx);
944
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
945
0
           x - 0.5*w*dy,
946
0
           y + 0.5*w*dx);
947
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
948
0
           x + w*dx - 0.5*w*dy,
949
0
           y + w*dy + 0.5*w*dx);
950
0
    appearBuf->append(fill ? "b\n" : "s\n");
951
0
    break;
952
0
  case annotLineEndCircle:
953
0
    w *= lineEndSize1;
954
0
    drawCircle(x + 0.5*w*dx, y + 0.5*w*dy, 0.5*w, fill ? "b" : "s");
955
0
    break;
956
0
  case annotLineEndDiamond:
957
0
    w *= lineEndSize1;
958
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n", x, y);
959
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
960
0
           x + 0.5*w*dx - 0.5*w*dy,
961
0
           y + 0.5*w*dy + 0.5*w*dx);
962
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
963
0
           x + w*dx,
964
0
           y + w*dy);
965
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
966
0
           x + 0.5*w*dx + 0.5*w*dy,
967
0
           y + 0.5*w*dy - 0.5*w*dx);
968
0
    appearBuf->append(fill ? "b\n" : "s\n");
969
0
    break;
970
0
  case annotLineEndOpenArrow:
971
0
    w *= lineEndSize2;
972
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
973
0
           x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
974
0
           y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
975
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
976
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
977
0
           x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
978
0
           y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
979
0
    appearBuf->append("S\n");
980
0
    break;
981
20
  case annotLineEndClosedArrow:
982
20
    w *= lineEndSize2;
983
20
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
984
20
           x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
985
20
           y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
986
20
    appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
987
20
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
988
20
           x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
989
20
           y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
990
20
    appearBuf->append(fill ? "b\n" : "s\n");
991
20
    break;
992
0
  case annotLineEndButt:
993
0
    w *= lineEndSize1;
994
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
995
0
           x + 0.5*w*dy,
996
0
           y - 0.5*w*dx);
997
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
998
0
           x - 0.5*w*dy,
999
0
           y + 0.5*w*dx);
1000
0
    appearBuf->append("S\n");
1001
0
    break;
1002
0
  case annotLineEndROpenArrow:
1003
0
    w *= lineEndSize2;
1004
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1005
0
           x + w*sin(lineArrowAngle)*dy,
1006
0
           y - w*sin(lineArrowAngle)*dx);
1007
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1008
0
           x + w*cos(lineArrowAngle)*dx,
1009
0
           y + w*cos(lineArrowAngle)*dy);
1010
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1011
0
           x - w*sin(lineArrowAngle)*dy,
1012
0
           y + w*sin(lineArrowAngle)*dx);
1013
0
    appearBuf->append("S\n");
1014
0
    break;
1015
0
  case annotLineEndRClosedArrow:
1016
0
    w *= lineEndSize2;
1017
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1018
0
           x + w*sin(lineArrowAngle)*dy,
1019
0
           y - w*sin(lineArrowAngle)*dx);
1020
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1021
0
           x + w*cos(lineArrowAngle)*dx,
1022
0
           y + w*cos(lineArrowAngle)*dy);
1023
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1024
0
           x - w*sin(lineArrowAngle)*dy,
1025
0
           y + w*sin(lineArrowAngle)*dx);
1026
0
    appearBuf->append(fill ? "b\n" : "s\n");
1027
0
    break;
1028
0
  case annotLineEndSlash:
1029
0
    w *= lineEndSize1;
1030
0
    appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1031
0
           x + 0.5*w*cos(lineArrowAngle)*dy
1032
0
             - 0.5*w*sin(lineArrowAngle)*dx,
1033
0
           y - 0.5*w*cos(lineArrowAngle)*dx
1034
0
             - 0.5*w*sin(lineArrowAngle)*dy);
1035
0
    appearBuf->appendf("{0:.4f} {1:.4f} l\n",
1036
0
           x - 0.5*w*cos(lineArrowAngle)*dy
1037
0
             + 0.5*w*sin(lineArrowAngle)*dx,
1038
0
           y + 0.5*w*cos(lineArrowAngle)*dx
1039
0
             + 0.5*w*sin(lineArrowAngle)*dy);
1040
0
    appearBuf->append("S\n");
1041
0
    break;
1042
150
  }
1043
150
}
1044
1045
// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1046
// <cmd> is used to draw the circle ("f", "s", or "b").
1047
0
void Annot::drawCircle(double cx, double cy, double r, const char *cmd) {
1048
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1049
0
         cx + r, cy);
1050
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1051
0
         cx + r, cy + bezierCircle * r,
1052
0
         cx + bezierCircle * r, cy + r,
1053
0
         cx, cy + r);
1054
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1055
0
         cx - bezierCircle * r, cy + r,
1056
0
         cx - r, cy + bezierCircle * r,
1057
0
         cx - r, cy);
1058
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1059
0
         cx - r, cy - bezierCircle * r,
1060
0
         cx - bezierCircle * r, cy - r,
1061
0
         cx, cy - r);
1062
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1063
0
         cx + bezierCircle * r, cy - r,
1064
0
         cx + r, cy - bezierCircle * r,
1065
0
         cx + r, cy);
1066
0
  appearBuf->appendf("{0:s}\n", cmd);
1067
0
}
1068
1069
// Draw the top-left half of an (approximate) circle of radius <r>
1070
// centered at (<cx>, <cy>).
1071
0
void Annot::drawCircleTopLeft(double cx, double cy, double r) {
1072
0
  double r2;
1073
1074
0
  r2 = r / sqrt(2.0);
1075
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1076
0
         cx + r2, cy + r2);
1077
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1078
0
         cx + (1 - bezierCircle) * r2,
1079
0
         cy + (1 + bezierCircle) * r2,
1080
0
         cx - (1 - bezierCircle) * r2,
1081
0
         cy + (1 + bezierCircle) * r2,
1082
0
         cx - r2,
1083
0
         cy + r2);
1084
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1085
0
         cx - (1 + bezierCircle) * r2,
1086
0
         cy + (1 - bezierCircle) * r2,
1087
0
         cx - (1 + bezierCircle) * r2,
1088
0
         cy - (1 - bezierCircle) * r2,
1089
0
         cx - r2,
1090
0
         cy - r2);
1091
0
  appearBuf->append("S\n");
1092
0
}
1093
1094
// Draw the bottom-right half of an (approximate) circle of radius <r>
1095
// centered at (<cx>, <cy>).
1096
0
void Annot::drawCircleBottomRight(double cx, double cy, double r) {
1097
0
  double r2;
1098
1099
0
  r2 = r / sqrt(2.0);
1100
0
  appearBuf->appendf("{0:.4f} {1:.4f} m\n",
1101
0
         cx - r2, cy - r2);
1102
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1103
0
         cx - (1 - bezierCircle) * r2,
1104
0
         cy - (1 + bezierCircle) * r2,
1105
0
         cx + (1 - bezierCircle) * r2,
1106
0
         cy - (1 + bezierCircle) * r2,
1107
0
         cx + r2,
1108
0
         cy - r2);
1109
0
  appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
1110
0
         cx + (1 + bezierCircle) * r2,
1111
0
         cy - (1 - bezierCircle) * r2,
1112
0
         cx + (1 + bezierCircle) * r2,
1113
0
         cy + (1 - bezierCircle) * r2,
1114
0
         cx + r2,
1115
0
         cy + r2);
1116
0
  appearBuf->append("S\n");
1117
0
}
1118
1119
void Annot::drawText(GString *text, GString *da, int quadding, double margin,
1120
60
         int rot) {
1121
60
  GString *text2, *tok;
1122
60
  GList *daToks;
1123
60
  const char *charName;
1124
60
  double dx, dy, fontSize, fontSize2, x, y, w;
1125
60
  Gushort charWidth;
1126
60
  int tfPos, tmPos, i, j, c;
1127
1128
  // check for a Unicode string
1129
  //~ this currently drops all non-Latin1 characters
1130
60
  if (text->getLength() >= 2 &&
1131
51
      text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
1132
15
    text2 = new GString();
1133
9.00k
    for (i = 2; i+1 < text->getLength(); i += 2) {
1134
8.98k
      c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
1135
8.98k
      if (c <= 0xff) {
1136
587
  text2->append((char)c);
1137
8.40k
      } else {
1138
8.40k
  text2->append('?');
1139
8.40k
      }
1140
8.98k
    }
1141
45
  } else {
1142
45
    text2 = text;
1143
45
  }
1144
1145
  // parse the default appearance string
1146
60
  tfPos = tmPos = -1;
1147
60
  if (da) {
1148
60
    daToks = new GList();
1149
60
    i = 0;
1150
1.17k
    while (i < da->getLength()) {
1151
3.16k
      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
1152
2.04k
  ++i;
1153
2.04k
      }
1154
1.11k
      if (i < da->getLength()) {
1155
1.11k
  for (j = i + 1;
1156
11.2k
       j < da->getLength() && !Lexer::isSpace(da->getChar(j));
1157
10.1k
       ++j) ;
1158
1.11k
  daToks->append(new GString(da, i, j - i));
1159
1.11k
  i = j;
1160
1.11k
      }
1161
1.11k
    }
1162
1.11k
    for (i = 2; i < daToks->getLength(); ++i) {
1163
1.05k
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
1164
14
  tfPos = i - 2;
1165
1.04k
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
1166
0
  tmPos = i - 6;
1167
0
      }
1168
1.05k
    }
1169
60
  } else {
1170
0
    daToks = NULL;
1171
0
  }
1172
1173
  // get the font and font size
1174
60
  fontSize = 0;
1175
60
  if (tfPos >= 0) {
1176
    //~ where do we look up the font?
1177
13
    tok = (GString *)daToks->get(tfPos);
1178
13
    tok->clear();
1179
13
    tok->append("/xpdf_default_font");
1180
13
    tok = (GString *)daToks->get(tfPos + 1);
1181
13
    fontSize = atof(tok->getCString());
1182
47
  } else {
1183
47
    error(errSyntaxError, -1,
1184
47
    "Missing 'Tf' operator in annotation's DA string");
1185
47
    daToks->append(new GString("/xpdf_default_font"));
1186
47
    daToks->append(new GString("10"));
1187
47
    daToks->append(new GString("Tf"));
1188
47
  }
1189
1190
  // setup
1191
60
  appearBuf->append("q\n");
1192
60
  if (rot == 90) {
1193
0
    appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin);
1194
0
    dx = yMax - yMin;
1195
0
    dy = xMax - xMin;
1196
60
  } else if (rot == 180) {
1197
0
    appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n",
1198
0
           xMax - xMin, yMax - yMin);
1199
0
    dx = xMax - yMax;
1200
0
    dy = yMax - yMin;
1201
60
  } else if (rot == 270) {
1202
0
    appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
1203
0
    dx = yMax - yMin;
1204
0
    dy = xMax - xMin;
1205
60
  } else { // assume rot == 0
1206
60
    dx = xMax - xMin;
1207
60
    dy = yMax - yMin;
1208
60
  }
1209
60
  appearBuf->append("BT\n");
1210
1211
  // compute string width
1212
  //~ this assumes we're substituting Helvetica/WinAnsiEncoding for everything
1213
60
  w = 0;
1214
35.3k
  for (i = 0; i < text2->getLength(); ++i) {
1215
35.3k
    charName = winAnsiEncoding[text->getChar(i) & 0xff];
1216
35.3k
    if (charName && builtinFonts[4].widths->getWidth(charName, &charWidth)) {
1217
27.7k
      w += charWidth;
1218
27.7k
    } else {
1219
7.62k
      w += 0.5;
1220
7.62k
    }
1221
35.3k
  }
1222
1223
  // compute font autosize
1224
60
  if (fontSize == 0) {
1225
49
    fontSize = dy - 2 * margin;
1226
49
    fontSize2 = (dx - 2 * margin) / w;
1227
49
    if (fontSize2 < fontSize) {
1228
45
      fontSize = fontSize2;
1229
45
    }
1230
49
    fontSize = floor(fontSize);
1231
49
    if (tfPos >= 0) {
1232
2
      tok = (GString *)daToks->get(tfPos + 1);
1233
2
      tok->clear();
1234
2
      tok->appendf("{0:.4f}", fontSize);
1235
2
    }
1236
49
  }
1237
1238
  // compute text start position
1239
60
  w *= fontSize;
1240
60
  switch (quadding) {
1241
47
  case 0:
1242
60
  default:
1243
60
    x = margin + 2;
1244
60
    break;
1245
0
  case 1:
1246
0
    x = (dx - w) / 2;
1247
0
    break;
1248
0
  case 2:
1249
0
    x = dx - margin - 2 - w;
1250
0
    break;
1251
60
  }
1252
60
  y = 0.5 * dy - 0.4 * fontSize;
1253
1254
  // set the font matrix
1255
60
  if (tmPos >= 0) {
1256
0
    tok = (GString *)daToks->get(tmPos + 4);
1257
0
    tok->clear();
1258
0
    tok->appendf("{0:.4f}", x);
1259
0
    tok = (GString *)daToks->get(tmPos + 5);
1260
0
    tok->clear();
1261
0
    tok->appendf("{0:.4f}", y);
1262
0
  }
1263
  
1264
  // write the DA string
1265
60
  if (daToks) {
1266
1.31k
    for (i = 0; i < daToks->getLength(); ++i) {
1267
1.25k
      appearBuf->append((GString *)daToks->get(i))->append(' ');
1268
1.25k
    }
1269
60
  }
1270
1271
  // write the font matrix (if not part of the DA string)
1272
60
  if (tmPos < 0) {
1273
60
    appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
1274
60
  }
1275
1276
  // write the text string
1277
60
  appearBuf->append('(');
1278
35.3k
  for (i = 0; i < text2->getLength(); ++i) {
1279
35.3k
    c = text2->getChar(i) & 0xff;
1280
35.3k
    if (c == '(' || c == ')' || c == '\\') {
1281
252
      appearBuf->append('\\');
1282
252
      appearBuf->append((char)c);
1283
35.0k
    } else if (c < 0x20 || c >= 0x80) {
1284
9.07k
      appearBuf->appendf("\\{0:03o}", c);
1285
26.0k
    } else {
1286
26.0k
      appearBuf->append((char)c);
1287
26.0k
    }
1288
35.3k
  }
1289
60
  appearBuf->append(") Tj\n");
1290
1291
  // cleanup
1292
60
  appearBuf->append("ET\n");
1293
60
  appearBuf->append("Q\n");
1294
1295
60
  if (daToks) {
1296
60
    deleteGList(daToks, GString);
1297
60
  }
1298
60
  if (text2 != text) {
1299
15
    delete text2;
1300
15
  }
1301
60
}
1302
1303
9.32k
void Annot::draw(Gfx *gfx, GBool printing) {
1304
9.32k
  GBool oc, isLink;
1305
1306
  // check the flags
1307
9.32k
  if ((flags & annotFlagHidden) ||
1308
9.17k
      (printing && !(flags & annotFlagPrint)) ||
1309
9.01k
      (!printing && (flags & annotFlagNoView))) {
1310
319
    return;
1311
319
  }
1312
1313
  // check the optional content entry
1314
9.00k
  if (doc->getOptionalContent()->evalOCObject(&ocObj, &oc) && !oc) {
1315
0
    return;
1316
0
  }
1317
1318
  // draw the appearance stream
1319
9.00k
  isLink = type && !type->cmp("Link");
1320
9.00k
  gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
1321
9.00k
     xMin, yMin, xMax, yMax);
1322
9.00k
}
1323
1324
372
Object *Annot::getObject(Object *obj) {
1325
372
  if (ref.num >= 0) {
1326
346
    xref->fetch(ref.num, ref.gen, obj);
1327
346
  } else {
1328
26
    obj->initNull();
1329
26
  }
1330
372
  return obj;
1331
372
}
1332
1333
//------------------------------------------------------------------------
1334
// PageAnnots
1335
//------------------------------------------------------------------------
1336
1337
class PageAnnots {
1338
public:
1339
1340
  PageAnnots();
1341
  ~PageAnnots();
1342
1343
  GList *annots;    // list of annots on the page [Annot]
1344
  GBool appearancesGenerated; // set after appearances have been generated
1345
};
1346
1347
99.0k
PageAnnots::PageAnnots() {
1348
99.0k
  annots = new GList();
1349
99.0k
  appearancesGenerated = gFalse;
1350
99.0k
}
1351
1352
99.0k
PageAnnots::~PageAnnots() {
1353
99.0k
  deleteGList(annots, Annot);
1354
99.0k
}
1355
1356
//------------------------------------------------------------------------
1357
// Annots
1358
//------------------------------------------------------------------------
1359
1360
12.6k
Annots::Annots(PDFDoc *docA) {
1361
12.6k
  doc = docA;
1362
12.6k
  pageAnnots = (PageAnnots **)gmallocn(doc->getNumPages(), sizeof(PageAnnots*));
1363
281k
  for (int page = 1; page <= doc->getNumPages(); ++page) {
1364
268k
    pageAnnots[page - 1] = NULL;
1365
268k
  }
1366
12.6k
  formFieldRefsSize = 0;
1367
12.6k
  formFieldRefs = NULL;
1368
12.6k
#if MULTITHREADED
1369
12.6k
  gInitMutex(&mutex);
1370
12.6k
#endif
1371
12.6k
}
1372
1373
12.6k
Annots::~Annots() {
1374
281k
  for (int page = 1; page <= doc->getNumPages(); ++page) {
1375
268k
    delete pageAnnots[page - 1];
1376
268k
  }
1377
12.6k
  gfree(pageAnnots);
1378
12.6k
  gfree(formFieldRefs);
1379
12.6k
#if MULTITHREADED
1380
12.6k
  gDestroyMutex(&mutex);
1381
12.6k
#endif
1382
12.6k
}
1383
1384
207k
void Annots::loadAnnots(int page) {
1385
207k
#if MULTITHREADED
1386
207k
  gLockMutex(&mutex);
1387
207k
#endif
1388
207k
  if (pageAnnots[page - 1]) {
1389
108k
#if MULTITHREADED
1390
108k
    gUnlockMutex(&mutex);
1391
108k
#endif
1392
108k
    return;
1393
108k
  }
1394
1395
99.0k
  pageAnnots[page - 1] = new PageAnnots();
1396
1397
99.0k
  Object annotsObj;
1398
99.0k
  doc->getCatalog()->getPage(page)->getAnnots(&annotsObj);
1399
99.0k
  if (!annotsObj.isArray()) {
1400
89.6k
    annotsObj.free();
1401
89.6k
#if MULTITHREADED
1402
89.6k
    gUnlockMutex(&mutex);
1403
89.6k
#endif
1404
89.6k
    return;
1405
89.6k
  }
1406
1407
9.36k
  loadFormFieldRefs();
1408
1409
508k
  for (int i = 0; i < annotsObj.arrayGetLength(); ++i) {
1410
499k
    Object annotObj;
1411
499k
    Ref annotRef;
1412
499k
    if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
1413
98.5k
      annotRef = annotObj.getRef();
1414
98.5k
      annotObj.free();
1415
98.5k
      annotsObj.arrayGet(i, &annotObj);
1416
400k
    } else {
1417
400k
      annotRef.num = annotRef.gen = -1;
1418
400k
    }
1419
499k
    if (!annotObj.isDict()) {
1420
463k
      annotObj.free();
1421
463k
      continue;
1422
463k
    }
1423
1424
    // skip any annotations which are used as AcroForm fields --
1425
    // they'll be rendered by the AcroForm module
1426
35.9k
    if (annotRef.num >= 0 && annotRef.num < formFieldRefsSize &&
1427
9.37k
  formFieldRefs[annotRef.num]) {
1428
6.73k
      annotObj.free();
1429
6.73k
      continue;
1430
6.73k
    }
1431
1432
29.1k
    Annot *annot = new Annot(doc, annotObj.getDict(), &annotRef);
1433
29.1k
    annotObj.free();
1434
29.1k
    if (annot->isOk()) {
1435
9.32k
      pageAnnots[page - 1]->annots->append(annot);
1436
19.8k
    } else {
1437
19.8k
      delete annot;
1438
19.8k
    }
1439
29.1k
  }
1440
1441
9.36k
  annotsObj.free();
1442
1443
9.36k
#if MULTITHREADED
1444
9.36k
  gUnlockMutex(&mutex);
1445
9.36k
#endif
1446
9.36k
}
1447
1448
// Build a set of object refs for AcroForm fields.
1449
9.36k
void Annots::loadFormFieldRefs() {
1450
9.36k
  if (formFieldRefs) {
1451
2.75k
    return;
1452
2.75k
  }
1453
1454
6.61k
  AcroForm *form = doc->getCatalog()->getForm();
1455
6.61k
  if (!form) {
1456
5.97k
    return;
1457
5.97k
  }
1458
1459
635
  int newFormFieldRefsSize = 256;
1460
3.92k
  for (int i = 0; i < form->getNumFields(); ++i) {
1461
3.28k
    AcroFormField *field = form->getField(i);
1462
3.28k
    Object fieldRef;
1463
3.28k
    field->getFieldRef(&fieldRef);
1464
3.28k
    if (fieldRef.isRef()) {
1465
3.28k
      if (fieldRef.getRefNum() >= formFieldRefsSize) {
1466
644
  while (fieldRef.getRefNum() >= newFormFieldRefsSize &&
1467
34
         newFormFieldRefsSize <= INT_MAX / 2) {
1468
34
    newFormFieldRefsSize *= 2;
1469
34
  }
1470
610
  if (fieldRef.getRefNum() >= newFormFieldRefsSize) {
1471
0
    continue;
1472
0
  }
1473
610
  formFieldRefs = (char *)grealloc(formFieldRefs, newFormFieldRefsSize);
1474
175k
  for (int j = formFieldRefsSize; j < newFormFieldRefsSize; ++j) {
1475
175k
    formFieldRefs[j] = (char)0;
1476
175k
  }
1477
610
  formFieldRefsSize = newFormFieldRefsSize;
1478
610
      }
1479
3.28k
      formFieldRefs[fieldRef.getRefNum()] = (char)1;
1480
3.28k
    }
1481
3.28k
    fieldRef.free();
1482
3.28k
  }
1483
635
}
1484
1485
99.0k
int Annots::getNumAnnots(int page) {
1486
99.0k
  loadAnnots(page);
1487
99.0k
  return pageAnnots[page - 1]->annots->getLength();
1488
99.0k
}
1489
1490
9.32k
Annot *Annots::getAnnot(int page, int idx) {
1491
9.32k
  loadAnnots(page);
1492
9.32k
  return (Annot *)pageAnnots[page - 1]->annots->get(idx);
1493
9.32k
}
1494
1495
0
Annot *Annots::find(int page, double x, double y) {
1496
0
  loadAnnots(page);
1497
0
  PageAnnots *pa = pageAnnots[page - 1];
1498
0
  for (int i = pa->annots->getLength() - 1; i >= 0; --i) {
1499
0
    Annot *annot = (Annot *)pa->annots->get(i);
1500
0
    if (annot->inRect(x, y)) {
1501
0
      return annot;
1502
0
    }
1503
0
  }
1504
0
  return NULL;
1505
0
}
1506
1507
0
int Annots::findIdx(int page, double x, double y) {
1508
0
  loadAnnots(page);
1509
0
  PageAnnots *pa = pageAnnots[page - 1];
1510
0
  for (int i = pa->annots->getLength() - 1; i >= 0; --i) {
1511
0
    Annot *annot = (Annot *)pa->annots->get(i);
1512
0
    if (annot->inRect(x, y)) {
1513
0
      return i;
1514
0
    }
1515
0
  }
1516
0
  return -1;
1517
0
}
1518
1519
0
void Annots::add(int page, Object *annotObj) {
1520
0
  if (!annotObj->isDict()) {
1521
0
    return;
1522
0
  }
1523
0
  Ref annotRef = {-1, -1};
1524
0
  Annot *annot = new Annot(doc, annotObj->getDict(), &annotRef);
1525
0
  if (annot->isOk()) {
1526
0
    annot->generateAnnotAppearance(annotObj);
1527
0
    pageAnnots[page - 1]->annots->append(annot);
1528
0
  } else {
1529
0
    delete annot;
1530
0
  }
1531
0
}
1532
1533
99.0k
void Annots::generateAnnotAppearances(int page) {
1534
99.0k
  loadAnnots(page);
1535
99.0k
  PageAnnots *pa = pageAnnots[page - 1];
1536
99.0k
#if MULTITHREADED
1537
99.0k
  gLockMutex(&mutex);
1538
99.0k
#endif
1539
99.0k
  if (!pa->appearancesGenerated) {
1540
108k
    for (int i = 0; i < pa->annots->getLength(); ++i) {
1541
9.32k
      Annot *annot = (Annot *)pa->annots->get(i);
1542
9.32k
      annot->generateAnnotAppearance(NULL);
1543
9.32k
    }
1544
99.0k
    pa->appearancesGenerated = gTrue;
1545
99.0k
  }
1546
99.0k
#if MULTITHREADED
1547
99.0k
  gUnlockMutex(&mutex);
1548
99.0k
#endif
1549
99.0k
}