1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
29 //------------------------------------------------------------------------
31 #define xrefSearchSize 1024 // read this many bytes at end of file
32 // to look for 'startxref'
35 //------------------------------------------------------------------------
37 //------------------------------------------------------------------------
39 #define permPrint (1<<2)
40 #define permChange (1<<3)
41 #define permCopy (1<<4)
42 #define permNotes (1<<5)
43 #define defPermFlags 0xfffc
46 //------------------------------------------------------------------------
47 // The global xref table
48 //------------------------------------------------------------------------
52 //------------------------------------------------------------------------
54 //------------------------------------------------------------------------
56 XRef::XRef(BaseStream *str, GString *userPassword) {
67 // get rid of old xref (otherwise it will try to fetch the Root object
68 // in the new document, using the old xref)
74 start = str->getStart();
77 // if there was a problem with the trailer,
78 // try to reconstruct the xref table
80 if (!(ok = constructXRef())) {
85 // trailer is ok - read the xref table
87 entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
88 for (i = 0; i < size; ++i) {
89 entries[i].offset = -1;
90 entries[i].used = gFalse;
92 while (readXRef(&pos)) ;
94 // if there was a problem with the xref table,
95 // try to reconstruct it
100 if (!(ok = constructXRef())) {
107 // set up new xref table
110 // check for encryption
111 #ifndef NO_DECRYPTION
114 if (checkEncrypted(userPassword)) {
129 // Read startxref position, xref table size, and root. Returns
130 // first xref position.
131 int XRef::readTrailer() {
134 char buf[xrefSearchSize+1];
140 // read last xrefSearchSize bytes
141 str->setPos(-xrefSearchSize);
142 for (n = 0; n < xrefSearchSize; ++n) {
143 if ((c = str->getChar()) == EOF)
150 for (i = n - 9; i >= 0; --i) {
151 if (!strncmp(&buf[i], "startxref", 9))
156 for (p = &buf[i+9]; isspace(*p); ++p) ;
157 pos = lastXRefPos = atoi(p);
159 // find trailer dict by looking after first xref table
160 // (NB: we can't just use the trailer dict at the end of the file --
161 // this won't work for linearized files.)
162 str->setPos(start + pos);
163 for (i = 0; i < 4; ++i)
164 buf[i] = str->getChar();
165 if (strncmp(buf, "xref", 4))
169 str->setPos(start + pos1);
170 for (i = 0; i < 35; ++i) {
171 if ((c = str->getChar()) == EOF)
175 if (!strncmp(buf, "trailer", 7))
178 while (isspace(*p)) ++p;
179 while ('0' <= *p && *p <= '9') ++p;
180 while (isspace(*p)) ++p;
182 while ('0' <= *p && *p <= '9') ++p;
183 while (isspace(*p)) ++p;
186 pos1 += (p - buf) + n * 20;
192 parser = new Parser(new Lexer(str->makeSubStream(start + pos1, -1, &obj)));
193 parser->getObj(&trailerDict);
194 if (trailerDict.isDict()) {
195 trailerDict.dictLookupNF("Size", &obj);
201 trailerDict.dictLookupNF("Root", &obj);
203 rootNum = obj.getRefNum();
204 rootGen = obj.getRefGen();
214 // return first xref position
218 // Read an xref table and the prev pointer from the trailer.
219 GBool XRef::readXRef(int *pos) {
227 // seek to xref in stream
228 str->setPos(start + *pos);
230 // make sure it's an xref table
231 while ((c = str->getChar()) != EOF && isspace(c)) ;
233 s[1] = (char)str->getChar();
234 s[2] = (char)str->getChar();
235 s[3] = (char)str->getChar();
236 if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f'))
241 while ((c = str->lookChar()) != EOF && isspace(c))
245 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
251 while ((c = str->lookChar()) != EOF && isspace(c))
253 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
259 while ((c = str->lookChar()) != EOF && isspace(c))
261 for (i = first; i < first + n; ++i) {
262 for (j = 0; j < 20; ++j) {
263 if ((c = str->getChar()) == EOF)
267 if (entries[i].offset < 0) {
269 entries[i].offset = atoi(s);
271 entries[i].gen = atoi(&s[11]);
273 entries[i].used = gTrue;
274 else if (s[17] == 'f')
275 entries[i].used = gFalse;
279 //~ PDF files of patents from the IBM Intellectual Property
280 //~ Network have a bug: the xref table claims to start at 1
282 if (i == 1 && first == 1 &&
283 entries[1].offset == 0 && entries[1].gen == 65535 &&
286 entries[0] = entries[1];
287 entries[1].offset = -1;
294 // read prev pointer from trailer dictionary
296 parser = new Parser(new Lexer(str->makeSubStream(str->getPos(), -1, &obj)));
297 parser->getObj(&obj);
298 if (!obj.isCmd("trailer"))
301 parser->getObj(&obj);
304 obj.getDict()->lookupNF("Prev", &obj2);
306 *pos = obj2.getInt();
324 // Attempt to construct an xref table for a damaged file.
325 GBool XRef::constructXRef() {
337 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
339 streamEndsLen = streamEndsSize = 0;
344 if (!str->getLine(buf, 256)) {
349 // got trailer dictionary
350 if (!strncmp(p, "trailer", 7)) {
352 parser = new Parser(new Lexer(
353 str->makeSubStream(start + pos + 7, -1, &obj)));
354 if (!trailerDict.isNone())
356 parser->getObj(&trailerDict);
357 if (trailerDict.isDict()) {
358 trailerDict.dictLookupNF("Root", &obj);
360 rootNum = obj.getRefNum();
361 rootGen = obj.getRefGen();
371 } else if (isdigit(*p)) {
375 } while (*p && isdigit(*p));
379 } while (*p && isspace(*p));
384 } while (*p && isdigit(*p));
388 } while (*p && isspace(*p));
389 if (!strncmp(p, "obj", 3)) {
391 newSize = (num + 1 + 255) & ~255;
392 entries = (XRefEntry *)
393 grealloc(entries, newSize * sizeof(XRefEntry));
394 for (i = size; i < newSize; ++i) {
395 entries[i].offset = -1;
396 entries[i].used = gFalse;
400 if (!entries[num].used || gen >= entries[num].gen) {
401 entries[num].offset = pos - start;
402 entries[num].gen = gen;
403 entries[num].used = gTrue;
410 } else if (!strncmp(p, "endstream", 9)) {
411 if (streamEndsLen == streamEndsSize) {
412 streamEndsSize += 64;
413 streamEnds = (int *)grealloc(streamEnds, streamEndsSize * sizeof(int));
415 streamEnds[streamEndsLen++] = pos;
422 error(-1, "Couldn't find trailer dictionary");
426 #ifndef NO_DECRYPTION
427 GBool XRef::checkEncrypted(GString *userPassword) {
428 Object encrypt, ownerKey, userKey, permissions, fileID, fileID1;
434 permFlags = defPermFlags;
435 trailerDict.dictLookup("Encrypt", &encrypt);
436 if ((encrypted1 = encrypt.isDict())) {
438 encrypt.dictLookup("O", &ownerKey);
439 encrypt.dictLookup("U", &userKey);
440 encrypt.dictLookup("P", &permissions);
441 trailerDict.dictLookup("ID", &fileID);
442 if (ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
443 userKey.isString() && userKey.getString()->getLength() == 32 &&
444 permissions.isInt() &&
446 permFlags = permissions.getInt();
447 fileID.arrayGet(0, &fileID1);
448 if (fileID1.isString()) {
449 if (Decrypt::makeFileKey(ownerKey.getString(), userKey.getString(),
450 permFlags, fileID1.getString(),
451 userPassword, fileKey)) {
454 error(-1, "Incorrect user password");
457 error(-1, "Weird encryption info");
461 error(-1, "Weird encryption info");
470 // this flag has to be set *after* we read the O/U/P strings
471 encrypted = encrypted1;
476 GBool XRef::checkEncrypted(GString *userPassword) {
480 trailerDict.dictLookup("Encrypt", &obj);
481 if ((encrypted = !obj.isNull())) {
482 error(-1, "PDF file is encrypted and cannot be displayed");
483 error(-1, "* Decryption support is currently not included in xpdf");
484 error(-1, "* due to legal restrictions: the U.S.A. still has bogus");
485 error(-1, "* export controls on cryptography software.");
492 GBool XRef::okToPrint() {
493 #ifndef NO_DECRYPTION
494 if (!(permFlags & permPrint)) {
501 GBool XRef::okToChange() {
502 #ifndef NO_DECRYPTION
503 if (!(permFlags & permChange)) {
510 GBool XRef::okToCopy() {
511 #ifndef NO_DECRYPTION
512 if (!(permFlags & permCopy)) {
519 GBool XRef::okToAddNotes() {
520 #ifndef NO_DECRYPTION
521 if (!(permFlags & permNotes)) {
528 Object *XRef::fetch(int num, int gen, Object *obj) {
531 Object obj1, obj2, obj3;
533 // check for bogus ref - this can happen in corrupted PDF files
534 if (num < 0 || num >= size) {
540 if (e->gen == gen && e->offset >= 0) {
542 parser = new Parser(new Lexer(
543 str->makeSubStream(start + e->offset, -1, &obj1)));
544 parser->getObj(&obj1);
545 parser->getObj(&obj2);
546 parser->getObj(&obj3);
547 if (obj1.isInt() && obj1.getInt() == num &&
548 obj2.isInt() && obj2.getInt() == gen &&
550 #ifndef NO_DECRYPTION
551 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, num, gen);
568 Object *XRef::getDocInfo(Object *obj) {
569 return trailerDict.dictLookup("Info", obj);
572 int XRef::getStreamEnd(int start) {
575 if (streamEndsLen == 0 ||
576 start > streamEnds[streamEndsLen - 1]) {
581 b = streamEndsLen - 1;
582 // invariant: streamEnds[a] < start <= streamEnds[b]
585 if (start <= streamEnds[m]) {
591 return streamEnds[b];