SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / poppler / poppler / Page.cc
1 //========================================================================
2 //
3 // Page.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stddef.h>
16 #include <limits.h>
17 #include "GlobalParams.h"
18 #include "Object.h"
19 #include "Array.h"
20 #include "Dict.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 "TextOutputDev.h"
29 #endif
30 #include "Error.h"
31 #include "Page.h"
32 #include "UGooString.h"
33
34 //------------------------------------------------------------------------
35 // PageAttrs
36 //------------------------------------------------------------------------
37
38 PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
39 Object obj1;
40
41 // get old/default values
42 if (attrs) {
43 mediaBox = attrs->mediaBox;
44 cropBox = attrs->cropBox;
45 haveCropBox = attrs->haveCropBox;
46 rotate = attrs->rotate;
47 attrs->resources.copy(&resources);
48 } else {
49 // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
50 // but some (non-compliant) PDF files don't specify a MediaBox
51 mediaBox.x1 = 0;
52 mediaBox.y1 = 0;
53 mediaBox.x2 = 612;
54 mediaBox.y2 = 792;
55 cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
56 haveCropBox = gFalse;
57 rotate = 0;
58 resources.initNull();
59 }
60
61 // media box
62 readBox(dict, "MediaBox", &mediaBox);
63
64 // crop box
65 if (readBox(dict, "CropBox", &cropBox)) {
66 haveCropBox = gTrue;
67 }
68 if (!haveCropBox) {
69 cropBox = mediaBox;
70 }
71 else
72 {
73 // cropBox can not be bigger than mediaBox
74 if (cropBox.x2 - cropBox.x1 > mediaBox.x2 - mediaBox.x1)
75 {
76 cropBox.x1 = mediaBox.x1;
77 cropBox.x2 = mediaBox.x2;
78 }
79 if (cropBox.y2 - cropBox.y1 > mediaBox.y2 - mediaBox.y1)
80 {
81 cropBox.y1 = mediaBox.y1;
82 cropBox.y2 = mediaBox.y2;
83 }
84 }
85
86 // other boxes
87 bleedBox = cropBox;
88 readBox(dict, "BleedBox", &bleedBox);
89 trimBox = cropBox;
90 readBox(dict, "TrimBox", &trimBox);
91 artBox = cropBox;
92 readBox(dict, "ArtBox", &artBox);
93
94 // rotate
95 dict->lookup("Rotate", &obj1);
96 if (obj1.isInt()) {
97 rotate = obj1.getInt();
98 }
99 obj1.free();
100 while (rotate < 0) {
101 rotate += 360;
102 }
103 while (rotate >= 360) {
104 rotate -= 360;
105 }
106
107 // misc attributes
108 dict->lookup("LastModified", &lastModified);
109 dict->lookup("BoxColorInfo", &boxColorInfo);
110 dict->lookup("Group", &group);
111 dict->lookup("Metadata", &metadata);
112 dict->lookup("PieceInfo", &pieceInfo);
113 dict->lookup("SeparationInfo", &separationInfo);
114
115 // resource dictionary
116 dict->lookup("Resources", &obj1);
117 if (obj1.isDict()) {
118 resources.free();
119 obj1.copy(&resources);
120 }
121 obj1.free();
122 }
123
124 PageAttrs::~PageAttrs() {
125 lastModified.free();
126 boxColorInfo.free();
127 group.free();
128 metadata.free();
129 pieceInfo.free();
130 separationInfo.free();
131 resources.free();
132 }
133
134 GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
135 PDFRectangle tmp;
136 double t;
137 Object obj1, obj2;
138 GBool ok;
139
140 dict->lookup(key, &obj1);
141 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
142 ok = gTrue;
143 obj1.arrayGet(0, &obj2);
144 if (obj2.isNum()) {
145 tmp.x1 = obj2.getNum();
146 } else {
147 ok = gFalse;
148 }
149 obj2.free();
150 obj1.arrayGet(1, &obj2);
151 if (obj2.isNum()) {
152 tmp.y1 = obj2.getNum();
153 } else {
154 ok = gFalse;
155 }
156 obj2.free();
157 obj1.arrayGet(2, &obj2);
158 if (obj2.isNum()) {
159 tmp.x2 = obj2.getNum();
160 } else {
161 ok = gFalse;
162 }
163 obj2.free();
164 obj1.arrayGet(3, &obj2);
165 if (obj2.isNum()) {
166 tmp.y2 = obj2.getNum();
167 } else {
168 ok = gFalse;
169 }
170 obj2.free();
171 if (ok) {
172 if (tmp.x1 > tmp.x2) {
173 t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
174 }
175 if (tmp.y1 > tmp.y2) {
176 t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
177 }
178 *box = tmp;
179 }
180 } else {
181 ok = gFalse;
182 }
183 obj1.free();
184 return ok;
185 }
186
187 //------------------------------------------------------------------------
188 // Page
189 //------------------------------------------------------------------------
190
191 Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
192 Object tmp;
193
194 ok = gTrue;
195 xref = xrefA;
196 num = numA;
197 duration = -1;
198
199 // get attributes
200 attrs = attrsA;
201
202 // transtion
203 pageDict->lookupNF("Trans", &trans);
204 if (!(trans.isDict() || trans.isNull())) {
205 error(-1, "Page transition object (page %d) is wrong type (%s)",
206 num, trans.getTypeName());
207 trans.free();
208 }
209
210 // duration
211 pageDict->lookupNF("Dur", &tmp);
212 if (!(tmp.isNum() || tmp.isNull())) {
213 error(-1, "Page duration object (page %d) is wrong type (%s)",
214 num, tmp.getTypeName());
215 } else if (tmp.isNum()) {
216 duration = tmp.getNum();
217 }
218 tmp.free();
219
220 // annotations
221 pageDict->lookupNF("Annots", &annots);
222 if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
223 error(-1, "Page annotations object (page %d) is wrong type (%s)",
224 num, annots.getTypeName());
225 annots.free();
226 goto err2;
227 }
228
229 // contents
230 pageDict->lookupNF("Contents", &contents);
231 if (!(contents.isRef() || contents.isArray() ||
232 contents.isNull())) {
233 error(-1, "Page contents object (page %d) is wrong type (%s)",
234 num, contents.getTypeName());
235 contents.free();
236 goto err1;
237 }
238
239 // thumb
240 pageDict->lookupNF("Thumb", &thumb);
241 if (!(thumb.isStream() || thumb.isNull() || thumb.isRef())) {
242 error(-1, "Page thumb object (page %d) is wrong type (%s)",
243 num, thumb.getTypeName());
244 thumb.initNull();
245 }
246
247 // actions
248 pageDict->lookupNF("AA", &actions);
249 if (!(actions.isDict() || actions.isNull())) {
250 error(-1, "Page additional action object (page %d) is wrong type (%s)",
251 num, actions.getTypeName());
252 actions.initNull();
253 }
254
255 return;
256
257 trans.initNull();
258 err2:
259 annots.initNull();
260 err1:
261 contents.initNull();
262 ok = gFalse;
263 }
264
265 Page::~Page() {
266 delete attrs;
267 annots.free();
268 contents.free();
269 }
270
271 void Page::display(OutputDev *out, double hDPI, double vDPI,
272 int rotate, GBool useMediaBox, GBool crop,
273 Links *links, Catalog *catalog,
274 GBool (*abortCheckCbk)(void *data),
275 void *abortCheckCbkData,
276 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
277 void *annotDisplayDecideCbkData) {
278 displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, links, catalog,
279 abortCheckCbk, abortCheckCbkData,
280 annotDisplayDecideCbk, annotDisplayDecideCbkData);
281 }
282
283 Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI,
284 int rotate, GBool useMediaBox, GBool crop,
285 int sliceX, int sliceY, int sliceW, int sliceH,
286 Links *links, Catalog *catalog,
287 GBool (*abortCheckCbk)(void *data),
288 void *abortCheckCbkData,
289 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
290 void *annotDisplayDecideCbkData) {
291 PDFRectangle *mediaBox, *cropBox, *baseBox;
292 PDFRectangle box;
293 Gfx *gfx;
294 double kx, ky;
295
296 rotate += getRotate();
297 if (rotate >= 360) {
298 rotate -= 360;
299 } else if (rotate < 0) {
300 rotate += 360;
301 }
302
303 mediaBox = getMediaBox();
304 cropBox = getCropBox();
305 if (sliceW >= 0 && sliceH >= 0) {
306 baseBox = useMediaBox ? mediaBox : cropBox;
307 kx = 72.0 / hDPI;
308 ky = 72.0 / vDPI;
309 if (rotate == 90) {
310 if (out->upsideDown()) {
311 box.x1 = baseBox->x1 + ky * sliceY;
312 box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
313 } else {
314 box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
315 box.x2 = baseBox->x2 - ky * sliceY;
316 }
317 box.y1 = baseBox->y1 + kx * sliceX;
318 box.y2 = baseBox->y1 + kx * (sliceX + sliceW);
319 } else if (rotate == 180) {
320 box.x1 = baseBox->x2 - kx * (sliceX + sliceW);
321 box.x2 = baseBox->x2 - kx * sliceX;
322 if (out->upsideDown()) {
323 box.y1 = baseBox->y1 + ky * sliceY;
324 box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
325 } else {
326 box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
327 box.y2 = baseBox->y2 - ky * sliceY;
328 }
329 } else if (rotate == 270) {
330 if (out->upsideDown()) {
331 box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
332 box.x2 = baseBox->x2 - ky * sliceY;
333 } else {
334 box.x1 = baseBox->x1 + ky * sliceY;
335 box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
336 }
337 box.y1 = baseBox->y2 - kx * (sliceX + sliceW);
338 box.y2 = baseBox->y2 - kx * sliceX;
339 } else {
340 box.x1 = baseBox->x1 + kx * sliceX;
341 box.x2 = baseBox->x1 + kx * (sliceX + sliceW);
342 if (out->upsideDown()) {
343 box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
344 box.y2 = baseBox->y2 - ky * sliceY;
345 } else {
346 box.y1 = baseBox->y1 + ky * sliceY;
347 box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
348 }
349 }
350 } else if (useMediaBox) {
351 box = *mediaBox;
352 } else {
353 box = *cropBox;
354 crop = gFalse;
355 }
356
357 if (globalParams->getPrintCommands()) {
358 printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
359 mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
360 printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
361 cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
362 printf("***** Rotate = %d\n", attrs->getRotate());
363 }
364
365 gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
366 hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
367 rotate, abortCheckCbk, abortCheckCbkData);
368
369 return gfx;
370 }
371
372 void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
373 int rotate, GBool useMediaBox, GBool crop,
374 int sliceX, int sliceY, int sliceW, int sliceH,
375 Links *links, Catalog *catalog,
376 GBool (*abortCheckCbk)(void *data),
377 void *abortCheckCbkData,
378 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
379 void *annotDisplayDecideCbkData) {
380 Gfx *gfx;
381 Object obj;
382 Link *link;
383 Annots *annotList;
384 int i;
385
386 gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop,
387 sliceX, sliceY, sliceW, sliceH,
388 links, catalog,
389 abortCheckCbk, abortCheckCbkData,
390 annotDisplayDecideCbk, annotDisplayDecideCbkData);
391
392 contents.fetch(xref, &obj);
393 if (!obj.isNull()) {
394 gfx->saveState();
395 gfx->display(&obj);
396 gfx->restoreState();
397 }
398 obj.free();
399
400 // draw links
401 if (links) {
402 gfx->saveState();
403 for (i = 0; i < links->getNumLinks(); ++i) {
404 link = links->getLink(i);
405 out->drawLink(link, catalog);
406 }
407 gfx->restoreState();
408 out->dump();
409 }
410
411 // draw non-link annotations
412 annotList = new Annots(xref, catalog, annots.fetch(xref, &obj));
413 obj.free();
414 if (annotList->getNumAnnots() > 0) {
415 if (globalParams->getPrintCommands()) {
416 printf("***** Annotations\n");
417 }
418 for (i = 0; i < annotList->getNumAnnots(); ++i) {
419 Annot *annot = annotList->getAnnot(i);
420 if ((annotDisplayDecideCbk &&
421 (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) ||
422 !annotDisplayDecideCbk)
423 annot->draw(gfx);
424 }
425 out->dump();
426 }
427 delete annotList;
428
429 delete gfx;
430 }
431
432 void Page::display(Gfx *gfx) {
433 Object obj;
434
435 contents.fetch(xref, &obj);
436 if (!obj.isNull()) {
437 gfx->saveState();
438 gfx->display(&obj);
439 gfx->restoreState();
440 }
441 obj.free();
442 }
443
444 GBool Page::loadThumb(unsigned char **data_out,
445 int *width_out, int *height_out,
446 int *rowstride_out)
447 {
448 ImageStream *imgstr = NULL;
449 unsigned char *pixbufdata;
450 unsigned int pixbufdatasize;
451 int row, col;
452 int width, height, bits;
453 unsigned char *p;
454 Object obj1, fetched_thumb;
455 Dict *dict;
456 GfxColorSpace *colorSpace;
457 GBool success = gFalse;
458 Stream *str;
459 GfxImageColorMap *colorMap = NULL;
460
461 /* Get stream dict */
462 thumb.fetch(xref, &fetched_thumb);
463 if (fetched_thumb.isNull()) {
464 fetched_thumb.free();
465 return gFalse;
466 }
467
468 dict = fetched_thumb.streamGetDict();
469 str = fetched_thumb.getStream();
470
471 if (!dict->lookupInt("Width", "W", &width))
472 goto fail1;
473 if (!dict->lookupInt("Height", "H", &height))
474 goto fail1;
475 if (!dict->lookupInt("BitsPerComponent", "BPC", &bits))
476 goto fail1;
477
478 /* Check for invalid dimensions and integer overflow. */
479 if (width <= 0 || height <= 0)
480 goto fail1;
481 if (width > INT_MAX / 3 / height)
482 goto fail1;
483 pixbufdatasize = width * height * 3;
484
485 /* Get color space */
486 dict->lookup ("ColorSpace", &obj1);
487 if (obj1.isNull ()) {
488 obj1.free ();
489 dict->lookup ("CS", &obj1);
490 }
491 colorSpace = GfxColorSpace::parse(&obj1);
492 obj1.free();
493 if (!colorSpace) {
494 fprintf (stderr, "Error: Cannot parse color space\n");
495 goto fail1;
496 }
497
498 dict->lookup("Decode", &obj1);
499 if (obj1.isNull()) {
500 obj1.free();
501 dict->lookup("D", &obj1);
502 }
503 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
504 obj1.free();
505 if (!colorMap->isOk()) {
506 fprintf (stderr, "Error: invalid colormap\n");
507 goto fail1;
508 }
509
510 pixbufdata = (unsigned char *) gmalloc(pixbufdatasize);
511 p = pixbufdata;
512 imgstr = new ImageStream(str, width,
513 colorMap->getNumPixelComps(),
514 colorMap->getBits());
515 imgstr->reset();
516 for (row = 0; row < height; ++row) {
517 for (col = 0; col < width; ++col) {
518 Guchar pix[gfxColorMaxComps];
519 GfxRGB rgb;
520
521 imgstr->getPixel(pix);
522 colorMap->getRGB(pix, &rgb);
523
524 *p++ = colToByte(rgb.r);
525 *p++ = colToByte(rgb.g);
526 *p++ = colToByte(rgb.b);
527 }
528 }
529
530 success = gTrue;
531
532 if (data_out)
533 *data_out = pixbufdata;
534 else
535 gfree(pixbufdata);
536 if (width_out)
537 *width_out = width;
538 if (height_out)
539 *height_out = height;
540 if (rowstride_out)
541 *rowstride_out = width * 3;
542
543 delete imgstr;
544 fail1:
545 delete colorMap;
546 fetched_thumb.free();
547
548 return success;
549 }
550
551 void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
552 int rotate, GBool upsideDown) {
553 GfxState *state;
554 int i;
555 rotate += getRotate();
556 if (rotate >= 360) {
557 rotate -= 360;
558 } else if (rotate < 0) {
559 rotate += 360;
560 }
561 state = new GfxState(hDPI, vDPI, getMediaBox(), rotate, upsideDown);
562 for (i = 0; i < 6; ++i) {
563 ctm[i] = state->getCTM()[i];
564 }
565 delete state;
566 }