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

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
// Copyright (C) 2005, 2006, 2008 Brad Hards <bradh@frogmouth.net>
17
// Copyright (C) 2005, 2007-2009 Albert Astals Cid <aacid@kde.org>
18 19 20
// Copyright (C) 2008 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2008 Pino Toscano <pino@kde.org>
// Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
21
// Copyright (C) 2009 Eric Toombs <ewtoombs@uwaterloo.ca>
22
// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
23 24 25 26 27 28
//
// 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
29 30 31 32 33 34 35 36
#include <config.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <locale.h>
#include <stdio.h>
37
#include <errno.h>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
38 39 40
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
41
#include <time.h>
Kovid Goyal's avatar
Kovid Goyal committed
42
#ifdef _WIN32
43 44
#  include <windows.h>
#endif
45
#include "goo/gstrtod.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
46 47 48 49 50 51 52 53 54 55 56 57 58
#include "goo/GooString.h"
#include "poppler-config.h"
#include "GlobalParams.h"
#include "Page.h"
#include "Catalog.h"
#include "Stream.h"
#include "XRef.h"
#include "Link.h"
#include "OutputDev.h"
#include "Error.h"
#include "ErrorCodes.h"
#include "Lexer.h"
#include "Parser.h"
59
#include "SecurityHandler.h"
60
#include "Decrypt.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
#ifndef DISABLE_OUTLINE
#include "Outline.h"
#endif
#include "PDFDoc.h"

//------------------------------------------------------------------------

#define headerSearchSize 1024	// read this many bytes at beginning of
				//   file to look for '%PDF'

//------------------------------------------------------------------------
// PDFDoc
//------------------------------------------------------------------------

PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword,
76
	       GooString *userPassword, void *guiDataA) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
77 78 79 80 81
  Object obj;

  ok = gFalse;
  errCode = errNone;

82 83
  guiData = guiDataA;

Kristian Høgsberg's avatar
Kristian Høgsberg committed
84 85 86 87 88 89 90 91 92 93 94 95
  file = NULL;
  str = NULL;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif

  fileName = fileNameA;

  // try to open file
#ifdef VMS
96
  file = fopen(fileName->getCString(), "rb", "ctx=stm");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
97
#else
98
  file = fopen(fileName->getCString(), "rb");
99
#endif
100
  if (file == NULL) {
101
    // fopen() has failed.
102 103 104 105 106 107 108
    // Keep a copy of the errno returned by fopen so that it can be 
    // referred to later.
    fopenErrno = errno;
    error(-1, "Couldn't open file '%s': %s.", fileName->getCString(),
                                              strerror(errno));
    errCode = errOpenFile;
    return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
109 110 111 112 113 114 115 116 117
  }

  // create stream
  obj.initNull();
  str = new FileStream(file, 0, gFalse, 0, &obj);

  ok = setup(ownerPassword, userPassword);
}

Kovid Goyal's avatar
Kovid Goyal committed
118
#ifdef _WIN32
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 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
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword,
	       GooString *userPassword, void *guiDataA) {
  OSVERSIONINFO version;
  wchar_t fileName2[_MAX_PATH + 1];
  Object obj;
  int i;

  ok = gFalse;
  errCode = errNone;

  guiData = guiDataA;

  file = NULL;
  str = NULL;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif

  //~ file name should be stored in Unicode (?)
  fileName = new GooString();
  for (i = 0; i < fileNameLen; ++i) {
    fileName->append((char)fileNameA[i]);
  }

  // zero-terminate the file name string
  for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
    fileName2[i] = fileNameA[i];
  }
  fileName2[i] = 0;

  // try to open file
  // NB: _wfopen is only available in NT
  version.dwOSVersionInfoSize = sizeof(version);
  GetVersionEx(&version);
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    file = _wfopen(fileName2, L"rb");
  } else {
    file = fopen(fileName->getCString(), "rb");
  }
  if (!file) {
    error(-1, "Couldn't open file '%s'", fileName->getCString());
    errCode = errOpenFile;
    return;
  }

  // create stream
  obj.initNull();
  str = new FileStream(file, 0, gFalse, 0, &obj);

  ok = setup(ownerPassword, userPassword);
}
#endif

Kristian Høgsberg's avatar
Kristian Høgsberg committed
174
PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword,
175
	       GooString *userPassword, void *guiDataA) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
176 177
  ok = gFalse;
  errCode = errNone;
178
  guiData = guiDataA;
179 180 181 182 183
  if (strA->getFileName()) {
    fileName = strA->getFileName()->copy();
  } else {
    fileName = NULL;
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
184 185 186 187 188 189 190 191 192 193 194
  file = NULL;
  str = strA;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif
  ok = setup(ownerPassword, userPassword);
}

GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) {
195
  str->setPos(0, -1);
196
  if (str->getPos() < 0)
197 198 199 200 201
  {
    error(-1, "Document base stream is not seekable");
    return gFalse;
  }

Kristian Høgsberg's avatar
Kristian Høgsberg committed
202 203
  str->reset();

204
  // check footer
205 206
  // Adobe does not seem to enforce %%EOF, so we do the same
//  if (!checkFooter()) return gFalse;
207
  
Kristian Høgsberg's avatar
Kristian Høgsberg committed
208 209 210 211
  // check header
  checkHeader();

  // read xref table
212
  xref = new XRef(str);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
213 214 215 216 217 218
  if (!xref->isOk()) {
    error(-1, "Couldn't read xref table");
    errCode = xref->getErrorCode();
    return gFalse;
  }

219 220 221 222 223 224
  // check for encryption
  if (!checkEncryption(ownerPassword, userPassword)) {
    errCode = errEncrypted;
    return gFalse;
  }

Kristian Høgsberg's avatar
Kristian Høgsberg committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
  // read catalog
  catalog = new Catalog(xref);
  if (!catalog->isOk()) {
    error(-1, "Couldn't read page catalog");
    errCode = errBadCatalog;
    return gFalse;
  }

#ifndef DISABLE_OUTLINE
  // read outline
  outline = new Outline(catalog->getOutline(), xref);
#endif

  // done
  return gTrue;
}

PDFDoc::~PDFDoc() {
#ifndef DISABLE_OUTLINE
  if (outline) {
    delete outline;
  }
#endif
  if (catalog) {
    delete catalog;
  }
  if (xref) {
    delete xref;
  }
  if (str) {
    delete str;
  }
  if (file) {
    fclose(file);
  }
  if (fileName) {
    delete fileName;
  }
}

265 266 267

// Check for a %%EOF at the end of this stream
GBool PDFDoc::checkFooter() {
Albert Astals Cid's avatar
Albert Astals Cid committed
268
  // we look in the last 1024 chars because Adobe does the same
269
  char *eof = new char[1025];
270
  int pos = str->getPos();
271 272 273 274 275 276 277 278 279 280
  str->setPos(1024, -1);
  int i, ch;
  for (i = 0; i < 1024; i++)
  {
    ch = str->getChar();
    if (ch == EOF)
      break;
    eof[i] = ch;
  }
  eof[i] = '\0';
Albert Astals Cid's avatar
Albert Astals Cid committed
281 282 283 284 285 286 287 288 289

  bool found = false;
  for (i = i - 5; i >= 0; i--) {
    if (strncmp (&eof[i], "%%EOF", 5) == 0) {
      found = true;
      break;
    }
  }
  if (!found)
290 291 292
  {
    error(-1, "Document has not the mandatory ending %%EOF");
    errCode = errDamaged;
293
    delete[] eof;
294 295
    return gFalse;
  }
296
  delete[] eof;
297 298 299 300
  str->setPos(pos);
  return gTrue;
}
  
Kristian Høgsberg's avatar
Kristian Høgsberg committed
301 302 303 304 305 306 307
// Check for a PDF header on this stream.  Skip past some garbage
// if necessary.
void PDFDoc::checkHeader() {
  char hdrBuf[headerSearchSize+1];
  char *p;
  int i;

308 309
  pdfMajorVersion = 0;
  pdfMinorVersion = 0;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323
  for (i = 0; i < headerSearchSize; ++i) {
    hdrBuf[i] = str->getChar();
  }
  hdrBuf[headerSearchSize] = '\0';
  for (i = 0; i < headerSearchSize - 5; ++i) {
    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
      break;
    }
  }
  if (i >= headerSearchSize - 5) {
    error(-1, "May not be a PDF file (continuing anyway)");
    return;
  }
  str->moveStart(i);
324 325 326 327
  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
    error(-1, "May not be a PDF file (continuing anyway)");
    return;
  }
328
  sscanf(p, "%d.%d", &pdfMajorVersion, &pdfMinorVersion);
329
  // We don't do the version check. Don't add it back in.
Kristian Høgsberg's avatar
Kristian Høgsberg committed
330 331
}

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) {
  Object encrypt;
  GBool encrypted;
  SecurityHandler *secHdlr;
  GBool ret;

  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
  if ((encrypted = encrypt.isDict())) {
    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
	// authorization succeeded
       	xref->setEncryption(secHdlr->getPermissionFlags(),
			    secHdlr->getOwnerPasswordOk(),
			    secHdlr->getFileKey(),
			    secHdlr->getFileKeyLength(),
			    secHdlr->getEncVersion(),
348 349
			    secHdlr->getEncRevision(),
			    secHdlr->getEncAlgorithm());
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	ret = gTrue;
      } else {
	// authorization failed
	ret = gFalse;
      }
      delete secHdlr;
    } else {
      // couldn't find the matching security handler
      ret = gFalse;
    }
  } else {
    // document is not encrypted
    ret = gTrue;
  }
  encrypt.free();
  return ret;
}

368 369 370
void PDFDoc::displayPage(OutputDev *out, int page,
			 double hDPI, double vDPI, int rotate,
			 GBool useMediaBox, GBool crop, GBool printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
371 372 373 374 375 376 377
			 GBool (*abortCheckCbk)(void *data),
			 void *abortCheckCbkData,
                         GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
                         void *annotDisplayDecideCbkData) {
  if (globalParams->getPrintCommands()) {
    printf("***** page %d *****\n", page);
  }
378 379 380 381
  catalog->getPage(page)->display(out, hDPI, vDPI,
				  rotate, useMediaBox, crop, printing, catalog,
				  abortCheckCbk, abortCheckCbkData,
				  annotDisplayDecideCbk, annotDisplayDecideCbkData);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
382 383 384
}

void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
385 386
			  double hDPI, double vDPI, int rotate,
			  GBool useMediaBox, GBool crop, GBool printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
387 388 389 390 391 392 393
			  GBool (*abortCheckCbk)(void *data),
			  void *abortCheckCbkData,
                          GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
                          void *annotDisplayDecideCbkData) {
  int page;

  for (page = firstPage; page <= lastPage; ++page) {
394
    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
395 396 397 398 399 400
		abortCheckCbk, abortCheckCbkData,
                annotDisplayDecideCbk, annotDisplayDecideCbkData);
  }
}

void PDFDoc::displayPageSlice(OutputDev *out, int page,
401 402
			      double hDPI, double vDPI, int rotate,
			      GBool useMediaBox, GBool crop, GBool printing,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
403 404 405 406 407
			      int sliceX, int sliceY, int sliceW, int sliceH,
			      GBool (*abortCheckCbk)(void *data),
			      void *abortCheckCbkData,
                              GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
                              void *annotDisplayDecideCbkData) {
408 409 410 411 412 413
  catalog->getPage(page)->displaySlice(out, hDPI, vDPI,
				       rotate, useMediaBox, crop,
				       sliceX, sliceY, sliceW, sliceH,
				       printing, catalog,
				       abortCheckCbk, abortCheckCbkData,
				       annotDisplayDecideCbk, annotDisplayDecideCbkData);
414 415
}

416 417 418 419 420 421
Links *PDFDoc::getLinks(int page) {
  return catalog->getPage(page)->getLinks(catalog);
}
  
void PDFDoc::processLinks(OutputDev *out, int page) {
  catalog->getPage(page)->processLinks(out, catalog);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
422 423 424 425 426 427 428 429 430 431 432
}

GBool PDFDoc::isLinearized() {
  Parser *parser;
  Object obj1, obj2, obj3, obj4, obj5;
  GBool lin;

  lin = gFalse;
  obj1.initNull();
  parser = new Parser(xref,
	     new Lexer(xref,
433 434
	       str->makeSubStream(str->getStart(), gFalse, 0, &obj1)),
	     gTrue);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
  parser->getObj(&obj1);
  parser->getObj(&obj2);
  parser->getObj(&obj3);
  parser->getObj(&obj4);
  if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
      obj4.isDict()) {
    obj4.dictLookup("Linearized", &obj5);
    if (obj5.isNum() && obj5.getNum() > 0) {
      lin = gTrue;
    }
    obj5.free();
  }
  obj4.free();
  obj3.free();
  obj2.free();
  obj1.free();
  delete parser;
  return lin;
}

455
int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
456
  FILE *f;
457
  OutStream *outStr;
458
  int res;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
459 460 461

  if (!(f = fopen(name->getCString(), "wb"))) {
    error(-1, "Couldn't open file '%s'", name->getCString());
462
    return errOpenFile;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
463
  }
464
  outStr = new FileOutStream(f,0);
465 466 467 468 469
  res = saveAs(outStr, mode);
  delete outStr;
  fclose(f);
  return res;
}
470

471
int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) {
472 473 474 475 476

  // we don't support files with Encrypt at the moment
  Object obj;
  xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj);
  if (!obj.isNull())
Albert Astals Cid's avatar
Albert Astals Cid committed
477 478
  {
    obj.free();
479
    return errEncrypted;
Albert Astals Cid's avatar
Albert Astals Cid committed
480 481
  }
  obj.free();
482

483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
  if (mode == writeForceRewrite) {
    saveCompleteRewrite(outStr);
  } else if (mode == writeForceIncremental) {
    saveIncrementalUpdate(outStr); 
  } else { // let poppler decide
    // find if we have updated objects
    GBool updated = gFalse;
    for(int i=0; i<xref->getNumObjects(); i++) {
      if (xref->getEntry(i)->updated) {
        updated = gTrue;
        break;
      }
    }
    if(updated) { 
      saveIncrementalUpdate(outStr);
    } else {
      // simply copy the original file
500
      saveWithoutChangesAs (outStr);
501 502 503
    }
  }

504
  return errNone;
505 506
}

507
int PDFDoc::saveWithoutChangesAs(GooString *name) {
508 509
  FILE *f;
  OutStream *outStr;
510
  int res;
511 512 513

  if (!(f = fopen(name->getCString(), "wb"))) {
    error(-1, "Couldn't open file '%s'", name->getCString());
514
    return errOpenFile;
515 516 517 518 519 520 521 522 523 524 525
  }
  
  outStr = new FileOutStream(f,0);
  res = saveWithoutChangesAs(outStr);
  delete outStr;

  fclose(f);

  return res;
}

526
int PDFDoc::saveWithoutChangesAs(OutStream *outStr) {
527 528 529 530 531 532 533 534
  int c;
  
  str->reset();
  while ((c = str->getChar()) != EOF) {
    outStr->put(c);
  }
  str->close();

535
  return errNone;
536 537
}

538 539 540 541 542
void PDFDoc::saveIncrementalUpdate (OutStream* outStr)
{
  XRef *uxref;
  int c;
  //copy the original file
Kristian Høgsberg's avatar
Kristian Høgsberg committed
543 544
  str->reset();
  while ((c = str->getChar()) != EOF) {
545
    outStr->put(c);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
546 547
  }
  str->close();
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573

  uxref = new XRef();
  uxref->add(0, 65535, 0, gFalse);
  int objectsCount = 0; //count the number of objects in the XRef(s)
  for(int i=0; i<xref->getNumObjects(); i++) {
    if ((xref->getEntry(i)->type == xrefEntryFree) && 
        (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects
      continue;
    objectsCount++;
    if (xref->getEntry(i)->updated) { //we have an updated object
      Object obj1;
      Ref ref;
      ref.num = i;
      ref.gen = xref->getEntry(i)->gen;
      xref->fetch(ref.num, ref.gen, &obj1);
      Guint offset = writeObject(&obj1, &ref, outStr);
      uxref->add(ref.num, ref.gen, offset, gTrue);
      obj1.free();
    }
  }
  if (uxref->getSize() == 0) { //we have nothing to update
    delete uxref;
    return;
  }

  Guint uxrefOffset = outStr->getPos();
574
  uxref->writeToFile(outStr, gFalse /* do not write unnecessary entries */);
575 576 577 578 579 580 581 582

  writeTrailer(uxrefOffset, objectsCount, outStr, gTrue);

  delete uxref;
}

void PDFDoc::saveCompleteRewrite (OutStream* outStr)
{
583
  outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion);
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
  XRef *uxref = new XRef();
  uxref->add(0, 65535, 0, gFalse);
  for(int i=0; i<xref->getNumObjects(); i++) {
    Object obj1;
    Ref ref;
    XRefEntryType type = xref->getEntry(i)->type;
    if (type == xrefEntryFree) {
      ref.num = i;
      ref.gen = xref->getEntry(i)->gen;
      /* the XRef class adds a lot of irrelevant free entries, we only want the significant one
          and we don't want the one with num=0 because it has already been added (gen = 65535)*/
      if (ref.gen > 0 && ref.num > 0)
        uxref->add(ref.num, ref.gen, 0, gFalse);
    } else if (type == xrefEntryUncompressed){ 
      ref.num = i;
      ref.gen = xref->getEntry(i)->gen;
      xref->fetch(ref.num, ref.gen, &obj1);
      Guint offset = writeObject(&obj1, &ref, outStr);
      uxref->add(ref.num, ref.gen, offset, gTrue);
      obj1.free();
    } else if (type == xrefEntryCompressed) {
      ref.num = i;
      ref.gen = 0; //compressed entries have gen == 0
      xref->fetch(ref.num, ref.gen, &obj1);
      Guint offset = writeObject(&obj1, &ref, outStr);
      uxref->add(ref.num, ref.gen, offset, gTrue);
      obj1.free();
    }
  }
  Guint uxrefOffset = outStr->getPos();
614
  uxref->writeToFile(outStr, gTrue /* write all entries */);
615 616 617 618 619 620 621 622 623 624 625 626 627

  writeTrailer(uxrefOffset, uxref->getSize(), outStr, gFalse);


  delete uxref;

}

void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr)
{
  Object obj1;
  outStr->printf("<<");
  for (int i=0; i<dict->getLength(); i++) {
628 629 630 631
    GooString keyName(dict->getKey(i));
    GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */);
    outStr->printf("/%s ", keyNameToPrint->getCString());
    delete keyNameToPrint;
632 633 634
    writeObject(dict->getValNF(i, &obj1), NULL, outStr);
    obj1.free();
  }
635
  outStr->printf(">> ");
636 637 638 639 640 641 642 643 644 645 646 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 674 675 676 677 678 679 680 681 682 683 684 685 686
}

void PDFDoc::writeStream (Stream* str, OutStream* outStr)
{
  outStr->printf("stream\r\n");
  str->reset();
  for (int c=str->getChar(); c!= EOF; c=str->getChar()) {
    outStr->printf("%c", c);  
  }
  outStr->printf("\r\nendstream\r\n");
}

void PDFDoc::writeRawStream (Stream* str, OutStream* outStr)
{
  Object obj1;
  str->getDict()->lookup("Length", &obj1);
  if (!obj1.isInt()) {
    error (-1, "PDFDoc::writeRawStream, no Length in stream dict");
    return;
  }

  const int length = obj1.getInt();
  obj1.free();

  outStr->printf("stream\r\n");
  str->unfilteredReset();
  for (int i=0; i<length; i++) {
    int c = str->getUnfilteredChar();
    outStr->printf("%c", c);  
  }
  str->reset();
  outStr->printf("\r\nendstream\r\n");
}

void PDFDoc::writeString (GooString* s, OutStream* outStr)
{
  if (s->hasUnicodeMarker()) {
    //unicode string don't necessary end with \0
    const char* c = s->getCString();
    outStr->printf("(");
    for(int i=0; i<s->getLength(); i++) {
      char unescaped = *(c+i)&0x000000ff;
      //escape if needed
      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
        outStr->printf("%c", '\\');
      outStr->printf("%c", unescaped);
    }
    outStr->printf(") ");
  } else {
    const char* c = s->getCString();
    outStr->printf("(");
687
    for(int i=0; i<s->getLength(); i++) {
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
      char unescaped = (*c)&0x000000ff;
      //escape if needed
      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
        outStr->printf("%c", '\\');
      outStr->printf("%c", unescaped);
      c++;
    }
    outStr->printf(") ");
  }
}

Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr)
{
  Array *array;
  Object obj1;
  Guint offset = outStr->getPos();
  int tmp;

  if(ref) 
707
    outStr->printf("%i %i obj ", ref->num, ref->gen);
708 709 710 711 712 713 714 715 716

  switch (obj->getType()) {
    case objBool:
      outStr->printf("%s ", obj->getBool()?"true":"false");
      break;
    case objInt:
      outStr->printf("%i ", obj->getInt());
      break;
    case objReal:
717 718
    {
      GooString s;
719
      s.appendf("{0:.10g}", obj->getReal());
720
      outStr->printf("%s ", s.getCString());
721
      break;
722
    }
723 724 725 726
    case objString:
      writeString(obj->getString(), outStr);
      break;
    case objName:
727 728 729 730 731
    {
      GooString name(obj->getName());
      GooString *nameToPrint = name.sanitizedName(gFalse /* non ps mode */);
      outStr->printf("/%s ", nameToPrint->getCString());
      delete nameToPrint;
732
      break;
733
    }
734 735 736 737 738 739 740 741 742 743
    case objNull:
      outStr->printf( "null");
      break;
    case objArray:
      array = obj->getArray();
      outStr->printf("[");
      for (int i=0; i<array->getLength(); i++) {
        writeObject(array->getNF(i, &obj1), NULL,outStr);
        obj1.free();
      }
744
      outStr->printf("] ");
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
      break;
    case objDict:
      writeDictionnary (obj->getDict(),outStr);
      break;
    case objStream: 
      {
        //We can't modify stream with the current implementation (no write functions in Stream API)
        // => the only type of streams which that have been modified are internal streams (=strWeird)
        Stream *stream = obj->getStream();
        if (stream->getKind() == strWeird) {
          //we write the stream unencoded => TODO: write stream encoder
          stream->reset();
          //recalculate stream length
          tmp = 0;
          for (int c=stream->getChar(); c!=EOF; c=stream->getChar()) {
            tmp++;
          }
          obj1.initInt(tmp);
          stream->getDict()->set("Length", &obj1);

          //Remove Stream encoding
          stream->getDict()->remove("Filter");
          stream->getDict()->remove("DecodeParms");

          writeDictionnary (stream->getDict(),outStr);
          writeStream (stream,outStr);
          obj1.free();
        } else {
          //raw stream copy
774 775 776 777 778 779 780 781 782 783 784 785
          FilterStream *fs = dynamic_cast<FilterStream*>(stream);
          if (fs) {
            BaseStream *bs = fs->getBaseStream();
            if (bs) {
              Guint streamEnd;
                if (xref->getStreamEnd(bs->getStart(), &streamEnd)) {
                  Object val;
                  val.initInt(streamEnd - bs->getStart());
                  stream->getDict()->set("Length", &val);
                }
              }
          }
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
          writeDictionnary (stream->getDict(), outStr);
          writeRawStream (stream, outStr);
        }
        break;
      }
    case objRef:
      outStr->printf("%i %i R ", obj->getRef().num, obj->getRef().gen);
      break;
    case objCmd:
      outStr->printf("cmd\r\n");
      break;
    case objError:
      outStr->printf("error\r\n");
      break;
    case objEOF:
      outStr->printf("eof\r\n");
      break;
    case objNone:
      outStr->printf("none\r\n");
      break;
    default:
      error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType());
      break;
  }
  if (ref)
    outStr->printf("endobj\r\n");
  return offset;
}

void PDFDoc::writeTrailer (Guint uxrefOffset, int uxrefSize, OutStream* outStr, GBool incrUpdate)
{
  Dict *trailerDict = new Dict(xref);
  Object obj1;
  obj1.initInt(uxrefSize);
  trailerDict->set("Size", &obj1);
  obj1.free();


  //build a new ID, as recommended in the reference, uses:
  // - current time
  // - file name
  // - file size
  // - values of entry in information dictionnary
  GooString message;
  char buffer[256];
  sprintf(buffer, "%i", (int)time(NULL));
  message.append(buffer);
833 834 835 836
  if (fileName)
    message.append(fileName);
  else
    message.append("streamwithoutfilename.pdf");
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
  // file size
  unsigned int fileSize = 0;
  int c;
  str->reset();
  while ((c = str->getChar()) != EOF) {
    fileSize++;
  }
  str->close();
  sprintf(buffer, "%i", fileSize);
  message.append(buffer);

  //info dict -- only use text string
  if (xref->getDocInfo(&obj1)->isDict()) {
    for(int i=0; i<obj1.getDict()->getLength(); i++) {
      Object obj2;
      obj1.getDict()->getVal(i, &obj2);  
      if (obj2.isString()) {
        message.append(obj2.getString());
      }
      obj2.free();
    }
  }
  obj1.free();

  //calculate md5 digest
  Guchar digest[16];
  Decrypt::md5((Guchar*)message.getCString(), message.getLength(), digest);
  obj1.initString(new GooString((const char*)digest, 16));

  //create ID array
867
  Object obj2,obj3,obj4,obj5;
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
  obj2.initArray(xref);

  if (incrUpdate) {
    //only update the second part of the array
    if(xref->getTrailerDict()->getDict()->lookup("ID", &obj4) != NULL) {
      if (!obj4.isArray()) {
        error(-1, "PDFDoc::writeTrailer original file's ID entry isn't an array. Trying to continue");
      } else {
        //Get the first part of the ID
        obj4.arrayGet(0,&obj3); 

        obj2.arrayAdd(&obj3); 
        obj2.arrayAdd(&obj1);
        trailerDict->set("ID", &obj2);
      }
    }
  } else {
    //new file => same values for the two identifiers
    obj2.arrayAdd(&obj1);
887
    obj1.initString(new GooString((const char*)digest, 16));
888 889 890 891 892 893 894 895 896 897 898 899
    obj2.arrayAdd(&obj1);
    trailerDict->set("ID", &obj2);
  }


  obj1.initRef(xref->getRootNum(), xref->getRootGen());
  trailerDict->set("Root", &obj1);

  if (incrUpdate) { 
    obj1.initInt(xref->getLastXRefPos());
    trailerDict->set("Prev", &obj1);
  }
900 901 902 903 904 905
  
  xref->getDocInfoNF(&obj5);
  if (!obj5.isNull()) {
    trailerDict->set("Info", &obj5);
  }
  
906 907 908 909 910
  outStr->printf( "trailer\r\n");
  writeDictionnary(trailerDict, outStr);
  outStr->printf( "\r\nstartxref\r\n");
  outStr->printf( "%i\r\n", uxrefOffset);
  outStr->printf( "%%%%EOF\r\n");
911 912

  delete trailerDict;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
913
}