1 //========================================================================
2 //
3 // Page.cc
4 //
5 // Copyright 1996-2007 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 #include <aconf.h>
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include <stddef.h>
16 #include "GlobalParams.h"
17 #include "Object.h"
18 #include "Array.h"
19 #include "Dict.h"
20 #include "PDFDoc.h"
21 #include "XRef.h"
22 #include "Link.h"
23 #include "OutputDev.h"
24 #ifndef PDF_PARSER_ONLY
25 #include "Gfx.h"
26 #include "GfxState.h"
27 #include "Annot.h"
28 #include "Form.h"
29 #endif
30 #include "Error.h"
31 #include "Catalog.h"
32 #include "Page.h"
33 
34 //------------------------------------------------------------------------
35 // PDFRectangle
36 //------------------------------------------------------------------------
37 
clipTo(PDFRectangle * rect)38 void PDFRectangle::clipTo(PDFRectangle *rect) {
39   if (x1 < rect->x1) {
40     x1 = rect->x1;
41   } else if (x1 > rect->x2) {
42     x1 = rect->x2;
43   }
44   if (x2 < rect->x1) {
45     x2 = rect->x1;
46   } else if (x2 > rect->x2) {
47     x2 = rect->x2;
48   }
49   if (y1 < rect->y1) {
50     y1 = rect->y1;
51   } else if (y1 > rect->y2) {
52     y1 = rect->y2;
53   }
54   if (y2 < rect->y1) {
55     y2 = rect->y1;
56   } else if (y2 > rect->y2) {
57     y2 = rect->y2;
58   }
59 }
60 
61 //------------------------------------------------------------------------
62 // PageAttrs
63 //------------------------------------------------------------------------
64 
PageAttrs(PageAttrs * attrs,Dict * dict)65 PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
66   Object obj1;
67 
68   // get old/default values
69   if (attrs) {
70     mediaBox = attrs->mediaBox;
71     cropBox = attrs->cropBox;
72     haveCropBox = attrs->haveCropBox;
73     rotate = attrs->rotate;
74     attrs->resources.copy(&resources);
75   } else {
76     // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
77     // but some (non-compliant) PDF files don't specify a MediaBox
78     mediaBox.x1 = 0;
79     mediaBox.y1 = 0;
80     mediaBox.x2 = 612;
81     mediaBox.y2 = 792;
82     cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
83     haveCropBox = gFalse;
84     rotate = 0;
85     resources.initNull();
86   }
87 
88   // media box
89   readBox(dict, "MediaBox", &mediaBox);
90 
91   // crop box
92   if (readBox(dict, "CropBox", &cropBox)) {
93     haveCropBox = gTrue;
94   }
95   if (!haveCropBox) {
96     cropBox = mediaBox;
97   }
98 
99   // other boxes
100   bleedBox = cropBox;
101   readBox(dict, "BleedBox", &bleedBox);
102   trimBox = cropBox;
103   readBox(dict, "TrimBox", &trimBox);
104   artBox = cropBox;
105   readBox(dict, "ArtBox", &artBox);
106 
107   // rotate
108   dict->lookup("Rotate", &obj1);
109   if (obj1.isInt()) {
110     rotate = obj1.getInt();
111   }
112   obj1.free();
113   while (rotate < 0) {
114     rotate += 360;
115   }
116   while (rotate >= 360) {
117     rotate -= 360;
118   }
119 
120   // misc attributes
121   dict->lookup("LastModified", &lastModified);
122   dict->lookup("BoxColorInfo", &boxColorInfo);
123   dict->lookup("Group", &group);
124   dict->lookup("Metadata", &metadata);
125   dict->lookup("PieceInfo", &pieceInfo);
126   dict->lookup("SeparationInfo", &separationInfo);
127   if (dict->lookup("UserUnit", &obj1)->isNum()) {
128     userUnit = obj1.getNum();
129     if (userUnit < 1) {
130       userUnit = 1;
131     }
132   } else {
133     userUnit = 1;
134   }
135   obj1.free();
136 
137   // resource dictionary
138   dict->lookup("Resources", &obj1);
139   if (obj1.isDict()) {
140     resources.free();
141     obj1.copy(&resources);
142   }
143   obj1.free();
144 }
145 
PageAttrs()146 PageAttrs::PageAttrs() {
147   mediaBox.x1 = mediaBox.y1 = 0;
148   mediaBox.x2 = mediaBox.y2 = 50;
149   cropBox = mediaBox;
150   haveCropBox = gFalse;
151   bleedBox = cropBox;
152   trimBox = cropBox;
153   artBox = cropBox;
154   rotate = 0;
155   lastModified.initNull();
156   boxColorInfo.initNull();
157   group.initNull();
158   metadata.initNull();
159   pieceInfo.initNull();
160   separationInfo.initNull();
161   resources.initNull();
162 }
163 
~PageAttrs()164 PageAttrs::~PageAttrs() {
165   lastModified.free();
166   boxColorInfo.free();
167   group.free();
168   metadata.free();
169   pieceInfo.free();
170   separationInfo.free();
171   resources.free();
172 }
173 
clipBoxes()174 void PageAttrs::clipBoxes() {
175   cropBox.clipTo(&mediaBox);
176   bleedBox.clipTo(&mediaBox);
177   trimBox.clipTo(&mediaBox);
178   artBox.clipTo(&mediaBox);
179 }
180 
readBox(Dict * dict,const char * key,PDFRectangle * box)181 GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) {
182   PDFRectangle tmp;
183   double t;
184   Object obj1, obj2;
185   GBool ok;
186 
187   dict->lookup(key, &obj1);
188   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
189     ok = gTrue;
190     obj1.arrayGet(0, &obj2);
191     if (obj2.isNum()) {
192       tmp.x1 = obj2.getNum();
193     } else {
194       ok = gFalse;
195     }
196     obj2.free();
197     obj1.arrayGet(1, &obj2);
198     if (obj2.isNum()) {
199       tmp.y1 = obj2.getNum();
200     } else {
201       ok = gFalse;
202     }
203     obj2.free();
204     obj1.arrayGet(2, &obj2);
205     if (obj2.isNum()) {
206       tmp.x2 = obj2.getNum();
207     } else {
208       ok = gFalse;
209     }
210     obj2.free();
211     obj1.arrayGet(3, &obj2);
212     if (obj2.isNum()) {
213       tmp.y2 = obj2.getNum();
214     } else {
215       ok = gFalse;
216     }
217     obj2.free();
218     if (ok) {
219       if (tmp.x1 > tmp.x2) {
220 	t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
221       }
222       if (tmp.y1 > tmp.y2) {
223 	t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
224       }
225       *box = tmp;
226     }
227   } else {
228     ok = gFalse;
229   }
230   obj1.free();
231   return ok;
232 }
233 
234 //------------------------------------------------------------------------
235 // Page
236 //------------------------------------------------------------------------
237 
Page(PDFDoc * docA,int numA,Dict * pageDict,PageAttrs * attrsA)238 Page::Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA) {
239   ok = gTrue;
240   doc = docA;
241   xref = doc->getXRef();
242   num = numA;
243 
244   // get attributes
245   attrs = attrsA;
246   attrs->clipBoxes();
247 
248   // annotations
249   pageDict->lookupNF("Annots", &annots);
250   if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
251     error(errSyntaxError, -1,
252 	  "Page annotations object (page {0:d}) is wrong type ({1:s})",
253 	  num, annots.getTypeName());
254     annots.free();
255     goto err2;
256   }
257 
258   // contents
259   pageDict->lookupNF("Contents", &contents);
260   if (!(contents.isRef() || contents.isArray() ||
261 	contents.isNull())) {
262     error(errSyntaxError, -1,
263 	  "Page contents object (page {0:d}) is wrong type ({1:s})",
264 	  num, contents.getTypeName());
265     contents.free();
266     goto err1;
267   }
268 
269   return;
270 
271  err2:
272   annots.initNull();
273  err1:
274   contents.initNull();
275   ok = gFalse;
276 }
277 
Page(PDFDoc * docA,int numA)278 Page::Page(PDFDoc *docA, int numA) {
279   doc = docA;
280   xref = doc->getXRef();
281   num = numA;
282   attrs = new PageAttrs();
283   annots.initNull();
284   contents.initNull();
285   ok = gTrue;
286 }
287 
~Page()288 Page::~Page() {
289   delete attrs;
290   annots.free();
291   contents.free();
292 }
293 
getLinks()294 Links *Page::getLinks() {
295   Links *links;
296   Object obj;
297 
298   links = new Links(getAnnots(&obj), doc->getCatalog()->getBaseURI());
299   obj.free();
300   return links;
301 }
302 
display(OutputDev * out,double hDPI,double vDPI,int rotate,GBool useMediaBox,GBool crop,GBool printing,GBool (* abortCheckCbk)(void * data),void * abortCheckCbkData)303 void Page::display(OutputDev *out, double hDPI, double vDPI,
304 		   int rotate, GBool useMediaBox, GBool crop,
305 		   GBool printing,
306 		   GBool (*abortCheckCbk)(void *data),
307 		   void *abortCheckCbkData) {
308   displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
309 	       -1, -1, -1, -1, printing,
310 	       abortCheckCbk, abortCheckCbkData);
311 }
312 
displaySlice(OutputDev * out,double hDPI,double vDPI,int rotate,GBool useMediaBox,GBool crop,int sliceX,int sliceY,int sliceW,int sliceH,GBool printing,GBool (* abortCheckCbk)(void * data),void * abortCheckCbkData)313 void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
314 			int rotate, GBool useMediaBox, GBool crop,
315 			int sliceX, int sliceY, int sliceW, int sliceH,
316 			GBool printing,
317 			GBool (*abortCheckCbk)(void *data),
318 			void *abortCheckCbkData) {
319 #ifndef PDF_PARSER_ONLY
320   PDFRectangle *mediaBox, *cropBox;
321   PDFRectangle box;
322   Gfx *gfx;
323   Object obj;
324   Annots *annotList;
325   Form *form;
326   int i;
327 
328   if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
329 			   sliceX, sliceY, sliceW, sliceH,
330 			   printing, abortCheckCbk, abortCheckCbkData)) {
331     return;
332   }
333 
334   rotate += getRotate();
335   if (rotate >= 360) {
336     rotate -= 360;
337   } else if (rotate < 0) {
338     rotate += 360;
339   }
340 
341   makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
342 	  sliceX, sliceY, sliceW, sliceH, &box, &crop);
343   cropBox = getCropBox();
344 
345   if (globalParams->getPrintCommands()) {
346     mediaBox = getMediaBox();
347     printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
348 	   mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
349     printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
350 	   cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
351     printf("***** Rotate = %d\n", attrs->getRotate());
352   }
353 
354   gfx = new Gfx(doc, out, num, attrs->getResourceDict(),
355 		hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
356 		rotate, abortCheckCbk, abortCheckCbkData);
357   contents.fetch(xref, &obj);
358   if (!obj.isNull()) {
359     gfx->saveState();
360     gfx->display(&contents);
361     while (gfx->getState()->hasSaves()) {
362       gfx->restoreState();
363     }
364   } else {
365     // empty pages need to call dump to do any setup required by the
366     // OutputDev
367     out->dump();
368   }
369   obj.free();
370 
371   // draw (non-form) annotations
372   if (globalParams->getDrawAnnotations()) {
373     annotList = new Annots(doc, getAnnots(&obj));
374     obj.free();
375     annotList->generateAnnotAppearances();
376     if (annotList->getNumAnnots() > 0) {
377       if (globalParams->getPrintCommands()) {
378 	printf("***** Annotations\n");
379       }
380       for (i = 0; i < annotList->getNumAnnots(); ++i) {
381 	annotList->getAnnot(i)->draw(gfx, printing);
382       }
383       out->dump();
384     }
385     delete annotList;
386   }
387 
388   // draw form fields
389   if ((form = doc->getCatalog()->getForm())) {
390     form->draw(num, gfx, printing);
391     out->dump();
392   }
393 
394   delete gfx;
395 #endif
396 }
397 
makeBox(double hDPI,double vDPI,int rotate,GBool useMediaBox,GBool upsideDown,double sliceX,double sliceY,double sliceW,double sliceH,PDFRectangle * box,GBool * crop)398 void Page::makeBox(double hDPI, double vDPI, int rotate,
399 		   GBool useMediaBox, GBool upsideDown,
400 		   double sliceX, double sliceY, double sliceW, double sliceH,
401 		   PDFRectangle *box, GBool *crop) {
402   PDFRectangle *mediaBox, *cropBox, *baseBox;
403   double kx, ky;
404 
405   mediaBox = getMediaBox();
406   cropBox = getCropBox();
407   if (sliceW >= 0 && sliceH >= 0) {
408     baseBox = useMediaBox ? mediaBox : cropBox;
409     kx = 72.0 / hDPI;
410     ky = 72.0 / vDPI;
411     if (rotate == 90) {
412       if (upsideDown) {
413 	box->x1 = baseBox->x1 + ky * sliceY;
414 	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
415       } else {
416 	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
417 	box->x2 = baseBox->x2 - ky * sliceY;
418       }
419       box->y1 = baseBox->y1 + kx * sliceX;
420       box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
421     } else if (rotate == 180) {
422       box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
423       box->x2 = baseBox->x2 - kx * sliceX;
424       if (upsideDown) {
425 	box->y1 = baseBox->y1 + ky * sliceY;
426 	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
427       } else {
428 	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
429 	box->y2 = baseBox->y2 - ky * sliceY;
430       }
431     } else if (rotate == 270) {
432       if (upsideDown) {
433 	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
434 	box->x2 = baseBox->x2 - ky * sliceY;
435       } else {
436 	box->x1 = baseBox->x1 + ky * sliceY;
437 	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
438       }
439       box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
440       box->y2 = baseBox->y2 - kx * sliceX;
441     } else {
442       box->x1 = baseBox->x1 + kx * sliceX;
443       box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
444       if (upsideDown) {
445 	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
446 	box->y2 = baseBox->y2 - ky * sliceY;
447       } else {
448 	box->y1 = baseBox->y1 + ky * sliceY;
449 	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
450       }
451     }
452   } else if (useMediaBox) {
453     *box = *mediaBox;
454   } else {
455     *box = *cropBox;
456     *crop = gFalse;
457   }
458 }
459 
processLinks(OutputDev * out)460 void Page::processLinks(OutputDev *out) {
461   Links *links;
462   int i;
463 
464   links = getLinks();
465   for (i = 0; i < links->getNumLinks(); ++i) {
466     out->processLink(links->getLink(i));
467   }
468   delete links;
469 }
470 
getDefaultCTM(double * ctm,double hDPI,double vDPI,int rotate,GBool useMediaBox,GBool upsideDown)471 void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
472 			 int rotate, GBool useMediaBox, GBool upsideDown) {
473 #ifndef PDF_PARSER_ONLY
474   GfxState *state;
475   int i;
476 
477   rotate += getRotate();
478   if (rotate >= 360) {
479     rotate -= 360;
480   } else if (rotate < 0) {
481     rotate += 360;
482   }
483   state = new GfxState(hDPI, vDPI,
484 		       useMediaBox ? getMediaBox() : getCropBox(),
485 		       rotate, upsideDown);
486   for (i = 0; i < 6; ++i) {
487     ctm[i] = state->getCTM()[i];
488   }
489   delete state;
490 #endif
491 }
492