1//========================================================================
2//
3// Page.cc
4//
5// Copyright 1996-2007 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17// Copyright (C) 2005 Jeff Muizelaar <jeff@infidigm.net>
18// Copyright (C) 2005-2013, 2016-2023 Albert Astals Cid <aacid@kde.org>
19// Copyright (C) 2006-2008 Pino Toscano <pino@kde.org>
20// Copyright (C) 2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
21// Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
22// Copyright (C) 2006-2011, 2015 Carlos Garcia Campos <carlosgc@gnome.org>
23// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
24// Copyright (C) 2008 Iñigo Martínez <inigomartinez@gmail.com>
25// Copyright (C) 2008 Brad Hards <bradh@kde.org>
26// Copyright (C) 2008 Ilya Gorenbein <igorenbein@finjan.com>
27// Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
28// Copyright (C) 2013, 2014 Thomas Freitag <Thomas.Freitag@alfa.de>
29// Copyright (C) 2013 Jason Crain <jason@aquaticape.us>
30// Copyright (C) 2013, 2017, 2023 Adrian Johnson <ajohnson@redneon.com>
31// Copyright (C) 2015 Philipp Reinkemeier <philipp.reinkemeier@offis.de>
32// Copyright (C) 2018, 2019 Adam Reichold <adam.reichold@t-online.de>
33// Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
34// Copyright (C) 2020, 2021 Nelson Benítez León <nbenitezl@gmail.com>
35// Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
36//
37// To see a description of the changes please see the Changelog file that
38// came with your tarball or type make ChangeLog if you are building from git
39//
40//========================================================================
41
42#include <config.h>
43
44#include <cstddef>
45#include <climits>
46#include "GlobalParams.h"
47#include "Object.h"
48#include "Array.h"
49#include "Dict.h"
50#include "PDFDoc.h"
51#include "XRef.h"
52#include "Link.h"
53#include "OutputDev.h"
54#include "Gfx.h"
55#include "GfxState.h"
56#include "Annot.h"
57#include "TextOutputDev.h"
58#include "Form.h"
59#include "Error.h"
60#include "Page.h"
61#include "Catalog.h"
62#include "Form.h"
63
64//------------------------------------------------------------------------
65// PDFRectangle
66//------------------------------------------------------------------------
67
68void PDFRectangle::clipTo(PDFRectangle *rect)
69{
70 if (x1 < rect->x1) {
71 x1 = rect->x1;
72 } else if (x1 > rect->x2) {
73 x1 = rect->x2;
74 }
75 if (x2 < rect->x1) {
76 x2 = rect->x1;
77 } else if (x2 > rect->x2) {
78 x2 = rect->x2;
79 }
80 if (y1 < rect->y1) {
81 y1 = rect->y1;
82 } else if (y1 > rect->y2) {
83 y1 = rect->y2;
84 }
85 if (y2 < rect->y1) {
86 y2 = rect->y1;
87 } else if (y2 > rect->y2) {
88 y2 = rect->y2;
89 }
90}
91
92//------------------------------------------------------------------------
93// PageAttrs
94//------------------------------------------------------------------------
95
96PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict)
97{
98 Object obj1;
99 PDFRectangle mBox;
100 const bool isPage = dict->is(type: "Page");
101
102 // get old/default values
103 if (attrs) {
104 mediaBox = attrs->mediaBox;
105 cropBox = attrs->cropBox;
106 haveCropBox = attrs->haveCropBox;
107 rotate = attrs->rotate;
108 resources = attrs->resources.copy();
109 } else {
110 // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
111 // but some (non-compliant) PDF files don't specify a MediaBox
112 mediaBox.x1 = 0;
113 mediaBox.y1 = 0;
114 mediaBox.x2 = 612;
115 mediaBox.y2 = 792;
116 cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
117 haveCropBox = false;
118 rotate = 0;
119 resources.setToNull();
120 }
121
122 // media box
123 if (readBox(dict, key: "MediaBox", box: &mBox)) {
124 mediaBox = mBox;
125 }
126
127 // crop box
128 if (readBox(dict, key: "CropBox", box: &cropBox)) {
129 haveCropBox = true;
130 }
131 if (!haveCropBox) {
132 cropBox = mediaBox;
133 }
134
135 if (isPage) {
136 // cropBox can not be bigger than mediaBox
137 if (cropBox.x2 - cropBox.x1 > mediaBox.x2 - mediaBox.x1) {
138 cropBox.x1 = mediaBox.x1;
139 cropBox.x2 = mediaBox.x2;
140 }
141 if (cropBox.y2 - cropBox.y1 > mediaBox.y2 - mediaBox.y1) {
142 cropBox.y1 = mediaBox.y1;
143 cropBox.y2 = mediaBox.y2;
144 }
145 }
146
147 // other boxes
148 bleedBox = cropBox;
149 readBox(dict, key: "BleedBox", box: &bleedBox);
150 trimBox = cropBox;
151 readBox(dict, key: "TrimBox", box: &trimBox);
152 artBox = cropBox;
153 readBox(dict, key: "ArtBox", box: &artBox);
154
155 // rotate
156 obj1 = dict->lookup(key: "Rotate");
157 if (obj1.isInt()) {
158 rotate = obj1.getInt();
159 }
160 while (rotate < 0) {
161 rotate += 360;
162 }
163 while (rotate >= 360) {
164 rotate -= 360;
165 }
166
167 // misc attributes
168 lastModified = dict->lookup(key: "LastModified");
169 boxColorInfo = dict->lookup(key: "BoxColorInfo");
170 group = dict->lookup(key: "Group");
171 metadata = dict->lookup(key: "Metadata");
172 pieceInfo = dict->lookup(key: "PieceInfo");
173 separationInfo = dict->lookup(key: "SeparationInfo");
174
175 // resource dictionary
176 Object objResources = dict->lookup(key: "Resources");
177 if (objResources.isDict()) {
178 resources = std::move(objResources);
179 }
180}
181
182PageAttrs::~PageAttrs() { }
183
184void PageAttrs::clipBoxes()
185{
186 cropBox.clipTo(rect: &mediaBox);
187 bleedBox.clipTo(rect: &mediaBox);
188 trimBox.clipTo(rect: &mediaBox);
189 artBox.clipTo(rect: &mediaBox);
190}
191
192bool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box)
193{
194 PDFRectangle tmp;
195 double t;
196 Object obj1, obj2;
197 bool ok;
198
199 obj1 = dict->lookup(key);
200 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
201 ok = true;
202 obj2 = obj1.arrayGet(i: 0);
203 if (obj2.isNum()) {
204 tmp.x1 = obj2.getNum();
205 } else {
206 ok = false;
207 }
208 obj2 = obj1.arrayGet(i: 1);
209 if (obj2.isNum()) {
210 tmp.y1 = obj2.getNum();
211 } else {
212 ok = false;
213 }
214 obj2 = obj1.arrayGet(i: 2);
215 if (obj2.isNum()) {
216 tmp.x2 = obj2.getNum();
217 } else {
218 ok = false;
219 }
220 obj2 = obj1.arrayGet(i: 3);
221 if (obj2.isNum()) {
222 tmp.y2 = obj2.getNum();
223 } else {
224 ok = false;
225 }
226 if (tmp.x1 == 0 && tmp.x2 == 0 && tmp.y1 == 0 && tmp.y2 == 0) {
227 ok = false;
228 }
229 if (ok) {
230 if (tmp.x1 > tmp.x2) {
231 t = tmp.x1;
232 tmp.x1 = tmp.x2;
233 tmp.x2 = t;
234 }
235 if (tmp.y1 > tmp.y2) {
236 t = tmp.y1;
237 tmp.y1 = tmp.y2;
238 tmp.y2 = t;
239 }
240 *box = tmp;
241 }
242 } else {
243 ok = false;
244 }
245 return ok;
246}
247
248//------------------------------------------------------------------------
249// Page
250//------------------------------------------------------------------------
251
252#define pageLocker() const std::scoped_lock locker(mutex)
253
254Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form) : pageRef(pageRefA)
255{
256 ok = true;
257 doc = docA;
258 xref = doc->getXRef();
259 num = numA;
260 duration = -1;
261 annots = nullptr;
262 structParents = -1;
263
264 pageObj = std::move(pageDict);
265
266 // get attributes
267 attrs = attrsA;
268 attrs->clipBoxes();
269
270 // transtion
271 trans = pageObj.dictLookupNF(key: "Trans").copy();
272 if (!(trans.isRef() || trans.isDict() || trans.isNull())) {
273 error(category: errSyntaxError, pos: -1, msg: "Page transition object (page {0:d}) is wrong type ({1:s})", num, trans.getTypeName());
274 trans = Object();
275 }
276
277 // duration
278 const Object &tmp = pageObj.dictLookupNF(key: "Dur");
279 if (!(tmp.isNum() || tmp.isNull())) {
280 error(category: errSyntaxError, pos: -1, msg: "Page duration object (page {0:d}) is wrong type ({1:s})", num, tmp.getTypeName());
281 } else if (tmp.isNum()) {
282 duration = tmp.getNum();
283 }
284
285 // structParents
286 const Object &tmp2 = pageObj.dictLookup(key: "StructParents");
287 if (!(tmp2.isInt() || tmp2.isNull())) {
288 error(category: errSyntaxError, pos: -1, msg: "Page StructParents object (page {0:d}) is wrong type ({1:s})", num, tmp2.getTypeName());
289 } else if (tmp2.isInt()) {
290 structParents = tmp2.getInt();
291 }
292
293 // annotations
294 annotsObj = pageObj.dictLookupNF(key: "Annots").copy();
295 if (!(annotsObj.isRef() || annotsObj.isArray() || annotsObj.isNull())) {
296 error(category: errSyntaxError, pos: -1, msg: "Page annotations object (page {0:d}) is wrong type ({1:s})", num, annotsObj.getTypeName());
297 goto err2;
298 }
299
300 // contents
301 contents = pageObj.dictLookupNF(key: "Contents").copy();
302 if (!(contents.isRef() || contents.isArray() || contents.isNull())) {
303 error(category: errSyntaxError, pos: -1, msg: "Page contents object (page {0:d}) is wrong type ({1:s})", num, contents.getTypeName());
304 goto err1;
305 }
306
307 // thumb
308 thumb = pageObj.dictLookupNF(key: "Thumb").copy();
309 if (!(thumb.isStream() || thumb.isNull() || thumb.isRef())) {
310 error(category: errSyntaxError, pos: -1, msg: "Page thumb object (page {0:d}) is wrong type ({1:s})", num, thumb.getTypeName());
311 thumb.setToNull();
312 }
313
314 // actions
315 actions = pageObj.dictLookupNF(key: "AA").copy();
316 if (!(actions.isDict() || actions.isNull())) {
317 error(category: errSyntaxError, pos: -1, msg: "Page additional action object (page {0:d}) is wrong type ({1:s})", num, actions.getTypeName());
318 actions.setToNull();
319 }
320
321 return;
322
323err2:
324 annotsObj.setToNull();
325err1:
326 contents.setToNull();
327 ok = false;
328}
329
330Page::~Page()
331{
332 delete attrs;
333 delete annots;
334 for (auto frmField : standaloneFields) {
335 delete frmField;
336 }
337}
338
339Dict *Page::getResourceDict()
340{
341 return attrs->getResourceDict();
342}
343
344Object *Page::getResourceDictObject()
345{
346 return attrs->getResourceDictObject();
347}
348
349Dict *Page::getResourceDictCopy(XRef *xrefA)
350{
351 pageLocker();
352 Dict *dict = attrs->getResourceDict();
353 return dict ? dict->copy(xrefA) : nullptr;
354}
355
356void Page::replaceXRef(XRef *xrefA)
357{
358 Dict *pageDict = pageObj.getDict()->copy(xrefA);
359 xref = xrefA;
360 trans = pageDict->lookupNF(key: "Trans").copy();
361 annotsObj = pageDict->lookupNF(key: "Annots").copy();
362 contents = pageDict->lookupNF(key: "Contents").copy();
363 if (contents.isArray()) {
364 contents = Object(contents.getArray()->copy(xrefA));
365 }
366 thumb = pageDict->lookupNF(key: "Thumb").copy();
367 actions = pageDict->lookupNF(key: "AA").copy();
368 Object resources = pageDict->lookup(key: "Resources");
369 if (resources.isDict()) {
370 attrs->replaceResource(obj1: std::move(resources));
371 }
372 delete pageDict;
373}
374
375/* Loads standalone fields into Page, should be called once per page only */
376void Page::loadStandaloneFields(Annots *annotations, Form *form)
377{
378 /* Look for standalone annots, identified by being: 1) of type Widget
379 * 2) not referenced from the Catalog's Form Field array */
380 for (Annot *annot : annots->getAnnots()) {
381
382 if (annot->getType() != Annot::typeWidget || !annot->getHasRef()) {
383 continue;
384 }
385
386 const Ref r = annot->getRef();
387 if (form && form->findWidgetByRef(aref: r)) {
388 continue; // this annot is referenced inside Form, skip it
389 }
390
391 std::set<int> parents;
392 FormField *field = Form::createFieldFromDict(obj: annot->getAnnotObj().copy(), docA: annot->getDoc(), aref: r, parent: nullptr, usedParents: &parents);
393
394 if (field && field->getNumWidgets() == 1) {
395
396 static_cast<AnnotWidget *>(annot)->setField(field);
397
398 field->setStandAlone(true);
399 FormWidget *formWidget = field->getWidget(i: 0);
400
401 if (!formWidget->getWidgetAnnotation()) {
402 formWidget->createWidgetAnnotation();
403 }
404
405 standaloneFields.push_back(x: field);
406
407 } else if (field) {
408 delete field;
409 }
410 }
411}
412
413Annots *Page::getAnnots(XRef *xrefA)
414{
415 if (!annots) {
416 Object obj = getAnnotsObject(xrefA);
417 annots = new Annots(doc, num, &obj);
418 // Load standalone fields once for the page
419 loadStandaloneFields(annotations: annots, form: doc->getCatalog()->getForm());
420 }
421
422 return annots;
423}
424
425bool Page::addAnnot(Annot *annot)
426{
427 if (unlikely(xref->getEntry(pageRef.num)->type == xrefEntryFree)) {
428 // something very wrong happened if we're here
429 error(category: errInternal, pos: -1, msg: "Can not addAnnot to page with an invalid ref");
430 return false;
431 }
432
433 const Ref annotRef = annot->getRef();
434
435 // Make sure we have annots before adding the new one
436 // even if it's an empty list so that we can safely
437 // call annots->appendAnnot(annot)
438 pageLocker();
439 getAnnots();
440
441 if (annotsObj.isNull()) {
442 Ref annotsRef;
443 // page doesn't have annots array,
444 // we have to create it
445
446 Array *annotsArray = new Array(xref);
447 annotsArray->add(elem: Object(annotRef));
448
449 annotsRef = xref->addIndirectObject(o: Object(annotsArray));
450 annotsObj = Object(annotsRef);
451 pageObj.dictSet(key: "Annots", val: Object(annotsRef));
452 xref->setModifiedObject(o: &pageObj, r: pageRef);
453 } else {
454 Object obj1 = getAnnotsObject();
455 if (obj1.isArray()) {
456 obj1.arrayAdd(elem: Object(annotRef));
457 if (annotsObj.isRef()) {
458 xref->setModifiedObject(o: &obj1, r: annotsObj.getRef());
459 } else {
460 xref->setModifiedObject(o: &pageObj, r: pageRef);
461 }
462 }
463 }
464
465 // Popup annots are already handled by markup annots,
466 // so add to the list only Popup annots without a
467 // markup annotation associated.
468 if (annot->getType() != Annot::typePopup || !static_cast<AnnotPopup *>(annot)->hasParent()) {
469 annots->appendAnnot(annot);
470 }
471 annot->setPage(pageIndex: num, updateP: true);
472
473 AnnotMarkup *annotMarkup = dynamic_cast<AnnotMarkup *>(annot);
474 if (annotMarkup) {
475 AnnotPopup *annotPopup = annotMarkup->getPopup();
476 if (annotPopup) {
477 addAnnot(annot: annotPopup);
478 }
479 }
480
481 return true;
482}
483
484void Page::removeAnnot(Annot *annot)
485{
486 Ref annotRef = annot->getRef();
487
488 pageLocker();
489 Object annArray = getAnnotsObject();
490 if (annArray.isArray()) {
491 int idx = -1;
492 // Get annotation position
493 for (int i = 0; idx == -1 && i < annArray.arrayGetLength(); ++i) {
494 const Object &tmp = annArray.arrayGetNF(i);
495 if (tmp.isRef()) {
496 const Ref currAnnot = tmp.getRef();
497 if (currAnnot == annotRef) {
498 idx = i;
499 }
500 }
501 }
502
503 if (idx == -1) {
504 error(category: errInternal, pos: -1, msg: "Annotation doesn't belong to this page");
505 return;
506 }
507 annots->removeAnnot(annot); // Gracefully fails on popup windows
508 annArray.arrayRemove(i: idx);
509
510 if (annotsObj.isRef()) {
511 xref->setModifiedObject(o: &annArray, r: annotsObj.getRef());
512 } else {
513 xref->setModifiedObject(o: &pageObj, r: pageRef);
514 }
515 }
516 annot->removeReferencedObjects(); // Note: Might recurse in removeAnnot again
517 if (annArray.isArray()) {
518 xref->removeIndirectObject(r: annotRef);
519 }
520 annot->setPage(pageIndex: 0, updateP: false);
521}
522
523std::unique_ptr<Links> Page::getLinks()
524{
525 return std::make_unique<Links>(args: getAnnots());
526}
527
528std::unique_ptr<FormPageWidgets> Page::getFormWidgets()
529{
530 auto frmPageWidgets = std::make_unique<FormPageWidgets>(args: getAnnots(), args&: num, args: doc->getCatalog()->getForm());
531 frmPageWidgets->addWidgets(addedWidgets: standaloneFields, page: num);
532
533 return frmPageWidgets;
534}
535
536void Page::display(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
537 void *annotDisplayDecideCbkData, bool copyXRef)
538{
539 displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX: -1, sliceY: -1, sliceW: -1, sliceH: -1, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData, copyXRef);
540}
541
542Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, XRef *xrefA)
543{
544 const PDFRectangle *mediaBox, *cropBox;
545 PDFRectangle box;
546 Gfx *gfx;
547
548 rotate += getRotate();
549 if (rotate >= 360) {
550 rotate -= 360;
551 } else if (rotate < 0) {
552 rotate += 360;
553 }
554
555 makeBox(hDPI, vDPI, rotate, useMediaBox, upsideDown: out->upsideDown(), sliceX, sliceY, sliceW, sliceH, box: &box, crop: &crop);
556 cropBox = getCropBox();
557 mediaBox = getMediaBox();
558
559 if (globalParams->getPrintCommands()) {
560 printf(format: "***** MediaBox = ll:%g,%g ur:%g,%g\n", mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
561 printf(format: "***** CropBox = ll:%g,%g ur:%g,%g\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
562 printf(format: "***** Rotate = %d\n", attrs->getRotate());
563 }
564
565 if (!crop) {
566 crop = (box == *cropBox) && out->needClipToCropBox();
567 }
568 gfx = new Gfx(doc, out, num, attrs->getResourceDict(), hDPI, vDPI, &box, crop ? cropBox : nullptr, rotate, abortCheckCbk, abortCheckCbkData, xrefA);
569
570 return gfx;
571}
572
573void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData,
574 bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData, bool copyXRef)
575{
576 Gfx *gfx;
577 Annots *annotList;
578
579 if (!out->checkPageSlice(page: this, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData)) {
580 return;
581 }
582 pageLocker();
583 XRef *localXRef = (copyXRef) ? xref->copy() : xref;
584 if (copyXRef) {
585 replaceXRef(xrefA: localXRef);
586 }
587
588 gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, xrefA: localXRef);
589
590 Object obj = contents.fetch(xref: localXRef);
591 if (!obj.isNull()) {
592 gfx->saveState();
593 gfx->display(obj: &obj);
594 gfx->restoreState();
595 } else {
596 // empty pages need to call dump to do any setup required by the
597 // OutputDev
598 out->dump();
599 }
600
601 // draw annotations
602 annotList = getAnnots();
603
604 if (!annotList->getAnnots().empty()) {
605 if (globalParams->getPrintCommands()) {
606 printf(format: "***** Annotations\n");
607 }
608 for (Annot *annot : annots->getAnnots()) {
609 if ((annotDisplayDecideCbk && (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || !annotDisplayDecideCbk) {
610 annot->draw(gfx, printing);
611 }
612 }
613 out->dump();
614 }
615
616 delete gfx;
617 if (copyXRef) {
618 replaceXRef(xrefA: doc->getXRef());
619 delete localXRef;
620 }
621}
622
623void Page::display(Gfx *gfx)
624{
625 Object obj = contents.fetch(xref);
626 if (!obj.isNull()) {
627 gfx->saveState();
628 gfx->display(obj: &obj);
629 gfx->restoreState();
630 }
631}
632
633bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, int *rowstride_out)
634{
635 unsigned int pixbufdatasize;
636 int width, height, bits;
637 Object obj1;
638 Dict *dict;
639 GfxColorSpace *colorSpace;
640 Stream *str;
641 GfxImageColorMap *colorMap;
642
643 /* Get stream dict */
644 pageLocker();
645 Object fetched_thumb = thumb.fetch(xref);
646 if (!fetched_thumb.isStream()) {
647 return false;
648 }
649
650 dict = fetched_thumb.streamGetDict();
651 str = fetched_thumb.getStream();
652
653 if (!dict->lookupInt(key: "Width", alt_key: "W", value: &width)) {
654 return false;
655 }
656 if (!dict->lookupInt(key: "Height", alt_key: "H", value: &height)) {
657 return false;
658 }
659 if (!dict->lookupInt(key: "BitsPerComponent", alt_key: "BPC", value: &bits)) {
660 return false;
661 }
662
663 /* Check for invalid dimensions and integer overflow. */
664 if (width <= 0 || height <= 0) {
665 return false;
666 }
667 if (width > INT_MAX / 3 / height) {
668 return false;
669 }
670 pixbufdatasize = width * height * 3;
671
672 /* Get color space */
673 obj1 = dict->lookup(key: "ColorSpace");
674 if (obj1.isNull()) {
675 obj1 = dict->lookup(key: "CS");
676 }
677 // Just initialize some dummy GfxState for GfxColorSpace::parse.
678 // This will set a sRGB profile for ICC-based colorspaces.
679 auto pdfrectangle = std::make_shared<PDFRectangle>();
680 auto state = std::make_shared<GfxState>(args: 72.0, args: 72.0, args: pdfrectangle.get(), args: 0, args: false);
681 colorSpace = GfxColorSpace::parse(res: nullptr, csObj: &obj1, out: nullptr, state: state.get());
682 if (!colorSpace) {
683 fprintf(stderr, format: "Error: Cannot parse color space\n");
684 return false;
685 }
686
687 obj1 = dict->lookup(key: "Decode");
688 if (obj1.isNull()) {
689 obj1 = dict->lookup(key: "D");
690 }
691 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
692 if (!colorMap->isOk()) {
693 fprintf(stderr, format: "Error: invalid colormap\n");
694 delete colorMap;
695 return false;
696 }
697
698 if (data_out) {
699 unsigned char *pixbufdata = (unsigned char *)gmalloc(size: pixbufdatasize);
700 unsigned char *p = pixbufdata;
701 ImageStream *imgstr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
702 imgstr->reset();
703 for (int row = 0; row < height; ++row) {
704 for (int col = 0; col < width; ++col) {
705 unsigned char pix[gfxColorMaxComps];
706 GfxRGB rgb;
707
708 imgstr->getPixel(pix);
709 colorMap->getRGB(x: pix, rgb: &rgb);
710
711 *p++ = colToByte(x: rgb.r);
712 *p++ = colToByte(x: rgb.g);
713 *p++ = colToByte(x: rgb.b);
714 }
715 }
716 *data_out = pixbufdata;
717 imgstr->close();
718 delete imgstr;
719 }
720
721 if (width_out) {
722 *width_out = width;
723 }
724 if (height_out) {
725 *height_out = height;
726 }
727 if (rowstride_out) {
728 *rowstride_out = width * 3;
729 }
730
731 delete colorMap;
732
733 return true;
734}
735
736void Page::makeBox(double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown, double sliceX, double sliceY, double sliceW, double sliceH, PDFRectangle *box, bool *crop)
737{
738 const PDFRectangle *mediaBox, *cropBox, *baseBox;
739 double kx, ky;
740
741 mediaBox = getMediaBox();
742 cropBox = getCropBox();
743 if (sliceW >= 0 && sliceH >= 0) {
744 baseBox = useMediaBox ? mediaBox : cropBox;
745 kx = 72.0 / hDPI;
746 ky = 72.0 / vDPI;
747 if (rotate == 90) {
748 if (upsideDown) {
749 box->x1 = baseBox->x1 + ky * sliceY;
750 box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
751 } else {
752 box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
753 box->x2 = baseBox->x2 - ky * sliceY;
754 }
755 box->y1 = baseBox->y1 + kx * sliceX;
756 box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
757 } else if (rotate == 180) {
758 box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
759 box->x2 = baseBox->x2 - kx * sliceX;
760 if (upsideDown) {
761 box->y1 = baseBox->y1 + ky * sliceY;
762 box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
763 } else {
764 box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
765 box->y2 = baseBox->y2 - ky * sliceY;
766 }
767 } else if (rotate == 270) {
768 if (upsideDown) {
769 box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
770 box->x2 = baseBox->x2 - ky * sliceY;
771 } else {
772 box->x1 = baseBox->x1 + ky * sliceY;
773 box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
774 }
775 box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
776 box->y2 = baseBox->y2 - kx * sliceX;
777 } else {
778 box->x1 = baseBox->x1 + kx * sliceX;
779 box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
780 if (upsideDown) {
781 box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
782 box->y2 = baseBox->y2 - ky * sliceY;
783 } else {
784 box->y1 = baseBox->y1 + ky * sliceY;
785 box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
786 }
787 }
788 } else if (useMediaBox) {
789 *box = *mediaBox;
790 } else {
791 *box = *cropBox;
792 *crop = false;
793 }
794}
795
796void Page::processLinks(OutputDev *out)
797{
798 std::unique_ptr<Links> links = getLinks();
799 for (AnnotLink *link : links->getLinks()) {
800 out->processLink(link);
801 }
802}
803
804void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown)
805{
806 GfxState *state;
807 int i;
808 rotate += getRotate();
809 if (rotate >= 360) {
810 rotate -= 360;
811 } else if (rotate < 0) {
812 rotate += 360;
813 }
814 state = new GfxState(hDPI, vDPI, useMediaBox ? getMediaBox() : getCropBox(), rotate, upsideDown);
815 for (i = 0; i < 6; ++i) {
816 ctm[i] = state->getCTM()[i];
817 }
818 delete state;
819}
820
821std::unique_ptr<LinkAction> Page::getAdditionalAction(PageAdditionalActionsType type)
822{
823 Object additionalActionsObject = actions.fetch(xref: doc->getXRef());
824 if (additionalActionsObject.isDict()) {
825 const char *key = (type == actionOpenPage ? "O" : type == actionClosePage ? "C" : nullptr);
826
827 Object actionObject = additionalActionsObject.dictLookup(key);
828 if (actionObject.isDict()) {
829 return LinkAction::parseAction(obj: &actionObject, baseURI: doc->getCatalog()->getBaseURI());
830 }
831 }
832
833 return nullptr;
834}
835

source code of poppler/poppler/Page.cc