1 //========================================================================
5 // Copyright 1996-2003 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) {
145 for (i = n - 9; i >= 0; --i) {
146 if (!strncmp(&buf[i], "startxref", 9)) {
153 for (p = &buf[i+9]; isspace(*p); ++p) ;
154 pos = lastXRefPos = strToUnsigned(p);
156 // find trailer dict by looking after first xref table
157 // (NB: we can't just use the trailer dict at the end of the file --
158 // this won't work for linearized files.)
159 str->setPos(start + pos);
160 for (i = 0; i < 4; ++i) {
161 buf[i] = str->getChar();
163 if (strncmp(buf, "xref", 4)) {
168 str->setPos(start + pos1);
169 for (i = 0; i < 35; ++i) {
170 if ((c = str->getChar()) == EOF) {
175 if (!strncmp(buf, "trailer", 7)) {
179 while (isspace(*p)) ++p;
180 while ('0' <= *p && *p <= '9') ++p;
181 while (isspace(*p)) ++p;
183 while ('0' <= *p && *p <= '9') ++p;
184 while (isspace(*p)) ++p;
188 pos1 += (p - buf) + n * 20;
194 parser = new Parser(NULL,
196 str->makeSubStream(start + pos1, gFalse, 0, &obj)));
197 parser->getObj(&trailerDict);
198 if (trailerDict.isDict()) {
199 trailerDict.dictLookupNF("Size", &obj);
206 trailerDict.dictLookupNF("Root", &obj);
208 rootNum = obj.getRefNum();
209 rootGen = obj.getRefGen();
219 // return first xref position
232 // Read an xref table and the prev pointer from the trailer.
233 GBool XRef::readXRef(Guint *pos) {
238 int first, newSize, n, i, j;
241 // seek to xref in stream
242 str->setPos(start + *pos);
244 // make sure it's an xref table
245 while ((c = str->getChar()) != EOF && isspace(c)) ;
247 s[1] = (char)str->getChar();
248 s[2] = (char)str->getChar();
249 s[3] = (char)str->getChar();
250 if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
256 while ((c = str->lookChar()) != EOF && isspace(c)) {
262 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
270 while ((c = str->lookChar()) != EOF && isspace(c)) {
273 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
281 while ((c = str->lookChar()) != EOF && isspace(c)) {
284 // check for buggy PDF files with an incorrect (too small) xref
286 if (first + n > size) {
288 entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
289 for (i = size; i < newSize; ++i) {
290 entries[i].offset = 0xffffffff;
291 entries[i].used = gFalse;
295 for (i = first; i < first + n; ++i) {
296 for (j = 0; j < 20; ++j) {
297 if ((c = str->getChar()) == EOF) {
302 if (entries[i].offset == 0xffffffff) {
304 entries[i].offset = strToUnsigned(s);
306 entries[i].gen = atoi(&s[11]);
308 entries[i].used = gTrue;
309 } else if (s[17] == 'f') {
310 entries[i].used = gFalse;
314 // PDF files of patents from the IBM Intellectual Property
315 // Network have a bug: the xref table claims to start at 1
317 if (i == 1 && first == 1 &&
318 entries[1].offset == 0 && entries[1].gen == 65535 &&
321 entries[0] = entries[1];
322 entries[1].offset = 0xffffffff;
328 // read prev pointer from trailer dictionary
330 parser = new Parser(NULL,
332 str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
333 parser->getObj(&obj);
334 if (!obj.isCmd("trailer")) {
338 parser->getObj(&obj);
342 obj.getDict()->lookupNF("Prev", &obj2);
344 *pos = (Guint)obj2.getInt();
346 } else if (obj2.isRef()) {
347 // certain buggy PDF generators generate "/Prev NNN 0 R" instead
349 *pos = (Guint)obj2.getRefNum();
367 // Attempt to construct an xref table for a damaged file.
368 GBool XRef::constructXRef() {
380 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
382 streamEndsLen = streamEndsSize = 0;
387 if (!str->getLine(buf, 256)) {
392 // got trailer dictionary
393 if (!strncmp(p, "trailer", 7)) {
396 parser = new Parser(NULL,
398 str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
399 if (!trailerDict.isNone()) {
402 parser->getObj(&trailerDict);
403 if (trailerDict.isDict()) {
404 trailerDict.dictLookupNF("Root", &obj);
406 rootNum = obj.getRefNum();
407 rootGen = obj.getRefGen();
417 } else if (isdigit(*p)) {
421 } while (*p && isdigit(*p));
425 } while (*p && isspace(*p));
430 } while (*p && isdigit(*p));
434 } while (*p && isspace(*p));
435 if (!strncmp(p, "obj", 3)) {
437 newSize = (num + 1 + 255) & ~255;
438 entries = (XRefEntry *)
439 grealloc(entries, newSize * sizeof(XRefEntry));
440 for (i = size; i < newSize; ++i) {
441 entries[i].offset = 0xffffffff;
442 entries[i].used = gFalse;
446 if (!entries[num].used || gen >= entries[num].gen) {
447 entries[num].offset = pos - start;
448 entries[num].gen = gen;
449 entries[num].used = gTrue;
456 } else if (!strncmp(p, "endstream", 9)) {
457 if (streamEndsLen == streamEndsSize) {
458 streamEndsSize += 64;
459 streamEnds = (Guint *)grealloc(streamEnds,
460 streamEndsSize * sizeof(int));
462 streamEnds[streamEndsLen++] = pos;
469 error(-1, "Couldn't find trailer dictionary");
473 #ifndef NO_DECRYPTION
474 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
475 Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
476 Object ownerKey, userKey, permissions, fileID, fileID1;
481 encVersion = encRevision = 0;
484 permFlags = defPermFlags;
485 ownerPasswordOk = gFalse;
486 trailerDict.dictLookup("Encrypt", &encrypt);
487 if ((encrypted1 = encrypt.isDict())) {
489 encrypt.dictLookup("Filter", &filterObj);
490 if (filterObj.isName("Standard")) {
491 encrypt.dictLookup("V", &versionObj);
492 encrypt.dictLookup("R", &revisionObj);
493 encrypt.dictLookup("Length", &lengthObj);
494 encrypt.dictLookup("O", &ownerKey);
495 encrypt.dictLookup("U", &userKey);
496 encrypt.dictLookup("P", &permissions);
497 trailerDict.dictLookup("ID", &fileID);
498 if (versionObj.isInt() &&
499 revisionObj.isInt() &&
500 ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
501 userKey.isString() && userKey.getString()->getLength() == 32 &&
502 permissions.isInt() &&
504 encVersion = versionObj.getInt();
505 encRevision = revisionObj.getInt();
506 if (lengthObj.isInt()) {
507 keyLength = lengthObj.getInt() / 8;
511 permFlags = permissions.getInt();
512 if (encVersion >= 1 && encVersion <= 2 &&
513 encRevision >= 2 && encRevision <= 3) {
514 fileID.arrayGet(0, &fileID1);
515 if (fileID1.isString()) {
516 if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
517 ownerKey.getString(), userKey.getString(),
518 permFlags, fileID1.getString(),
519 ownerPassword, userPassword, fileKey,
521 if (ownerPassword && !ownerPasswordOk) {
522 error(-1, "Incorrect owner password");
526 error(-1, "Incorrect password");
529 error(-1, "Weird encryption info");
533 error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
534 encVersion, encRevision);
537 error(-1, "Weird encryption info");
547 error(-1, "Unknown security handler '%s'",
548 filterObj.isName() ? filterObj.getName() : "???");
554 // this flag has to be set *after* we read the O/U/P strings
555 encrypted = encrypted1;
560 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
564 trailerDict.dictLookup("Encrypt", &obj);
565 if ((encrypted = !obj.isNull())) {
566 error(-1, "PDF file is encrypted and this version of the Xpdf tools");
567 error(-1, "was built without decryption support.");
574 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
575 #ifndef NO_DECRYPTION
576 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
582 GBool XRef::okToChange(GBool ignoreOwnerPW) {
583 #ifndef NO_DECRYPTION
584 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
590 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
591 #ifndef NO_DECRYPTION
592 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
598 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
599 #ifndef NO_DECRYPTION
600 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
606 Object *XRef::fetch(int num, int gen, Object *obj) {
609 Object obj1, obj2, obj3;
611 // check for bogus ref - this can happen in corrupted PDF files
612 if (num < 0 || num >= size) {
618 if (e->gen == gen && e->offset != 0xffffffff) {
620 parser = new Parser(this,
622 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
623 parser->getObj(&obj1);
624 parser->getObj(&obj2);
625 parser->getObj(&obj3);
626 if (obj1.isInt() && obj1.getInt() == num &&
627 obj2.isInt() && obj2.getInt() == gen &&
629 #ifndef NO_DECRYPTION
630 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
648 Object *XRef::getDocInfo(Object *obj) {
649 return trailerDict.dictLookup("Info", obj);
652 // Added for the pdftex project.
653 Object *XRef::getDocInfoNF(Object *obj) {
654 return trailerDict.dictLookupNF("Info", obj);
657 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
660 if (streamEndsLen == 0 ||
661 streamStart > streamEnds[streamEndsLen - 1]) {
666 b = streamEndsLen - 1;
667 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
670 if (streamStart <= streamEnds[m]) {
676 *streamEnd = streamEnds[b];
680 Guint XRef::strToUnsigned(char *s) {
686 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
687 x = 10 * x + (*p - '0');