Coverage Report

Created: 2026-02-04 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Page.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Page.cc
4
//
5
// Copyright 1996-2007 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stddef.h>
12
#include "gmempp.h"
13
#include "Trace.h"
14
#include "GlobalParams.h"
15
#include "Object.h"
16
#include "Array.h"
17
#include "Dict.h"
18
#include "PDFDoc.h"
19
#include "XRef.h"
20
#include "Link.h"
21
#include "OutputDev.h"
22
#ifndef PDF_PARSER_ONLY
23
#include "Gfx.h"
24
#include "GfxState.h"
25
#include "Annot.h"
26
#include "AcroForm.h"
27
#endif
28
#include "Error.h"
29
#include "Catalog.h"
30
#include "Page.h"
31
32
//------------------------------------------------------------------------
33
// PDFRectangle
34
//------------------------------------------------------------------------
35
36
1.08M
void PDFRectangle::clipTo(PDFRectangle *rect) {
37
1.08M
  if (x1 < rect->x1) {
38
14.0k
    x1 = rect->x1;
39
1.07M
  } else if (x1 > rect->x2) {
40
166k
    x1 = rect->x2;
41
166k
  }
42
1.08M
  if (x2 < rect->x1) {
43
1.40k
    x2 = rect->x1;
44
1.08M
  } else if (x2 > rect->x2) {
45
202k
    x2 = rect->x2;
46
202k
  }
47
1.08M
  if (y1 < rect->y1) {
48
12.4k
    y1 = rect->y1;
49
1.07M
  } else if (y1 > rect->y2) {
50
2.10k
    y1 = rect->y2;
51
2.10k
  }
52
1.08M
  if (y2 < rect->y1) {
53
2.01k
    y2 = rect->y1;
54
1.08M
  } else if (y2 > rect->y2) {
55
205k
    y2 = rect->y2;
56
205k
  }
57
1.08M
}
58
59
//------------------------------------------------------------------------
60
// PageAttrs
61
//------------------------------------------------------------------------
62
63
283k
PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict, XRef *xref) {
64
283k
  Object obj1;
65
66
  // get old/default values
67
283k
  if (attrs) {
68
69.5k
    mediaBox = attrs->mediaBox;
69
69.5k
    cropBox = attrs->cropBox;
70
69.5k
    haveCropBox = attrs->haveCropBox;
71
69.5k
    rotate = attrs->rotate;
72
213k
  } else {
73
    // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
74
    // but some (non-compliant) PDF files don't specify a MediaBox
75
213k
    mediaBox.x1 = 0;
76
213k
    mediaBox.y1 = 0;
77
213k
    mediaBox.x2 = 612;
78
213k
    mediaBox.y2 = 792;
79
213k
    cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
80
213k
    haveCropBox = gFalse;
81
213k
    rotate = 0;
82
213k
  }
83
84
  // media box
85
283k
  readBox(dict, "MediaBox", &mediaBox);
86
87
  // crop box
88
283k
  if (readBox(dict, "CropBox", &cropBox)) {
89
57.3k
    haveCropBox = gTrue;
90
57.3k
  }
91
283k
  if (!haveCropBox) {
92
226k
    cropBox = mediaBox;
93
226k
  }
94
95
  // other boxes
96
283k
  bleedBox = cropBox;
97
283k
  readBox(dict, "BleedBox", &bleedBox);
98
283k
  trimBox = cropBox;
99
283k
  readBox(dict, "TrimBox", &trimBox);
100
283k
  artBox = cropBox;
101
283k
  readBox(dict, "ArtBox", &artBox);
102
103
  // rotate
104
283k
  dict->lookup("Rotate", &obj1);
105
283k
  if (obj1.isInt()) {
106
23.7k
    rotate = obj1.getInt();
107
23.7k
  }
108
283k
  obj1.free();
109
2.10G
  while (rotate < 0) {
110
2.10G
    rotate += 360;
111
2.10G
  }
112
65.1M
  while (rotate >= 360) {
113
64.8M
    rotate -= 360;
114
64.8M
  }
115
116
  // misc attributes
117
283k
  dict->lookup("LastModified", &lastModified);
118
283k
  dict->lookup("BoxColorInfo", &boxColorInfo);
119
283k
  dict->lookup("Group", &group);
120
283k
  dict->lookup("Metadata", &metadata);
121
283k
  dict->lookup("PieceInfo", &pieceInfo);
122
283k
  dict->lookup("SeparationInfo", &separationInfo);
123
283k
  if (dict->lookup("UserUnit", &obj1)->isNum()) {
124
1.96k
    userUnit = obj1.getNum();
125
1.96k
    if (userUnit < 1) {
126
1.57k
      userUnit = 1;
127
1.57k
    }
128
281k
  } else {
129
281k
    userUnit = 1;
130
281k
  }
131
283k
  obj1.free();
132
133
  // resource dictionary
134
283k
  Object childResDictObj;
135
283k
  dict->lookup("Resources", &childResDictObj);
136
283k
  if (attrs && attrs->resources.isDict() && childResDictObj.isDict()) {
137
    // merge this node's resources into the parent's resources
138
    // (some PDF files violate the PDF spec and expect this merging)
139
6.13k
    resources.initDict(xref);
140
6.13k
    Dict *resDict = resources.getDict();
141
6.13k
    Dict *parentResDict = attrs->resources.getDict();
142
14.4k
    for (int i = 0; i < parentResDict->getLength(); ++i) {
143
8.29k
      char *resType = parentResDict->getKey(i);
144
8.29k
      Object subdictObj1;
145
8.29k
      if (parentResDict->getVal(i, &subdictObj1)->isDict()) {
146
4.42k
  Dict *subdict1 = subdictObj1.getDict();
147
4.42k
  Object subdictObj2;
148
4.42k
  subdictObj2.initDict(xref);
149
4.42k
  Dict *subdict2 = subdictObj2.getDict();
150
16.1k
  for (int j = 0; j < subdict1->getLength(); ++j) {
151
11.7k
    subdict1->getValNF(j, &obj1);
152
11.7k
    subdict2->add(copyString(subdict1->getKey(j)), &obj1);
153
11.7k
  }
154
4.42k
  resDict->add(copyString(resType), &subdictObj2);
155
4.42k
      }
156
8.29k
      subdictObj1.free();
157
8.29k
    }
158
6.13k
    Dict *childResDict = childResDictObj.getDict();
159
85.0k
    for (int i = 0; i < childResDict->getLength(); ++i) {
160
78.9k
      char *resType = childResDict->getKey(i);
161
78.9k
      Object subdictObj1;
162
78.9k
      if (childResDict->getVal(i, &subdictObj1)->isDict()) {
163
27.9k
  Object subdictObj2;
164
27.9k
  if (resDict->lookup(resType, &subdictObj2)->isDict()) {
165
4.28k
    Dict *subdict1 = subdictObj1.getDict();
166
4.28k
    Dict *subdict2 = subdictObj2.getDict();
167
15.6k
    for (int j = 0; j < subdict1->getLength(); ++j) {
168
11.3k
      subdict1->getValNF(j, &obj1);
169
11.3k
      subdict2->add(copyString(subdict1->getKey(j)), &obj1);
170
11.3k
    }
171
4.28k
    subdictObj2.free();
172
23.6k
  } else {
173
23.6k
    subdictObj2.free();
174
23.6k
    resDict->add(copyString(resType), subdictObj1.copy(&subdictObj2));
175
23.6k
  }
176
27.9k
      }
177
78.9k
      subdictObj1.free();
178
78.9k
    }
179
277k
  } else if (attrs && attrs->resources.isDict()) {
180
5.46k
    attrs->resources.copy(&resources);
181
271k
  } else if (childResDictObj.isDict()) {
182
45.4k
    childResDictObj.copy(&resources);
183
226k
  } else {
184
226k
    resources.initNull();
185
226k
  }
186
283k
  childResDictObj.free();
187
283k
}
188
189
165k
PageAttrs::PageAttrs() {
190
165k
  mediaBox.x1 = mediaBox.y1 = 0;
191
165k
  mediaBox.x2 = mediaBox.y2 = 50;
192
165k
  cropBox = mediaBox;
193
165k
  haveCropBox = gFalse;
194
165k
  bleedBox = cropBox;
195
165k
  trimBox = cropBox;
196
165k
  artBox = cropBox;
197
165k
  rotate = 0;
198
165k
  lastModified.initNull();
199
165k
  boxColorInfo.initNull();
200
165k
  group.initNull();
201
165k
  metadata.initNull();
202
165k
  pieceInfo.initNull();
203
165k
  separationInfo.initNull();
204
165k
  userUnit = 1;
205
165k
  resources.initNull();
206
165k
}
207
208
449k
PageAttrs::~PageAttrs() {
209
449k
  lastModified.free();
210
449k
  boxColorInfo.free();
211
449k
  group.free();
212
449k
  metadata.free();
213
449k
  pieceInfo.free();
214
449k
  separationInfo.free();
215
449k
  resources.free();
216
449k
}
217
218
271k
void PageAttrs::clipBoxes() {
219
271k
  cropBox.clipTo(&mediaBox);
220
271k
  bleedBox.clipTo(&mediaBox);
221
271k
  trimBox.clipTo(&mediaBox);
222
271k
  artBox.clipTo(&mediaBox);
223
271k
}
224
225
1.41M
GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) {
226
1.41M
  PDFRectangle tmp;
227
1.41M
  double t;
228
1.41M
  Object obj1, obj2;
229
1.41M
  GBool ok;
230
231
1.41M
  dict->lookup(key, &obj1);
232
1.41M
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
233
106k
    ok = gTrue;
234
106k
    obj1.arrayGet(0, &obj2);
235
106k
    if (obj2.isNum()) {
236
103k
      tmp.x1 = obj2.getNum();
237
103k
    } else {
238
3.21k
      ok = gFalse;
239
3.21k
    }
240
106k
    obj2.free();
241
106k
    obj1.arrayGet(1, &obj2);
242
106k
    if (obj2.isNum()) {
243
105k
      tmp.y1 = obj2.getNum();
244
105k
    } else {
245
1.23k
      ok = gFalse;
246
1.23k
    }
247
106k
    obj2.free();
248
106k
    obj1.arrayGet(2, &obj2);
249
106k
    if (obj2.isNum()) {
250
105k
      tmp.x2 = obj2.getNum();
251
105k
    } else {
252
1.46k
      ok = gFalse;
253
1.46k
    }
254
106k
    obj2.free();
255
106k
    obj1.arrayGet(3, &obj2);
256
106k
    if (obj2.isNum()) {
257
104k
      tmp.y2 = obj2.getNum();
258
104k
    } else {
259
2.30k
      ok = gFalse;
260
2.30k
    }
261
106k
    obj2.free();
262
106k
    if (ok) {
263
      // limit the box coords so they can be converted to 32-bit ints later
264
99.1k
      if (tmp.x1 < -1e9) {
265
19
  tmp.x1 = -1e9;
266
99.1k
      } else if (tmp.x1 > 1e9) {
267
8
  tmp.x1 = 1e9;
268
8
      }
269
99.1k
      if (tmp.y1 < -1e9) {
270
7
  tmp.y1 = -1e9;
271
99.1k
      } else if (tmp.y1 > 1e9) {
272
42
  tmp.y1 = 1e9;
273
42
      }
274
99.1k
      if (tmp.x2 < -1e9) {
275
54
  tmp.x2 = -1e9;
276
99.1k
      } else if (tmp.x2 > 1e9) {
277
10
  tmp.x2 = 1e9;
278
10
      }
279
99.1k
      if (tmp.y2 < -1e9) {
280
228
  tmp.y2 = -1e9;
281
98.9k
      } else if (tmp.y2 > 1e9) {
282
2.06k
  tmp.y2 = 1e9;
283
2.06k
      }
284
99.1k
      if (tmp.x1 > tmp.x2) {
285
42.1k
  t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
286
42.1k
      }
287
99.1k
      if (tmp.y1 > tmp.y2) {
288
41.7k
  t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
289
41.7k
      }
290
99.1k
      *box = tmp;
291
99.1k
    }
292
1.31M
  } else {
293
1.31M
    ok = gFalse;
294
1.31M
  }
295
1.41M
  obj1.free();
296
1.41M
  return ok;
297
1.41M
}
298
299
//------------------------------------------------------------------------
300
// Page
301
//------------------------------------------------------------------------
302
303
271k
Page::Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA) {
304
271k
  ok = gTrue;
305
271k
  doc = docA;
306
271k
  xref = doc->getXRef();
307
271k
  num = numA;
308
309
  // get attributes
310
271k
  attrs = attrsA;
311
271k
  attrs->clipBoxes();
312
313
  // annotations
314
271k
  pageDict->lookupNF("Annots", &annots);
315
271k
  if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
316
279
    error(errSyntaxError, -1,
317
279
    "Page annotations object (page {0:d}) is wrong type ({1:s})",
318
279
    num, annots.getTypeName());
319
279
    annots.free();
320
279
    goto err2;
321
279
  }
322
323
  // contents
324
271k
  pageDict->lookupNF("Contents", &contents);
325
271k
  if (!(contents.isRef() || contents.isArray() ||
326
219k
  contents.isNull())) {
327
478
    error(errSyntaxError, -1,
328
478
    "Page contents object (page {0:d}) is wrong type ({1:s})",
329
478
    num, contents.getTypeName());
330
478
    contents.free();
331
478
    goto err1;
332
478
  }
333
334
  // thumbnail
335
270k
  pageDict->lookupNF("Thumb", &thumbnail);
336
270k
  if (!thumbnail.isRef()) {
337
269k
    if (!thumbnail.isNull()) {
338
355
      thumbnail.free();
339
355
      thumbnail.initNull();
340
355
    }
341
269k
  }
342
343
270k
  return;
344
345
279
 err2:
346
279
  annots.initNull();
347
757
 err1:
348
757
  contents.initNull();
349
757
  thumbnail.initNull();
350
757
  ok = gFalse;
351
757
}
352
353
165k
Page::Page(PDFDoc *docA, int numA) {
354
165k
  doc = docA;
355
165k
  xref = doc->getXRef();
356
165k
  num = numA;
357
165k
  attrs = new PageAttrs();
358
165k
  annots.initNull();
359
165k
  contents.initNull();
360
165k
  thumbnail.initNull();
361
165k
  ok = gTrue;
362
165k
}
363
364
437k
Page::~Page() {
365
437k
  delete attrs;
366
437k
  annots.free();
367
437k
  contents.free();
368
437k
  thumbnail.free();
369
437k
}
370
371
232k
Links *Page::getLinks() {
372
232k
  Links *links;
373
232k
  Object obj;
374
375
232k
  links = new Links(getAnnots(&obj), doc->getCatalog()->getBaseURI());
376
232k
  obj.free();
377
232k
  return links;
378
232k
}
379
380
void Page::display(OutputDev *out, LocalParams *localParams,
381
       double hDPI, double vDPI,
382
       int rotate, GBool useMediaBox, GBool crop,
383
       GBool printing,
384
       GBool (*abortCheckCbk)(void *data),
385
232k
       void *abortCheckCbkData) {
386
232k
  displaySlice(out, localParams, hDPI, vDPI, rotate, useMediaBox, crop,
387
232k
         -1, -1, -1, -1, printing,
388
232k
         abortCheckCbk, abortCheckCbkData);
389
232k
}
390
391
void Page::displaySlice(OutputDev *out, LocalParams *localParams,
392
      double hDPI, double vDPI,
393
      int rotate, GBool useMediaBox, GBool crop,
394
      int sliceX, int sliceY, int sliceW, int sliceH,
395
      GBool printing,
396
      GBool (*abortCheckCbk)(void *data),
397
232k
      void *abortCheckCbkData) {
398
232k
#ifndef PDF_PARSER_ONLY
399
232k
  PDFRectangle *mediaBox, *cropBox;
400
232k
  PDFRectangle box;
401
232k
  Gfx *gfx;
402
232k
  Object obj;
403
232k
  AcroForm *form;
404
232k
  int i;
405
406
232k
  if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
407
232k
         sliceX, sliceY, sliceW, sliceH,
408
232k
         printing, abortCheckCbk, abortCheckCbkData)) {
409
0
    return;
410
0
  }
411
412
232k
  traceBegin(this, "begin page");
413
414
232k
  rotate += getRotate();
415
232k
  if (rotate >= 360) {
416
81.7k
    rotate -= 360;
417
150k
  } else if (rotate < 0) {
418
150k
    rotate += 360;
419
150k
  }
420
421
232k
  makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
422
232k
    sliceX, sliceY, sliceW, sliceH, &box, &crop);
423
232k
  cropBox = getCropBox();
424
425
232k
  if (globalParams->getPrintCommands()) {
426
0
    mediaBox = getMediaBox();
427
0
    printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
428
0
     mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
429
0
    printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
430
0
     cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
431
0
    printf("***** Rotate = %d\n", attrs->getRotate());
432
0
  }
433
434
232k
  gfx = new Gfx(doc, out, localParams, num, attrs->getResourceDict(),
435
232k
    hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
436
232k
    rotate, abortCheckCbk, abortCheckCbkData);
437
232k
  contents.fetch(xref, &obj);
438
232k
  if (!obj.isNull()) {
439
35.0k
    gfx->saveState();
440
35.0k
    gfx->display(&contents);
441
35.0k
    gfx->endOfPage();
442
35.0k
  }
443
232k
  obj.free();
444
445
  // draw (non-form) annotations
446
232k
  if (globalParams->getDrawAnnotations()) {
447
232k
    Annots *annots2 = doc->getAnnots();
448
232k
    annots2->generateAnnotAppearances(num);
449
232k
    int n = annots2->getNumAnnots(num);
450
232k
    if (n > 0) {
451
5.18k
      if (globalParams->getPrintCommands()) {
452
0
  printf("***** Annotations\n");
453
0
      }
454
15.5k
      for (i = 0; i < n; ++i) {
455
10.4k
  if (abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData)) {
456
0
    break;
457
0
  }
458
10.4k
  annots2->getAnnot(num, i)->draw(gfx, printing);
459
10.4k
      }
460
5.18k
    }
461
232k
  }
462
463
  // draw form fields
464
232k
  if (globalParams->getDrawFormFields()) {
465
232k
    if ((form = doc->getCatalog()->getForm())) {
466
54.5k
      if (!(abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData))) {
467
54.5k
  form->draw(num, gfx, printing);
468
54.5k
      }
469
54.5k
    }
470
232k
  }
471
472
232k
  delete gfx;
473
232k
#endif // PDF_PARSER_ONLY
474
475
232k
  traceEnd(this, "end page");
476
232k
}
477
478
void Page::makeBox(double hDPI, double vDPI, int rotate,
479
       GBool useMediaBox, GBool upsideDown,
480
       double sliceX, double sliceY, double sliceW, double sliceH,
481
232k
       PDFRectangle *box, GBool *crop) {
482
232k
  PDFRectangle *mediaBox, *cropBox, *baseBox;
483
232k
  double kx, ky;
484
485
232k
  mediaBox = getMediaBox();
486
232k
  cropBox = getCropBox();
487
232k
  if (sliceW >= 0 && sliceH >= 0) {
488
0
    baseBox = useMediaBox ? mediaBox : cropBox;
489
0
    kx = 72.0 / hDPI;
490
0
    ky = 72.0 / vDPI;
491
0
    if (rotate == 90) {
492
0
      if (upsideDown) {
493
0
  box->x1 = baseBox->x1 + ky * sliceY;
494
0
  box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
495
0
      } else {
496
0
  box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
497
0
  box->x2 = baseBox->x2 - ky * sliceY;
498
0
      }
499
0
      box->y1 = baseBox->y1 + kx * sliceX;
500
0
      box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
501
0
    } else if (rotate == 180) {
502
0
      box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
503
0
      box->x2 = baseBox->x2 - kx * sliceX;
504
0
      if (upsideDown) {
505
0
  box->y1 = baseBox->y1 + ky * sliceY;
506
0
  box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
507
0
      } else {
508
0
  box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
509
0
  box->y2 = baseBox->y2 - ky * sliceY;
510
0
      }
511
0
    } else if (rotate == 270) {
512
0
      if (upsideDown) {
513
0
  box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
514
0
  box->x2 = baseBox->x2 - ky * sliceY;
515
0
      } else {
516
0
  box->x1 = baseBox->x1 + ky * sliceY;
517
0
  box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
518
0
      }
519
0
      box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
520
0
      box->y2 = baseBox->y2 - kx * sliceX;
521
0
    } else {
522
0
      box->x1 = baseBox->x1 + kx * sliceX;
523
0
      box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
524
0
      if (upsideDown) {
525
0
  box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
526
0
  box->y2 = baseBox->y2 - ky * sliceY;
527
0
      } else {
528
0
  box->y1 = baseBox->y1 + ky * sliceY;
529
0
  box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
530
0
      }
531
0
    }
532
232k
  } else if (useMediaBox) {
533
64.1k
    *box = *mediaBox;
534
168k
  } else {
535
168k
    *box = *cropBox;
536
168k
    *crop = gFalse;
537
168k
  }
538
232k
}
539
540
0
void Page::processLinks(OutputDev *out) {
541
0
  Links *links;
542
0
  int i;
543
544
0
  links = getLinks();
545
0
  for (i = 0; i < links->getNumLinks(); ++i) {
546
0
    out->processLink(links->getLink(i));
547
0
  }
548
0
  delete links;
549
0
}
550
551
#ifndef PDF_PARSER_ONLY
552
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
553
0
       int rotate, GBool useMediaBox, GBool upsideDown) {
554
0
  GfxState *state;
555
0
  int i;
556
557
0
  rotate += getRotate();
558
0
  if (rotate >= 360) {
559
0
    rotate -= 360;
560
0
  } else if (rotate < 0) {
561
0
    rotate += 360;
562
0
  }
563
0
  state = new GfxState(NULL, hDPI, vDPI,
564
0
           useMediaBox ? getMediaBox() : getCropBox(),
565
0
           rotate, upsideDown);
566
0
  for (i = 0; i < 6; ++i) {
567
0
    ctm[i] = state->getCTM()[i];
568
0
  }
569
0
  delete state;
570
0
}
571
#endif
572