~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tests/poppler/poppler/PDFDoc.cc

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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, 2010 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
// 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
//
 
30
// To see a description of the changes please see the Changelog file that
 
31
// came with your tarball or type make ChangeLog if you are building from git
 
32
//
 
33
//========================================================================
 
34
 
 
35
#include <config.h>
 
36
 
 
37
#ifdef USE_GCC_PRAGMAS
 
38
#pragma implementation
 
39
#endif
 
40
 
 
41
#include <ctype.h>
 
42
#include <locale.h>
 
43
#include <stdio.h>
 
44
#include <errno.h>
 
45
#include <stdlib.h>
 
46
#include <stddef.h>
 
47
#include <string.h>
 
48
#include <time.h>
 
49
#ifdef _WIN32
 
50
#  include <windows.h>
 
51
#endif
 
52
#include <sys/stat.h>
 
53
#include "goo/gstrtod.h"
 
54
#include "goo/GooString.h"
 
55
#include "poppler-config.h"
 
56
#include "GlobalParams.h"
 
57
#include "Page.h"
 
58
#include "Catalog.h"
 
59
#include "Stream.h"
 
60
#include "XRef.h"
 
61
#include "Linearization.h"
 
62
#include "Link.h"
 
63
#include "OutputDev.h"
 
64
#include "Error.h"
 
65
#include "ErrorCodes.h"
 
66
#include "Lexer.h"
 
67
#include "Parser.h"
 
68
#include "SecurityHandler.h"
 
69
#include "Decrypt.h"
 
70
#ifndef DISABLE_OUTLINE
 
71
#include "Outline.h"
 
72
#endif
 
73
#include "PDFDoc.h"
 
74
#include "Hints.h"
 
75
 
 
76
//------------------------------------------------------------------------
 
77
 
 
78
#define headerSearchSize 1024   // read this many bytes at beginning of
 
79
                                //   file to look for '%PDF'
 
80
#define pdfIdLength 32   // PDF Document IDs (PermanentId, UpdateId) length
 
81
 
 
82
#define linearizationSearchSize 1024    // read this many bytes at beginning of
 
83
                                        // file to look for linearization
 
84
                                        // dictionary
 
85
 
 
86
#define xrefSearchSize 1024     // read this many bytes at end of file
 
87
                                //   to look for 'startxref'
 
88
 
 
89
//------------------------------------------------------------------------
 
90
// PDFDoc
 
91
//------------------------------------------------------------------------
 
92
 
 
93
void PDFDoc::init()
 
94
{
 
95
  ok = gFalse;
 
96
  errCode = errNone;
 
97
  fileName = NULL;
 
98
  file = NULL;
 
99
  str = NULL;
 
100
  xref = NULL;
 
101
  linearization = NULL;
 
102
  catalog = NULL;
 
103
  hints = NULL;
 
104
#ifndef DISABLE_OUTLINE
 
105
  outline = NULL;
 
106
#endif
 
107
  startXRefPos = ~(Guint)0;
 
108
  secHdlr = NULL;
 
109
  pageCache = NULL;
 
110
}
 
111
 
 
112
PDFDoc::PDFDoc()
 
113
{
 
114
  init();
 
115
}
 
116
 
 
117
PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword,
 
118
               GooString *userPassword, void *guiDataA) {
 
119
  Object obj;
 
120
  int size = 0;
 
121
 
 
122
  init();
 
123
 
 
124
  fileName = fileNameA;
 
125
  guiData = guiDataA;
 
126
 
 
127
  struct stat buf;
 
128
  if (stat(fileName->getCString(), &buf) == 0) {
 
129
     size = buf.st_size;
 
130
  }
 
131
 
 
132
  // try to open file
 
133
#ifdef VMS
 
134
  file = fopen(fileName->getCString(), "rb", "ctx=stm");
 
135
#else
 
136
  file = fopen(fileName->getCString(), "rb");
 
137
#endif
 
138
  if (file == NULL) {
 
139
    // fopen() has failed.
 
140
    // Keep a copy of the errno returned by fopen so that it can be 
 
141
    // referred to later.
 
142
    fopenErrno = errno;
 
143
    error(-1, "Couldn't open file '%s': %s.", fileName->getCString(),
 
144
                                              strerror(errno));
 
145
    errCode = errOpenFile;
 
146
    return;
 
147
  }
 
148
 
 
149
  // create stream
 
150
  obj.initNull();
 
151
  str = new FileStream(file, 0, gFalse, size, &obj);
 
152
 
 
153
  ok = setup(ownerPassword, userPassword);
 
154
}
 
155
 
 
156
#ifdef _WIN32
 
157
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword,
 
158
               GooString *userPassword, void *guiDataA) {
 
159
  OSVERSIONINFO version;
 
160
  wchar_t fileName2[MAX_PATH + 1];
 
161
  Object obj;
 
162
  int i;
 
163
 
 
164
  init();
 
165
 
 
166
  guiData = guiDataA;
 
167
 
 
168
  //~ file name should be stored in Unicode (?)
 
169
  fileName = new GooString();
 
170
  for (i = 0; i < fileNameLen; ++i) {
 
171
    fileName->append((char)fileNameA[i]);
 
172
  }
 
173
 
 
174
  // zero-terminate the file name string
 
175
  for (i = 0; i < fileNameLen && i < MAX_PATH; ++i) {
 
176
    fileName2[i] = fileNameA[i];
 
177
  }
 
178
  fileName2[i] = 0;
 
179
 
 
180
  // try to open file
 
181
  // NB: _wfopen is only available in NT
 
182
  struct _stat buf;
 
183
  int size;
 
184
  version.dwOSVersionInfoSize = sizeof(version);
 
185
  GetVersionEx(&version);
 
186
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
 
187
    if (_wstat(fileName2, &buf) == 0) {
 
188
      size = buf.st_size;
 
189
    }
 
190
    file = _wfopen(fileName2, L"rb");
 
191
  } else {
 
192
    if (_stat(fileName->getCString(), &buf) == 0) {
 
193
      size = buf.st_size;
 
194
    }
 
195
    file = fopen(fileName->getCString(), "rb");
 
196
  }
 
197
  if (!file) {
 
198
    error(-1, "Couldn't open file '%s'", fileName->getCString());
 
199
    errCode = errOpenFile;
 
200
    return;
 
201
  }
 
202
 
 
203
  // create stream
 
204
  obj.initNull();
 
205
  str = new FileStream(file, 0, gFalse, size, &obj);
 
206
 
 
207
  ok = setup(ownerPassword, userPassword);
 
208
}
 
209
#endif
 
210
 
 
211
PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword,
 
212
               GooString *userPassword, void *guiDataA) {
 
213
 
 
214
  init();
 
215
  guiData = guiDataA;
 
216
  if (strA->getFileName()) {
 
217
    fileName = strA->getFileName()->copy();
 
218
  } else {
 
219
    fileName = NULL;
 
220
  }
 
221
  str = strA;
 
222
  ok = setup(ownerPassword, userPassword);
 
223
}
 
224
 
 
225
GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) {
 
226
  str->setPos(0, -1);
 
227
  if (str->getPos() < 0)
 
228
  {
 
229
    error(-1, "Document base stream is not seekable");
 
230
    return gFalse;
 
231
  }
 
232
 
 
233
  str->reset();
 
234
 
 
235
  // check footer
 
236
  // Adobe does not seem to enforce %%EOF, so we do the same
 
237
//  if (!checkFooter()) return gFalse;
 
238
  
 
239
  // check header
 
240
  checkHeader();
 
241
 
 
242
  GBool wasReconstructed = false;
 
243
 
 
244
  // read xref table
 
245
  xref = new XRef(str, getStartXRef(), getMainXRefEntriesOffset(), &wasReconstructed);
 
246
  if (!xref->isOk()) {
 
247
    error(-1, "Couldn't read xref table");
 
248
    errCode = xref->getErrorCode();
 
249
    return gFalse;
 
250
  }
 
251
 
 
252
  // check for encryption
 
253
  if (!checkEncryption(ownerPassword, userPassword)) {
 
254
    errCode = errEncrypted;
 
255
    return gFalse;
 
256
  }
 
257
 
 
258
  // read catalog
 
259
  catalog = new Catalog(xref);
 
260
  if (catalog && !catalog->isOk()) {
 
261
    if (!wasReconstructed)
 
262
    {
 
263
      // try one more time to contruct the Catalog, maybe the problem is damaged XRef 
 
264
      delete catalog;
 
265
      delete xref;
 
266
      xref = new XRef(str, 0, 0, NULL, true);
 
267
      catalog = new Catalog(xref);
 
268
    }
 
269
 
 
270
    if (catalog && !catalog->isOk()) {
 
271
      error(-1, "Couldn't read page catalog");
 
272
      errCode = errBadCatalog;
 
273
      return gFalse;
 
274
    }
 
275
  }
 
276
 
 
277
  // done
 
278
  return gTrue;
 
279
}
 
280
 
 
281
PDFDoc::~PDFDoc() {
 
282
  if (pageCache) {
 
283
    for (int i = 0; i < getNumPages(); i++) {
 
284
      if (pageCache[i]) {
 
285
        delete pageCache[i];
 
286
      }
 
287
    }
 
288
    gfree(pageCache);
 
289
  }
 
290
  delete secHdlr;
 
291
#ifndef DISABLE_OUTLINE
 
292
  if (outline) {
 
293
    delete outline;
 
294
  }
 
295
#endif
 
296
  if (catalog) {
 
297
    delete catalog;
 
298
  }
 
299
  if (xref) {
 
300
    delete xref;
 
301
  }
 
302
  if (hints) {
 
303
    delete hints;
 
304
  }
 
305
  if (linearization) {
 
306
    delete linearization;
 
307
  }
 
308
  if (str) {
 
309
    delete str;
 
310
  }
 
311
  if (file) {
 
312
    fclose(file);
 
313
  }
 
314
  if (fileName) {
 
315
    delete fileName;
 
316
  }
 
317
}
 
318
 
 
319
 
 
320
// Check for a %%EOF at the end of this stream
 
321
GBool PDFDoc::checkFooter() {
 
322
  // we look in the last 1024 chars because Adobe does the same
 
323
  char *eof = new char[1025];
 
324
  int pos = str->getPos();
 
325
  str->setPos(1024, -1);
 
326
  int i, ch;
 
327
  for (i = 0; i < 1024; i++)
 
328
  {
 
329
    ch = str->getChar();
 
330
    if (ch == EOF)
 
331
      break;
 
332
    eof[i] = ch;
 
333
  }
 
334
  eof[i] = '\0';
 
335
 
 
336
  bool found = false;
 
337
  for (i = i - 5; i >= 0; i--) {
 
338
    if (strncmp (&eof[i], "%%EOF", 5) == 0) {
 
339
      found = true;
 
340
      break;
 
341
    }
 
342
  }
 
343
  if (!found)
 
344
  {
 
345
    error(-1, "Document has not the mandatory ending %%EOF");
 
346
    errCode = errDamaged;
 
347
    delete[] eof;
 
348
    return gFalse;
 
349
  }
 
350
  delete[] eof;
 
351
  str->setPos(pos);
 
352
  return gTrue;
 
353
}
 
354
  
 
355
// Check for a PDF header on this stream.  Skip past some garbage
 
356
// if necessary.
 
357
void PDFDoc::checkHeader() {
 
358
  char hdrBuf[headerSearchSize+1];
 
359
  char *p;
 
360
  char *tokptr;
 
361
  int i;
 
362
 
 
363
  pdfMajorVersion = 0;
 
364
  pdfMinorVersion = 0;
 
365
  for (i = 0; i < headerSearchSize; ++i) {
 
366
    hdrBuf[i] = str->getChar();
 
367
  }
 
368
  hdrBuf[headerSearchSize] = '\0';
 
369
  for (i = 0; i < headerSearchSize - 5; ++i) {
 
370
    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
 
371
      break;
 
372
    }
 
373
  }
 
374
  if (i >= headerSearchSize - 5) {
 
375
    error(-1, "May not be a PDF file (continuing anyway)");
 
376
    return;
 
377
  }
 
378
  str->moveStart(i);
 
379
  if (!(p = strtok_r(&hdrBuf[i+5], " \t\n\r", &tokptr))) {
 
380
    error(-1, "May not be a PDF file (continuing anyway)");
 
381
    return;
 
382
  }
 
383
  sscanf(p, "%d.%d", &pdfMajorVersion, &pdfMinorVersion);
 
384
  // We don't do the version check. Don't add it back in.
 
385
}
 
386
 
 
387
GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) {
 
388
  Object encrypt;
 
389
  GBool encrypted;
 
390
  GBool ret;
 
391
 
 
392
  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
 
393
  if ((encrypted = encrypt.isDict())) {
 
394
    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
 
395
      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
 
396
        // authorization succeeded
 
397
        xref->setEncryption(secHdlr->getPermissionFlags(),
 
398
                            secHdlr->getOwnerPasswordOk(),
 
399
                            secHdlr->getFileKey(),
 
400
                            secHdlr->getFileKeyLength(),
 
401
                            secHdlr->getEncVersion(),
 
402
                            secHdlr->getEncRevision(),
 
403
                            secHdlr->getEncAlgorithm());
 
404
        ret = gTrue;
 
405
      } else {
 
406
        // authorization failed
 
407
        ret = gFalse;
 
408
      }
 
409
    } else {
 
410
      // couldn't find the matching security handler
 
411
      ret = gFalse;
 
412
    }
 
413
  } else {
 
414
    // document is not encrypted
 
415
    ret = gTrue;
 
416
  }
 
417
  encrypt.free();
 
418
  return ret;
 
419
}
 
420
 
 
421
void PDFDoc::displayPage(OutputDev *out, int page,
 
422
                         double hDPI, double vDPI, int rotate,
 
423
                         GBool useMediaBox, GBool crop, GBool printing,
 
424
                         GBool (*abortCheckCbk)(void *data),
 
425
                         void *abortCheckCbkData,
 
426
                         GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
 
427
                         void *annotDisplayDecideCbkData) {
 
428
  if (globalParams->getPrintCommands()) {
 
429
    printf("***** page %d *****\n", page);
 
430
  }
 
431
 
 
432
  if (getPage(page))
 
433
    getPage(page)->display(out, hDPI, vDPI,
 
434
                                    rotate, useMediaBox, crop, printing, catalog,
 
435
                                    abortCheckCbk, abortCheckCbkData,
 
436
                                    annotDisplayDecideCbk, annotDisplayDecideCbkData);
 
437
 
 
438
}
 
439
 
 
440
void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
 
441
                          double hDPI, double vDPI, int rotate,
 
442
                          GBool useMediaBox, GBool crop, GBool printing,
 
443
                          GBool (*abortCheckCbk)(void *data),
 
444
                          void *abortCheckCbkData,
 
445
                          GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
 
446
                          void *annotDisplayDecideCbkData) {
 
447
  int page;
 
448
 
 
449
  for (page = firstPage; page <= lastPage; ++page) {
 
450
    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
 
451
                abortCheckCbk, abortCheckCbkData,
 
452
                annotDisplayDecideCbk, annotDisplayDecideCbkData);
 
453
  }
 
454
}
 
455
 
 
456
void PDFDoc::displayPageSlice(OutputDev *out, int page,
 
457
                              double hDPI, double vDPI, int rotate,
 
458
                              GBool useMediaBox, GBool crop, GBool printing,
 
459
                              int sliceX, int sliceY, int sliceW, int sliceH,
 
460
                              GBool (*abortCheckCbk)(void *data),
 
461
                              void *abortCheckCbkData,
 
462
                              GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
 
463
                              void *annotDisplayDecideCbkData) {
 
464
  if (getPage(page))
 
465
    getPage(page)->displaySlice(out, hDPI, vDPI,
 
466
                                         rotate, useMediaBox, crop,
 
467
                                         sliceX, sliceY, sliceW, sliceH,
 
468
                                         printing, catalog,
 
469
                                         abortCheckCbk, abortCheckCbkData,
 
470
                                         annotDisplayDecideCbk, annotDisplayDecideCbkData);
 
471
}
 
472
 
 
473
Links *PDFDoc::getLinks(int page) {
 
474
  Page *p = getPage(page);
 
475
  if (!p) {
 
476
    Object obj;
 
477
    obj.initNull();
 
478
    return new Links (&obj, NULL);
 
479
  }
 
480
  return p->getLinks(catalog);
 
481
}
 
482
 
 
483
void PDFDoc::processLinks(OutputDev *out, int page) {
 
484
  if (getPage(page))
 
485
    getPage(page)->processLinks(out, catalog);
 
486
}
 
487
 
 
488
Linearization *PDFDoc::getLinearization()
 
489
{
 
490
  if (!linearization) {
 
491
    linearization = new Linearization(str);
 
492
  }
 
493
  return linearization;
 
494
}
 
495
 
 
496
GBool PDFDoc::isLinearized() {
 
497
  if ((str->getLength()) &&
 
498
      (getLinearization()->getLength() == str->getLength()))
 
499
    return gTrue;
 
500
  else
 
501
    return gFalse;
 
502
}
 
503
 
 
504
static GBool
 
505
get_id (GooString *encodedidstring, GooString *id) {
 
506
  const char *encodedid = encodedidstring->getCString();
 
507
  char pdfid[pdfIdLength + 1];
 
508
  int n;
 
509
 
 
510
  if (encodedidstring->getLength() != pdfIdLength / 2)
 
511
    return gFalse;
 
512
 
 
513
  n = sprintf(pdfid, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
 
514
              encodedid[0] & 0xff, encodedid[1] & 0xff, encodedid[2] & 0xff, encodedid[3] & 0xff,
 
515
              encodedid[4] & 0xff, encodedid[5] & 0xff, encodedid[6] & 0xff, encodedid[7] & 0xff,
 
516
              encodedid[8] & 0xff, encodedid[9] & 0xff, encodedid[10] & 0xff, encodedid[11] & 0xff,
 
517
              encodedid[12] & 0xff, encodedid[13] & 0xff, encodedid[14] & 0xff, encodedid[15] & 0xff);
 
518
  if (n != pdfIdLength)
 
519
    return gFalse;
 
520
 
 
521
  id->Set(pdfid, pdfIdLength);
 
522
  return gTrue;
 
523
}
 
524
 
 
525
GBool PDFDoc::getID(GooString *permanent_id, GooString *update_id) {
 
526
  Object obj;
 
527
  xref->getTrailerDict()->dictLookup ("ID", &obj);
 
528
 
 
529
  if (obj.isArray() && obj.arrayGetLength() == 2) {
 
530
    Object obj2;
 
531
 
 
532
    if (permanent_id) {
 
533
      if (obj.arrayGet(0, &obj2)->isString()) {
 
534
        if (!get_id (obj2.getString(), permanent_id)) {
 
535
          obj2.free();
 
536
          return gFalse;
 
537
        }
 
538
      } else {
 
539
        error(-1, "Invalid permanent ID");
 
540
        obj2.free();
 
541
        return gFalse;
 
542
      }
 
543
      obj2.free();
 
544
    }
 
545
 
 
546
    if (update_id) {
 
547
      if (obj.arrayGet(1, &obj2)->isString()) {
 
548
        if (!get_id (obj2.getString(), update_id)) {
 
549
          obj2.free();
 
550
          return gFalse;
 
551
        }
 
552
      } else {
 
553
        error(-1, "Invalid update ID");
 
554
        obj2.free();
 
555
        return gFalse;
 
556
      }
 
557
      obj2.free();
 
558
    }
 
559
 
 
560
    obj.free();
 
561
 
 
562
    return gTrue;
 
563
  }
 
564
  obj.free();
 
565
 
 
566
  return gFalse;
 
567
}
 
568
 
 
569
Hints *PDFDoc::getHints()
 
570
{
 
571
  if (!hints && isLinearized()) {
 
572
    hints = new Hints(str, getLinearization(), getXRef(), secHdlr);
 
573
  }
 
574
 
 
575
  return hints;
 
576
}
 
577
 
 
578
int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) {
 
579
  FILE *f;
 
580
  OutStream *outStr;
 
581
  int res;
 
582
 
 
583
  if (!(f = fopen(name->getCString(), "wb"))) {
 
584
    error(-1, "Couldn't open file '%s'", name->getCString());
 
585
    return errOpenFile;
 
586
  }
 
587
  outStr = new FileOutStream(f,0);
 
588
  res = saveAs(outStr, mode);
 
589
  delete outStr;
 
590
  fclose(f);
 
591
  return res;
 
592
}
 
593
 
 
594
int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) {
 
595
 
 
596
  // we don't support files with Encrypt at the moment
 
597
  Object obj;
 
598
  xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj);
 
599
  if (!obj.isNull())
 
600
  {
 
601
    obj.free();
 
602
    return errEncrypted;
 
603
  }
 
604
  obj.free();
 
605
 
 
606
  if (mode == writeForceRewrite) {
 
607
    saveCompleteRewrite(outStr);
 
608
  } else if (mode == writeForceIncremental) {
 
609
    saveIncrementalUpdate(outStr); 
 
610
  } else { // let poppler decide
 
611
    // find if we have updated objects
 
612
    GBool updated = gFalse;
 
613
    for(int i=0; i<xref->getNumObjects(); i++) {
 
614
      if (xref->getEntry(i)->updated) {
 
615
        updated = gTrue;
 
616
        break;
 
617
      }
 
618
    }
 
619
    if(updated) { 
 
620
      saveIncrementalUpdate(outStr);
 
621
    } else {
 
622
      // simply copy the original file
 
623
      saveWithoutChangesAs (outStr);
 
624
    }
 
625
  }
 
626
 
 
627
  return errNone;
 
628
}
 
629
 
 
630
int PDFDoc::saveWithoutChangesAs(GooString *name) {
 
631
  FILE *f;
 
632
  OutStream *outStr;
 
633
  int res;
 
634
 
 
635
  if (!(f = fopen(name->getCString(), "wb"))) {
 
636
    error(-1, "Couldn't open file '%s'", name->getCString());
 
637
    return errOpenFile;
 
638
  }
 
639
  
 
640
  outStr = new FileOutStream(f,0);
 
641
  res = saveWithoutChangesAs(outStr);
 
642
  delete outStr;
 
643
 
 
644
  fclose(f);
 
645
 
 
646
  return res;
 
647
}
 
648
 
 
649
int PDFDoc::saveWithoutChangesAs(OutStream *outStr) {
 
650
  int c;
 
651
  
 
652
  str->reset();
 
653
  while ((c = str->getChar()) != EOF) {
 
654
    outStr->put(c);
 
655
  }
 
656
  str->close();
 
657
 
 
658
  return errNone;
 
659
}
 
660
 
 
661
void PDFDoc::saveIncrementalUpdate (OutStream* outStr)
 
662
{
 
663
  XRef *uxref;
 
664
  int c;
 
665
  //copy the original file
 
666
  str->reset();
 
667
  while ((c = str->getChar()) != EOF) {
 
668
    outStr->put(c);
 
669
  }
 
670
  str->close();
 
671
 
 
672
  uxref = new XRef();
 
673
  uxref->add(0, 65535, 0, gFalse);
 
674
  for(int i=0; i<xref->getNumObjects(); i++) {
 
675
    if ((xref->getEntry(i)->type == xrefEntryFree) && 
 
676
        (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects
 
677
      continue;
 
678
 
 
679
    if (xref->getEntry(i)->updated) { //we have an updated object
 
680
      Object obj1;
 
681
      Ref ref;
 
682
      ref.num = i;
 
683
      ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen;
 
684
      xref->fetch(ref.num, ref.gen, &obj1);
 
685
      Guint offset = writeObject(&obj1, &ref, outStr);
 
686
      uxref->add(ref.num, ref.gen, offset, gTrue);
 
687
      obj1.free();
 
688
    }
 
689
  }
 
690
  if (uxref->getSize() == 0) { //we have nothing to update
 
691
    delete uxref;
 
692
    return;
 
693
  }
 
694
 
 
695
  Guint uxrefOffset = outStr->getPos();
 
696
  uxref->writeToFile(outStr, gFalse /* do not write unnecessary entries */);
 
697
 
 
698
  writeTrailer(uxrefOffset, xref->getSize(), outStr, gTrue);
 
699
 
 
700
  delete uxref;
 
701
}
 
702
 
 
703
void PDFDoc::saveCompleteRewrite (OutStream* outStr)
 
704
{
 
705
  outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion);
 
706
  XRef *uxref = new XRef();
 
707
  uxref->add(0, 65535, 0, gFalse);
 
708
  for(int i=0; i<xref->getNumObjects(); i++) {
 
709
    Object obj1;
 
710
    Ref ref;
 
711
    XRefEntryType type = xref->getEntry(i)->type;
 
712
    if (type == xrefEntryFree) {
 
713
      ref.num = i;
 
714
      ref.gen = xref->getEntry(i)->gen;
 
715
      /* the XRef class adds a lot of irrelevant free entries, we only want the significant one
 
716
          and we don't want the one with num=0 because it has already been added (gen = 65535)*/
 
717
      if (ref.gen > 0 && ref.num > 0)
 
718
        uxref->add(ref.num, ref.gen, 0, gFalse);
 
719
    } else if (type == xrefEntryUncompressed){ 
 
720
      ref.num = i;
 
721
      ref.gen = xref->getEntry(i)->gen;
 
722
      xref->fetch(ref.num, ref.gen, &obj1);
 
723
      Guint offset = writeObject(&obj1, &ref, outStr);
 
724
      uxref->add(ref.num, ref.gen, offset, gTrue);
 
725
      obj1.free();
 
726
    } else if (type == xrefEntryCompressed) {
 
727
      ref.num = i;
 
728
      ref.gen = 0; //compressed entries have gen == 0
 
729
      xref->fetch(ref.num, ref.gen, &obj1);
 
730
      Guint offset = writeObject(&obj1, &ref, outStr);
 
731
      uxref->add(ref.num, ref.gen, offset, gTrue);
 
732
      obj1.free();
 
733
    }
 
734
  }
 
735
  Guint uxrefOffset = outStr->getPos();
 
736
  uxref->writeToFile(outStr, gTrue /* write all entries */);
 
737
 
 
738
  writeTrailer(uxrefOffset, uxref->getSize(), outStr, gFalse);
 
739
 
 
740
 
 
741
  delete uxref;
 
742
 
 
743
}
 
744
 
 
745
void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr)
 
746
{
 
747
  Object obj1;
 
748
  outStr->printf("<<");
 
749
  for (int i=0; i<dict->getLength(); i++) {
 
750
    GooString keyName(dict->getKey(i));
 
751
    GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */);
 
752
    outStr->printf("/%s ", keyNameToPrint->getCString());
 
753
    delete keyNameToPrint;
 
754
    writeObject(dict->getValNF(i, &obj1), NULL, outStr);
 
755
    obj1.free();
 
756
  }
 
757
  outStr->printf(">> ");
 
758
}
 
759
 
 
760
void PDFDoc::writeStream (Stream* str, OutStream* outStr)
 
761
{
 
762
  outStr->printf("stream\r\n");
 
763
  str->reset();
 
764
  for (int c=str->getChar(); c!= EOF; c=str->getChar()) {
 
765
    outStr->printf("%c", c);  
 
766
  }
 
767
  outStr->printf("\r\nendstream\r\n");
 
768
}
 
769
 
 
770
void PDFDoc::writeRawStream (Stream* str, OutStream* outStr)
 
771
{
 
772
  Object obj1;
 
773
  str->getDict()->lookup("Length", &obj1);
 
774
  if (!obj1.isInt()) {
 
775
    error (-1, "PDFDoc::writeRawStream, no Length in stream dict");
 
776
    return;
 
777
  }
 
778
 
 
779
  const int length = obj1.getInt();
 
780
  obj1.free();
 
781
 
 
782
  outStr->printf("stream\r\n");
 
783
  str->unfilteredReset();
 
784
  for (int i=0; i<length; i++) {
 
785
    int c = str->getUnfilteredChar();
 
786
    outStr->printf("%c", c);  
 
787
  }
 
788
  str->reset();
 
789
  outStr->printf("\r\nendstream\r\n");
 
790
}
 
791
 
 
792
void PDFDoc::writeString (GooString* s, OutStream* outStr)
 
793
{
 
794
  if (s->hasUnicodeMarker()) {
 
795
    //unicode string don't necessary end with \0
 
796
    const char* c = s->getCString();
 
797
    outStr->printf("(");
 
798
    for(int i=0; i<s->getLength(); i++) {
 
799
      char unescaped = *(c+i)&0x000000ff;
 
800
      //escape if needed
 
801
      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
 
802
        outStr->printf("%c", '\\');
 
803
      outStr->printf("%c", unescaped);
 
804
    }
 
805
    outStr->printf(") ");
 
806
  } else {
 
807
    const char* c = s->getCString();
 
808
    outStr->printf("(");
 
809
    for(int i=0; i<s->getLength(); i++) {
 
810
      char unescaped = (*c)&0x000000ff;
 
811
      //escape if needed
 
812
      if (unescaped == '(' || unescaped == ')' || unescaped == '\\')
 
813
        outStr->printf("%c", '\\');
 
814
      outStr->printf("%c", unescaped);
 
815
      c++;
 
816
    }
 
817
    outStr->printf(") ");
 
818
  }
 
819
}
 
820
 
 
821
Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr)
 
822
{
 
823
  Array *array;
 
824
  Object obj1;
 
825
  Guint offset = outStr->getPos();
 
826
  int tmp;
 
827
 
 
828
  if(ref) 
 
829
    outStr->printf("%i %i obj ", ref->num, ref->gen);
 
830
 
 
831
  switch (obj->getType()) {
 
832
    case objBool:
 
833
      outStr->printf("%s ", obj->getBool()?"true":"false");
 
834
      break;
 
835
    case objInt:
 
836
      outStr->printf("%i ", obj->getInt());
 
837
      break;
 
838
    case objReal:
 
839
    {
 
840
      GooString s;
 
841
      s.appendf("{0:.10g}", obj->getReal());
 
842
      outStr->printf("%s ", s.getCString());
 
843
      break;
 
844
    }
 
845
    case objString:
 
846
      writeString(obj->getString(), outStr);
 
847
      break;
 
848
    case objName:
 
849
    {
 
850
      GooString name(obj->getName());
 
851
      GooString *nameToPrint = name.sanitizedName(gFalse /* non ps mode */);
 
852
      outStr->printf("/%s ", nameToPrint->getCString());
 
853
      delete nameToPrint;
 
854
      break;
 
855
    }
 
856
    case objNull:
 
857
      outStr->printf( "null ");
 
858
      break;
 
859
    case objArray:
 
860
      array = obj->getArray();
 
861
      outStr->printf("[");
 
862
      for (int i=0; i<array->getLength(); i++) {
 
863
        writeObject(array->getNF(i, &obj1), NULL,outStr);
 
864
        obj1.free();
 
865
      }
 
866
      outStr->printf("] ");
 
867
      break;
 
868
    case objDict:
 
869
      writeDictionnary (obj->getDict(),outStr);
 
870
      break;
 
871
    case objStream: 
 
872
      {
 
873
        //We can't modify stream with the current implementation (no write functions in Stream API)
 
874
        // => the only type of streams which that have been modified are internal streams (=strWeird)
 
875
        Stream *stream = obj->getStream();
 
876
        if (stream->getKind() == strWeird) {
 
877
          //we write the stream unencoded => TODO: write stream encoder
 
878
          stream->reset();
 
879
          //recalculate stream length
 
880
          tmp = 0;
 
881
          for (int c=stream->getChar(); c!=EOF; c=stream->getChar()) {
 
882
            tmp++;
 
883
          }
 
884
          obj1.initInt(tmp);
 
885
          stream->getDict()->set("Length", &obj1);
 
886
 
 
887
          //Remove Stream encoding
 
888
          stream->getDict()->remove("Filter");
 
889
          stream->getDict()->remove("DecodeParms");
 
890
 
 
891
          writeDictionnary (stream->getDict(),outStr);
 
892
          writeStream (stream,outStr);
 
893
          obj1.free();
 
894
        } else {
 
895
          //raw stream copy
 
896
          FilterStream *fs = dynamic_cast<FilterStream*>(stream);
 
897
          if (fs) {
 
898
            BaseStream *bs = fs->getBaseStream();
 
899
            if (bs) {
 
900
              Guint streamEnd;
 
901
                if (xref->getStreamEnd(bs->getStart(), &streamEnd)) {
 
902
                  Object val;
 
903
                  val.initInt(streamEnd - bs->getStart());
 
904
                  stream->getDict()->set("Length", &val);
 
905
                }
 
906
              }
 
907
          }
 
908
          writeDictionnary (stream->getDict(), outStr);
 
909
          writeRawStream (stream, outStr);
 
910
        }
 
911
        break;
 
912
      }
 
913
    case objRef:
 
914
      outStr->printf("%i %i R ", obj->getRef().num, obj->getRef().gen);
 
915
      break;
 
916
    case objCmd:
 
917
      outStr->printf("cmd\r\n");
 
918
      break;
 
919
    case objError:
 
920
      outStr->printf("error\r\n");
 
921
      break;
 
922
    case objEOF:
 
923
      outStr->printf("eof\r\n");
 
924
      break;
 
925
    case objNone:
 
926
      outStr->printf("none\r\n");
 
927
      break;
 
928
    default:
 
929
      error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType());
 
930
      break;
 
931
  }
 
932
  if (ref)
 
933
    outStr->printf("endobj\r\n");
 
934
  return offset;
 
935
}
 
936
 
 
937
void PDFDoc::writeTrailer (Guint uxrefOffset, int uxrefSize, OutStream* outStr, GBool incrUpdate)
 
938
{
 
939
  Dict *trailerDict = new Dict(xref);
 
940
  Object obj1;
 
941
  obj1.initInt(uxrefSize);
 
942
  trailerDict->set("Size", &obj1);
 
943
  obj1.free();
 
944
 
 
945
 
 
946
  //build a new ID, as recommended in the reference, uses:
 
947
  // - current time
 
948
  // - file name
 
949
  // - file size
 
950
  // - values of entry in information dictionnary
 
951
  GooString message;
 
952
  char buffer[256];
 
953
  sprintf(buffer, "%i", (int)time(NULL));
 
954
  message.append(buffer);
 
955
  if (fileName)
 
956
    message.append(fileName);
 
957
  else
 
958
    message.append("streamwithoutfilename.pdf");
 
959
  // file size
 
960
  unsigned int fileSize = 0;
 
961
  int c;
 
962
  str->reset();
 
963
  while ((c = str->getChar()) != EOF) {
 
964
    fileSize++;
 
965
  }
 
966
  str->close();
 
967
  sprintf(buffer, "%i", fileSize);
 
968
  message.append(buffer);
 
969
 
 
970
  //info dict -- only use text string
 
971
  if (xref->getDocInfo(&obj1)->isDict()) {
 
972
    for(int i=0; i<obj1.getDict()->getLength(); i++) {
 
973
      Object obj2;
 
974
      obj1.getDict()->getVal(i, &obj2);  
 
975
      if (obj2.isString()) {
 
976
        message.append(obj2.getString());
 
977
      }
 
978
      obj2.free();
 
979
    }
 
980
  }
 
981
  obj1.free();
 
982
 
 
983
  //calculate md5 digest
 
984
  Guchar digest[16];
 
985
  Decrypt::md5((Guchar*)message.getCString(), message.getLength(), digest);
 
986
  obj1.initString(new GooString((const char*)digest, 16));
 
987
 
 
988
  //create ID array
 
989
  Object obj2,obj3,obj4,obj5;
 
990
  obj2.initArray(xref);
 
991
 
 
992
  if (incrUpdate) {
 
993
    //only update the second part of the array
 
994
    if(xref->getTrailerDict()->getDict()->lookup("ID", &obj4) != NULL) {
 
995
      if (!obj4.isArray()) {
 
996
        error(-1, "PDFDoc::writeTrailer original file's ID entry isn't an array. Trying to continue");
 
997
      } else {
 
998
        //Get the first part of the ID
 
999
        obj4.arrayGet(0,&obj3); 
 
1000
 
 
1001
        obj2.arrayAdd(&obj3); 
 
1002
        obj2.arrayAdd(&obj1);
 
1003
        trailerDict->set("ID", &obj2);
 
1004
      }
 
1005
    }
 
1006
  } else {
 
1007
    //new file => same values for the two identifiers
 
1008
    obj2.arrayAdd(&obj1);
 
1009
    obj1.initString(new GooString((const char*)digest, 16));
 
1010
    obj2.arrayAdd(&obj1);
 
1011
    trailerDict->set("ID", &obj2);
 
1012
  }
 
1013
 
 
1014
 
 
1015
  obj1.initRef(xref->getRootNum(), xref->getRootGen());
 
1016
  trailerDict->set("Root", &obj1);
 
1017
 
 
1018
  if (incrUpdate) { 
 
1019
    obj1.initInt(getStartXRef());
 
1020
    trailerDict->set("Prev", &obj1);
 
1021
  }
 
1022
  
 
1023
  xref->getDocInfoNF(&obj5);
 
1024
  if (!obj5.isNull()) {
 
1025
    trailerDict->set("Info", &obj5);
 
1026
  }
 
1027
  
 
1028
  outStr->printf( "trailer\r\n");
 
1029
  writeDictionnary(trailerDict, outStr);
 
1030
  outStr->printf( "\r\nstartxref\r\n");
 
1031
  outStr->printf( "%i\r\n", uxrefOffset);
 
1032
  outStr->printf( "%%%%EOF\r\n");
 
1033
 
 
1034
  delete trailerDict;
 
1035
}
 
1036
 
 
1037
#ifndef DISABLE_OUTLINE
 
1038
Outline *PDFDoc::getOutline()
 
1039
{
 
1040
  if (!outline) {
 
1041
    // read outline
 
1042
    outline = new Outline(catalog->getOutline(), xref);
 
1043
  }
 
1044
 
 
1045
  return outline;
 
1046
}
 
1047
#endif
 
1048
 
 
1049
PDFDoc *PDFDoc::ErrorPDFDoc(int errorCode, GooString *fileNameA)
 
1050
{
 
1051
  PDFDoc *doc = new PDFDoc();
 
1052
  doc->errCode = errorCode;
 
1053
  doc->fileName = fileNameA;
 
1054
 
 
1055
  return doc;
 
1056
}
 
1057
 
 
1058
Guint PDFDoc::strToUnsigned(char *s) {
 
1059
  Guint x;
 
1060
  char *p;
 
1061
  int i;
 
1062
 
 
1063
  x = 0;
 
1064
  for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
 
1065
    x = 10 * x + (*p - '0');
 
1066
  }
 
1067
  return x;
 
1068
}
 
1069
 
 
1070
// Read the 'startxref' position.
 
1071
Guint PDFDoc::getStartXRef()
 
1072
{
 
1073
  if (startXRefPos == ~(Guint)0) {
 
1074
 
 
1075
    if (isLinearized()) {
 
1076
      char buf[linearizationSearchSize+1];
 
1077
      int c, n, i;
 
1078
 
 
1079
      str->setPos(0);
 
1080
      for (n = 0; n < linearizationSearchSize; ++n) {
 
1081
        if ((c = str->getChar()) == EOF) {
 
1082
          break;
 
1083
        }
 
1084
        buf[n] = c;
 
1085
      }
 
1086
      buf[n] = '\0';
 
1087
 
 
1088
      // find end of first obj
 
1089
      startXRefPos = 0;
 
1090
      for (i = 0; i < n; i++) {
 
1091
        if (!strncmp("endobj", &buf[i], 6)) {
 
1092
           startXRefPos = i+6;
 
1093
           break;
 
1094
        }
 
1095
      }
 
1096
    } else {
 
1097
      char buf[xrefSearchSize+1];
 
1098
      char *p;
 
1099
      int c, n, i;
 
1100
 
 
1101
      // read last xrefSearchSize bytes
 
1102
      str->setPos(xrefSearchSize, -1);
 
1103
      for (n = 0; n < xrefSearchSize; ++n) {
 
1104
        if ((c = str->getChar()) == EOF) {
 
1105
          break;
 
1106
        }
 
1107
        buf[n] = c;
 
1108
      }
 
1109
      buf[n] = '\0';
 
1110
 
 
1111
      // find startxref
 
1112
      for (i = n - 9; i >= 0; --i) {
 
1113
        if (!strncmp(&buf[i], "startxref", 9)) {
 
1114
          break;
 
1115
        }
 
1116
      }
 
1117
      if (i < 0) {
 
1118
        startXRefPos = 0;
 
1119
      }
 
1120
      for (p = &buf[i+9]; isspace(*p); ++p) ;
 
1121
      startXRefPos =  strToUnsigned(p);
 
1122
    }
 
1123
 
 
1124
  }
 
1125
 
 
1126
  return startXRefPos;
 
1127
}
 
1128
 
 
1129
Guint PDFDoc::getMainXRefEntriesOffset()
 
1130
{
 
1131
  Guint mainXRefEntriesOffset = 0;
 
1132
 
 
1133
  if (isLinearized()) {
 
1134
    mainXRefEntriesOffset = getLinearization()->getMainXRefEntriesOffset();
 
1135
  }
 
1136
 
 
1137
  return mainXRefEntriesOffset;
 
1138
}
 
1139
 
 
1140
int PDFDoc::getNumPages()
 
1141
{
 
1142
  if (isLinearized()) {
 
1143
    int n;
 
1144
    if ((n = getLinearization()->getNumPages())) {
 
1145
      return n;
 
1146
    }
 
1147
  }
 
1148
 
 
1149
  return catalog->getNumPages();
 
1150
}
 
1151
 
 
1152
Page *PDFDoc::parsePage(int page)
 
1153
{
 
1154
  Page *p = NULL;
 
1155
  Object obj;
 
1156
  Ref pageRef;
 
1157
  Dict *pageDict;
 
1158
 
 
1159
  pageRef.num = getHints()->getPageObjectNum(page);
 
1160
  if (!pageRef.num) {
 
1161
    error(-1, "Failed to get object num from hint tables for page %d", page);
 
1162
    return NULL;
 
1163
  }
 
1164
 
 
1165
  // check for bogus ref - this can happen in corrupted PDF files
 
1166
  if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) {
 
1167
    error(-1, "Invalid object num (%d) for page %d", pageRef.num, page);
 
1168
    return NULL;
 
1169
  }
 
1170
 
 
1171
  pageRef.gen = xref->getEntry(pageRef.num)->gen;
 
1172
  xref->fetch(pageRef.num, pageRef.gen, &obj);
 
1173
  if (!obj.isDict()) {
 
1174
    obj.free();
 
1175
    error(-1, "Object (%d %d) is not a pageDict", pageRef.num, pageRef.gen);
 
1176
    return NULL;
 
1177
  }
 
1178
  pageDict = obj.getDict();
 
1179
 
 
1180
  p = new Page(xref, page, pageDict, pageRef,
 
1181
               new PageAttrs(NULL, pageDict), catalog->getForm());
 
1182
  obj.free();
 
1183
 
 
1184
  return p;
 
1185
}
 
1186
 
 
1187
Page *PDFDoc::getPage(int page)
 
1188
{
 
1189
  if ((page < 1) || page > getNumPages()) return NULL;
 
1190
 
 
1191
  if (isLinearized()) {
 
1192
    if (!pageCache) {
 
1193
      pageCache = (Page **) gmallocn(getNumPages(), sizeof(Page *));
 
1194
      for (int i = 0; i < getNumPages(); i++) {
 
1195
        pageCache[i] = NULL;
 
1196
      }
 
1197
    }
 
1198
    if (!pageCache[page-1]) {
 
1199
      pageCache[page-1] = parsePage(page);
 
1200
    }
 
1201
    if (pageCache[page-1]) {
 
1202
       return pageCache[page-1];
 
1203
    } else {
 
1204
       error(-1, "Failed parsing page %d using hint tables", page);
 
1205
    }
 
1206
  }
 
1207
 
 
1208
  return catalog->getPage(page);
 
1209
}