root/middleware-offline/trunk/_src/eidmw/pteid-poppler/poppler/PDFDoc.cc @ 350

Revision 350, 50.1 KB (checked in by vsilva, 3 years ago)

Add Professional Signature Add PDF Preview. New features

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, 2011, 2012 Albert Astals Cid <aacid@kde.org>
18// Copyright (C) 2008 Julien Rebetez <julienr@svn.gnome.org>
19// Copyright (C) 2008, 2010 Pino Toscano <pino@kde.org>
20// Copyright (C) 2008, 2010, 2011 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, 2011 Axel Struebing <axel.struebing@freenet.de>
24// Copyright (C) 2010, 2011 Hib Eris <hib@hiberis.nl>
25// Copyright (C) 2010 Jakub Wilk <ubanus@users.sf.net>
26// Copyright (C) 2010 Ilya Gorenbein <igorenbein@finjan.com>
27// Copyright (C) 2010 Srinivas Adicherla <srinivas.adicherla@geodesic.com>
28// Copyright (C) 2010 Philip Lorenz <lorenzph+freedesktop@gmail.com>
29// Copyright (C) 2011, 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
30// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
31//
32// To see a description of the changes please see the Changelog file that
33// came with your tarball or type make ChangeLog if you are building from git
34//
35//========================================================================
36
37#include <config.h>
38
39#ifdef USE_GCC_PRAGMAS
40#pragma implementation
41#endif
42
43#include <ctype.h>
44#include <locale.h>
45#include <stdio.h>
46#include <errno.h>
47#include <stdlib.h>
48#include <stddef.h>
49#include <string.h>
50#include <memory>
51#include <time.h>
52#if defined(_DEBUG) && !defined(_WIN32)
53#include <fcntl.h>
54#include <unistd.h>
55#endif
56
57#ifdef _WIN32
58#include <windows.h>
59
60#endif
61#include <sys/stat.h>
62#include "goo/gstrtod.h"
63#include "goo/GooString.h"
64#include "poppler-config.h"
65#include "Page.h"
66#include "Catalog.h"
67#include "Stream.h"
68#include "XRef.h"
69#include "Linearization.h"
70//#include "DeflateStream.h"
71#include "Link.h"
72#include "Error.h"
73#include "ErrorCodes.h"
74#include "Lexer.h"
75#include "Parser.h"
76#include "SecurityHandler.h"
77#include "Decrypt.h"
78#ifndef DISABLE_OUTLINE
79#include "Outline.h"
80#endif
81#include "PDFDoc.h"
82#include "Hints.h"
83
84extern "C"
85{
86//Definition of our own memmem(), generally its only available in Linux
87//and Mac OSX Lion and later so it's better to just use our own implementation
88POPPLER_API void *memmem(const void *haystack, size_t n, const void *needle, size_t m);
89}
90
91//------------------------------------------------------------------------
92
93#define headerSearchSize 1024   // read this many bytes at beginning of
94                                //   file to look for '%PDF'
95#define pdfIdLength 32   // PDF Document IDs (PermanentId, UpdateId) length
96
97#define linearizationSearchSize 1024    // read this many bytes at beginning of
98                                        // file to look for linearization
99                                        // dictionary
100
101#define xrefSearchSize 1024     // read this many bytes at end of file
102                                //   to look for 'startxref'
103
104
105//------------------------------------------------------------------------
106// PDFDoc
107//------------------------------------------------------------------------
108
109void PDFDoc::init()
110{
111  ok = gFalse;
112  errCode = errNone;
113  fileName = NULL;
114  file = NULL;
115  str = NULL;
116  xref = NULL;
117  linearization = NULL;
118  catalog = NULL;
119  hints = NULL;
120#ifndef DISABLE_OUTLINE
121  outline = NULL;
122#endif
123  startXRefPos = ~(Guint)0;
124  secHdlr = NULL;
125  pageCache = NULL;
126  signature_mode = gFalse;
127  m_image_data_jpeg = NULL;
128  m_image_length = 0;
129}
130
131PDFDoc::PDFDoc()
132{
133  init();
134}
135
136PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword,
137               GooString *userPassword, void *guiDataA) {
138  Object obj;
139  int size = 0;
140#ifdef _WIN32
141  int n, i;
142#endif
143
144  init();
145
146  fileName = fileNameA;
147  guiData = guiDataA;
148#ifdef _WIN32
149  n = fileName->getLength();
150  fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
151  for (i = 0; i < n; ++i) {
152    fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
153  }
154  fileNameU[n] = L'\0';
155#endif
156
157  struct stat buf;
158  if (stat(fileName->getCString(), &buf) == 0) {
159     size = buf.st_size;
160     this->fileSize = buf.st_size;
161  }
162
163  // try to open file
164#ifdef VMS
165  file = fopen(fileName->getCString(), "rb", "ctx=stm");
166#else
167  file = fopen(fileName->getCString(), "rb");
168#endif
169  if (file == NULL) {
170    // fopen() has failed.
171    // Keep a copy of the errno returned by fopen so that it can be
172    // referred to later.
173    fopenErrno = errno;
174    error(errIO, -1, "Couldn't open file '{0:t}': {0:s}.", fileName, strerror(errno));
175    errCode = errOpenFile;
176    return;
177  }
178
179  // create stream
180  obj.initNull();
181  str = new FileStream(file, 0, gFalse, size, &obj);
182
183  ok = setup(ownerPassword, userPassword);
184}
185
186#ifdef _WIN32
187PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword,
188               GooString *userPassword, void *guiDataA) {
189  OSVERSIONINFO version;
190  Object obj;
191  int i;
192
193  init();
194
195  guiData = guiDataA;
196
197  // save both Unicode and 8-bit copies of the file name
198  fileName = new GooString();
199  fileNameU = (wchar_t *)gmallocn(fileNameLen + 1, sizeof(wchar_t));
200  for (i = 0; i < fileNameLen; ++i) {
201    fileName->append((char)fileNameA[i]);
202    fileNameU[i] = fileNameA[i];
203  }
204  fileNameU[fileNameLen] = L'\0';
205
206
207  // try to open file
208  // NB: _wfopen is only available in NT
209  struct _stat buf;
210  int size;
211  version.dwOSVersionInfoSize = sizeof(version);
212  GetVersionEx(&version);
213  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
214    if (_wstat(fileNameU, &buf) == 0) {
215      size = buf.st_size;
216    }
217    file = _wfopen(fileNameU, L"rb");
218  } else {
219    if (_stat(fileName->getCString(), &buf) == 0) {
220      size = buf.st_size;
221    }
222    file = fopen(fileName->getCString(), "rb");
223  }
224  if (!file) {
225    error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
226    errCode = errOpenFile;
227    return;
228  }
229
230  // create stream
231  obj.initNull();
232  str = new FileStream(file, 0, gFalse, size, &obj);
233
234  ok = setup(ownerPassword, userPassword);
235}
236#endif
237
238PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword,
239               GooString *userPassword, void *guiDataA) {
240#ifdef _WIN32
241  int n, i;
242#endif
243
244  init();
245  guiData = guiDataA;
246  if (strA->getFileName()) {
247    fileName = strA->getFileName()->copy();
248#ifdef _WIN32
249    n = fileName->getLength();
250    fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
251    for (i = 0; i < n; ++i) {
252      fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
253    }
254    fileNameU[n] = L'\0';
255#endif
256  } else {
257    fileName = NULL;
258#ifdef _WIN32
259    fileNameU = NULL;
260#endif
261  }
262  str = strA;
263  ok = setup(ownerPassword, userPassword);
264}
265
266GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) {
267  str->setPos(0, -1);
268  if (str->getPos() < 0)
269  {
270    error(errSyntaxError, -1, "Document base stream is not seekable");
271    return gFalse;
272  }
273
274  str->reset();
275
276  // check footer
277  // Adobe does not seem to enforce %%EOF, so we do the same
278//  if (!checkFooter()) return gFalse;
279 
280  // check header
281  checkHeader();
282
283  GBool wasReconstructed = false;
284
285  // read xref table
286  xref = new XRef(str, getStartXRef(), getMainXRefEntriesOffset(), &wasReconstructed);
287  if (!xref->isOk()) {
288    error(errSyntaxError, -1, "Couldn't read xref table");
289    errCode = xref->getErrorCode();
290    return gFalse;
291  }
292
293  // check for encryption
294  if (!checkEncryption(ownerPassword, userPassword)) {
295    errCode = errEncrypted;
296    return gFalse;
297  }
298
299  // read catalog
300  catalog = new Catalog(this);
301  if (catalog && !catalog->isOk()) {
302    if (!wasReconstructed)
303    {
304      // try one more time to contruct the Catalog, maybe the problem is damaged XRef
305      delete catalog;
306      delete xref;
307      xref = new XRef(str, 0, 0, NULL, true);
308      catalog = new Catalog(this);
309    }
310
311    if (catalog && !catalog->isOk()) {
312      error(errSyntaxError, -1, "Couldn't read page catalog");
313      errCode = errBadCatalog;
314      return gFalse;
315    }
316  }
317
318  // done
319  return gTrue;
320}
321
322PDFDoc::~PDFDoc() {
323  if (pageCache) {
324    for (int i = 0; i < getNumPages(); i++) {
325      if (pageCache[i]) {
326        delete pageCache[i];
327      }
328    }
329    gfree(pageCache);
330  }
331  delete secHdlr;
332#ifndef DISABLE_OUTLINE
333  if (outline) {
334    delete outline;
335  }
336#endif
337  if (catalog) {
338    delete catalog;
339  }
340  if (xref) {
341    delete xref;
342  }
343  if (hints) {
344    delete hints;
345  }
346  if (linearization) {
347    delete linearization;
348  }
349  if (str) {
350    delete str;
351  }
352  if (file) {
353    fclose(file);
354  }
355  if (fileName) {
356    delete fileName;
357  }
358#ifdef _WIN32
359  if (fileNameU) {
360    gfree(fileNameU);
361  }
362#endif
363}
364
365
366// Check for a %%EOF at the end of this stream
367GBool PDFDoc::checkFooter() {
368  // we look in the last 1024 chars because Adobe does the same
369  char *eof = new char[1025];
370  int pos = str->getPos();
371  str->setPos(1024, -1);
372  int i, ch;
373  for (i = 0; i < 1024; i++)
374  {
375    ch = str->getChar();
376    if (ch == EOF)
377      break;
378    eof[i] = ch;
379  }
380  eof[i] = '\0';
381
382  bool found = false;
383  for (i = i - 5; i >= 0; i--) {
384    if (strncmp (&eof[i], "%%EOF", 5) == 0) {
385      found = true;
386      break;
387    }
388  }
389  if (!found)
390  {
391    error(errSyntaxError, -1, "Document has not the mandatory ending %%EOF");
392    errCode = errDamaged;
393    delete[] eof;
394    return gFalse;
395  }
396  delete[] eof;
397  str->setPos(pos);
398  return gTrue;
399}
400
401void PDFDoc::addCustomSignatureImage(unsigned char *image_data, unsigned long image_length)
402{
403  m_image_data_jpeg = image_data;
404  m_image_length = image_length;
405}
406 
407// Check for a PDF header on this stream.  Skip past some garbage
408// if necessary.
409void PDFDoc::checkHeader() {
410  char hdrBuf[headerSearchSize+1];
411  char *p;
412  char *tokptr;
413  int i;
414
415  pdfMajorVersion = 0;
416  pdfMinorVersion = 0;
417  for (i = 0; i < headerSearchSize; ++i) {
418    hdrBuf[i] = str->getChar();
419  }
420  hdrBuf[headerSearchSize] = '\0';
421  for (i = 0; i < headerSearchSize - 5; ++i) {
422    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
423      break;
424    }
425  }
426  if (i >= headerSearchSize - 5) {
427    error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
428    return;
429  }
430  str->moveStart(i);
431  if (!(p = strtok_r(&hdrBuf[i+5], " \t\n\r", &tokptr))) {
432    error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
433    return;
434  }
435  sscanf(p, "%d.%d", &pdfMajorVersion, &pdfMinorVersion);
436  // We don't do the version check. Don't add it back in.
437}
438
439GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) {
440  Object encrypt;
441  GBool encrypted;
442  GBool ret;
443
444  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
445  if ((encrypted = encrypt.isDict())) {
446    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
447      if (secHdlr->isUnencrypted()) {
448        // no encryption
449        ret = gTrue;
450      } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
451        // authorization succeeded
452        xref->setEncryption(secHdlr->getPermissionFlags(),
453                            secHdlr->getOwnerPasswordOk(),
454                            secHdlr->getFileKey(),
455                            secHdlr->getFileKeyLength(),
456                            secHdlr->getEncVersion(),
457                            secHdlr->getEncRevision(),
458                            secHdlr->getEncAlgorithm());
459        ret = gTrue;
460      } else {
461        // authorization failed
462        ret = gFalse;
463      }
464    } else {
465      // couldn't find the matching security handler
466      ret = gFalse;
467    }
468  } else {
469    // document is not encrypted
470    ret = gTrue;
471  }
472  encrypt.free();
473  return ret;
474}
475
476GBool PDFDoc::isOk()
477{ 
478        return ok; 
479}
480
481#define SIGFLAGS_SIGNATURES_EXIST   0x1
482#define SIGFLAGS_APPEND_ONLY        0x2
483
484GBool PDFDoc::isSigned() {
485    GBool ret;
486    Object sigFlags;
487    if (getCatalog()->getAcroForm()->isNull())
488    {
489        return gFalse;
490    }
491    getCatalog()->getAcroForm()->dictLookup("SigFlags", &sigFlags);
492
493    if (sigFlags.isInt()) {
494        ret = sigFlags.getInt() & SIGFLAGS_SIGNATURES_EXIST;
495    } else {
496        ret = gFalse;
497    }
498    sigFlags.free();
499
500    return ret;
501}
502
503
504char *PDFDoc::getOccupiedSectors(int page)
505{
506        GooString *sectors = new GooString();
507  Page *p = getPage(page);
508        Object type, obj1;
509
510        if(!p)
511  {
512    delete sectors;
513                return NULL;
514  }
515
516        Annots *annotations = p->getAnnots();
517       
518        //Find the Signature Field and retrieve /Contents
519        for (int i = 0; i != annotations->getNumAnnots(); i++)
520        {
521            Object sector;     
522            Annot *an = annotations->getAnnot(i);
523            if (an->getType() != Annot::typeWidget)
524                    continue;
525            Object f = an->getDict();
526
527            f.dictLookup("Type", &type);
528            f.dictLookup("FT", &obj1);
529
530            if (obj1.isNull())
531                   continue;
532            //Search for signature fields
533            if (strcmp(obj1.getName(), "Sig") == 0)
534            {
535                    f.dictLookup("SigSector", &sector);
536                    if (sector.isNull())
537                            continue;
538
539        std::auto_ptr<GooString> sector_str(sectors->getLength() == 0 ? GooString::format("{0:d}", sector.getInt()) :
540             GooString::format(",{0:d}", sector.getInt()));
541
542                    // If we find a signature field marked
543                    // with SigSector save this value
544                    sectors->append(sector_str.get());
545
546            }
547        }
548       
549        return sectors->getCString();
550
551}
552
553
554GBool PDFDoc::isReaderEnabled()
555{
556
557        return getCatalog()->getUS3Dict();
558
559}
560
561
562//TODO: The next 2 methods consider only the first signature they happen to find
563// in the file
564int PDFDoc::getSignatureContents(unsigned char **contents)
565{
566        Object *acro_form = getCatalog()->getAcroForm();
567        Object fields, f, sig_dict, contents_obj, type, obj1;
568
569        if (acro_form->isNull())
570                return 0;
571        acro_form->dictLookup("Fields", &fields);
572       
573        //Find the Signature Field and retrieve /Contents
574        for (int i = 0; i != fields.arrayGetLength(); i++)
575        {
576            fields.arrayGet(i, &f);
577
578            f.dictLookup("Type", &type);
579            f.dictLookup("FT", &obj1);
580            if (strcmp(type.getName(), "Annot") == 0
581                            && strcmp(obj1.getName(), "Sig") == 0)
582            {
583                f.dictLookup("V", &sig_dict);
584                sig_dict.dictLookup("Contents", &contents_obj);
585                if (contents_obj.isString())
586                {
587                   GooString *str = contents_obj.getString();
588                   int ret = str->getLength(); 
589                   *contents = (unsigned char *)malloc(ret);
590                   memcpy(*contents, str->getCString(), ret);
591                   return ret;
592                }
593
594            }
595
596        }
597
598        return 0;
599}
600
601Object *PDFDoc::getByteRange()
602{
603
604        Object *acro_form = getCatalog()->getAcroForm();
605        Object fields, f, sig_dict, byterange_obj, type, obj1;
606
607        if (acro_form->isNull())
608                return 0;
609        acro_form->dictLookup("Fields", &fields);
610       
611        //Find the Signature Field and retrieve /Contents
612        for (int i = 0; i != fields.arrayGetLength(); i++)
613        {
614            fields.arrayGet(i, &f);
615
616            f.dictLookup("Type", &type);
617            f.dictLookup("FT", &obj1);
618            if (strcmp(type.getName(), "Annot") == 0
619                            && strcmp(obj1.getName(), "Sig") == 0)
620            {
621                f.dictLookup("V", &sig_dict);
622                if (!sig_dict.isDict())
623                        return NULL;
624                sig_dict.dictLookup("ByteRange", &byterange_obj);
625                if (byterange_obj.isArray())
626                {
627                   return new Object(byterange_obj);
628                }
629            }
630
631        }
632
633        return NULL;
634}
635
636#if defined(_DEBUG) && !defined(_WIN32)
637char *read_random_bytes(int n)
638{
639        char * buf = (char *)malloc(n+1);
640        int fd = open("/dev/urandom", O_RDONLY);
641        read(fd, buf, n);
642        close(fd);
643        //Replace by readable char
644        for (unsigned int i=0; i!= n; i++)
645            buf[i] = (buf[i] % 26) + 0x61;
646       
647        buf[n] = '\0';
648        return buf;
649}
650
651
652void dump_to_file(unsigned char * buf, unsigned int len)
653{
654        char * home = getenv("HOME"); 
655
656        char *path = (char *)calloc(200, 1);
657        strncat(path, home, 199);
658        strcat(path, "/data_to_be_signed_");
659        strcat(path, read_random_bytes(10));
660        strcat(path, ".bin");
661        fprintf(stderr, "DEBUG: storing file to %s\n", path);
662
663        FILE* out_fp = fopen(path, "wb");
664
665        size_t bytes_wrote = fwrite(buf, 1, len, out_fp);
666
667        if (bytes_wrote != len)
668                fprintf(stderr, "Error dumping file\n");
669
670        fclose(out_fp);
671
672}
673#endif
674
675
676/* Workaround for linearized documents */
677
678Ref PDFDoc::getPageRef(int page)
679{
680
681  Ref pageRef;
682
683  pageRef.num = getHints()->getPageObjectNum(page);
684  if (!pageRef.num) {
685    error(errSyntaxWarning, -1, "Failed to get object num from hint tables for page 1");
686  }
687
688  // check for bogus ref - this can happen in corrupted PDF files
689  if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) {
690    error(errSyntaxWarning, -1, "Invalid object num ({0:d}) for page 1", pageRef.num);
691  }
692
693  pageRef.gen = xref->getEntry(pageRef.num)->gen;
694
695  return pageRef;
696
697}
698
699
700void PDFDoc::prepareSignature(bool incremental_mode, PDFRectangle *rect,
701                const char * name, const char *civil_number, const char *location, const char *reason, int page, int sector,
702                bool isPTLanguage)
703{
704        const char needle[] = "/Type /Sig";
705        // Turn Signature mode On
706        // This class-level flag affects Trailer /ID generation
707        signature_mode = gTrue;
708        long found = 0;
709        char *base_search = NULL;
710
711        getCatalog()->setIncrementalSignature(incremental_mode);
712
713        if (isLinearized())
714        {
715           Ref first_page = getPageRef(page);
716           getCatalog()->prepareSignature(rect, name, &first_page, location,
717                   civil_number, reason, this->fileSize, page, sector, m_image_data_jpeg, m_image_length, isPTLanguage);
718        }
719        else
720           getCatalog()->prepareSignature(rect, name, NULL, location,
721           civil_number, reason, this->fileSize, page, sector, m_image_data_jpeg, m_image_length, isPTLanguage);
722       
723        //Add enough space for the placeholder string
724        MemOutStream mem_stream(this->fileSize + 20000); 
725        OutStream * str = &mem_stream;
726
727        if(incremental_mode)
728        {
729                //We're adding additional signature so it has to be an incremental update
730                saveIncrementalUpdate(str);
731        }
732        else
733        {
734                saveAs(str, writeForceRewrite);
735        }
736
737        long haystack = (long)mem_stream.getData();
738
739        //Start searching after the end of current file to skip previous
740        //Sig fields
741        if (incremental_mode)
742                base_search = (char *)mem_stream.getData()+this->fileSize;
743        else
744                base_search = (char *)mem_stream.getData();
745
746        found = (long)memmem(base_search, mem_stream.size(),
747                                (const void *) needle, sizeof(needle)-1);
748
749        m_sig_offset = found - haystack + 21;
750        if (found == 0)
751  {
752                error(errInternal, -1, "prepareSignature: can't find signature offset. Aborting signature!");
753    return;
754  }
755       
756        getCatalog()->setSignatureByteRange(m_sig_offset, ESTIMATED_LEN, mem_stream.size());
757
758}
759
760/* Allocates and fills a byte array with the PDF content that will be signed
761 * i.e. everything except the placeholder hex string <0000...>
762   The return value is the size of the array
763*/
764unsigned long PDFDoc::getSigByteArray(unsigned char **byte_array, bool incremental_mode)
765{
766        MemOutStream mem_stream(this->fileSize+ESTIMATED_LEN +190000);
767        unsigned int i = 0, ret_len = 0;
768        OutStream * out_str = &mem_stream;
769
770        if (incremental_mode)
771            saveIncrementalUpdate(out_str);
772        else 
773            saveAs(out_str, writeForceRewrite);
774       
775        char * base_ptr = (char *)mem_stream.getData();
776
777        ret_len = mem_stream.size() - ESTIMATED_LEN - 2; 
778        *byte_array = (unsigned char *)gmalloc(ret_len);
779
780        memcpy((*byte_array)+i, base_ptr, m_sig_offset);
781        i+= m_sig_offset;
782
783        int len2 = mem_stream.size() - m_sig_offset - ESTIMATED_LEN -2;
784        memcpy((*byte_array)+i, base_ptr + m_sig_offset+
785                        ESTIMATED_LEN + 2, len2);
786
787        return ret_len;
788}
789
790void PDFDoc::closeSignature(const char *signature_contents)
791{
792        getCatalog()->closeSignature(signature_contents, ESTIMATED_LEN);
793}
794
795unsigned int PDFDoc::getSignedVersionLen()
796{
797        return preparedSigLength;
798}
799
800
801Links *PDFDoc::getLinks(int page) {
802  Page *p = getPage(page);
803  if (!p) {
804    return new Links (NULL);
805  }
806  return p->getLinks();
807}
808
809Linearization *PDFDoc::getLinearization()
810{
811  if (!linearization) {
812    linearization = new Linearization(str);
813  }
814  return linearization;
815}
816
817GBool PDFDoc::isLinearized() {
818  if ((str->getLength()) &&
819      (getLinearization()->getLength() == str->getLength()))
820    return gTrue;
821  else
822    return gFalse;
823}
824
825static GBool
826get_id (GooString *encodedidstring, GooString *id) {
827  const char *encodedid = encodedidstring->getCString();
828  char pdfid[pdfIdLength + 1];
829  int n;
830
831  if (encodedidstring->getLength() != pdfIdLength / 2)
832    return gFalse;
833
834  n = sprintf(pdfid, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
835              encodedid[0] & 0xff, encodedid[1] & 0xff, encodedid[2] & 0xff, encodedid[3] & 0xff,
836              encodedid[4] & 0xff, encodedid[5] & 0xff, encodedid[6] & 0xff, encodedid[7] & 0xff,
837              encodedid[8] & 0xff, encodedid[9] & 0xff, encodedid[10] & 0xff, encodedid[11] & 0xff,
838              encodedid[12] & 0xff, encodedid[13] & 0xff, encodedid[14] & 0xff, encodedid[15] & 0xff);
839  if (n != pdfIdLength)
840    return gFalse;
841
842  id->Set(pdfid, pdfIdLength);
843  return gTrue;
844}
845
846GBool PDFDoc::getID(GooString *permanent_id, GooString *update_id) {
847  Object obj;
848  xref->getTrailerDict()->dictLookup ("ID", &obj);
849
850  if (obj.isArray() && obj.arrayGetLength() == 2) {
851    Object obj2;
852
853    if (permanent_id) {
854      if (obj.arrayGet(0, &obj2)->isString()) {
855        if (!get_id (obj2.getString(), permanent_id)) {
856          obj2.free();
857          return gFalse;
858        }
859      } else {
860        error(errSyntaxError, -1, "Invalid permanent ID");
861        obj2.free();
862        return gFalse;
863      }
864      obj2.free();
865    }
866
867    if (update_id) {
868      if (obj.arrayGet(1, &obj2)->isString()) {
869        if (!get_id (obj2.getString(), update_id)) {
870          obj2.free();
871          return gFalse;
872        }
873      } else {
874        error(errSyntaxError, -1, "Invalid update ID");
875        obj2.free();
876        return gFalse;
877      }
878      obj2.free();
879    }
880
881    obj.free();
882
883    return gTrue;
884  }
885  obj.free();
886
887  return gFalse;
888}
889
890Hints *PDFDoc::getHints()
891{
892  if (!hints && isLinearized()) {
893    hints = new Hints(str, getLinearization(), getXRef(), secHdlr);
894  }
895
896  return hints;
897}
898
899int PDFDoc::savePageAs(GooString *name, int pageNo) 
900{
901  FILE *f;
902  OutStream *outStr;
903  XRef *yRef, *countRef;
904  int rootNum = getXRef()->getNumObjects() + 1;
905
906  if (pageNo < 1 || pageNo > getNumPages()) {
907    error(errInternal, -1, "Illegal pageNo: {0:d}({1:d})", pageNo, getNumPages() );
908    return errOpenFile;
909  }
910  PDFRectangle *cropBox = NULL;
911  if (getCatalog()->getPage(pageNo)->isCropped()) {
912    cropBox = getCatalog()->getPage(pageNo)->getCropBox();
913  }
914  replacePageDict(pageNo, 
915    getCatalog()->getPage(pageNo)->getRotate(),
916    getCatalog()->getPage(pageNo)->getMediaBox(),
917    cropBox, NULL);
918  Ref *refPage = getCatalog()->getPageRef(pageNo);
919  Object page;
920  getXRef()->fetch(refPage->num, refPage->gen, &page);
921
922  if (!(f = fopen(name->getCString(), "wb"))) {
923    error(errIO, -1, "Couldn't open file '{0:t}'", name);
924    return errOpenFile;
925  }
926  outStr = new FileOutStream(f,0);
927
928  yRef = new XRef(getXRef()->getTrailerDict());
929  countRef = new XRef();
930  yRef->add(0, 65535, 0, gFalse);
931  writeHeader(outStr, getPDFMajorVersion(), getPDFMinorVersion());
932
933  // get and mark info dict
934  Object infoObj;
935  getXRef()->getDocInfo(&infoObj);
936  if (infoObj.isDict()) {
937    Dict *infoDict = infoObj.getDict();
938    markPageObjects(infoDict, yRef, countRef, 0);
939    Object *trailerObj = getXRef()->getTrailerDict();
940    if (trailerObj->isDict()) {
941      Dict *trailerDict = trailerObj->getDict();
942      Object ref;
943      trailerDict->lookupNF("Info", &ref);
944      if (ref.isRef()) {
945        yRef->add(ref.getRef().num, ref.getRef().gen, 0, gTrue);
946        if (getXRef()->getEntry(ref.getRef().num)->type == xrefEntryCompressed) {
947          yRef->getEntry(ref.getRef().num)->type = xrefEntryCompressed;
948        }
949      }
950      ref.free();
951    }
952  }
953  infoObj.free();
954 
955  // get and mark output intents etc.
956  Object catObj;
957  getXRef()->getCatalog(&catObj);
958  Dict *catDict = catObj.getDict();
959  markPageObjects(catDict, yRef, countRef, 0);
960
961  Dict *pageDict = page.getDict();
962  markPageObjects(pageDict, yRef, countRef, 0);
963  Guint objectsCount = writePageObjects(outStr, yRef, 0);
964
965  yRef->add(rootNum,0,outStr->getPos(),gTrue);
966  outStr->printf("%d 0 obj\n", rootNum);
967  outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); 
968  for (int j = 0; j < catDict->getLength(); j++) {
969    const char *key = catDict->getKey(j);
970    if (strcmp(key, "Type") != 0 &&
971      strcmp(key, "Catalog") != 0 &&
972      strcmp(key, "Pages") != 0) 
973    {
974      if (j > 0) outStr->printf(" ");
975      Object value; catDict->getValNF(j, &value);
976      outStr->printf("/%s ", key);
977      writeObject(&value, NULL, outStr, getXRef(), 0);
978      value.free();
979    }
980  }
981  catObj.free();
982  outStr->printf(">>\nendobj\n");
983  objectsCount++;
984
985  yRef->add(rootNum + 1,0,outStr->getPos(),gTrue);
986  outStr->printf("%d 0 obj\n", rootNum + 1);
987  outStr->printf("<< /Type /Pages /Kids [ %d 0 R ] /Count 1 >>\n", rootNum + 2);
988  outStr->printf("endobj\n");
989  objectsCount++;
990
991  yRef->add(rootNum + 2,0,outStr->getPos(),gTrue);
992  outStr->printf("%d 0 obj\n", rootNum + 2);
993  outStr->printf("<< ");
994  for (int n = 0; n < pageDict->getLength(); n++) {
995    if (n > 0) outStr->printf(" ");
996    const char *key = pageDict->getKey(n);
997    Object value; pageDict->getValNF(n, &value);
998    if (strcmp(key, "Parent") == 0) {
999      outStr->printf("/Parent %d 0 R", rootNum + 1);
1000    } else {
1001      outStr->printf("/%s ", key);
1002      writeObject(&value, NULL, outStr, getXRef(), 0); 
1003    }
1004    value.free();
1005  }
1006  outStr->printf(" >>\nendobj\n");
1007  objectsCount++;
1008  page.free();
1009
1010  Guint uxrefOffset = outStr->getPos();
1011  Ref ref;
1012  ref.num = rootNum;
1013  ref.gen = 0;
1014  Dict *trailerDict = createTrailerDict(objectsCount, gFalse, 0, &ref, getXRef(),
1015                                        name->getCString(), uxrefOffset);
1016  writeXRefTableTrailer(trailerDict, yRef, gFalse /* do not write unnecessary entries */,
1017                        uxrefOffset, outStr, getXRef());
1018  delete trailerDict;
1019
1020  outStr->close();
1021  fclose(f);
1022  delete yRef;
1023  delete countRef;
1024
1025  return errNone;
1026}
1027
1028int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) {
1029  FILE *f;
1030  OutStream *outStr;
1031  int res;
1032
1033  if (!(f = fopen(name->getCString(), "wb"))) {
1034    error(errIO, -1, "Couldn't open file '{0:t}'", name);
1035    return errOpenFile;
1036  }
1037  outStr = new FileOutStream(f,0);
1038  res = saveAs(outStr, mode);
1039  delete outStr;
1040  fclose(f);
1041  return res;
1042}
1043
1044int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) {
1045
1046  // find if we have updated objects
1047  GBool updated = gFalse;
1048  for(int i=0; i<xref->getNumObjects(); i++) {
1049    if (xref->getEntry(i)->updated) {
1050      updated = gTrue;
1051      break;
1052    }
1053  }
1054
1055  // we don't support rewriting files with Encrypt at the moment
1056  Object obj;
1057  xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj);
1058  if (!obj.isNull())
1059  {
1060    obj.free();
1061    if (!updated && mode == writeStandard) {
1062      // simply copy the original file
1063      saveWithoutChangesAs (outStr);
1064    } else {
1065      return errEncrypted;
1066    }
1067  }
1068  else
1069  {
1070    obj.free();
1071
1072    if (mode == writeForceRewrite) {
1073      saveCompleteRewrite(outStr);
1074    } else if (mode == writeForceIncremental) {
1075      saveIncrementalUpdate(outStr); 
1076    } else { // let poppler decide
1077      if(updated) { 
1078        saveIncrementalUpdate(outStr);
1079      } else {
1080        // simply copy the original file
1081        saveWithoutChangesAs (outStr);
1082      }
1083    }
1084  }
1085
1086  return errNone;
1087}
1088
1089int PDFDoc::saveWithoutChangesAs(GooString *name) {
1090  FILE *f;
1091  OutStream *outStr;
1092  int res;
1093
1094  if (!(f = fopen(name->getCString(), "wb"))) {
1095    error(errIO, -1, "Couldn't open file '{0:t}'", name);
1096    return errOpenFile;
1097  }
1098 
1099  outStr = new FileOutStream(f,0);
1100  res = saveWithoutChangesAs(outStr);
1101  delete outStr;
1102
1103  fclose(f);
1104
1105  return res;
1106}
1107
1108int PDFDoc::saveWithoutChangesAs(OutStream *outStr) {
1109  int c;
1110 
1111  str->reset();
1112  while ((c = str->getChar()) != EOF) {
1113    outStr->put(c);
1114  }
1115  str->close();
1116
1117  return errNone;
1118}
1119
1120void PDFDoc::saveIncrementalUpdate (OutStream* outStr)
1121{
1122  XRef *uxref;
1123  int c;
1124  //copy the original file
1125  str->reset();
1126  while ((c = str->getChar()) != EOF) {
1127    outStr->put(c);
1128  }
1129  str->close();
1130
1131  uxref = new XRef();
1132  uxref->add(0, 65535, 0, gFalse);
1133  for(int i=0; i<xref->getNumObjects(); i++) {
1134    if ((xref->getEntry(i)->type == xrefEntryFree) && 
1135        (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects
1136      continue;
1137
1138    if (xref->getEntry(i)->updated) { //we have an updated object
1139      Ref ref;
1140      ref.num = i;
1141      ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen;
1142      if (xref->getEntry(i)->type != xrefEntryFree) {
1143        Object obj1;
1144        xref->fetch(ref.num, ref.gen, &obj1);
1145        Guint offset = writeObject(&obj1, &ref, outStr);
1146        uxref->add(ref.num, ref.gen, offset, gTrue);
1147        obj1.free();
1148      } else {
1149        uxref->add(ref.num, ref.gen, 0, gFalse);
1150      }
1151    }
1152  }
1153  if (uxref->getNumObjects() == 0) { //we have nothing to update
1154    delete uxref;
1155    return;
1156  }
1157
1158  Guint uxrefOffset = outStr->getPos();
1159  int numobjects = xref->getNumObjects();
1160  const char *fileNameA = fileName ? fileName->getCString() : NULL;
1161  Ref rootRef, uxrefStreamRef;
1162  rootRef.num = getXRef()->getRootNum();
1163  rootRef.gen = getXRef()->getRootGen();
1164
1165  // Output a xref stream if there is a xref stream already
1166  GBool xRefStream = xref->isXRefStream();
1167
1168  if (xRefStream) {
1169    // Append an entry for the xref stream itself
1170    uxrefStreamRef.num = numobjects++;
1171    uxrefStreamRef.gen = 0;
1172    uxref->add(uxrefStreamRef.num, uxrefStreamRef.gen, uxrefOffset, gTrue);
1173  }
1174
1175  Dict *trailerDict = createTrailerDict(numobjects, gTrue, getStartXRef(), &rootRef, getXRef(),
1176                  fileNameA, uxrefOffset, signature_mode);
1177  if (xRefStream) {
1178    writeXRefStreamTrailer(trailerDict, uxref, &uxrefStreamRef, uxrefOffset, outStr, getXRef());
1179  } else {
1180    writeXRefTableTrailer(trailerDict, uxref, gFalse, uxrefOffset, outStr, getXRef());
1181  }
1182
1183  delete trailerDict;
1184  delete uxref;
1185}
1186
1187void PDFDoc::saveCompleteRewrite (OutStream* outStr)
1188{
1189  outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion);
1190  XRef *uxref = new XRef();
1191  uxref->add(0, 65535, 0, gFalse);
1192  for(int i=0; i<xref->getNumObjects(); i++) {
1193    Object obj1;
1194    Ref ref;
1195    XRefEntryType type = xref->getEntry(i)->type;
1196    if (type == xrefEntryFree) {
1197      ref.num = i;
1198      ref.gen = xref->getEntry(i)->gen;
1199      /* the XRef class adds a lot of irrelevant free entries, we only want the significant one
1200          and we don't want the one with num=0 because it has already been added (gen = 65535)*/
1201      if (ref.gen > 0 && ref.num > 0)
1202        uxref->add(ref.num, ref.gen, 0, gFalse);
1203    } else if (type == xrefEntryUncompressed){ 
1204      ref.num = i;
1205      ref.gen = xref->getEntry(i)->gen;
1206      xref->fetch(ref.num, ref.gen, &obj1);
1207      Guint offset = writeObject(&obj1, &ref, outStr);
1208      uxref->add(ref.num, ref.gen, offset, gTrue);
1209      obj1.free();
1210    } else if (type == xrefEntryCompressed) {
1211      ref.num = i;
1212      ref.gen = 0; //compressed entries have gen == 0
1213      xref->fetch(ref.num, ref.gen, &obj1);
1214      Guint offset = writeObject(&obj1, &ref, outStr);
1215      uxref->add(ref.num, ref.gen, offset, gTrue);
1216      obj1.free();
1217    }
1218  }
1219  Guint uxrefOffset = outStr->getPos();
1220  writeXRefTableTrailer(uxrefOffset, uxref, gTrue /* write all entries */,
1221                        uxref->getNumObjects(), outStr, gFalse /* complete rewrite */);
1222  delete uxref;
1223}
1224
1225void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset)
1226{
1227  Object obj1;
1228  outStr->printf("<<");
1229  for (int i=0; i<dict->getLength(); i++) {
1230    GooString keyName(dict->getKey(i));
1231    GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */);
1232    outStr->printf("/%s ", keyNameToPrint->getCString());
1233    delete keyNameToPrint;
1234    writeObject(dict->getValNF(i, &obj1), NULL, outStr, xRef, numOffset);
1235    obj1.free();
1236  }
1237  if(dict->isSignatureDict())
1238   {
1239        SignatureDict *sig = dynamic_cast<SignatureDict *>(dict);
1240        char padding[100] = { 0 };
1241        memset(padding, ' ', sig->getPadding());
1242        outStr->printf("%s", padding);
1243   }
1244  outStr->printf(">> ");
1245}
1246
1247void PDFDoc::writeStream (Stream* str, OutStream* outStr)
1248{
1249  outStr->printf("stream\r\n");
1250  str->reset();
1251  for (int c=str->getChar(); c!= EOF; c=str->getChar()) {
1252    outStr->printf("%c", c); 
1253  }
1254  outStr->printf("\r\nendstream\r\n");
1255}
1256
1257void PDFDoc::writeRawStream (Stream* str, OutStream* outStr)
1258{
1259  Object obj1;
1260  str->getDict()->lookup("Length", &obj1);
1261  if (!obj1.isInt()) {
1262    error (errSyntaxError, -1, "PDFDoc::writeRawStream, no Length in stream dict");
1263    return;
1264  }
1265
1266  const int length = obj1.getInt();
1267  obj1.free();
1268
1269  outStr->printf("stream\r\n");
1270  str->unfilteredReset();
1271  for (int i=0; i<length; i++) {
1272    int c = str->getUnfilteredChar();
1273    outStr->printf("%c", c); 
1274  }
1275  str->reset();
1276  outStr->printf("\r\nendstream\r\n");
1277}
1278
1279void PDFDoc::writeString (GooString* s, OutStream* outStr)
1280{
1281
1282        //Support for PDF hexstrings
1283        const char * begin_char = s->isHexString() ? "<" : "(";
1284        const char * end_char = s->isHexString() ? "> " : ") ";
1285        if (s->hasUnicodeMarker()) {
1286    //unicode string don't necessary end with \0
1287    const char* c = s->getCString();
1288    outStr->printf(begin_char);
1289    for(int i=0; i<s->getLength(); i++) {
1290      char unescaped = *(c+i)&0x000000ff;
1291      //escape if needed
1292      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
1293        outStr->printf("%c", '\\');
1294      outStr->printf("%c", unescaped);
1295    }
1296    outStr->printf(end_char);
1297  } else {
1298    const char* c = s->getCString();
1299    outStr->printf(begin_char);
1300    for(int i=0; i<s->getLength(); i++) {
1301      char unescaped = *(c+i)&0x000000ff;
1302      //escape if needed
1303      if (unescaped == '\r')
1304        outStr->printf("\\r");
1305      else if (unescaped == '\n')
1306        outStr->printf("\\n");
1307      else {
1308        if (unescaped == '(' || unescaped == ')' || unescaped == '\\') {
1309          outStr->printf("%c", '\\');
1310        }
1311        outStr->printf("%c", unescaped);
1312      }
1313    }
1314    outStr->printf(end_char);
1315  }
1316}
1317
1318Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr, XRef *xRef, Guint numOffset)
1319{
1320  Array *array;
1321  Object obj1;
1322  Guint offset = outStr->getPos();
1323  int tmp;
1324
1325  if(ref) 
1326    outStr->printf("%i %i obj ", ref->num, ref->gen);
1327
1328  switch (obj->getType()) {
1329    case objBool:
1330      outStr->printf("%s ", obj->getBool()?"true":"false");
1331      break;
1332    case objInt:
1333      outStr->printf("%i ", obj->getInt());
1334      break;
1335    case objReal:
1336    {
1337      GooString s;
1338      s.appendf("{0:.10g}", obj->getReal());
1339      outStr->printf("%s ", s.getCString());
1340      break;
1341    }
1342    case objString:
1343      writeString(obj->getString(), outStr);
1344      break;
1345    case objName:
1346    {
1347      GooString name(obj->getName());
1348      GooString *nameToPrint = name.sanitizedName(gFalse /* non ps mode */);
1349      outStr->printf("/%s ", nameToPrint->getCString());
1350      delete nameToPrint;
1351      break;
1352    }
1353    case objNull:
1354      outStr->printf( "null ");
1355      break;
1356    case objArray:
1357      array = obj->getArray();
1358      outStr->printf("[");
1359      for (int i=0; i<array->getLength(); i++) {
1360        writeObject(array->getNF(i, &obj1), NULL,outStr, xRef, numOffset);
1361        obj1.free();
1362      }
1363      outStr->printf("] ");
1364      break;
1365    case objDict:
1366      writeDictionnary (obj->getDict(),outStr, xRef, numOffset);
1367      break;
1368    case objStream: 
1369      {
1370        //We can't modify stream with the current implementation (no write functions in Stream API)
1371        // => the only type of streams which that have been modified are internal streams (=strWeird)
1372        Stream *stream = obj->getStream();
1373        if (stream->getKind() == strWeird) {
1374          //we write the stream unencoded => TODO: write stream encoder
1375          stream->reset();
1376          //recalculate stream length
1377          tmp = 0;
1378          for (int c=stream->getChar(); c!=EOF; c=stream->getChar()) {
1379            tmp++;
1380          }
1381          obj1.initInt(tmp);
1382          stream->getDict()->set("Length", &obj1);
1383
1384          //Remove Stream encoding
1385          //We need to comment it out because we're creating Streams of type strWeird
1386          //which are actually FlateEncoded, poppler stream classes are not really
1387          //suited for this, so its a hack ...
1388          //stream->getDict()->remove("Filter");
1389          //stream->getDict()->remove("DecodeParms");
1390
1391          writeDictionnary (stream->getDict(),outStr, xRef, numOffset);
1392          writeStream (stream,outStr);
1393          obj1.free();
1394        }
1395        /*
1396        else if (stream->getKind() == strDeflate) {
1397
1398                Object obj_stream_dict;
1399                obj_stream_dict.initDict(xRef);
1400                //TODO complete with ObjStm attributes
1401                //obj_stream.dictAdd("", );
1402                //TODO
1403                DeflateStream *def = dynamic_cast<DeflateStream*>(stream);
1404
1405          MemOutStream *out_mem_str = new MemOutStream(256);
1406          writeDictionnary(stream->getDict(), out_mem_str, xRef, numOffset);
1407
1408          def->setStream(out_mem_str);
1409          writeStream (stream,outStr);
1410
1411        } */
1412        else {
1413          //raw stream copy
1414          FilterStream *fs = dynamic_cast<FilterStream*>(stream);
1415          if (fs) {
1416            BaseStream *bs = fs->getBaseStream();
1417            if (bs) {
1418              Guint streamEnd;
1419                if (xRef->getStreamEnd(bs->getStart(), &streamEnd)) {
1420                  Object val;
1421                  val.initInt(streamEnd - bs->getStart());
1422                  stream->getDict()->set("Length", &val);
1423                }
1424              }
1425          }
1426          writeDictionnary (stream->getDict(), outStr, xRef, numOffset);
1427          writeRawStream (stream, outStr);
1428        }
1429        break;
1430      }
1431    case objRef:
1432      outStr->printf("%i %i R ", obj->getRef().num + numOffset, obj->getRef().gen);
1433      break;
1434    case objCmd:
1435      outStr->printf("%s\n", obj->getCmd());
1436      break;
1437    case objError:
1438      outStr->printf("error\r\n");
1439      break;
1440    case objEOF:
1441      outStr->printf("eof\r\n");
1442      break;
1443    case objNone:
1444      outStr->printf("none\r\n");
1445      break;
1446    default:
1447      error(errUnimplemented, -1,"Unhandled objType : {0:d}, please report a bug with a testcase\r\n", obj->getType());
1448      break;
1449  }
1450  if (ref)
1451    outStr->printf("endobj\r\n");
1452  return offset;
1453}
1454
1455Dict *PDFDoc::createTrailerDict(int uxrefSize, GBool incrUpdate, Guint startxRef,
1456                                Ref *root, XRef *xRef, const char *fileName, Guint fileSize, GBool is_signature_mode) 
1457{
1458  Dict *trailerDict = new Dict(xRef);
1459  Object obj1;
1460  obj1.initInt(uxrefSize);
1461  trailerDict->set("Size", &obj1);
1462  obj1.free();
1463
1464  //build a new ID, as recommended in the reference, uses:
1465  // - current time
1466  // - file name
1467  // - file size
1468  // - values of entry in information dictionnary
1469  GooString message;
1470  char buffer[256];
1471  sprintf(buffer, "%i", (int)time(NULL));
1472  message.append(buffer);
1473
1474  if (fileName)
1475    message.append(fileName);
1476
1477  sprintf(buffer, "%i", fileSize);
1478  message.append(buffer);
1479
1480  //info dict -- only use text string
1481  if (!xRef->getTrailerDict()->isNone() && xRef->getDocInfo(&obj1)->isDict()) {
1482    for(int i=0; i<obj1.getDict()->getLength(); i++) {
1483      Object obj2;
1484      obj1.getDict()->getVal(i, &obj2); 
1485      if (obj2.isString()) {
1486        message.append(obj2.getString());
1487      }
1488      obj2.free();
1489    }
1490  }
1491  obj1.free();
1492
1493  //calculate md5 digest
1494  Guchar digest[16];
1495  md5((Guchar*)message.getCString(), message.getLength(), digest);
1496  obj1.initString(new GooString((const char*)digest, 16));
1497
1498  //create ID array
1499  Object obj2,obj3,obj5;
1500  obj2.initArray(xRef);
1501
1502  Object obj4;
1503  xRef->getTrailerDict()->getDict()->lookup("ID", &obj4);
1504  if (is_signature_mode)
1505  {
1506          if (!obj4.isArray()) {
1507                  error(errSyntaxWarning, -1, "PDFDoc::createTrailerDict original file's ID entry isn't an array. Trying to continue");
1508          }
1509          else
1510          {
1511
1512                  obj4.arrayGet(0,&obj3); 
1513
1514                  obj2.arrayAdd(&obj3);
1515                  obj2.arrayAdd(&obj3);
1516                  trailerDict->set("ID", &obj4); //Don't change the File ID
1517          }
1518
1519  } else {
1520          //new file => same values for the two identifiers
1521          obj2.arrayAdd(&obj1);
1522          obj1.initString(new GooString((const char*)digest, 16));
1523          obj2.arrayAdd(&obj1);
1524          trailerDict->set("ID", &obj2);
1525  }
1526
1527  //obj4.free();
1528
1529  obj1.initRef(root->num, root->gen);
1530  trailerDict->set("Root", &obj1);
1531
1532  if (incrUpdate) { 
1533    obj1.initInt(startxRef);
1534    trailerDict->set("Prev", &obj1);
1535  }
1536 
1537  if (!xRef->getTrailerDict()->isNone()) {
1538    xRef->getDocInfoNF(&obj5);
1539    if (!obj5.isNull()) {
1540      trailerDict->set("Info", &obj5);
1541    }
1542  }
1543
1544  return trailerDict;
1545}
1546
1547void PDFDoc::writeXRefTableTrailer(Dict *trailerDict, XRef *uxref, GBool writeAllEntries, Guint uxrefOffset, OutStream* outStr, XRef *xRef)
1548{
1549  uxref->writeTableToFile( outStr, writeAllEntries );
1550  outStr->printf( "trailer\r\n");
1551  writeDictionnary(trailerDict, outStr, xRef, 0);
1552  outStr->printf( "\r\nstartxref\r\n");
1553  outStr->printf( "%i\r\n", uxrefOffset);
1554  outStr->printf( "%%%%EOF\r\n");
1555}
1556
1557void PDFDoc::writeXRefStreamTrailer (Dict *trailerDict, XRef *uxref, Ref *uxrefStreamRef, Guint uxrefOffset, OutStream* outStr, XRef *xRef)
1558{
1559  GooString stmData;
1560
1561  // Fill stmData and some trailerDict fields
1562  uxref->writeStreamToBuffer(&stmData, trailerDict, xRef);
1563
1564  // Create XRef stream object and write it
1565  Object obj1;
1566  MemStream *mStream = new MemStream( stmData.getCString(), 0,
1567                                      stmData.getLength(), obj1.initDict(trailerDict) );
1568  writeObject(obj1.initStream(mStream), uxrefStreamRef, outStr, xRef, 0);
1569  obj1.free();
1570
1571  outStr->printf( "startxref\r\n");
1572  outStr->printf( "%i\r\n", uxrefOffset);
1573  outStr->printf( "%%%%EOF\r\n");
1574}
1575
1576void PDFDoc::writeXRefTableTrailer(Guint uxrefOffset, XRef *uxref, GBool writeAllEntries,
1577                                   int uxrefSize, OutStream* outStr, GBool incrUpdate)
1578{
1579  const char *fileNameA = fileName ? fileName->getCString() : NULL;
1580  // file size (doesn't include the trailer)
1581  unsigned int fileSize = 0;
1582  int c;
1583  str->reset();
1584  while ((c = str->getChar()) != EOF) {
1585    fileSize++;
1586  }
1587  str->close();
1588  Ref ref;
1589  ref.num = getXRef()->getRootNum();
1590  ref.gen = getXRef()->getRootGen();
1591  Dict * trailerDict = createTrailerDict(uxrefSize, incrUpdate, getStartXRef(), &ref,
1592                                         getXRef(), fileNameA, fileSize,signature_mode);
1593  writeXRefTableTrailer(trailerDict, uxref, writeAllEntries, uxrefOffset, outStr, getXRef());
1594  delete trailerDict;
1595}
1596
1597void PDFDoc::writeHeader(OutStream *outStr, int major, int minor)
1598{
1599   outStr->printf("%%PDF-%d.%d\n", major, minor);
1600   outStr->printf("%%\xE2\xE3\xCF\xD3\n");
1601}
1602
1603void PDFDoc::markDictionnary (Dict* dict, XRef * xRef, XRef *countRef, Guint numOffset)
1604{
1605  Object obj1;
1606  for (int i=0; i<dict->getLength(); i++) {
1607    markObject(dict->getValNF(i, &obj1), xRef, countRef, numOffset);
1608    obj1.free();
1609  }
1610}
1611
1612void PDFDoc::markObject (Object* obj, XRef *xRef, XRef *countRef, Guint numOffset)
1613{
1614  Array *array;
1615  Object obj1;
1616
1617  switch (obj->getType()) {
1618    case objArray:
1619      array = obj->getArray();
1620      for (int i=0; i<array->getLength(); i++) {
1621        markObject(array->getNF(i, &obj1), xRef, countRef, numOffset);
1622        obj1.free();
1623      }
1624      break;
1625    case objDict:
1626      markDictionnary (obj->getDict(), xRef, countRef, numOffset);
1627      break;
1628    case objStream: 
1629      {
1630        Stream *stream = obj->getStream();
1631        markDictionnary (stream->getDict(), xRef, countRef, numOffset);
1632      }
1633      break;
1634    case objRef:
1635      {
1636        if (obj->getRef().num + (int) numOffset >= xRef->getNumObjects() || xRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree) {
1637          if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryFree) {
1638            return;  // already marked as free => should be replaced
1639          }
1640          xRef->add(obj->getRef().num + numOffset, obj->getRef().gen, 0, gTrue);
1641          if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryCompressed) {
1642            xRef->getEntry(obj->getRef().num + numOffset)->type = xrefEntryCompressed;
1643          }
1644        }
1645        if (obj->getRef().num + (int) numOffset >= countRef->getNumObjects() || 
1646            countRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree)
1647        {
1648          countRef->add(obj->getRef().num + numOffset, 1, 0, gTrue);
1649        } else {
1650          XRefEntry *entry = countRef->getEntry(obj->getRef().num + numOffset);
1651          entry->gen++;
1652          if (entry->gen > 9)
1653            break;
1654        } 
1655        Object obj1;
1656        getXRef()->fetch(obj->getRef().num, obj->getRef().gen, &obj1);
1657        markObject(&obj1, xRef, countRef, numOffset);
1658        obj1.free();
1659      }
1660      break;
1661    default:
1662      break;
1663  }
1664}
1665
1666void PDFDoc::replacePageDict(int pageNo, int rotate,
1667                             PDFRectangle *mediaBox, 
1668                             PDFRectangle *cropBox, Object *pageCTM)
1669{
1670  Ref *refPage = getCatalog()->getPageRef(pageNo);
1671  Object page;
1672  getXRef()->fetch(refPage->num, refPage->gen, &page);
1673  Dict *pageDict = page.getDict();
1674  pageDict->remove("MediaBox");
1675  pageDict->remove("CropBox");
1676  pageDict->remove("ArtBox");
1677  pageDict->remove("BleedBox");
1678  pageDict->remove("TrimBox");
1679  pageDict->remove("Rotate");
1680  Object mediaBoxObj;
1681  mediaBoxObj.initArray(getXRef());
1682  Object murx;
1683  murx.initReal(mediaBox->x1);
1684  Object mury;
1685  mury.initReal(mediaBox->y1);
1686  Object mllx;
1687  mllx.initReal(mediaBox->x2);
1688  Object mlly;
1689  mlly.initReal(mediaBox->y2);
1690  mediaBoxObj.arrayAdd(&murx);
1691  mediaBoxObj.arrayAdd(&mury);
1692  mediaBoxObj.arrayAdd(&mllx);
1693  mediaBoxObj.arrayAdd(&mlly);
1694  pageDict->add(copyString("MediaBox"), &mediaBoxObj);
1695
1696  if (cropBox != NULL) {
1697    Object cropBoxObj;
1698    cropBoxObj.initArray(getXRef());
1699    Object curx;
1700    curx.initReal(cropBox->x1);
1701    Object cury;
1702    cury.initReal(cropBox->y1);
1703    Object cllx;
1704    cllx.initReal(cropBox->x2);
1705    Object clly;
1706    clly.initReal(cropBox->y2);
1707    cropBoxObj.arrayAdd(&curx);
1708    cropBoxObj.arrayAdd(&cury);
1709    cropBoxObj.arrayAdd(&cllx);
1710    cropBoxObj.arrayAdd(&clly);
1711    pageDict->add(copyString("CropBox"), &cropBoxObj);
1712    cropBoxObj.getArray()->incRef();
1713    pageDict->add(copyString("TrimBox"), &cropBoxObj);
1714
1715  } else {
1716    mediaBoxObj.getArray()->incRef();
1717    pageDict->add(copyString("TrimBox"), &mediaBoxObj);
1718  }
1719
1720  Object rotateObj;
1721  rotateObj.initInt(rotate);
1722  pageDict->add(copyString("Rotate"), &rotateObj);
1723 
1724  getXRef()->setModifiedObject(&page, *refPage);
1725  page.free();
1726}
1727
1728void PDFDoc::markPageObjects(Dict *pageDict, XRef *xRef, XRef *countRef, Guint numOffset) 
1729{
1730  pageDict->remove("Names");
1731  pageDict->remove("OpenAction");
1732  pageDict->remove("Outlines");
1733  pageDict->remove("StructTreeRoot");
1734
1735  for (int n = 0; n < pageDict->getLength(); n++) {
1736    const char *key = pageDict->getKey(n);
1737    Object value; pageDict->getValNF(n, &value);
1738    if (strcmp(key, "Parent") != 0 &&
1739              strcmp(key, "Pages") != 0) {
1740      markObject(&value, xRef, countRef, numOffset);
1741    }
1742    value.free();
1743  }
1744}
1745
1746Guint PDFDoc::writePageObjects(OutStream *outStr, XRef *xRef, Guint numOffset) 
1747{
1748  Guint objectsCount = 0; //count the number of objects in the XRef(s)
1749
1750  for (int n = numOffset; n < xRef->getNumObjects(); n++) {
1751    if (xRef->getEntry(n)->type != xrefEntryFree) {
1752      Object obj;
1753      Ref ref;
1754      ref.num = n;
1755      ref.gen = xRef->getEntry(n)->gen;
1756      objectsCount++;
1757      getXRef()->fetch(ref.num - numOffset, ref.gen, &obj);
1758      Guint offset = writeObject(&obj, &ref, outStr, xRef, numOffset);
1759      xRef->add(ref.num, ref.gen, offset, gTrue);
1760      obj.free();
1761    }
1762  }
1763  return objectsCount;
1764}
1765
1766#ifndef DISABLE_OUTLINE
1767Outline *PDFDoc::getOutline()
1768{
1769  if (!outline) {
1770    // read outline
1771    outline = new Outline(catalog->getOutline(), xref);
1772  }
1773
1774  return outline;
1775}
1776#endif
1777
1778PDFDoc *PDFDoc::ErrorPDFDoc(int errorCode, GooString *fileNameA)
1779{
1780  PDFDoc *doc = new PDFDoc();
1781  doc->errCode = errorCode;
1782  doc->fileName = fileNameA;
1783
1784  return doc;
1785}
1786
1787Guint PDFDoc::strToUnsigned(char *s) {
1788  Guint x, d;
1789  char *p;
1790
1791  x = 0;
1792  for (p = s; *p && isdigit(*p & 0xff); ++p) {
1793    d = *p - '0';
1794    if (x > (UINT_MAX - d) / 10) {
1795      break;
1796    }
1797    x = 10 * x + d;
1798  }
1799  return x;
1800}
1801
1802// Read the 'startxref' position.
1803Guint PDFDoc::getStartXRef()
1804{
1805  if (startXRefPos == ~(Guint)0) {
1806
1807    if (isLinearized()) {
1808      char buf[linearizationSearchSize+1];
1809      int c, n, i;
1810
1811      str->setPos(0);
1812      for (n = 0; n < linearizationSearchSize; ++n) {
1813        if ((c = str->getChar()) == EOF) {
1814          break;
1815        }
1816        buf[n] = c;
1817      }
1818      buf[n] = '\0';
1819
1820      // find end of first obj (linearization dictionary)
1821      startXRefPos = 0;
1822      for (i = 0; i < n; i++) {
1823        if (!strncmp("endobj", &buf[i], 6)) {
1824          i += 6;
1825          //skip whitespace
1826          while (buf[i] && Lexer::isSpace(buf[i])) ++i;
1827          startXRefPos = i;
1828          break;
1829        }
1830      }
1831    } else {
1832      char buf[xrefSearchSize+1];
1833      char *p;
1834      int c, n, i;
1835
1836      // read last xrefSearchSize bytes
1837      str->setPos(xrefSearchSize, -1);
1838      for (n = 0; n < xrefSearchSize; ++n) {
1839        if ((c = str->getChar()) == EOF) {
1840          break;
1841        }
1842        buf[n] = c;
1843      }
1844      buf[n] = '\0';
1845
1846      // find startxref
1847      for (i = n - 9; i >= 0; --i) {
1848        if (!strncmp(&buf[i], "startxref", 9)) {
1849          break;
1850        }
1851      }
1852      if (i < 0) {
1853        startXRefPos = 0;
1854      } else {
1855        for (p = &buf[i+9]; isspace(*p); ++p) ;
1856        startXRefPos =  strToUnsigned(p);
1857      }
1858    }
1859
1860  }
1861
1862  return startXRefPos;
1863}
1864
1865Guint PDFDoc::getMainXRefEntriesOffset()
1866{
1867  Guint mainXRefEntriesOffset = 0;
1868
1869  if (isLinearized()) {
1870    mainXRefEntriesOffset = getLinearization()->getMainXRefEntriesOffset();
1871  }
1872
1873  return mainXRefEntriesOffset;
1874}
1875
1876int PDFDoc::getNumPages()
1877{
1878  if (isLinearized()) {
1879    int n;
1880    if ((n = getLinearization()->getNumPages())) {
1881      return n;
1882    }
1883  }
1884
1885  return catalog->getNumPages();
1886}
1887
1888Page *PDFDoc::parsePage(int page)
1889{
1890  Page *p = NULL;
1891  Object obj;
1892  Ref pageRef;
1893  Dict *pageDict;
1894
1895  pageRef.num = getHints()->getPageObjectNum(page);
1896  if (!pageRef.num) {
1897    error(errSyntaxWarning, -1, "Failed to get object num from hint tables for page {0:d}", page);
1898    return NULL;
1899  }
1900
1901  // check for bogus ref - this can happen in corrupted PDF files
1902  if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) {
1903    error(errSyntaxWarning, -1, "Invalid object num ({0:d}) for page {1:d}", pageRef.num, page);
1904    return NULL;
1905  }
1906
1907  pageRef.gen = xref->getEntry(pageRef.num)->gen;
1908  xref->fetch(pageRef.num, pageRef.gen, &obj);
1909  if (!obj.isDict("Page")) {
1910    obj.free();
1911    error(errSyntaxWarning, -1, "Object ({0:d} {1:d}) is not a pageDict", pageRef.num, pageRef.gen);
1912    return NULL;
1913  }
1914  pageDict = obj.getDict();
1915
1916  p = new Page(this, page, pageDict, pageRef,
1917               new PageAttrs(NULL, pageDict), catalog->getForm());
1918  obj.free();
1919
1920  return p;
1921}
1922
1923Page *PDFDoc::getPage(int page)
1924{
1925  if ((page < 1) || page > getNumPages()) return NULL;
1926 
1927  if (isLinearized()) {
1928    if (!pageCache) {
1929      pageCache = (Page **) gmallocn(getNumPages(), sizeof(Page *));
1930      for (int i = 0; i < getNumPages(); i++) {
1931        pageCache[i] = NULL;
1932      }
1933    }
1934    if (!pageCache[page-1]) {
1935      pageCache[page-1] = parsePage(page);
1936    }
1937    if (pageCache[page-1]) {
1938       return pageCache[page-1];
1939    } else {
1940       error(errSyntaxWarning, -1, "Failed parsing page {0:d} using hint tables", page);
1941    }
1942  }
1943 
1944
1945  return catalog->getPage(page);
1946}
Note: See TracBrowser for help on using the browser.