source: trunk/poppler/mypoppler/poppler/Page.cc @ 257

Last change on this file since 257 was 257, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.0

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