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

Last change on this file since 461 was 277, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.3

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