1
//========================================================================
5
// Copyright 1996-2002 Glyph & Cog, LLC
7
//========================================================================
11
#ifdef USE_GCC_PRAGMAS
12
#pragma implementation
29
#include "errorcodes.h"
32
//------------------------------------------------------------------------
34
#define xrefSearchSize 1024 // read this many bytes at end of file
35
// to look for 'startxref'
38
//------------------------------------------------------------------------
40
//------------------------------------------------------------------------
42
#define permPrint (1<<2)
43
#define permChange (1<<3)
44
#define permCopy (1<<4)
45
#define permNotes (1<<5)
46
#define defPermFlags 0xfffc
49
//------------------------------------------------------------------------
51
//------------------------------------------------------------------------
53
XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
66
start = str->getStart();
69
// if there was a problem with the trailer,
70
// try to reconstruct the xref table
72
if (!(ok = constructXRef())) {
77
// trailer is ok - read the xref table
79
entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
80
for (i = 0; i < size; ++i) {
81
entries[i].offset = 0xffffffff;
82
entries[i].used = gFalse;
84
while (readXRef(&pos)) ;
86
// if there was a problem with the xref table,
87
// try to reconstruct it
92
if (!(ok = constructXRef())) {
99
// now set the trailer dictionary's xref pointer so we can fetch
100
// indirect objects from it
101
trailerDict.getDict()->setXRef(this);
103
// check for encryption
104
#ifndef NO_DECRYPTION
107
if (checkEncrypted(ownerPassword, userPassword)) {
109
errCode = errEncrypted;
122
// Read startxref position, xref table size, and root. Returns
123
// first xref position.
124
Guint XRef::readTrailer() {
127
char buf[xrefSearchSize+1];
134
// read last xrefSearchSize bytes
135
str->setPos(xrefSearchSize, -1);
136
for (n = 0; n < xrefSearchSize; ++n) {
137
if ((c = str->getChar()) == EOF)
144
for (i = n - 9; i >= 0; --i) {
145
if (!strncmp(&buf[i], "startxref", 9))
150
for (p = &buf[i+9]; isspace(*p); ++p) ;
151
pos = lastXRefPos = strToUnsigned(p);
153
// find trailer dict by looking after first xref table
154
// (NB: we can't just use the trailer dict at the end of the file --
155
// this won't work for linearized files.)
156
str->setPos(start + pos);
157
for (i = 0; i < 4; ++i)
158
buf[i] = str->getChar();
159
if (strncmp(buf, "xref", 4))
163
str->setPos(start + pos1);
164
for (i = 0; i < 35; ++i) {
165
if ((c = str->getChar()) == EOF)
169
if (!strncmp(buf, "trailer", 7))
172
while (isspace(*p)) ++p;
173
while ('0' <= *p && *p <= '9') ++p;
174
while (isspace(*p)) ++p;
176
while ('0' <= *p && *p <= '9') ++p;
177
while (isspace(*p)) ++p;
180
pos1 += (p - buf) + n * 20;
186
parser = new Parser(NULL,
188
str->makeSubStream(start + pos1, gFalse, 0, &obj)));
189
parser->getObj(&trailerDict);
190
if (trailerDict.isDict()) {
191
trailerDict.dictLookupNF("Size", &obj);
197
trailerDict.dictLookupNF("Root", &obj);
199
rootNum = obj.getRefNum();
200
rootGen = obj.getRefGen();
210
// return first xref position
214
// Read an xref table and the prev pointer from the trailer.
215
GBool XRef::readXRef(Guint *pos) {
220
int first, newSize, n, i, j;
223
// seek to xref in stream
224
str->setPos(start + *pos);
226
// make sure it's an xref table
227
while ((c = str->getChar()) != EOF && isspace(c)) ;
229
s[1] = (char)str->getChar();
230
s[2] = (char)str->getChar();
231
s[3] = (char)str->getChar();
232
if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
238
while ((c = str->lookChar()) != EOF && isspace(c)) {
244
for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
252
while ((c = str->lookChar()) != EOF && isspace(c)) {
255
for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
263
while ((c = str->lookChar()) != EOF && isspace(c)) {
266
// check for buggy PDF files with an incorrect (too small) xref
268
if (first + n > size) {
269
newSize = size + 256;
270
entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
271
for (i = size; i < newSize; ++i) {
272
entries[i].offset = 0xffffffff;
273
entries[i].used = gFalse;
277
for (i = first; i < first + n; ++i) {
278
for (j = 0; j < 20; ++j) {
279
if ((c = str->getChar()) == EOF) {
284
if (entries[i].offset == 0xffffffff) {
286
entries[i].offset = strToUnsigned(s);
288
entries[i].gen = atoi(&s[11]);
290
entries[i].used = gTrue;
291
} else if (s[17] == 'f') {
292
entries[i].used = gFalse;
296
// PDF files of patents from the IBM Intellectual Property
297
// Network have a bug: the xref table claims to start at 1
299
if (i == 1 && first == 1 &&
300
entries[1].offset == 0 && entries[1].gen == 65535 &&
303
entries[0] = entries[1];
304
entries[1].offset = 0xffffffff;
310
// read prev pointer from trailer dictionary
312
parser = new Parser(NULL,
314
str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
315
parser->getObj(&obj);
316
if (!obj.isCmd("trailer")) {
320
parser->getObj(&obj);
324
obj.getDict()->lookupNF("Prev", &obj2);
326
*pos = (Guint)obj2.getInt();
344
// Attempt to construct an xref table for a damaged file.
345
GBool XRef::constructXRef() {
357
error(0, "PDF file is damaged - attempting to reconstruct xref table...");
359
streamEndsLen = streamEndsSize = 0;
364
if (!str->getLine(buf, 256)) {
369
// got trailer dictionary
370
if (!strncmp(p, "trailer", 7)) {
372
parser = new Parser(NULL,
374
str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
375
if (!trailerDict.isNone())
377
parser->getObj(&trailerDict);
378
if (trailerDict.isDict()) {
379
trailerDict.dictLookupNF("Root", &obj);
381
rootNum = obj.getRefNum();
382
rootGen = obj.getRefGen();
392
} else if (isdigit(*p)) {
396
} while (*p && isdigit(*p));
400
} while (*p && isspace(*p));
405
} while (*p && isdigit(*p));
409
} while (*p && isspace(*p));
410
if (!strncmp(p, "obj", 3)) {
412
newSize = (num + 1 + 255) & ~255;
413
entries = (XRefEntry *)
414
grealloc(entries, newSize * sizeof(XRefEntry));
415
for (i = size; i < newSize; ++i) {
416
entries[i].offset = 0xffffffff;
417
entries[i].used = gFalse;
421
if (!entries[num].used || gen >= entries[num].gen) {
422
entries[num].offset = pos - start;
423
entries[num].gen = gen;
424
entries[num].used = gTrue;
431
} else if (!strncmp(p, "endstream", 9)) {
432
if (streamEndsLen == streamEndsSize) {
433
streamEndsSize += 64;
434
streamEnds = (Guint *)grealloc(streamEnds,
435
streamEndsSize * sizeof(int));
437
streamEnds[streamEndsLen++] = pos;
444
error(-1, "Couldn't find trailer dictionary");
448
#ifndef NO_DECRYPTION
449
GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
450
Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
451
Object ownerKey, userKey, permissions, fileID, fileID1;
457
permFlags = defPermFlags;
458
ownerPasswordOk = gFalse;
459
trailerDict.dictLookup("Encrypt", &encrypt);
460
if ((encrypted1 = encrypt.isDict())) {
462
encrypt.dictLookup("Filter", &filterObj);
463
if (filterObj.isName("Standard")) {
464
encrypt.dictLookup("V", &versionObj);
465
encrypt.dictLookup("R", &revisionObj);
466
encrypt.dictLookup("Length", &lengthObj);
467
encrypt.dictLookup("O", &ownerKey);
468
encrypt.dictLookup("U", &userKey);
469
encrypt.dictLookup("P", &permissions);
470
trailerDict.dictLookup("ID", &fileID);
471
if (versionObj.isInt() &&
472
revisionObj.isInt() &&
473
ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
474
userKey.isString() && userKey.getString()->getLength() == 32 &&
475
permissions.isInt() &&
477
encVersion = versionObj.getInt();
478
encRevision = revisionObj.getInt();
479
if (lengthObj.isInt()) {
480
keyLength = lengthObj.getInt() / 8;
484
permFlags = permissions.getInt();
485
if (encVersion >= 1 && encVersion <= 2 &&
486
encRevision >= 2 && encRevision <= 3) {
487
fileID.arrayGet(0, &fileID1);
488
if (fileID1.isString()) {
489
if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
490
ownerKey.getString(), userKey.getString(),
491
permFlags, fileID1.getString(),
492
ownerPassword, userPassword, fileKey,
494
if (ownerPassword && !ownerPasswordOk) {
495
error(-1, "Incorrect owner password");
499
error(-1, "Incorrect password");
502
error(-1, "Weird encryption info");
506
error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
507
encVersion, encRevision);
510
error(-1, "Weird encryption info");
520
error(-1, "Unknown security handler '%s'",
521
filterObj.isName() ? filterObj.getName() : "???");
527
// this flag has to be set *after* we read the O/U/P strings
528
encrypted = encrypted1;
533
GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
537
trailerDict.dictLookup("Encrypt", &obj);
538
if ((encrypted = !obj.isNull())) {
539
error(-1, "PDF file is encrypted and this version of the Xpdf tools");
540
error(-1, "was built without decryption support.");
547
GBool XRef::okToPrint(GBool ignoreOwnerPW) {
548
#ifndef NO_DECRYPTION
549
if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
556
GBool XRef::okToChange(GBool ignoreOwnerPW) {
557
#ifndef NO_DECRYPTION
558
if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
565
GBool XRef::okToCopy(GBool ignoreOwnerPW) {
566
#ifndef NO_DECRYPTION
567
if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
574
GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
575
#ifndef NO_DECRYPTION
576
if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
583
Object *XRef::fetch(int num, int gen, Object *obj) {
586
Object obj1, obj2, obj3;
588
// check for bogus ref - this can happen in corrupted PDF files
589
if (num < 0 || num >= size) {
595
if (e->gen == gen && e->offset != 0xffffffff) {
597
parser = new Parser(this,
599
str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
600
parser->getObj(&obj1);
601
parser->getObj(&obj2);
602
parser->getObj(&obj3);
603
if (obj1.isInt() && obj1.getInt() == num &&
604
obj2.isInt() && obj2.getInt() == gen &&
606
#ifndef NO_DECRYPTION
607
parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
625
Object *XRef::getDocInfo(Object *obj) {
626
return trailerDict.dictLookup("Info", obj);
629
// Added for the pdftex project.
630
Object *XRef::getDocInfoNF(Object *obj) {
631
return trailerDict.dictLookupNF("Info", obj);
634
GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
637
if (streamEndsLen == 0 ||
638
streamStart > streamEnds[streamEndsLen - 1]) {
643
b = streamEndsLen - 1;
644
// invariant: streamEnds[a] < streamStart <= streamEnds[b]
647
if (streamStart <= streamEnds[m]) {
653
*streamEnd = streamEnds[b];
657
Guint XRef::strToUnsigned(char *s) {
663
for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
664
x = 10 * x + (*p - '0');