Page.cc 20.9 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1 2 3 4
//========================================================================
//
// Page.cc
//
5
// Copyright 1996-2007 Glyph & Cog, LLC
Kristian Høgsberg's avatar
Kristian Høgsberg committed
6 7 8
//
//========================================================================

9 10 11 12
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
13 14 15
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
16 17
// Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
// Copyright (C) 2005 Jeff Muizelaar <jeff@infidigm.net>
18
// Copyright (C) 2005-2013 Albert Astals Cid <aacid@kde.org>
19 20 21
// Copyright (C) 2006-2008 Pino Toscano <pino@kde.org>
// Copyright (C) 2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
// Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
22
// Copyright (C) 2006-2011 Carlos Garcia Campos <carlosgc@gnome.org>
23 24 25
// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2008 Iñigo Martínez <inigomartinez@gmail.com>
// Copyright (C) 2008 Brad Hards <bradh@kde.org>
Ilya Gorenbein's avatar
Ilya Gorenbein committed
26
// Copyright (C) 2008 Ilya Gorenbein <igorenbein@finjan.com>
Fabio D'Urso's avatar
Fabio D'Urso committed
27
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
Thomas Freitag's avatar
Thomas Freitag committed
28
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
29
// Copyright (C) 2013 Jason Crain <jason@aquaticape.us>
30 31 32 33 34 35
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================

Kristian Høgsberg's avatar
Kristian Høgsberg committed
36 37 38 39 40 41 42
#include <config.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
43
#include <limits.h>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
44 45 46 47
#include "GlobalParams.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
48
#include "PDFDoc.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
49 50 51 52
#include "XRef.h"
#include "Link.h"
#include "OutputDev.h"
#include "Gfx.h"
53
#include "GfxState.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
54
#include "Annot.h"
55
#include "TextOutputDev.h"
56
#include "Form.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
57 58
#include "Error.h"
#include "Page.h"
59
#include "Catalog.h"
60
#include "Form.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
61

Thomas Freitag's avatar
Thomas Freitag committed
62
#if MULTITHREADED
63
#  define pageLocker()   MutexLocker locker(&mutex)
Thomas Freitag's avatar
Thomas Freitag committed
64
#else
65
#  define pageLocker()
Thomas Freitag's avatar
Thomas Freitag committed
66
#endif
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
//------------------------------------------------------------------------
// PDFRectangle
//------------------------------------------------------------------------

void PDFRectangle::clipTo(PDFRectangle *rect) {
  if (x1 < rect->x1) {
    x1 = rect->x1;
  } else if (x1 > rect->x2) {
    x1 = rect->x2;
  }
  if (x2 < rect->x1) {
    x2 = rect->x1;
  } else if (x2 > rect->x2) {
    x2 = rect->x2;
  }
  if (y1 < rect->y1) {
    y1 = rect->y1;
  } else if (y1 > rect->y2) {
    y1 = rect->y2;
  }
  if (y2 < rect->y1) {
    y2 = rect->y1;
  } else if (y2 > rect->y2) {
    y2 = rect->y2;
  }
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
94 95 96 97 98 99
//------------------------------------------------------------------------
// PageAttrs
//------------------------------------------------------------------------

PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
  Object obj1;
100
  PDFRectangle mBox;
101
  const GBool isPage = dict->is("Page");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

  // get old/default values
  if (attrs) {
    mediaBox = attrs->mediaBox;
    cropBox = attrs->cropBox;
    haveCropBox = attrs->haveCropBox;
    rotate = attrs->rotate;
    attrs->resources.copy(&resources);
  } else {
    // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
    // but some (non-compliant) PDF files don't specify a MediaBox
    mediaBox.x1 = 0;
    mediaBox.y1 = 0;
    mediaBox.x2 = 612;
    mediaBox.y2 = 792;
    cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
    haveCropBox = gFalse;
    rotate = 0;
    resources.initNull();
  }

  // media box
124 125 126
  if (readBox(dict, "MediaBox", &mBox)) {
    mediaBox = mBox;
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
127 128 129 130 131 132 133 134

  // crop box
  if (readBox(dict, "CropBox", &cropBox)) {
    haveCropBox = gTrue;
  }
  if (!haveCropBox) {
    cropBox = mediaBox;
  }
135 136

  if (isPage) {
137
    // cropBox can not be bigger than mediaBox
138 139 140 141 142 143 144 145 146 147
    if (cropBox.x2 - cropBox.x1 > mediaBox.x2 - mediaBox.x1)
    {
      cropBox.x1 = mediaBox.x1;
      cropBox.x2 = mediaBox.x2;
    }
    if (cropBox.y2 - cropBox.y1 > mediaBox.y2 - mediaBox.y1)
    {
      cropBox.y1 = mediaBox.y1;
      cropBox.y2 = mediaBox.y2;
    }
148
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

  // other boxes
  bleedBox = cropBox;
  readBox(dict, "BleedBox", &bleedBox);
  trimBox = cropBox;
  readBox(dict, "TrimBox", &trimBox);
  artBox = cropBox;
  readBox(dict, "ArtBox", &artBox);

  // rotate
  dict->lookup("Rotate", &obj1);
  if (obj1.isInt()) {
    rotate = obj1.getInt();
  }
  obj1.free();
  while (rotate < 0) {
    rotate += 360;
  }
  while (rotate >= 360) {
    rotate -= 360;
  }

  // misc attributes
  dict->lookup("LastModified", &lastModified);
  dict->lookup("BoxColorInfo", &boxColorInfo);
  dict->lookup("Group", &group);
  dict->lookup("Metadata", &metadata);
  dict->lookup("PieceInfo", &pieceInfo);
  dict->lookup("SeparationInfo", &separationInfo);

  // resource dictionary
  dict->lookup("Resources", &obj1);
  if (obj1.isDict()) {
    resources.free();
    obj1.copy(&resources);
  }
  obj1.free();
}

PageAttrs::~PageAttrs() {
  lastModified.free();
  boxColorInfo.free();
  group.free();
  metadata.free();
  pieceInfo.free();
  separationInfo.free();
  resources.free();
}

198 199 200 201 202 203 204
void PageAttrs::clipBoxes() {
  cropBox.clipTo(&mediaBox);
  bleedBox.clipTo(&mediaBox);
  trimBox.clipTo(&mediaBox);
  artBox.clipTo(&mediaBox);
}

205
GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
206
  PDFRectangle tmp;
207
  double t;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
  Object obj1, obj2;
  GBool ok;

  dict->lookup(key, &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
    ok = gTrue;
    obj1.arrayGet(0, &obj2);
    if (obj2.isNum()) {
      tmp.x1 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(1, &obj2);
    if (obj2.isNum()) {
      tmp.y1 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(2, &obj2);
    if (obj2.isNum()) {
      tmp.x2 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(3, &obj2);
    if (obj2.isNum()) {
      tmp.y2 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
242 243
    if (tmp.x1 == 0 && tmp.x2 == 0 && tmp.y1 == 0 && tmp.y2 == 0)
      ok = gFalse;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
244
    if (ok) {
245 246 247 248 249 250
      if (tmp.x1 > tmp.x2) {
	t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
      }
      if (tmp.y1 > tmp.y2) {
	t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
      }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
251 252 253 254 255 256 257 258 259 260 261 262 263
      *box = tmp;
    }
  } else {
    ok = gFalse;
  }
  obj1.free();
  return ok;
}

//------------------------------------------------------------------------
// Page
//------------------------------------------------------------------------

264
Page::Page(PDFDoc *docA, int numA, Dict *pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form) {
265 266
  Object tmp;
	
Thomas Freitag's avatar
Thomas Freitag committed
267 268 269
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
Kristian Høgsberg's avatar
Kristian Høgsberg committed
270
  ok = gTrue;
271 272
  doc = docA;
  xref = doc->getXRef();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
273
  num = numA;
274
  duration = -1;
275
  annots = NULL;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
276

277 278 279
  pageObj.initDict(pageDict);
  pageRef = pageRefA;

Kristian Høgsberg's avatar
Kristian Høgsberg committed
280 281
  // get attributes
  attrs = attrsA;
282
  attrs->clipBoxes();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
283

284 285
  // transtion
  pageDict->lookupNF("Trans", &trans);
286
  if (!(trans.isRef() || trans.isDict() || trans.isNull())) {
287
    error(errSyntaxError, -1, "Page transition object (page {0:d}) is wrong type ({1:s})",
288 289 290 291
	  num, trans.getTypeName());
    trans.free();
  }

292 293 294
  // duration
  pageDict->lookupNF("Dur", &tmp);
  if (!(tmp.isNum() || tmp.isNull())) {
295
    error(errSyntaxError, -1, "Page duration object (page {0:d}) is wrong type ({1:s})",
296 297 298 299 300 301
	  num, tmp.getTypeName());
  } else if (tmp.isNum()) {
    duration = tmp.getNum();
  }
  tmp.free();

Kristian Høgsberg's avatar
Kristian Høgsberg committed
302
  // annotations
303 304
  pageDict->lookupNF("Annots", &annotsObj);
  if (!(annotsObj.isRef() || annotsObj.isArray() || annotsObj.isNull())) {
305
    error(errSyntaxError, -1, "Page annotations object (page {0:d}) is wrong type ({1:s})",
306 307
	  num, annotsObj.getTypeName());
    annotsObj.free();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
308 309 310 311 312 313 314
    goto err2;
  }

  // contents
  pageDict->lookupNF("Contents", &contents);
  if (!(contents.isRef() || contents.isArray() ||
	contents.isNull())) {
315
    error(errSyntaxError, -1, "Page contents object (page {0:d}) is wrong type ({1:s})",
Kristian Høgsberg's avatar
Kristian Høgsberg committed
316 317 318 319 320 321 322 323
	  num, contents.getTypeName());
    contents.free();
    goto err1;
  }

  // thumb
  pageDict->lookupNF("Thumb", &thumb);
  if (!(thumb.isStream() || thumb.isNull() || thumb.isRef())) {
324
      error(errSyntaxError, -1, "Page thumb object (page {0:d}) is wrong type ({1:s})",
Kristian Høgsberg's avatar
Kristian Høgsberg committed
325 326 327
            num, thumb.getTypeName());
      thumb.initNull(); 
  }
Albert Astals Cid's avatar
Albert Astals Cid committed
328 329 330 331

  // actions
  pageDict->lookupNF("AA", &actions);
  if (!(actions.isDict() || actions.isNull())) {
332
      error(errSyntaxError, -1, "Page additional action object (page {0:d}) is wrong type ({1:s})",
Albert Astals Cid's avatar
Albert Astals Cid committed
333 334 335
            num, actions.getTypeName());
      actions.initNull();
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
336 337 338
  
  return;

339
  trans.initNull();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
340
 err2:
341
  annotsObj.initNull();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
342 343 344 345 346 347 348
 err1:
  contents.initNull();
  ok = gFalse;
}

Page::~Page() {
  delete attrs;
349
  delete annots;
350
  pageObj.free();
351
  annotsObj.free();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
352
  contents.free();
Ilya Gorenbein's avatar
Ilya Gorenbein committed
353 354 355
  trans.free();
  thumb.free();
  actions.free();
Thomas Freitag's avatar
Thomas Freitag committed
356 357 358 359 360
#if MULTITHREADED
  gDestroyMutex(&mutex);
#endif
}

361 362 363 364 365
Dict *Page::getResourceDict() { 
  return attrs->getResourceDict();
}

Dict *Page::getResourceDictCopy(XRef *xrefA) { 
366
  pageLocker();
367 368
  Dict *dict = attrs->getResourceDict();
  return dict ? dict->copy(xrefA) : NULL;
Thomas Freitag's avatar
Thomas Freitag committed
369 370
}

Thomas Freitag's avatar
Thomas Freitag committed
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
void Page::replaceXRef(XRef *xrefA) {
  Object obj1;
  Dict *pageDict = pageObj.getDict()->copy(xrefA);
  xref = xrefA;
  trans.free();
  pageDict->lookupNF("Trans", &trans);
  annotsObj.free();
  pageDict->lookupNF("Annots", &annotsObj);
  contents.free();
  pageDict->lookupNF("Contents", &contents);
  if (contents.isArray()) {
    contents.free();
    pageDict->lookupNF("Contents", &obj1)->getArray()->copy(xrefA, &contents);
    obj1.free();
  }
  thumb.free();
  pageDict->lookupNF("Thumb", &thumb);
  actions.free();
  pageDict->lookupNF("AA", &actions);
  pageDict->lookup("Resources", &obj1);
  if (obj1.isDict()) {
    attrs->replaceResource(obj1);
  }
  obj1.free();
  delete pageDict;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
396 397
}

Thomas Freitag's avatar
Thomas Freitag committed
398
Annots *Page::getAnnots(XRef *xrefA) {
399 400
  if (!annots) {
    Object obj;
Thomas Freitag's avatar
Thomas Freitag committed
401
    annots = new Annots(doc, num, getAnnots(&obj, (xrefA == NULL) ? xref : xrefA));
402 403
    obj.free();
  }
404 405 406 407

  return annots;
}

408
void Page::addAnnot(Annot *annot) {
409 410 411 412
  Object obj1;
  Object tmp;
  Ref annotRef = annot->getRef ();

413 414 415
  // Make sure we have annots before adding the new one
  // even if it's an empty list so that we can safely
  // call annots->appendAnnot(annot)
416
  pageLocker();
417
  getAnnots();
418 419

  if (annotsObj.isNull()) {
420 421 422 423 424 425 426 427 428
    Ref annotsRef;
    // page doesn't have annots array,
    // we have to create it

    obj1.initArray(xref);
    obj1.arrayAdd(tmp.initRef (annotRef.num, annotRef.gen));
    tmp.free();

    annotsRef = xref->addIndirectObject (&obj1);
429 430
    annotsObj.initRef(annotsRef.num, annotsRef.gen);
    pageObj.dictSet ("Annots", &annotsObj);
431 432 433 434 435
    xref->setModifiedObject (&pageObj, pageRef);
  } else {
    getAnnots(&obj1);
    if (obj1.isArray()) {
      obj1.arrayAdd (tmp.initRef (annotRef.num, annotRef.gen));
436 437
      if (annotsObj.isRef())
        xref->setModifiedObject (&obj1, annotsObj.getRef());
438 439
      else
        xref->setModifiedObject (&pageObj, pageRef);
440 441 442
    }
    obj1.free();
  }
443

444
  annots->appendAnnot(annot);
445
  annot->setPage(num, gTrue);
446 447
}

Fabio D'Urso's avatar
Fabio D'Urso committed
448 449 450 451
void Page::removeAnnot(Annot *annot) {
  Ref annotRef = annot->getRef();
  Object annArray;

452
  pageLocker();
Fabio D'Urso's avatar
Fabio D'Urso committed
453 454 455 456 457 458
  getAnnots(&annArray);
  if (annArray.isArray()) {
    int idx = -1;
    // Get annotation position
    for (int i = 0; idx == -1 && i < annArray.arrayGetLength(); ++i) {
      Object tmp;
459 460 461 462 463
      if (annArray.arrayGetNF(i, &tmp)->isRef()) {
        Ref currAnnot = tmp.getRef();
        if (currAnnot.num == annotRef.num && currAnnot.gen == annotRef.gen) {
          idx = i;
        }
Fabio D'Urso's avatar
Fabio D'Urso committed
464
      }
465
      tmp.free();
Fabio D'Urso's avatar
Fabio D'Urso committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    }

    if (idx == -1) {
      error(errInternal, -1, "Annotation doesn't belong to this page");
      annArray.free();
      return;
    }
    annots->removeAnnot(annot); // Gracefully fails on popup windows
    annArray.arrayRemove(idx);
    xref->removeIndirectObject(annotRef);

    if (annotsObj.isRef()) {
      xref->setModifiedObject (&annArray, annotsObj.getRef());
    } else {
      xref->setModifiedObject (&pageObj, pageRef);
    }
  }
  annArray.free();
484 485
  annot->removeReferencedObjects(); // Note: Might recurse in removeAnnot again
  annot->setPage(0, gFalse);
Fabio D'Urso's avatar
Fabio D'Urso committed
486 487
}

488 489
Links *Page::getLinks() {
  return new Links(getAnnots());
490
}
491

492 493
FormPageWidgets *Page::getFormWidgets() {
  return new FormPageWidgets(getAnnots(), num, doc->getCatalog()->getForm());
494 495
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
496
void Page::display(OutputDev *out, double hDPI, double vDPI,
497
		   int rotate, GBool useMediaBox, GBool crop,
498
		   GBool printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
499 500 501
		   GBool (*abortCheckCbk)(void *data),
		   void *abortCheckCbkData,
                   GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
Thomas Freitag's avatar
Thomas Freitag committed
502 503
                   void *annotDisplayDecideCbkData,
                   GBool copyXRef) {
504
  displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
505
	       abortCheckCbk, abortCheckCbkData,
Thomas Freitag's avatar
Thomas Freitag committed
506
               annotDisplayDecideCbk, annotDisplayDecideCbkData, copyXRef);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
507 508
}

509
Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI,
510
		     int rotate, GBool useMediaBox, GBool crop,
511
		     int sliceX, int sliceY, int sliceW, int sliceH,
512
		     GBool printing,
513
		     GBool (*abortCheckCbk)(void *data),
Thomas Freitag's avatar
Thomas Freitag committed
514
		     void *abortCheckCbkData, XRef *xrefA) {
515
  PDFRectangle *mediaBox, *cropBox;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
516 517 518 519 520 521 522 523 524 525
  PDFRectangle box;
  Gfx *gfx;

  rotate += getRotate();
  if (rotate >= 360) {
    rotate -= 360;
  } else if (rotate < 0) {
    rotate += 360;
  }

526 527
  makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
	  sliceX, sliceY, sliceW, sliceH, &box, &crop);
528
  cropBox = getCropBox();
Krzysztof Kowalczyk's avatar
Krzysztof Kowalczyk committed
529
  mediaBox = getMediaBox();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
530 531 532

  if (globalParams->getPrintCommands()) {
    printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
533
	    mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
534 535 536 537 538
      printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
	     cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
    printf("***** Rotate = %d\n", attrs->getRotate());
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
539 540 541
  if (!crop) {
    crop = (box == *cropBox) && out->needClipToCropBox();
  }
542
  gfx = new Gfx(doc, out, num, attrs->getResourceDict(),
543
		hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
Thomas Freitag's avatar
Thomas Freitag committed
544
		rotate, abortCheckCbk, abortCheckCbkData, xrefA);
545 546 547 548 549

  return gfx;
}

void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
550
			int rotate, GBool useMediaBox, GBool crop,
551
			int sliceX, int sliceY, int sliceW, int sliceH,
552
			GBool printing,
553 554 555
			GBool (*abortCheckCbk)(void *data),
			void *abortCheckCbkData,
                        GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
Thomas Freitag's avatar
Thomas Freitag committed
556 557
                        void *annotDisplayDecideCbkData,
                        GBool copyXRef) {
558 559 560 561
  Gfx *gfx;
  Object obj;
  Annots *annotList;
  int i;
562 563 564
  
  if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
			   sliceX, sliceY, sliceW, sliceH,
565
			   printing,
566 567
			   abortCheckCbk, abortCheckCbkData,
			   annotDisplayDecideCbk, annotDisplayDecideCbkData)) {
568 569
    return;
  }
570
  pageLocker();
Thomas Freitag's avatar
Thomas Freitag committed
571 572 573 574
  XRef *localXRef = (copyXRef) ? xref->copy() : xref;
  if (copyXRef) {
    replaceXRef(localXRef);
  }
575

576
  gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop,
577
		  sliceX, sliceY, sliceW, sliceH,
578
		  printing,
Thomas Freitag's avatar
Thomas Freitag committed
579
		  abortCheckCbk, abortCheckCbkData, localXRef);
580

Thomas Freitag's avatar
Thomas Freitag committed
581
  contents.fetch(localXRef, &obj);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
582 583 584 585
  if (!obj.isNull()) {
    gfx->saveState();
    gfx->display(&obj);
    gfx->restoreState();
586 587 588 589
  } else {
    // empty pages need to call dump to do any setup required by the
    // OutputDev
    out->dump();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
590 591 592
  }
  obj.free();

593
  // draw annotations
594
  annotList = getAnnots();
595

Kristian Høgsberg's avatar
Kristian Høgsberg committed
596 597 598 599 600 601 602 603
  if (annotList->getNumAnnots() > 0) {
    if (globalParams->getPrintCommands()) {
      printf("***** Annotations\n");
    }
    for (i = 0; i < annotList->getNumAnnots(); ++i) {
        Annot *annot = annotList->getAnnot(i);
        if ((annotDisplayDecideCbk &&
             (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || 
604
            !annotDisplayDecideCbk) {
605
             annotList->getAnnot(i)->draw(gfx, printing);
606
	}
607
    }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
608 609 610 611
    out->dump();
  }

  delete gfx;
Thomas Freitag's avatar
Thomas Freitag committed
612 613 614 615
  if (copyXRef) {
    replaceXRef(doc->getXRef());
    delete localXRef;
  }
616 617 618 619 620 621 622 623 624 625 626 627
}

void Page::display(Gfx *gfx) {
  Object obj;

  contents.fetch(xref, &obj);
  if (!obj.isNull()) {
    gfx->saveState();
    gfx->display(&obj);
    gfx->restoreState();
  }
  obj.free();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
628
}
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

GBool Page::loadThumb(unsigned char **data_out,
		      int *width_out, int *height_out,
		      int *rowstride_out)
{
  unsigned int pixbufdatasize;
  int width, height, bits;
  Object obj1, fetched_thumb;
  Dict *dict;
  GfxColorSpace *colorSpace;
  GBool success = gFalse;
  Stream *str;
  GfxImageColorMap *colorMap;

  /* Get stream dict */
644
  pageLocker();
645
  thumb.fetch(xref, &fetched_thumb);
646
  if (!fetched_thumb.isStream()) {
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
    fetched_thumb.free();
    return gFalse;
  }

  dict = fetched_thumb.streamGetDict();
  str = fetched_thumb.getStream(); 
		
  if (!dict->lookupInt("Width", "W", &width))
    goto fail1;
  if (!dict->lookupInt("Height", "H", &height))
    goto fail1;
  if (!dict->lookupInt("BitsPerComponent", "BPC", &bits))
    goto fail1;
		
  /* Check for invalid dimensions and integer overflow. */
  if (width <= 0 || height <= 0)
    goto fail1;
  if (width > INT_MAX / 3 / height)
    goto fail1;
  pixbufdatasize = width * height * 3;

  /* Get color space */
  dict->lookup ("ColorSpace", &obj1);
  if (obj1.isNull ()) {
    obj1.free ();
    dict->lookup ("CS", &obj1);
  }
674
  colorSpace = GfxColorSpace::parse(&obj1, NULL);
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
  obj1.free();
  if (!colorSpace) {
    fprintf (stderr, "Error: Cannot parse color space\n");
    goto fail1;
  }

  dict->lookup("Decode", &obj1);
  if (obj1.isNull()) {
    obj1.free();
    dict->lookup("D", &obj1);
  }
  colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
  obj1.free();
  if (!colorMap->isOk()) {
    fprintf (stderr, "Error: invalid colormap\n");
690
    delete colorMap;
691 692 693
    goto fail1;
  }

694
  if (data_out) {
695 696 697
    unsigned char *pixbufdata = (unsigned char *) gmalloc(pixbufdatasize);
    unsigned char *p = pixbufdata;
    ImageStream *imgstr = new ImageStream(str, width,
698 699
			   colorMap->getNumPixelComps(),
			   colorMap->getBits());
700
    imgstr->reset();
701 702
    for (int row = 0; row < height; ++row) {
      for (int col = 0; col < width; ++col) {
703 704 705 706 707 708 709 710 711 712
        Guchar pix[gfxColorMaxComps];
        GfxRGB rgb;

        imgstr->getPixel(pix);
        colorMap->getRGB(pix, &rgb);

        *p++ = colToByte(rgb.r);
        *p++ = colToByte(rgb.g);
        *p++ = colToByte(rgb.b);
      }
713
    }
714
    *data_out = pixbufdata;
715
    imgstr->close();
716
    delete imgstr;
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
  }

  success = gTrue;

  if (width_out)
    *width_out = width;
  if (height_out)
    *height_out = height;
  if (rowstride_out)
    *rowstride_out = width * 3;

  delete colorMap;
 fail1:
  fetched_thumb.free();

  return success;
}
734

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
void Page::makeBox(double hDPI, double vDPI, int rotate,
		   GBool useMediaBox, GBool upsideDown,
		   double sliceX, double sliceY, double sliceW, double sliceH,
		   PDFRectangle *box, GBool *crop) {
  PDFRectangle *mediaBox, *cropBox, *baseBox;
  double kx, ky;

  mediaBox = getMediaBox();
  cropBox = getCropBox();
  if (sliceW >= 0 && sliceH >= 0) {
    baseBox = useMediaBox ? mediaBox : cropBox;
    kx = 72.0 / hDPI;
    ky = 72.0 / vDPI;
    if (rotate == 90) {
      if (upsideDown) {
	box->x1 = baseBox->x1 + ky * sliceY;
	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
      } else {
	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
	box->x2 = baseBox->x2 - ky * sliceY;
      }
      box->y1 = baseBox->y1 + kx * sliceX;
      box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
    } else if (rotate == 180) {
      box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
      box->x2 = baseBox->x2 - kx * sliceX;
      if (upsideDown) {
	box->y1 = baseBox->y1 + ky * sliceY;
	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
      } else {
	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
	box->y2 = baseBox->y2 - ky * sliceY;
      }
    } else if (rotate == 270) {
      if (upsideDown) {
	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
	box->x2 = baseBox->x2 - ky * sliceY;
      } else {
	box->x1 = baseBox->x1 + ky * sliceY;
	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
      }
      box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
      box->y2 = baseBox->y2 - kx * sliceX;
    } else {
      box->x1 = baseBox->x1 + kx * sliceX;
      box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
      if (upsideDown) {
	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
	box->y2 = baseBox->y2 - ky * sliceY;
      } else {
	box->y1 = baseBox->y1 + ky * sliceY;
	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
      }
    }
  } else if (useMediaBox) {
    *box = *mediaBox;
  } else {
    *box = *cropBox;
    *crop = gFalse;
  }
}

797
void Page::processLinks(OutputDev *out) {
798 799 800
  Links *links;
  int i;

801
  links = getLinks();
802
  for (i = 0; i < links->getNumLinks(); ++i) {
803
    out->processLink(links->getLink(i));
804 805 806 807
  }
  delete links;
}

808
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
809
			 int rotate, GBool useMediaBox, GBool upsideDown) {
810 811 812 813 814 815 816 817
  GfxState *state;
  int i;
  rotate += getRotate();
  if (rotate >= 360) {
    rotate -= 360;
  } else if (rotate < 0) {
    rotate += 360;
  }
818 819 820
  state = new GfxState(hDPI, vDPI,
		       useMediaBox ? getMediaBox() : getCropBox(),
		       rotate, upsideDown);
821 822 823 824 825
  for (i = 0; i < 6; ++i) {
    ctm[i] = state->getCTM()[i];
  }
 delete state;
}