1 //========================================================================
5 // Copyright 1996-2002 Glyph & Cog, LLC
7 //========================================================================
10 #pragma implementation
28 #include "ErrorCodes.h"
31 //------------------------------------------------------------------------
33 #define xrefSearchSize 1024 // read this many bytes at end of file
34 // to look for 'startxref'
37 //------------------------------------------------------------------------
39 //------------------------------------------------------------------------
41 #define permPrint (1<<2)
42 #define permChange (1<<3)
43 #define permCopy (1<<4)
44 #define permNotes (1<<5)
45 #define defPermFlags 0xfffc
48 //------------------------------------------------------------------------
50 //------------------------------------------------------------------------
52 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
65 start = str->getStart();
68 // if there was a problem with the trailer,
69 // try to reconstruct the xref table
71 if (!(ok = constructXRef())) {
76 // trailer is ok - read the xref table
78 entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
79 for (i = 0; i < size; ++i) {
80 entries[i].offset = 0xffffffff;
81 entries[i].used = gFalse;
83 while (readXRef(&pos)) ;
85 // if there was a problem with the xref table,
86 // try to reconstruct it
91 if (!(ok = constructXRef())) {
98 // now set the trailer dictionary's xref pointer so we can fetch
99 // indirect objects from it
100 trailerDict.getDict()->setXRef(this);
102 // check for encryption
103 #ifndef NO_DECRYPTION
106 if (checkEncrypted(ownerPassword, userPassword)) {
108 errCode = errEncrypted;
121 // Read startxref position, xref table size, and root. Returns
122 // first xref position.
123 Guint XRef::readTrailer() {
126 char buf[xrefSearchSize+1];
133 // read last xrefSearchSize bytes
134 str->setPos(xrefSearchSize, -1);
135 for (n = 0; n < xrefSearchSize; ++n) {
136 if ((c = str->getChar()) == EOF)
143 for (i = n - 9; i >= 0; --i) {
144 if (!strncmp(&buf[i], "startxref", 9))
149 for (p = &buf[i+9]; isspace(*p); ++p) ;
150 pos = lastXRefPos = strToUnsigned(p);
152 // find trailer dict by looking after first xref table
153 // (NB: we can't just use the trailer dict at the end of the file --
154 // this won't work for linearized files.)
155 str->setPos(start + pos);
156 for (i = 0; i < 4; ++i)
157 buf[i] = str->getChar();
158 if (strncmp(buf, "xref", 4))
162 str->setPos(start + pos1);
163 for (i = 0; i < 35; ++i) {
164 if ((c = str->getChar()) == EOF)
168 if (!strncmp(buf, "trailer", 7))
171 while (isspace(*p)) ++p;
172 while ('0' <= *p && *p <= '9') ++p;
173 while (isspace(*p)) ++p;
175 while ('0' <= *p && *p <= '9') ++p;
176 while (isspace(*p)) ++p;
179 pos1 += (p - buf) + n * 20;
185 parser = new Parser(NULL,
187 str->makeSubStream(start + pos1, gFalse, 0, &obj)));
188 parser->getObj(&trailerDict);
189 if (trailerDict.isDict()) {
190 trailerDict.dictLookupNF("Size", &obj);
196 trailerDict.dictLookupNF("Root", &obj);
198 rootNum = obj.getRefNum();
199 rootGen = obj.getRefGen();
209 // return first xref position
213 // Read an xref table and the prev pointer from the trailer.
214 GBool XRef::readXRef(Guint *pos) {
219 int first, newSize, n, i, j;
222 // seek to xref in stream
223 str->setPos(start + *pos);
225 // make sure it's an xref table
226 while ((c = str->getChar()) != EOF && isspace(c)) ;
228 s[1] = (char)str->getChar();
229 s[2] = (char)str->getChar();
230 s[3] = (char)str->getChar();
231 if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
237 while ((c = str->lookChar()) != EOF && isspace(c)) {
243 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
251 while ((c = str->lookChar()) != EOF && isspace(c)) {
254 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
262 while ((c = str->lookChar()) != EOF && isspace(c)) {
265 // check for buggy PDF files with an incorrect (too small) xref
267 if (first + n > size) {
268 newSize = size + 256;
269 entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
270 for (i = size; i < newSize; ++i) {
271 entries[i].offset = 0xffffffff;
272 entries[i].used = gFalse;
276 for (i = first; i < first + n; ++i) {
277 for (j = 0; j < 20; ++j) {
278 if ((c = str->getChar()) == EOF) {
283 if (entries[i].offset == 0xffffffff) {
285 entries[i].offset = strToUnsigned(s);
287 entries[i].gen = atoi(&s[11]);
289 entries[i].used = gTrue;
290 } else if (s[17] == 'f') {
291 entries[i].used = gFalse;
295 // PDF files of patents from the IBM Intellectual Property
296 // Network have a bug: the xref table claims to start at 1
298 if (i == 1 && first == 1 &&
299 entries[1].offset == 0 && entries[1].gen == 65535 &&
302 entries[0] = entries[1];
303 entries[1].offset = 0xffffffff;
309 // read prev pointer from trailer dictionary
311 parser = new Parser(NULL,
313 str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
314 parser->getObj(&obj);
315 if (!obj.isCmd("trailer")) {
319 parser->getObj(&obj);
323 obj.getDict()->lookupNF("Prev", &obj2);
325 *pos = (Guint)obj2.getInt();
343 // Attempt to construct an xref table for a damaged file.
344 GBool XRef::constructXRef() {
356 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
358 streamEndsLen = streamEndsSize = 0;
363 if (!str->getLine(buf, 256)) {
368 // got trailer dictionary
369 if (!strncmp(p, "trailer", 7)) {
371 parser = new Parser(NULL,
373 str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
374 if (!trailerDict.isNone())
376 parser->getObj(&trailerDict);
377 if (trailerDict.isDict()) {
378 trailerDict.dictLookupNF("Root", &obj);
380 rootNum = obj.getRefNum();
381 rootGen = obj.getRefGen();
391 } else if (isdigit(*p)) {
395 } while (*p && isdigit(*p));
399 } while (*p && isspace(*p));
404 } while (*p && isdigit(*p));
408 } while (*p && isspace(*p));
409 if (!strncmp(p, "obj", 3)) {
411 newSize = (num + 1 + 255) & ~255;
412 entries = (XRefEntry *)
413 grealloc(entries, newSize * sizeof(XRefEntry));
414 for (i = size; i < newSize; ++i) {
415 entries[i].offset = 0xffffffff;
416 entries[i].used = gFalse;
420 if (!entries[num].used || gen >= entries[num].gen) {
421 entries[num].offset = pos - start;
422 entries[num].gen = gen;
423 entries[num].used = gTrue;
430 } else if (!strncmp(p, "endstream", 9)) {
431 if (streamEndsLen == streamEndsSize) {
432 streamEndsSize += 64;
433 streamEnds = (Guint *)grealloc(streamEnds,
434 streamEndsSize * sizeof(int));
436 streamEnds[streamEndsLen++] = pos;
443 error(-1, "Couldn't find trailer dictionary");
447 #ifndef NO_DECRYPTION
448 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
449 Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
450 Object ownerKey, userKey, permissions, fileID, fileID1;
456 permFlags = defPermFlags;
457 trailerDict.dictLookup("Encrypt", &encrypt);
458 if ((encrypted1 = encrypt.isDict())) {
460 encrypt.dictLookup("Filter", &filterObj);
461 if (filterObj.isName("Standard")) {
462 encrypt.dictLookup("V", &versionObj);
463 encrypt.dictLookup("R", &revisionObj);
464 encrypt.dictLookup("Length", &lengthObj);
465 encrypt.dictLookup("O", &ownerKey);
466 encrypt.dictLookup("U", &userKey);
467 encrypt.dictLookup("P", &permissions);
468 trailerDict.dictLookup("ID", &fileID);
469 if (versionObj.isInt() &&
470 revisionObj.isInt() &&
471 ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
472 userKey.isString() && userKey.getString()->getLength() == 32 &&
473 permissions.isInt() &&
475 encVersion = versionObj.getInt();
476 encRevision = revisionObj.getInt();
477 if (lengthObj.isInt()) {
478 keyLength = lengthObj.getInt() / 8;
482 permFlags = permissions.getInt();
483 if (encVersion >= 1 && encVersion <= 2 &&
484 encRevision >= 2 && encRevision <= 3) {
485 fileID.arrayGet(0, &fileID1);
486 if (fileID1.isString()) {
487 if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
488 ownerKey.getString(), userKey.getString(),
489 permFlags, fileID1.getString(),
490 ownerPassword, userPassword, fileKey,
492 if (ownerPassword && !ownerPasswordOk) {
493 error(-1, "Incorrect owner password");
497 error(-1, "Incorrect password");
500 error(-1, "Weird encryption info");
504 error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
505 encVersion, encRevision);
508 error(-1, "Weird encryption info");
518 error(-1, "Unknown security handler '%s'",
519 filterObj.isName() ? filterObj.getName() : "???");
525 // this flag has to be set *after* we read the O/U/P strings
526 encrypted = encrypted1;
531 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
535 trailerDict.dictLookup("Encrypt", &obj);
536 if ((encrypted = !obj.isNull())) {
537 error(-1, "PDF file is encrypted and this version of the Xpdf tools");
538 error(-1, "was built without decryption support.");
545 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
546 #ifndef NO_DECRYPTION
547 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
554 GBool XRef::okToChange(GBool ignoreOwnerPW) {
555 #ifndef NO_DECRYPTION
556 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
563 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
564 #ifndef NO_DECRYPTION
565 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
572 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
573 #ifndef NO_DECRYPTION
574 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
581 Object *XRef::fetch(int num, int gen, Object *obj) {
584 Object obj1, obj2, obj3;
586 // check for bogus ref - this can happen in corrupted PDF files
587 if (num < 0 || num >= size) {
593 if (e->gen == gen && e->offset != 0xffffffff) {
595 parser = new Parser(this,
597 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
598 parser->getObj(&obj1);
599 parser->getObj(&obj2);
600 parser->getObj(&obj3);
601 if (obj1.isInt() && obj1.getInt() == num &&
602 obj2.isInt() && obj2.getInt() == gen &&
604 #ifndef NO_DECRYPTION
605 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
623 Object *XRef::getDocInfo(Object *obj) {
624 return trailerDict.dictLookup("Info", obj);
627 // Added for the pdftex project.
628 Object *XRef::getDocInfoNF(Object *obj) {
629 return trailerDict.dictLookupNF("Info", obj);
632 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
635 if (streamEndsLen == 0 ||
636 streamStart > streamEnds[streamEndsLen - 1]) {
641 b = streamEndsLen - 1;
642 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
645 if (streamStart <= streamEnds[m]) {
651 *streamEnd = streamEnds[b];
655 Guint XRef::strToUnsigned(char *s) {
661 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
662 x = 10 * x + (*p - '0');