Coverage Report

Created: 2025-08-26 07:24

/src/xpdf-4.05/xpdf/Page.cc
Line
Count
Source (jump to first uncovered line)
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
9.19k
void PDFRectangle::clipTo(PDFRectangle *rect) {
37
9.19k
  if (x1 < rect->x1) {
38
0
    x1 = rect->x1;
39
9.19k
  } else if (x1 > rect->x2) {
40
12
    x1 = rect->x2;
41
12
  }
42
9.19k
  if (x2 < rect->x1) {
43
0
    x2 = rect->x1;
44
9.19k
  } else if (x2 > rect->x2) {
45
12
    x2 = rect->x2;
46
12
  }
47
9.19k
  if (y1 < rect->y1) {
48
12
    y1 = rect->y1;
49
9.18k
  } else if (y1 > rect->y2) {
50
0
    y1 = rect->y2;
51
0
  }
52
9.19k
  if (y2 < rect->y1) {
53
0
    y2 = rect->y1;
54
9.19k
  } else if (y2 > rect->y2) {
55
1.61k
    y2 = rect->y2;
56
1.61k
  }
57
9.19k
}
58
59
//------------------------------------------------------------------------
60
// PageAttrs
61
//------------------------------------------------------------------------
62
63
3.14k
PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict, XRef *xref) {
64
3.14k
  Object obj1;
65
66
  // get old/default values
67
3.14k
  if (attrs) {
68
1.83k
    mediaBox = attrs->mediaBox;
69
1.83k
    cropBox = attrs->cropBox;
70
1.83k
    haveCropBox = attrs->haveCropBox;
71
1.83k
    rotate = attrs->rotate;
72
1.83k
  } 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
1.31k
    mediaBox.x1 = 0;
76
1.31k
    mediaBox.y1 = 0;
77
1.31k
    mediaBox.x2 = 612;
78
1.31k
    mediaBox.y2 = 792;
79
1.31k
    cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
80
1.31k
    haveCropBox = gFalse;
81
1.31k
    rotate = 0;
82
1.31k
  }
83
84
  // media box
85
3.14k
  readBox(dict, "MediaBox", &mediaBox);
86
87
  // crop box
88
3.14k
  if (readBox(dict, "CropBox", &cropBox)) {
89
572
    haveCropBox = gTrue;
90
572
  }
91
3.14k
  if (!haveCropBox) {
92
2.56k
    cropBox = mediaBox;
93
2.56k
  }
94
95
  // other boxes
96
3.14k
  bleedBox = cropBox;
97
3.14k
  readBox(dict, "BleedBox", &bleedBox);
98
3.14k
  trimBox = cropBox;
99
3.14k
  readBox(dict, "TrimBox", &trimBox);
100
3.14k
  artBox = cropBox;
101
3.14k
  readBox(dict, "ArtBox", &artBox);
102
103
  // rotate
104
3.14k
  dict->lookup("Rotate", &obj1);
105
3.14k
  if (obj1.isInt()) {
106
749
    rotate = obj1.getInt();
107
749
  }
108
3.14k
  obj1.free();
109
3.61k
  while (rotate < 0) {
110
465
    rotate += 360;
111
465
  }
112
3.18k
  while (rotate >= 360) {
113
42
    rotate -= 360;
114
42
  }
115
116
  // misc attributes
117
3.14k
  dict->lookup("LastModified", &lastModified);
118
3.14k
  dict->lookup("BoxColorInfo", &boxColorInfo);
119
3.14k
  dict->lookup("Group", &group);
120
3.14k
  dict->lookup("Metadata", &metadata);
121
3.14k
  dict->lookup("PieceInfo", &pieceInfo);
122
3.14k
  dict->lookup("SeparationInfo", &separationInfo);
123
3.14k
  if (dict->lookup("UserUnit", &obj1)->isNum()) {
124
263
    userUnit = obj1.getNum();
125
263
    if (userUnit < 1) {
126
240
      userUnit = 1;
127
240
    }
128
2.88k
  } else {
129
2.88k
    userUnit = 1;
130
2.88k
  }
131
3.14k
  obj1.free();
132
133
  // resource dictionary
134
3.14k
  Object childResDictObj;
135
3.14k
  dict->lookup("Resources", &childResDictObj);
136
3.14k
  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
102
    resources.initDict(xref);
140
102
    Dict *resDict = resources.getDict();
141
102
    Dict *parentResDict = attrs->resources.getDict();
142
371
    for (int i = 0; i < parentResDict->getLength(); ++i) {
143
269
      char *resType = parentResDict->getKey(i);
144
269
      Object subdictObj1;
145
269
      if (parentResDict->getVal(i, &subdictObj1)->isDict()) {
146
95
  Dict *subdict1 = subdictObj1.getDict();
147
95
  Object subdictObj2;
148
95
  subdictObj2.initDict(xref);
149
95
  Dict *subdict2 = subdictObj2.getDict();
150
511
  for (int j = 0; j < subdict1->getLength(); ++j) {
151
416
    subdict1->getValNF(j, &obj1);
152
416
    subdict2->add(copyString(subdict1->getKey(j)), &obj1);
153
416
  }
154
95
  resDict->add(copyString(resType), &subdictObj2);
155
95
      }
156
269
      subdictObj1.free();
157
269
    }
158
102
    Dict *childResDict = childResDictObj.getDict();
159
669
    for (int i = 0; i < childResDict->getLength(); ++i) {
160
567
      char *resType = childResDict->getKey(i);
161
567
      Object subdictObj1;
162
567
      if (childResDict->getVal(i, &subdictObj1)->isDict()) {
163
181
  Object subdictObj2;
164
181
  if (resDict->lookup(resType, &subdictObj2)->isDict()) {
165
67
    Dict *subdict1 = subdictObj1.getDict();
166
67
    Dict *subdict2 = subdictObj2.getDict();
167
397
    for (int j = 0; j < subdict1->getLength(); ++j) {
168
330
      subdict1->getValNF(j, &obj1);
169
330
      subdict2->add(copyString(subdict1->getKey(j)), &obj1);
170
330
    }
171
67
    subdictObj2.free();
172
114
  } else {
173
114
    subdictObj2.free();
174
114
    resDict->add(copyString(resType), subdictObj1.copy(&subdictObj2));
175
114
  }
176
181
      }
177
567
      subdictObj1.free();
178
567
    }
179
3.04k
  } else if (attrs && attrs->resources.isDict()) {
180
79
    attrs->resources.copy(&resources);
181
2.96k
  } else if (childResDictObj.isDict()) {
182
844
    childResDictObj.copy(&resources);
183
2.12k
  } else {
184
2.12k
    resources.initNull();
185
2.12k
  }
186
3.14k
  childResDictObj.free();
187
3.14k
}
188
189
23.3k
PageAttrs::PageAttrs() {
190
23.3k
  mediaBox.x1 = mediaBox.y1 = 0;
191
23.3k
  mediaBox.x2 = mediaBox.y2 = 50;
192
23.3k
  cropBox = mediaBox;
193
23.3k
  haveCropBox = gFalse;
194
23.3k
  bleedBox = cropBox;
195
23.3k
  trimBox = cropBox;
196
23.3k
  artBox = cropBox;
197
23.3k
  rotate = 0;
198
23.3k
  lastModified.initNull();
199
23.3k
  boxColorInfo.initNull();
200
23.3k
  group.initNull();
201
23.3k
  metadata.initNull();
202
23.3k
  pieceInfo.initNull();
203
23.3k
  separationInfo.initNull();
204
23.3k
  userUnit = 1;
205
23.3k
  resources.initNull();
206
23.3k
}
207
208
26.5k
PageAttrs::~PageAttrs() {
209
26.5k
  lastModified.free();
210
26.5k
  boxColorInfo.free();
211
26.5k
  group.free();
212
26.5k
  metadata.free();
213
26.5k
  pieceInfo.free();
214
26.5k
  separationInfo.free();
215
26.5k
  resources.free();
216
26.5k
}
217
218
2.29k
void PageAttrs::clipBoxes() {
219
2.29k
  cropBox.clipTo(&mediaBox);
220
2.29k
  bleedBox.clipTo(&mediaBox);
221
2.29k
  trimBox.clipTo(&mediaBox);
222
2.29k
  artBox.clipTo(&mediaBox);
223
2.29k
}
224
225
15.7k
GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) {
226
15.7k
  PDFRectangle tmp;
227
15.7k
  double t;
228
15.7k
  Object obj1, obj2;
229
15.7k
  GBool ok;
230
231
15.7k
  dict->lookup(key, &obj1);
232
15.7k
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
233
1.02k
    ok = gTrue;
234
1.02k
    obj1.arrayGet(0, &obj2);
235
1.02k
    if (obj2.isNum()) {
236
1.02k
      tmp.x1 = obj2.getNum();
237
1.02k
    } else {
238
2
      ok = gFalse;
239
2
    }
240
1.02k
    obj2.free();
241
1.02k
    obj1.arrayGet(1, &obj2);
242
1.02k
    if (obj2.isNum()) {
243
981
      tmp.y1 = obj2.getNum();
244
981
    } else {
245
44
      ok = gFalse;
246
44
    }
247
1.02k
    obj2.free();
248
1.02k
    obj1.arrayGet(2, &obj2);
249
1.02k
    if (obj2.isNum()) {
250
962
      tmp.x2 = obj2.getNum();
251
962
    } else {
252
63
      ok = gFalse;
253
63
    }
254
1.02k
    obj2.free();
255
1.02k
    obj1.arrayGet(3, &obj2);
256
1.02k
    if (obj2.isNum()) {
257
1.02k
      tmp.y2 = obj2.getNum();
258
1.02k
    } else {
259
3
      ok = gFalse;
260
3
    }
261
1.02k
    obj2.free();
262
1.02k
    if (ok) {
263
913
      if (tmp.x1 > tmp.x2) {
264
2
  t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
265
2
      }
266
913
      if (tmp.y1 > tmp.y2) {
267
3
  t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
268
3
      }
269
913
      *box = tmp;
270
913
    }
271
14.7k
  } else {
272
14.7k
    ok = gFalse;
273
14.7k
  }
274
15.7k
  obj1.free();
275
15.7k
  return ok;
276
15.7k
}
277
278
//------------------------------------------------------------------------
279
// Page
280
//------------------------------------------------------------------------
281
282
2.29k
Page::Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA) {
283
2.29k
  ok = gTrue;
284
2.29k
  doc = docA;
285
2.29k
  xref = doc->getXRef();
286
2.29k
  num = numA;
287
288
  // get attributes
289
2.29k
  attrs = attrsA;
290
2.29k
  attrs->clipBoxes();
291
292
  // annotations
293
2.29k
  pageDict->lookupNF("Annots", &annots);
294
2.29k
  if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
295
547
    error(errSyntaxError, -1,
296
547
    "Page annotations object (page {0:d}) is wrong type ({1:s})",
297
547
    num, annots.getTypeName());
298
547
    annots.free();
299
547
    goto err2;
300
547
  }
301
302
  // contents
303
1.75k
  pageDict->lookupNF("Contents", &contents);
304
1.75k
  if (!(contents.isRef() || contents.isArray() ||
305
1.75k
  contents.isNull())) {
306
4
    error(errSyntaxError, -1,
307
4
    "Page contents object (page {0:d}) is wrong type ({1:s})",
308
4
    num, contents.getTypeName());
309
4
    contents.free();
310
4
    goto err1;
311
4
  }
312
313
  // thumbnail
314
1.74k
  pageDict->lookupNF("Thumb", &thumbnail);
315
1.74k
  if (!thumbnail.isRef()) {
316
1.74k
    if (!thumbnail.isNull()) {
317
28
      thumbnail.free();
318
28
      thumbnail.initNull();
319
28
    }
320
1.74k
  }
321
322
1.74k
  return;
323
324
547
 err2:
325
547
  annots.initNull();
326
551
 err1:
327
551
  contents.initNull();
328
551
  thumbnail.initNull();
329
551
  ok = gFalse;
330
551
}
331
332
23.3k
Page::Page(PDFDoc *docA, int numA) {
333
23.3k
  doc = docA;
334
23.3k
  xref = doc->getXRef();
335
23.3k
  num = numA;
336
23.3k
  attrs = new PageAttrs();
337
23.3k
  annots.initNull();
338
23.3k
  contents.initNull();
339
23.3k
  thumbnail.initNull();
340
23.3k
  ok = gTrue;
341
23.3k
}
342
343
25.6k
Page::~Page() {
344
25.6k
  delete attrs;
345
25.6k
  annots.free();
346
25.6k
  contents.free();
347
25.6k
  thumbnail.free();
348
25.6k
}
349
350
0
Links *Page::getLinks() {
351
0
  Links *links;
352
0
  Object obj;
353
354
0
  links = new Links(getAnnots(&obj), doc->getCatalog()->getBaseURI());
355
0
  obj.free();
356
0
  return links;
357
0
}
358
359
void Page::display(OutputDev *out, double hDPI, double vDPI,
360
       int rotate, GBool useMediaBox, GBool crop,
361
       GBool printing,
362
       GBool (*abortCheckCbk)(void *data),
363
0
       void *abortCheckCbkData) {
364
0
  displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
365
0
         -1, -1, -1, -1, printing,
366
0
         abortCheckCbk, abortCheckCbkData);
367
0
}
368
369
void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
370
      int rotate, GBool useMediaBox, GBool crop,
371
      int sliceX, int sliceY, int sliceW, int sliceH,
372
      GBool printing,
373
      GBool (*abortCheckCbk)(void *data),
374
0
      void *abortCheckCbkData) {
375
0
#ifndef PDF_PARSER_ONLY
376
0
  PDFRectangle *mediaBox, *cropBox;
377
0
  PDFRectangle box;
378
0
  Gfx *gfx;
379
0
  Object obj;
380
0
  AcroForm *form;
381
0
  int i;
382
383
0
  if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
384
0
         sliceX, sliceY, sliceW, sliceH,
385
0
         printing, abortCheckCbk, abortCheckCbkData)) {
386
0
    return;
387
0
  }
388
389
0
  traceBegin(this, "begin page");
390
391
0
  rotate += getRotate();
392
0
  if (rotate >= 360) {
393
0
    rotate -= 360;
394
0
  } else if (rotate < 0) {
395
0
    rotate += 360;
396
0
  }
397
398
0
  makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
399
0
    sliceX, sliceY, sliceW, sliceH, &box, &crop);
400
0
  cropBox = getCropBox();
401
402
0
  if (globalParams->getPrintCommands()) {
403
0
    mediaBox = getMediaBox();
404
0
    printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
405
0
     mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
406
0
    printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
407
0
     cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
408
0
    printf("***** Rotate = %d\n", attrs->getRotate());
409
0
  }
410
411
0
  gfx = new Gfx(doc, out, num, attrs->getResourceDict(),
412
0
    hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
413
0
    rotate, abortCheckCbk, abortCheckCbkData);
414
0
  contents.fetch(xref, &obj);
415
0
  if (!obj.isNull()) {
416
0
    gfx->saveState();
417
0
    gfx->display(&contents);
418
0
    gfx->endOfPage();
419
0
  }
420
0
  obj.free();
421
422
  // draw (non-form) annotations
423
0
  if (globalParams->getDrawAnnotations()) {
424
0
    Annots *annots2 = doc->getAnnots();
425
0
    annots2->generateAnnotAppearances(num);
426
0
    int n = annots2->getNumAnnots(num);
427
0
    if (n > 0) {
428
0
      if (globalParams->getPrintCommands()) {
429
0
  printf("***** Annotations\n");
430
0
      }
431
0
      for (i = 0; i < n; ++i) {
432
0
  if (abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData)) {
433
0
    break;
434
0
  }
435
0
  annots2->getAnnot(num, i)->draw(gfx, printing);
436
0
      }
437
0
    }
438
0
  }
439
440
  // draw form fields
441
0
  if (globalParams->getDrawFormFields()) {
442
0
    if ((form = doc->getCatalog()->getForm())) {
443
0
      if (!(abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData))) {
444
0
  form->draw(num, gfx, printing);
445
0
      }
446
0
    }
447
0
  }
448
449
0
  delete gfx;
450
0
#endif // PDF_PARSER_ONLY
451
452
0
  traceEnd(this, "end page");
453
0
}
454
455
void Page::makeBox(double hDPI, double vDPI, int rotate,
456
       GBool useMediaBox, GBool upsideDown,
457
       double sliceX, double sliceY, double sliceW, double sliceH,
458
0
       PDFRectangle *box, GBool *crop) {
459
0
  PDFRectangle *mediaBox, *cropBox, *baseBox;
460
0
  double kx, ky;
461
462
0
  mediaBox = getMediaBox();
463
0
  cropBox = getCropBox();
464
0
  if (sliceW >= 0 && sliceH >= 0) {
465
0
    baseBox = useMediaBox ? mediaBox : cropBox;
466
0
    kx = 72.0 / hDPI;
467
0
    ky = 72.0 / vDPI;
468
0
    if (rotate == 90) {
469
0
      if (upsideDown) {
470
0
  box->x1 = baseBox->x1 + ky * sliceY;
471
0
  box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
472
0
      } else {
473
0
  box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
474
0
  box->x2 = baseBox->x2 - ky * sliceY;
475
0
      }
476
0
      box->y1 = baseBox->y1 + kx * sliceX;
477
0
      box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
478
0
    } else if (rotate == 180) {
479
0
      box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
480
0
      box->x2 = baseBox->x2 - kx * sliceX;
481
0
      if (upsideDown) {
482
0
  box->y1 = baseBox->y1 + ky * sliceY;
483
0
  box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
484
0
      } else {
485
0
  box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
486
0
  box->y2 = baseBox->y2 - ky * sliceY;
487
0
      }
488
0
    } else if (rotate == 270) {
489
0
      if (upsideDown) {
490
0
  box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
491
0
  box->x2 = baseBox->x2 - ky * sliceY;
492
0
      } else {
493
0
  box->x1 = baseBox->x1 + ky * sliceY;
494
0
  box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
495
0
      }
496
0
      box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
497
0
      box->y2 = baseBox->y2 - kx * sliceX;
498
0
    } else {
499
0
      box->x1 = baseBox->x1 + kx * sliceX;
500
0
      box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
501
0
      if (upsideDown) {
502
0
  box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
503
0
  box->y2 = baseBox->y2 - ky * sliceY;
504
0
      } else {
505
0
  box->y1 = baseBox->y1 + ky * sliceY;
506
0
  box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
507
0
      }
508
0
    }
509
0
  } else if (useMediaBox) {
510
0
    *box = *mediaBox;
511
0
  } else {
512
0
    *box = *cropBox;
513
0
    *crop = gFalse;
514
0
  }
515
0
}
516
517
0
void Page::processLinks(OutputDev *out) {
518
0
  Links *links;
519
0
  int i;
520
521
0
  links = getLinks();
522
0
  for (i = 0; i < links->getNumLinks(); ++i) {
523
0
    out->processLink(links->getLink(i));
524
0
  }
525
0
  delete links;
526
0
}
527
528
#ifndef PDF_PARSER_ONLY
529
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
530
0
       int rotate, GBool useMediaBox, GBool upsideDown) {
531
0
  GfxState *state;
532
0
  int i;
533
534
0
  rotate += getRotate();
535
0
  if (rotate >= 360) {
536
0
    rotate -= 360;
537
0
  } else if (rotate < 0) {
538
0
    rotate += 360;
539
0
  }
540
0
  state = new GfxState(hDPI, vDPI,
541
0
           useMediaBox ? getMediaBox() : getCropBox(),
542
0
           rotate, upsideDown);
543
0
  for (i = 0; i < 6; ++i) {
544
0
    ctm[i] = state->getCTM()[i];
545
0
  }
546
0
  delete state;
547
0
}
548
#endif
549