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

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

File size: 23.7 KB
Line 
1//========================================================================
2//
3// PDFDoc.cc
4//
5// Copyright 1996-2003 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, 2006, 2008 Brad Hards <bradh@frogmouth.net>
17// Copyright (C) 2005, 2007-2009 Albert Astals Cid <aacid@kde.org>
18// Copyright (C) 2008 Julien Rebetez <julienr@svn.gnome.org>
19// Copyright (C) 2008 Pino Toscano <pino@kde.org>
20// Copyright (C) 2008, 2010 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// Copyright (C) 2009 Axel Struebing <axel.struebing@freenet.de>
24// Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
25// Copyright (C) 2010 Jakub Wilk <ubanus@users.sf.net>
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 <locale.h>
39#include <stdio.h>
40#include <errno.h>
41#include <stdlib.h>
42#include <stddef.h>
43#include <string.h>
44#include <time.h>
45#ifdef _WIN32
46#  include <windows.h>
47#endif
48#include "goo/gstrtod.h"
49#include "goo/GooString.h"
50#include "poppler-config.h"
51#include "GlobalParams.h"
52#include "Page.h"
53#include "Catalog.h"
54#include "Stream.h"
55#include "XRef.h"
56#include "Link.h"
57#include "OutputDev.h"
58#include "Error.h"
59#include "ErrorCodes.h"
60#include "Lexer.h"
61#include "Parser.h"
62#include "SecurityHandler.h"
63#include "Decrypt.h"
64#ifndef DISABLE_OUTLINE
65#include "Outline.h"
66#endif
67#include "PDFDoc.h"
68
69//------------------------------------------------------------------------
70
71#define headerSearchSize 1024   // read this many bytes at beginning of
72                                //   file to look for '%PDF'
73
74//------------------------------------------------------------------------
75// PDFDoc
76//------------------------------------------------------------------------
77
78void PDFDoc::init()
79{
80  ok = gFalse;
81  errCode = errNone;
82  fileName = NULL;
83  file = NULL;
84  str = NULL;
85  xref = NULL;
86  catalog = NULL;
87#ifndef DISABLE_OUTLINE
88  outline = NULL;
89#endif
90}
91
92PDFDoc::PDFDoc()
93{
94  init();
95}
96
97PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword,
98               GooString *userPassword, void *guiDataA) {
99  Object obj;
100
101  init();
102
103  fileName = fileNameA;
104  guiData = guiDataA;
105
106  // try to open file
107#ifdef VMS
108  file = fopen(fileName->getCString(), "rb", "ctx=stm");
109#else
110  file = fopen(fileName->getCString(), "rb");
111#endif
112  if (file == NULL) {
113    // fopen() has failed.
114    // Keep a copy of the errno returned by fopen so that it can be
115    // referred to later.
116    fopenErrno = errno;
117    error(-1, "Couldn't open file '%s': %s.", fileName->getCString(),
118                                              strerror(errno));
119    errCode = errOpenFile;
120    return;
121  }
122
123  // create stream
124  obj.initNull();
125  str = new FileStream(file, 0, gFalse, 0, &obj);
126
127  ok = setup(ownerPassword, userPassword);
128}
129
130#ifdef _WIN32
131PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword,
132               GooString *userPassword, void *guiDataA) {
133  OSVERSIONINFO version;
134  wchar_t fileName2[MAX_PATH + 1];
135  Object obj;
136  int i;
137
138  init();
139
140  guiData = guiDataA;
141
142  //~ file name should be stored in Unicode (?)
143  fileName = new GooString();
144  for (i = 0; i < fileNameLen; ++i) {
145    fileName->append((char)fileNameA[i]);
146  }
147
148  // zero-terminate the file name string
149  for (i = 0; i < fileNameLen && i < MAX_PATH; ++i) {
150    fileName2[i] = fileNameA[i];
151  }
152  fileName2[i] = 0;
153
154  // try to open file
155  // NB: _wfopen is only available in NT
156  version.dwOSVersionInfoSize = sizeof(version);
157  GetVersionEx(&version);
158  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
159    file = _wfopen(fileName2, L"rb");
160  } else {
161    file = fopen(fileName->getCString(), "rb");
162  }
163  if (!file) {
164    error(-1, "Couldn't open file '%s'", fileName->getCString());
165    errCode = errOpenFile;
166    return;
167  }
168
169  // create stream
170  obj.initNull();
171  str = new FileStream(file, 0, gFalse, 0, &obj);
172
173  ok = setup(ownerPassword, userPassword);
174}
175#endif
176
177PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword,
178               GooString *userPassword, void *guiDataA) {
179
180  init();
181  guiData = guiDataA;
182  if (strA->getFileName()) {
183    fileName = strA->getFileName()->copy();
184  } else {
185    fileName = NULL;
186  }
187  str = strA;
188  ok = setup(ownerPassword, userPassword);
189}
190
191GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) {
192  str->setPos(0, -1);
193  if (str->getPos() < 0)
194  {
195    error(-1, "Document base stream is not seekable");
196    return gFalse;
197  }
198
199  str->reset();
200
201  // check footer
202  // Adobe does not seem to enforce %%EOF, so we do the same
203//  if (!checkFooter()) return gFalse;
204 
205  // check header
206  checkHeader();
207
208  // read xref table
209  xref = new XRef(str);
210  if (!xref->isOk()) {
211    error(-1, "Couldn't read xref table");
212    errCode = xref->getErrorCode();
213    return gFalse;
214  }
215
216  // check for encryption
217  if (!checkEncryption(ownerPassword, userPassword)) {
218    errCode = errEncrypted;
219    return gFalse;
220  }
221
222  // read catalog
223  catalog = new Catalog(xref);
224  if (!catalog->isOk()) {
225    error(-1, "Couldn't read page catalog");
226    errCode = errBadCatalog;
227    return gFalse;
228  }
229
230  // done
231  return gTrue;
232}
233
234PDFDoc::~PDFDoc() {
235#ifndef DISABLE_OUTLINE
236  if (outline) {
237    delete outline;
238  }
239#endif
240  if (catalog) {
241    delete catalog;
242  }
243  if (xref) {
244    delete xref;
245  }
246  if (str) {
247    delete str;
248  }
249  if (file) {
250    fclose(file);
251  }
252  if (fileName) {
253    delete fileName;
254  }
255}
256
257
258// Check for a %%EOF at the end of this stream
259GBool PDFDoc::checkFooter() {
260  // we look in the last 1024 chars because Adobe does the same
261  char *eof = new char[1025];
262  int pos = str->getPos();
263  str->setPos(1024, -1);
264  int i, ch;
265  for (i = 0; i < 1024; i++)
266  {
267    ch = str->getChar();
268    if (ch == EOF)
269      break;
270    eof[i] = ch;
271  }
272  eof[i] = '\0';
273
274  bool found = false;
275  for (i = i - 5; i >= 0; i--) {
276    if (strncmp (&eof[i], "%%EOF", 5) == 0) {
277      found = true;
278      break;
279    }
280  }
281  if (!found)
282  {
283    error(-1, "Document has not the mandatory ending %%EOF");
284    errCode = errDamaged;
285    delete[] eof;
286    return gFalse;
287  }
288  delete[] eof;
289  str->setPos(pos);
290  return gTrue;
291}
292 
293// Check for a PDF header on this stream.  Skip past some garbage
294// if necessary.
295void PDFDoc::checkHeader() {
296  char hdrBuf[headerSearchSize+1];
297  char *p;
298  char *tokptr;
299  int i;
300
301  pdfMajorVersion = 0;
302  pdfMinorVersion = 0;
303  for (i = 0; i < headerSearchSize; ++i) {
304    hdrBuf[i] = str->getChar();
305  }
306  hdrBuf[headerSearchSize] = '\0';
307  for (i = 0; i < headerSearchSize - 5; ++i) {
308    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
309      break;
310    }
311  }
312  if (i >= headerSearchSize - 5) {
313    error(-1, "May not be a PDF file (continuing anyway)");
314    return;
315  }
316  str->moveStart(i);
317  if (!(p = strtok_r(&hdrBuf[i+5], " \t\n\r", &tokptr))) {
318    error(-1, "May not be a PDF file (continuing anyway)");
319    return;
320  }
321  sscanf(p, "%d.%d", &pdfMajorVersion, &pdfMinorVersion);
322  // We don't do the version check. Don't add it back in.
323}
324
325GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) {
326  Object encrypt;
327  GBool encrypted;
328  SecurityHandler *secHdlr;
329  GBool ret;
330
331  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
332  if ((encrypted = encrypt.isDict())) {
333    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
334      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
335        // authorization succeeded
336        xref->setEncryption(secHdlr->getPermissionFlags(),
337                            secHdlr->getOwnerPasswordOk(),
338                            secHdlr->getFileKey(),
339                            secHdlr->getFileKeyLength(),
340                            secHdlr->getEncVersion(),
341                            secHdlr->getEncRevision(),
342                            secHdlr->getEncAlgorithm());
343        ret = gTrue;
344      } else {
345        // authorization failed
346        ret = gFalse;
347      }
348      delete secHdlr;
349    } else {
350      // couldn't find the matching security handler
351      ret = gFalse;
352    }
353  } else {
354    // document is not encrypted
355    ret = gTrue;
356  }
357  encrypt.free();
358  return ret;
359}
360
361void PDFDoc::displayPage(OutputDev *out, int page,
362                         double hDPI, double vDPI, int rotate,
363                         GBool useMediaBox, GBool crop, GBool printing,
364                         GBool (*abortCheckCbk)(void *data),
365                         void *abortCheckCbkData,
366                         GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
367                         void *annotDisplayDecideCbkData) {
368  if (globalParams->getPrintCommands()) {
369    printf("***** page %d *****\n", page);
370  }
371  if (catalog->getPage(page))
372    catalog->getPage(page)->display(out, hDPI, vDPI,
373                                    rotate, useMediaBox, crop, printing, catalog,
374                                    abortCheckCbk, abortCheckCbkData,
375                                    annotDisplayDecideCbk, annotDisplayDecideCbkData);
376}
377
378void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
379                          double hDPI, double vDPI, int rotate,
380                          GBool useMediaBox, GBool crop, GBool printing,
381                          GBool (*abortCheckCbk)(void *data),
382                          void *abortCheckCbkData,
383                          GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
384                          void *annotDisplayDecideCbkData) {
385  int page;
386
387  for (page = firstPage; page <= lastPage; ++page) {
388    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
389                abortCheckCbk, abortCheckCbkData,
390                annotDisplayDecideCbk, annotDisplayDecideCbkData);
391  }
392}
393
394void PDFDoc::displayPageSlice(OutputDev *out, int page,
395                              double hDPI, double vDPI, int rotate,
396                              GBool useMediaBox, GBool crop, GBool printing,
397                              int sliceX, int sliceY, int sliceW, int sliceH,
398                              GBool (*abortCheckCbk)(void *data),
399                              void *abortCheckCbkData,
400                              GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
401                              void *annotDisplayDecideCbkData) {
402  if (catalog->getPage(page))
403    catalog->getPage(page)->displaySlice(out, hDPI, vDPI,
404                                         rotate, useMediaBox, crop,
405                                         sliceX, sliceY, sliceW, sliceH,
406                                         printing, catalog,
407                                         abortCheckCbk, abortCheckCbkData,
408                                         annotDisplayDecideCbk, annotDisplayDecideCbkData);
409}
410
411Links *PDFDoc::getLinks(int page) {
412  return catalog->getPage(page) ? catalog->getPage(page)->getLinks(catalog) : NULL;
413}
414 
415void PDFDoc::processLinks(OutputDev *out, int page) {
416  if (catalog->getPage(page))
417    catalog->getPage(page)->processLinks(out, catalog);
418}
419
420GBool PDFDoc::isLinearized() {
421  Parser *parser;
422  Object obj1, obj2, obj3, obj4, obj5;
423  GBool lin;
424
425  lin = gFalse;
426  obj1.initNull();
427  parser = new Parser(xref,
428             new Lexer(xref,
429               str->makeSubStream(str->getStart(), gFalse, 0, &obj1)),
430             gTrue);
431  parser->getObj(&obj1);
432  parser->getObj(&obj2);
433  parser->getObj(&obj3);
434  parser->getObj(&obj4);
435  if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
436      obj4.isDict()) {
437    obj4.dictLookup("Linearized", &obj5);
438    if (obj5.isNum() && obj5.getNum() > 0) {
439      lin = gTrue;
440    }
441    obj5.free();
442  }
443  obj4.free();
444  obj3.free();
445  obj2.free();
446  obj1.free();
447  delete parser;
448  return lin;
449}
450
451int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) {
452  FILE *f;
453  OutStream *outStr;
454  int res;
455
456  if (!(f = fopen(name->getCString(), "wb"))) {
457    error(-1, "Couldn't open file '%s'", name->getCString());
458    return errOpenFile;
459  }
460  outStr = new FileOutStream(f,0);
461  res = saveAs(outStr, mode);
462  delete outStr;
463  fclose(f);
464  return res;
465}
466
467int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) {
468
469  // we don't support files with Encrypt at the moment
470  Object obj;
471  xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj);
472  if (!obj.isNull())
473  {
474    obj.free();
475    return errEncrypted;
476  }
477  obj.free();
478
479  if (mode == writeForceRewrite) {
480    saveCompleteRewrite(outStr);
481  } else if (mode == writeForceIncremental) {
482    saveIncrementalUpdate(outStr); 
483  } else { // let poppler decide
484    // find if we have updated objects
485    GBool updated = gFalse;
486    for(int i=0; i<xref->getNumObjects(); i++) {
487      if (xref->getEntry(i)->updated) {
488        updated = gTrue;
489        break;
490      }
491    }
492    if(updated) { 
493      saveIncrementalUpdate(outStr);
494    } else {
495      // simply copy the original file
496      saveWithoutChangesAs (outStr);
497    }
498  }
499
500  return errNone;
501}
502
503int PDFDoc::saveWithoutChangesAs(GooString *name) {
504  FILE *f;
505  OutStream *outStr;
506  int res;
507
508  if (!(f = fopen(name->getCString(), "wb"))) {
509    error(-1, "Couldn't open file '%s'", name->getCString());
510    return errOpenFile;
511  }
512 
513  outStr = new FileOutStream(f,0);
514  res = saveWithoutChangesAs(outStr);
515  delete outStr;
516
517  fclose(f);
518
519  return res;
520}
521
522int PDFDoc::saveWithoutChangesAs(OutStream *outStr) {
523  int c;
524 
525  str->reset();
526  while ((c = str->getChar()) != EOF) {
527    outStr->put(c);
528  }
529  str->close();
530
531  return errNone;
532}
533
534void PDFDoc::saveIncrementalUpdate (OutStream* outStr)
535{
536  XRef *uxref;
537  int c;
538  //copy the original file
539  str->reset();
540  while ((c = str->getChar()) != EOF) {
541    outStr->put(c);
542  }
543  str->close();
544
545  uxref = new XRef();
546  uxref->add(0, 65535, 0, gFalse);
547  for(int i=0; i<xref->getNumObjects(); i++) {
548    if ((xref->getEntry(i)->type == xrefEntryFree) && 
549        (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects
550      continue;
551
552    if (xref->getEntry(i)->updated) { //we have an updated object
553      Object obj1;
554      Ref ref;
555      ref.num = i;
556      ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen;
557      xref->fetch(ref.num, ref.gen, &obj1);
558      Guint offset = writeObject(&obj1, &ref, outStr);
559      uxref->add(ref.num, ref.gen, offset, gTrue);
560      obj1.free();
561    }
562  }
563  if (uxref->getSize() == 0) { //we have nothing to update
564    delete uxref;
565    return;
566  }
567
568  Guint uxrefOffset = outStr->getPos();
569  uxref->writeToFile(outStr, gFalse /* do not write unnecessary entries */);
570
571  writeTrailer(uxrefOffset, xref->getSize(), outStr, gTrue);
572
573  delete uxref;
574}
575
576void PDFDoc::saveCompleteRewrite (OutStream* outStr)
577{
578  outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion);
579  XRef *uxref = new XRef();
580  uxref->add(0, 65535, 0, gFalse);
581  for(int i=0; i<xref->getNumObjects(); i++) {
582    Object obj1;
583    Ref ref;
584    XRefEntryType type = xref->getEntry(i)->type;
585    if (type == xrefEntryFree) {
586      ref.num = i;
587      ref.gen = xref->getEntry(i)->gen;
588      /* the XRef class adds a lot of irrelevant free entries, we only want the significant one
589          and we don't want the one with num=0 because it has already been added (gen = 65535)*/
590      if (ref.gen > 0 && ref.num > 0)
591        uxref->add(ref.num, ref.gen, 0, gFalse);
592    } else if (type == xrefEntryUncompressed){ 
593      ref.num = i;
594      ref.gen = xref->getEntry(i)->gen;
595      xref->fetch(ref.num, ref.gen, &obj1);
596      Guint offset = writeObject(&obj1, &ref, outStr);
597      uxref->add(ref.num, ref.gen, offset, gTrue);
598      obj1.free();
599    } else if (type == xrefEntryCompressed) {
600      ref.num = i;
601      ref.gen = 0; //compressed entries have gen == 0
602      xref->fetch(ref.num, ref.gen, &obj1);
603      Guint offset = writeObject(&obj1, &ref, outStr);
604      uxref->add(ref.num, ref.gen, offset, gTrue);
605      obj1.free();
606    }
607  }
608  Guint uxrefOffset = outStr->getPos();
609  uxref->writeToFile(outStr, gTrue /* write all entries */);
610
611  writeTrailer(uxrefOffset, uxref->getSize(), outStr, gFalse);
612
613
614  delete uxref;
615
616}
617
618void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr)
619{
620  Object obj1;
621  outStr->printf("<<");
622  for (int i=0; i<dict->getLength(); i++) {
623    GooString keyName(dict->getKey(i));
624    GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */);
625    outStr->printf("/%s ", keyNameToPrint->getCString());
626    delete keyNameToPrint;
627    writeObject(dict->getValNF(i, &obj1), NULL, outStr);
628    obj1.free();
629  }
630  outStr->printf(">> ");
631}
632
633void PDFDoc::writeStream (Stream* str, OutStream* outStr)
634{
635  outStr->printf("stream\r\n");
636  str->reset();
637  for (int c=str->getChar(); c!= EOF; c=str->getChar()) {
638    outStr->printf("%c", c); 
639  }
640  outStr->printf("\r\nendstream\r\n");
641}
642
643void PDFDoc::writeRawStream (Stream* str, OutStream* outStr)
644{
645  Object obj1;
646  str->getDict()->lookup("Length", &obj1);
647  if (!obj1.isInt()) {
648    error (-1, "PDFDoc::writeRawStream, no Length in stream dict");
649    return;
650  }
651
652  const int length = obj1.getInt();
653  obj1.free();
654
655  outStr->printf("stream\r\n");
656  str->unfilteredReset();
657  for (int i=0; i<length; i++) {
658    int c = str->getUnfilteredChar();
659    outStr->printf("%c", c); 
660  }
661  str->reset();
662  outStr->printf("\r\nendstream\r\n");
663}
664
665void PDFDoc::writeString (GooString* s, OutStream* outStr)
666{
667  if (s->hasUnicodeMarker()) {
668    //unicode string don't necessary end with \0
669    const char* c = s->getCString();
670    outStr->printf("(");
671    for(int i=0; i<s->getLength(); i++) {
672      char unescaped = *(c+i)&0x000000ff;
673      //escape if needed
674      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
675        outStr->printf("%c", '\\');
676      outStr->printf("%c", unescaped);
677    }
678    outStr->printf(") ");
679  } else {
680    const char* c = s->getCString();
681    outStr->printf("(");
682    for(int i=0; i<s->getLength(); i++) {
683      char unescaped = (*c)&0x000000ff;
684      //escape if needed
685      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
686        outStr->printf("%c", '\\');
687      outStr->printf("%c", unescaped);
688      c++;
689    }
690    outStr->printf(") ");
691  }
692}
693
694Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr)
695{
696  Array *array;
697  Object obj1;
698  Guint offset = outStr->getPos();
699  int tmp;
700
701  if(ref) 
702    outStr->printf("%i %i obj ", ref->num, ref->gen);
703
704  switch (obj->getType()) {
705    case objBool:
706      outStr->printf("%s ", obj->getBool()?"true":"false");
707      break;
708    case objInt:
709      outStr->printf("%i ", obj->getInt());
710      break;
711    case objReal:
712    {
713      GooString s;
714      s.appendf("{0:.10g}", obj->getReal());
715      outStr->printf("%s ", s.getCString());
716      break;
717    }
718    case objString:
719      writeString(obj->getString(), outStr);
720      break;
721    case objName:
722    {
723      GooString name(obj->getName());
724      GooString *nameToPrint = name.sanitizedName(gFalse /* non ps mode */);
725      outStr->printf("/%s ", nameToPrint->getCString());
726      delete nameToPrint;
727      break;
728    }
729    case objNull:
730      outStr->printf( "null ");
731      break;
732    case objArray:
733      array = obj->getArray();
734      outStr->printf("[");
735      for (int i=0; i<array->getLength(); i++) {
736        writeObject(array->getNF(i, &obj1), NULL,outStr);
737        obj1.free();
738      }
739      outStr->printf("] ");
740      break;
741    case objDict:
742      writeDictionnary (obj->getDict(),outStr);
743      break;
744    case objStream: 
745      {
746        //We can't modify stream with the current implementation (no write functions in Stream API)
747        // => the only type of streams which that have been modified are internal streams (=strWeird)
748        Stream *stream = obj->getStream();
749        if (stream->getKind() == strWeird) {
750          //we write the stream unencoded => TODO: write stream encoder
751          stream->reset();
752          //recalculate stream length
753          tmp = 0;
754          for (int c=stream->getChar(); c!=EOF; c=stream->getChar()) {
755            tmp++;
756          }
757          obj1.initInt(tmp);
758          stream->getDict()->set("Length", &obj1);
759
760          //Remove Stream encoding
761          stream->getDict()->remove("Filter");
762          stream->getDict()->remove("DecodeParms");
763
764          writeDictionnary (stream->getDict(),outStr);
765          writeStream (stream,outStr);
766          obj1.free();
767        } else {
768          //raw stream copy
769          FilterStream *fs = dynamic_cast<FilterStream*>(stream);
770          if (fs) {
771            BaseStream *bs = fs->getBaseStream();
772            if (bs) {
773              Guint streamEnd;
774                if (xref->getStreamEnd(bs->getStart(), &streamEnd)) {
775                  Object val;
776                  val.initInt(streamEnd - bs->getStart());
777                  stream->getDict()->set("Length", &val);
778                }
779              }
780          }
781          writeDictionnary (stream->getDict(), outStr);
782          writeRawStream (stream, outStr);
783        }
784        break;
785      }
786    case objRef:
787      outStr->printf("%i %i R ", obj->getRef().num, obj->getRef().gen);
788      break;
789    case objCmd:
790      outStr->printf("cmd\r\n");
791      break;
792    case objError:
793      outStr->printf("error\r\n");
794      break;
795    case objEOF:
796      outStr->printf("eof\r\n");
797      break;
798    case objNone:
799      outStr->printf("none\r\n");
800      break;
801    default:
802      error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType());
803      break;
804  }
805  if (ref)
806    outStr->printf("endobj\r\n");
807  return offset;
808}
809
810void PDFDoc::writeTrailer (Guint uxrefOffset, int uxrefSize, OutStream* outStr, GBool incrUpdate)
811{
812  Dict *trailerDict = new Dict(xref);
813  Object obj1;
814  obj1.initInt(uxrefSize);
815  trailerDict->set("Size", &obj1);
816  obj1.free();
817
818
819  //build a new ID, as recommended in the reference, uses:
820  // - current time
821  // - file name
822  // - file size
823  // - values of entry in information dictionnary
824  GooString message;
825  char buffer[256];
826  sprintf(buffer, "%i", (int)time(NULL));
827  message.append(buffer);
828  if (fileName)
829    message.append(fileName);
830  else
831    message.append("streamwithoutfilename.pdf");
832  // file size
833  unsigned int fileSize = 0;
834  int c;
835  str->reset();
836  while ((c = str->getChar()) != EOF) {
837    fileSize++;
838  }
839  str->close();
840  sprintf(buffer, "%i", fileSize);
841  message.append(buffer);
842
843  //info dict -- only use text string
844  if (xref->getDocInfo(&obj1)->isDict()) {
845    for(int i=0; i<obj1.getDict()->getLength(); i++) {
846      Object obj2;
847      obj1.getDict()->getVal(i, &obj2); 
848      if (obj2.isString()) {
849        message.append(obj2.getString());
850      }
851      obj2.free();
852    }
853  }
854  obj1.free();
855
856  //calculate md5 digest
857  Guchar digest[16];
858  Decrypt::md5((Guchar*)message.getCString(), message.getLength(), digest);
859  obj1.initString(new GooString((const char*)digest, 16));
860
861  //create ID array
862  Object obj2,obj3,obj4,obj5;
863  obj2.initArray(xref);
864
865  if (incrUpdate) {
866    //only update the second part of the array
867    if(xref->getTrailerDict()->getDict()->lookup("ID", &obj4) != NULL) {
868      if (!obj4.isArray()) {
869        error(-1, "PDFDoc::writeTrailer original file's ID entry isn't an array. Trying to continue");
870      } else {
871        //Get the first part of the ID
872        obj4.arrayGet(0,&obj3); 
873
874        obj2.arrayAdd(&obj3); 
875        obj2.arrayAdd(&obj1);
876        trailerDict->set("ID", &obj2);
877      }
878    }
879  } else {
880    //new file => same values for the two identifiers
881    obj2.arrayAdd(&obj1);
882    obj1.initString(new GooString((const char*)digest, 16));
883    obj2.arrayAdd(&obj1);
884    trailerDict->set("ID", &obj2);
885  }
886
887
888  obj1.initRef(xref->getRootNum(), xref->getRootGen());
889  trailerDict->set("Root", &obj1);
890
891  if (incrUpdate) { 
892    obj1.initInt(xref->getLastXRefPos());
893    trailerDict->set("Prev", &obj1);
894  }
895 
896  xref->getDocInfoNF(&obj5);
897  if (!obj5.isNull()) {
898    trailerDict->set("Info", &obj5);
899  }
900 
901  outStr->printf( "trailer\r\n");
902  writeDictionnary(trailerDict, outStr);
903  outStr->printf( "\r\nstartxref\r\n");
904  outStr->printf( "%i\r\n", uxrefOffset);
905  outStr->printf( "%%%%EOF\r\n");
906
907  delete trailerDict;
908}
909
910#ifndef DISABLE_OUTLINE
911Outline *PDFDoc::getOutline()
912{
913  if (!outline) {
914    // read outline
915    outline = new Outline(catalog->getOutline(), xref);
916  }
917
918  return outline;
919}
920#endif
921
922PDFDoc *PDFDoc::ErrorPDFDoc(int errorCode, GooString *fileNameA)
923{
924  PDFDoc *doc = new PDFDoc();
925  doc->errCode = errorCode;
926  doc->fileName = fileNameA;
927
928  return doc;
929}
Note: See TracBrowser for help on using the repository browser.