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

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

PDF plugin: Poppler library updated to version 0.10.0

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