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