1 //========================================================================
5 // Copyright 1996-2007 Glyph & Cog, LLC
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17 // Copyright (C) 2005 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2005-2013 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 Adrian Johnson <ajohnson@redneon.com>
31 // Copyright (C) 2015 Philipp Reinkemeier <philipp.reinkemeier@offis.de>
33 // To see a description of the changes please see the Changelog file that
34 // came with your tarball or type make ChangeLog if you are building from git
36 //========================================================================
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
46 #include "GlobalParams.h"
53 #include "OutputDev.h"
57 #include "TextOutputDev.h"
65 # define pageLocker() MutexLocker locker(&mutex)
69 //------------------------------------------------------------------------
71 //------------------------------------------------------------------------
73 void PDFRectangle::clipTo(PDFRectangle
*rect
) {
76 } else if (x1
> rect
->x2
) {
81 } else if (x2
> rect
->x2
) {
86 } else if (y1
> rect
->y2
) {
91 } else if (y2
> rect
->y2
) {
96 //------------------------------------------------------------------------
98 //------------------------------------------------------------------------
100 PageAttrs::PageAttrs(PageAttrs
*attrs
, Dict
*dict
) {
103 const GBool isPage
= dict
->is("Page");
105 // get old/default values
107 mediaBox
= attrs
->mediaBox
;
108 cropBox
= attrs
->cropBox
;
109 haveCropBox
= attrs
->haveCropBox
;
110 rotate
= attrs
->rotate
;
111 attrs
->resources
.copy(&resources
);
113 // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
114 // but some (non-compliant) PDF files don't specify a MediaBox
119 cropBox
.x1
= cropBox
.y1
= cropBox
.x2
= cropBox
.y2
= 0;
120 haveCropBox
= gFalse
;
122 resources
.initNull();
126 if (readBox(dict
, "MediaBox", &mBox
)) {
131 if (readBox(dict
, "CropBox", &cropBox
)) {
139 // cropBox can not be bigger than mediaBox
140 if (cropBox
.x2
- cropBox
.x1
> mediaBox
.x2
- mediaBox
.x1
)
142 cropBox
.x1
= mediaBox
.x1
;
143 cropBox
.x2
= mediaBox
.x2
;
145 if (cropBox
.y2
- cropBox
.y1
> mediaBox
.y2
- mediaBox
.y1
)
147 cropBox
.y1
= mediaBox
.y1
;
148 cropBox
.y2
= mediaBox
.y2
;
154 readBox(dict
, "BleedBox", &bleedBox
);
156 readBox(dict
, "TrimBox", &trimBox
);
158 readBox(dict
, "ArtBox", &artBox
);
161 dict
->lookup("Rotate", &obj1
);
163 rotate
= obj1
.getInt();
169 while (rotate
>= 360) {
174 dict
->lookup("LastModified", &lastModified
);
175 dict
->lookup("BoxColorInfo", &boxColorInfo
);
176 dict
->lookup("Group", &group
);
177 dict
->lookup("Metadata", &metadata
);
178 dict
->lookup("PieceInfo", &pieceInfo
);
179 dict
->lookup("SeparationInfo", &separationInfo
);
181 // resource dictionary
182 dict
->lookup("Resources", &obj1
);
185 obj1
.copy(&resources
);
190 PageAttrs::~PageAttrs() {
196 separationInfo
.free();
200 void PageAttrs::clipBoxes() {
201 cropBox
.clipTo(&mediaBox
);
202 bleedBox
.clipTo(&mediaBox
);
203 trimBox
.clipTo(&mediaBox
);
204 artBox
.clipTo(&mediaBox
);
207 GBool
PageAttrs::readBox(Dict
*dict
, const char *key
, PDFRectangle
*box
) {
213 dict
->lookup(key
, &obj1
);
214 if (obj1
.isArray() && obj1
.arrayGetLength() == 4) {
216 obj1
.arrayGet(0, &obj2
);
218 tmp
.x1
= obj2
.getNum();
223 obj1
.arrayGet(1, &obj2
);
225 tmp
.y1
= obj2
.getNum();
230 obj1
.arrayGet(2, &obj2
);
232 tmp
.x2
= obj2
.getNum();
237 obj1
.arrayGet(3, &obj2
);
239 tmp
.y2
= obj2
.getNum();
244 if (tmp
.x1
== 0 && tmp
.x2
== 0 && tmp
.y1
== 0 && tmp
.y2
== 0)
247 if (tmp
.x1
> tmp
.x2
) {
248 t
= tmp
.x1
; tmp
.x1
= tmp
.x2
; tmp
.x2
= t
;
250 if (tmp
.y1
> tmp
.y2
) {
251 t
= tmp
.y1
; tmp
.y1
= tmp
.y2
; tmp
.y2
= t
;
262 //------------------------------------------------------------------------
264 //------------------------------------------------------------------------
266 Page::Page(PDFDoc
*docA
, int numA
, Dict
*pageDict
, Ref pageRefA
, PageAttrs
*attrsA
, Form
*form
) {
274 xref
= doc
->getXRef();
279 pageObj
.initDict(pageDict
);
287 pageDict
->lookupNF("Trans", &trans
);
288 if (!(trans
.isRef() || trans
.isDict() || trans
.isNull())) {
289 error(errSyntaxError
, -1, "Page transition object (page {0:d}) is wrong type ({1:s})",
290 num
, trans
.getTypeName());
295 pageDict
->lookupNF("Dur", &tmp
);
296 if (!(tmp
.isNum() || tmp
.isNull())) {
297 error(errSyntaxError
, -1, "Page duration object (page {0:d}) is wrong type ({1:s})",
298 num
, tmp
.getTypeName());
299 } else if (tmp
.isNum()) {
300 duration
= tmp
.getNum();
305 pageDict
->lookupNF("Annots", &annotsObj
);
306 if (!(annotsObj
.isRef() || annotsObj
.isArray() || annotsObj
.isNull())) {
307 error(errSyntaxError
, -1, "Page annotations object (page {0:d}) is wrong type ({1:s})",
308 num
, annotsObj
.getTypeName());
314 pageDict
->lookupNF("Contents", &contents
);
315 if (!(contents
.isRef() || contents
.isArray() ||
316 contents
.isNull())) {
317 error(errSyntaxError
, -1, "Page contents object (page {0:d}) is wrong type ({1:s})",
318 num
, contents
.getTypeName());
324 pageDict
->lookupNF("Thumb", &thumb
);
325 if (!(thumb
.isStream() || thumb
.isNull() || thumb
.isRef())) {
326 error(errSyntaxError
, -1, "Page thumb object (page {0:d}) is wrong type ({1:s})",
327 num
, thumb
.getTypeName());
332 pageDict
->lookupNF("AA", &actions
);
333 if (!(actions
.isDict() || actions
.isNull())) {
334 error(errSyntaxError
, -1, "Page additional action object (page {0:d}) is wrong type ({1:s})",
335 num
, actions
.getTypeName());
343 annotsObj
.initNull();
359 gDestroyMutex(&mutex
);
363 Dict
*Page::getResourceDict() {
364 return attrs
->getResourceDict();
367 Dict
*Page::getResourceDictCopy(XRef
*xrefA
) {
369 Dict
*dict
= attrs
->getResourceDict();
370 return dict
? dict
->copy(xrefA
) : NULL
;
373 void Page::replaceXRef(XRef
*xrefA
) {
375 Dict
*pageDict
= pageObj
.getDict()->copy(xrefA
);
378 pageDict
->lookupNF("Trans", &trans
);
380 pageDict
->lookupNF("Annots", &annotsObj
);
382 pageDict
->lookupNF("Contents", &contents
);
383 if (contents
.isArray()) {
385 pageDict
->lookupNF("Contents", &obj1
)->getArray()->copy(xrefA
, &contents
);
389 pageDict
->lookupNF("Thumb", &thumb
);
391 pageDict
->lookupNF("AA", &actions
);
392 pageDict
->lookup("Resources", &obj1
);
394 attrs
->replaceResource(obj1
);
400 Annots
*Page::getAnnots(XRef
*xrefA
) {
403 annots
= new Annots(doc
, num
, getAnnots(&obj
, (xrefA
== NULL
) ? xref
: xrefA
));
410 void Page::addAnnot(Annot
*annot
) {
413 Ref annotRef
= annot
->getRef ();
415 // Make sure we have annots before adding the new one
416 // even if it's an empty list so that we can safely
417 // call annots->appendAnnot(annot)
421 if (annotsObj
.isNull()) {
423 // page doesn't have annots array,
424 // we have to create it
426 obj1
.initArray(xref
);
427 obj1
.arrayAdd(tmp
.initRef (annotRef
.num
, annotRef
.gen
));
430 annotsRef
= xref
->addIndirectObject (&obj1
);
431 annotsObj
.initRef(annotsRef
.num
, annotsRef
.gen
);
432 pageObj
.dictSet ("Annots", &annotsObj
);
433 xref
->setModifiedObject (&pageObj
, pageRef
);
436 if (obj1
.isArray()) {
437 obj1
.arrayAdd (tmp
.initRef (annotRef
.num
, annotRef
.gen
));
438 if (annotsObj
.isRef())
439 xref
->setModifiedObject (&obj1
, annotsObj
.getRef());
441 xref
->setModifiedObject (&pageObj
, pageRef
);
446 // Popup annots are already handled by markup annots,
447 // so add to the list only Popup annots without a
448 // markup annotation associated.
449 if (annot
->getType() != Annot::typePopup
||
450 static_cast<AnnotPopup
*>(annot
)->getParentNF()->isNull()) {
451 annots
->appendAnnot(annot
);
453 annot
->setPage(num
, gTrue
);
455 AnnotMarkup
*annotMarkup
= dynamic_cast<AnnotMarkup
*>(annot
);
457 AnnotPopup
*annotPopup
= annotMarkup
->getPopup();
459 addAnnot(annotPopup
);
463 void Page::removeAnnot(Annot
*annot
) {
464 Ref annotRef
= annot
->getRef();
468 getAnnots(&annArray
);
469 if (annArray
.isArray()) {
471 // Get annotation position
472 for (int i
= 0; idx
== -1 && i
< annArray
.arrayGetLength(); ++i
) {
474 if (annArray
.arrayGetNF(i
, &tmp
)->isRef()) {
475 Ref currAnnot
= tmp
.getRef();
476 if (currAnnot
.num
== annotRef
.num
&& currAnnot
.gen
== annotRef
.gen
) {
484 error(errInternal
, -1, "Annotation doesn't belong to this page");
488 annots
->removeAnnot(annot
); // Gracefully fails on popup windows
489 annArray
.arrayRemove(idx
);
490 xref
->removeIndirectObject(annotRef
);
492 if (annotsObj
.isRef()) {
493 xref
->setModifiedObject (&annArray
, annotsObj
.getRef());
495 xref
->setModifiedObject (&pageObj
, pageRef
);
499 annot
->removeReferencedObjects(); // Note: Might recurse in removeAnnot again
500 annot
->setPage(0, gFalse
);
503 Links
*Page::getLinks() {
504 return new Links(getAnnots());
507 FormPageWidgets
*Page::getFormWidgets() {
508 return new FormPageWidgets(getAnnots(), num
, doc
->getCatalog()->getForm());
511 void Page::display(OutputDev
*out
, double hDPI
, double vDPI
,
512 int rotate
, GBool useMediaBox
, GBool crop
,
514 GBool (*abortCheckCbk
)(void *data
),
515 void *abortCheckCbkData
,
516 GBool (*annotDisplayDecideCbk
)(Annot
*annot
, void *user_data
),
517 void *annotDisplayDecideCbkData
,
519 displaySlice(out
, hDPI
, vDPI
, rotate
, useMediaBox
, crop
, -1, -1, -1, -1, printing
,
520 abortCheckCbk
, abortCheckCbkData
,
521 annotDisplayDecideCbk
, annotDisplayDecideCbkData
, copyXRef
);
524 Gfx
*Page::createGfx(OutputDev
*out
, double hDPI
, double vDPI
,
525 int rotate
, GBool useMediaBox
, GBool crop
,
526 int sliceX
, int sliceY
, int sliceW
, int sliceH
,
528 GBool (*abortCheckCbk
)(void *data
),
529 void *abortCheckCbkData
, XRef
*xrefA
) {
530 PDFRectangle
*mediaBox
, *cropBox
;
534 rotate
+= getRotate();
537 } else if (rotate
< 0) {
541 makeBox(hDPI
, vDPI
, rotate
, useMediaBox
, out
->upsideDown(),
542 sliceX
, sliceY
, sliceW
, sliceH
, &box
, &crop
);
543 cropBox
= getCropBox();
544 mediaBox
= getMediaBox();
546 if (globalParams
->getPrintCommands()) {
547 printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
548 mediaBox
->x1
, mediaBox
->y1
, mediaBox
->x2
, mediaBox
->y2
);
549 printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
550 cropBox
->x1
, cropBox
->y1
, cropBox
->x2
, cropBox
->y2
);
551 printf("***** Rotate = %d\n", attrs
->getRotate());
555 crop
= (box
== *cropBox
) && out
->needClipToCropBox();
557 gfx
= new Gfx(doc
, out
, num
, attrs
->getResourceDict(),
558 hDPI
, vDPI
, &box
, crop
? cropBox
: (PDFRectangle
*)NULL
,
559 rotate
, abortCheckCbk
, abortCheckCbkData
, xrefA
);
564 void Page::displaySlice(OutputDev
*out
, double hDPI
, double vDPI
,
565 int rotate
, GBool useMediaBox
, GBool crop
,
566 int sliceX
, int sliceY
, int sliceW
, int sliceH
,
568 GBool (*abortCheckCbk
)(void *data
),
569 void *abortCheckCbkData
,
570 GBool (*annotDisplayDecideCbk
)(Annot
*annot
, void *user_data
),
571 void *annotDisplayDecideCbkData
,
578 if (!out
->checkPageSlice(this, hDPI
, vDPI
, rotate
, useMediaBox
, crop
,
579 sliceX
, sliceY
, sliceW
, sliceH
,
581 abortCheckCbk
, abortCheckCbkData
,
582 annotDisplayDecideCbk
, annotDisplayDecideCbkData
)) {
586 XRef
*localXRef
= (copyXRef
) ? xref
->copy() : xref
;
588 replaceXRef(localXRef
);
591 gfx
= createGfx(out
, hDPI
, vDPI
, rotate
, useMediaBox
, crop
,
592 sliceX
, sliceY
, sliceW
, sliceH
,
594 abortCheckCbk
, abortCheckCbkData
, localXRef
);
596 contents
.fetch(localXRef
, &obj
);
602 // empty pages need to call dump to do any setup required by the
609 annotList
= getAnnots();
611 if (annotList
->getNumAnnots() > 0) {
612 if (globalParams
->getPrintCommands()) {
613 printf("***** Annotations\n");
615 for (i
= 0; i
< annotList
->getNumAnnots(); ++i
) {
616 Annot
*annot
= annotList
->getAnnot(i
);
617 if ((annotDisplayDecideCbk
&&
618 (*annotDisplayDecideCbk
)(annot
, annotDisplayDecideCbkData
)) ||
619 !annotDisplayDecideCbk
) {
620 annotList
->getAnnot(i
)->draw(gfx
, printing
);
628 replaceXRef(doc
->getXRef());
633 void Page::display(Gfx
*gfx
) {
636 contents
.fetch(xref
, &obj
);
645 GBool
Page::loadThumb(unsigned char **data_out
,
646 int *width_out
, int *height_out
,
649 unsigned int pixbufdatasize
;
650 int width
, height
, bits
;
651 Object obj1
, fetched_thumb
;
653 GfxColorSpace
*colorSpace
;
654 GBool success
= gFalse
;
656 GfxImageColorMap
*colorMap
;
658 /* Get stream dict */
660 thumb
.fetch(xref
, &fetched_thumb
);
661 if (!fetched_thumb
.isStream()) {
662 fetched_thumb
.free();
666 dict
= fetched_thumb
.streamGetDict();
667 str
= fetched_thumb
.getStream();
669 if (!dict
->lookupInt("Width", "W", &width
))
671 if (!dict
->lookupInt("Height", "H", &height
))
673 if (!dict
->lookupInt("BitsPerComponent", "BPC", &bits
))
676 /* Check for invalid dimensions and integer overflow. */
677 if (width
<= 0 || height
<= 0)
679 if (width
> INT_MAX
/ 3 / height
)
681 pixbufdatasize
= width
* height
* 3;
683 /* Get color space */
684 dict
->lookup ("ColorSpace", &obj1
);
685 if (obj1
.isNull ()) {
687 dict
->lookup ("CS", &obj1
);
689 colorSpace
= GfxColorSpace::parse(NULL
, &obj1
, NULL
, NULL
);
692 fprintf (stderr
, "Error: Cannot parse color space\n");
696 dict
->lookup("Decode", &obj1
);
699 dict
->lookup("D", &obj1
);
701 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
703 if (!colorMap
->isOk()) {
704 fprintf (stderr
, "Error: invalid colormap\n");
710 unsigned char *pixbufdata
= (unsigned char *) gmalloc(pixbufdatasize
);
711 unsigned char *p
= pixbufdata
;
712 ImageStream
*imgstr
= new ImageStream(str
, width
,
713 colorMap
->getNumPixelComps(),
714 colorMap
->getBits());
716 for (int row
= 0; row
< height
; ++row
) {
717 for (int col
= 0; col
< width
; ++col
) {
718 Guchar pix
[gfxColorMaxComps
];
721 imgstr
->getPixel(pix
);
722 colorMap
->getRGB(pix
, &rgb
);
724 *p
++ = colToByte(rgb
.r
);
725 *p
++ = colToByte(rgb
.g
);
726 *p
++ = colToByte(rgb
.b
);
729 *data_out
= pixbufdata
;
739 *height_out
= height
;
741 *rowstride_out
= width
* 3;
745 fetched_thumb
.free();
750 void Page::makeBox(double hDPI
, double vDPI
, int rotate
,
751 GBool useMediaBox
, GBool upsideDown
,
752 double sliceX
, double sliceY
, double sliceW
, double sliceH
,
753 PDFRectangle
*box
, GBool
*crop
) {
754 PDFRectangle
*mediaBox
, *cropBox
, *baseBox
;
757 mediaBox
= getMediaBox();
758 cropBox
= getCropBox();
759 if (sliceW
>= 0 && sliceH
>= 0) {
760 baseBox
= useMediaBox
? mediaBox
: cropBox
;
765 box
->x1
= baseBox
->x1
+ ky
* sliceY
;
766 box
->x2
= baseBox
->x1
+ ky
* (sliceY
+ sliceH
);
768 box
->x1
= baseBox
->x2
- ky
* (sliceY
+ sliceH
);
769 box
->x2
= baseBox
->x2
- ky
* sliceY
;
771 box
->y1
= baseBox
->y1
+ kx
* sliceX
;
772 box
->y2
= baseBox
->y1
+ kx
* (sliceX
+ sliceW
);
773 } else if (rotate
== 180) {
774 box
->x1
= baseBox
->x2
- kx
* (sliceX
+ sliceW
);
775 box
->x2
= baseBox
->x2
- kx
* sliceX
;
777 box
->y1
= baseBox
->y1
+ ky
* sliceY
;
778 box
->y2
= baseBox
->y1
+ ky
* (sliceY
+ sliceH
);
780 box
->y1
= baseBox
->y2
- ky
* (sliceY
+ sliceH
);
781 box
->y2
= baseBox
->y2
- ky
* sliceY
;
783 } else if (rotate
== 270) {
785 box
->x1
= baseBox
->x2
- ky
* (sliceY
+ sliceH
);
786 box
->x2
= baseBox
->x2
- ky
* sliceY
;
788 box
->x1
= baseBox
->x1
+ ky
* sliceY
;
789 box
->x2
= baseBox
->x1
+ ky
* (sliceY
+ sliceH
);
791 box
->y1
= baseBox
->y2
- kx
* (sliceX
+ sliceW
);
792 box
->y2
= baseBox
->y2
- kx
* sliceX
;
794 box
->x1
= baseBox
->x1
+ kx
* sliceX
;
795 box
->x2
= baseBox
->x1
+ kx
* (sliceX
+ sliceW
);
797 box
->y1
= baseBox
->y2
- ky
* (sliceY
+ sliceH
);
798 box
->y2
= baseBox
->y2
- ky
* sliceY
;
800 box
->y1
= baseBox
->y1
+ ky
* sliceY
;
801 box
->y2
= baseBox
->y1
+ ky
* (sliceY
+ sliceH
);
804 } else if (useMediaBox
) {
812 void Page::processLinks(OutputDev
*out
) {
817 for (i
= 0; i
< links
->getNumLinks(); ++i
) {
818 out
->processLink(links
->getLink(i
));
823 void Page::getDefaultCTM(double *ctm
, double hDPI
, double vDPI
,
824 int rotate
, GBool useMediaBox
, GBool upsideDown
) {
827 rotate
+= getRotate();
830 } else if (rotate
< 0) {
833 state
= new GfxState(hDPI
, vDPI
,
834 useMediaBox
? getMediaBox() : getCropBox(),
836 for (i
= 0; i
< 6; ++i
) {
837 ctm
[i
] = state
->getCTM()[i
];
842 LinkAction
* Page::getAdditionalAction(PageAdditionalActionsType type
) {
843 Object additionalActionsObject
;
844 LinkAction
*linkAction
= NULL
;
846 if (actions
.fetch(doc
->getXRef(), &additionalActionsObject
)->isDict()) {
847 const char *key
= (type
== actionOpenPage
? "O" :
848 type
== actionClosePage
? "C" : NULL
);
852 if (additionalActionsObject
.dictLookup(key
, &actionObject
)->isDict())
853 linkAction
= LinkAction::parseAction(&actionObject
, doc
->getCatalog()->getBaseURI());
857 additionalActionsObject
.free();