upgrade to xpdf 3.00.
[swftools.git] / pdf2swf / xpdf / XRef.cc
blobe0d82d23c62ef46ddff1a8c771ceeb576c436758
1 //========================================================================
2 //
3 // XRef.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <aconf.h>
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
15 #include <stdlib.h>
16 #include <stddef.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include "gmem.h"
20 #include "Object.h"
21 #include "Stream.h"
22 #include "Lexer.h"
23 #include "Parser.h"
24 #include "Dict.h"
25 #ifndef NO_DECRYPTION
26 #include "Decrypt.h"
27 #endif
28 #include "Error.h"
29 #include "ErrorCodes.h"
30 #include "XRef.h"
32 //------------------------------------------------------------------------
34 #define xrefSearchSize 1024 // read this many bytes at end of file
35 // to look for 'startxref'
37 #ifndef NO_DECRYPTION
38 //------------------------------------------------------------------------
39 // Permission bits
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
47 #endif
49 //------------------------------------------------------------------------
50 // ObjectStream
51 //------------------------------------------------------------------------
53 class ObjectStream {
54 public:
56 // Create an object stream, using object number <objStrNum>,
57 // generation 0.
58 ObjectStream(XRef *xref, int objStrNumA);
60 ~ObjectStream();
62 // Return the object number of this object stream.
63 int getObjStrNum() { return objStrNum; }
65 // Get the <objIdx>th object from this stream, which should be
66 // object number <objNum>, generation 0.
67 Object *getObject(int objIdx, int objNum, Object *obj);
69 private:
71 int objStrNum; // object number of the object stream
72 int nObjects; // number of objects in the stream
73 Object *objs; // the objects (length = nObjects)
74 int *objNums; // the object numbers (length = nObjects)
77 ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
78 Stream *str;
79 Parser *parser;
80 int *offsets;
81 Object objStr, obj1, obj2;
82 int first, i;
84 objStrNum = objStrNumA;
85 nObjects = 0;
86 objs = NULL;
87 objNums = NULL;
89 if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
90 goto err1;
93 if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
94 obj1.free();
95 goto err1;
97 nObjects = obj1.getInt();
98 obj1.free();
99 if (nObjects == 0) {
100 goto err1;
103 if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
104 obj1.free();
105 goto err1;
107 first = obj1.getInt();
108 obj1.free();
110 objs = new Object[nObjects];
111 objNums = (int *)gmalloc(nObjects * sizeof(int));
112 offsets = (int *)gmalloc(nObjects * sizeof(int));
114 // parse the header: object numbers and offsets
115 objStr.streamReset();
116 obj1.initNull();
117 str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
118 parser = new Parser(xref, new Lexer(xref, str));
119 for (i = 0; i < nObjects; ++i) {
120 parser->getObj(&obj1);
121 parser->getObj(&obj2);
122 if (!obj1.isInt() || !obj2.isInt()) {
123 obj1.free();
124 obj2.free();
125 delete parser;
126 gfree(offsets);
127 goto err1;
129 objNums[i] = obj1.getInt();
130 offsets[i] = obj2.getInt();
131 obj1.free();
132 obj2.free();
134 while (str->getChar() != EOF) ;
135 delete parser;
137 // skip to the first object - this shouldn't be necessary because
138 // the First key is supposed to be equal to offsets[0], but just in
139 // case...
140 for (i = first; i < offsets[0]; ++i) {
141 objStr.getStream()->getChar();
144 // parse the objects
145 for (i = 0; i < nObjects; ++i) {
146 obj1.initNull();
147 if (i == nObjects - 1) {
148 str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
149 } else {
150 str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
151 offsets[i+1] - offsets[i]);
153 parser = new Parser(xref, new Lexer(xref, str));
154 parser->getObj(&objs[i]);
155 while (str->getChar() != EOF) ;
156 delete parser;
159 gfree(offsets);
161 err1:
162 objStr.free();
163 return;
166 ObjectStream::~ObjectStream() {
167 int i;
169 if (objs) {
170 for (i = 0; i < nObjects; ++i) {
171 objs[i].free();
173 delete[] objs;
175 gfree(objNums);
178 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
179 if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
180 return obj->initNull();
182 return objs[objIdx].copy(obj);
185 //------------------------------------------------------------------------
186 // XRef
187 //------------------------------------------------------------------------
189 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
190 Guint pos;
191 Object obj;
193 ok = gTrue;
194 errCode = errNone;
195 size = 0;
196 entries = NULL;
197 streamEnds = NULL;
198 streamEndsLen = 0;
199 objStr = NULL;
201 // read the trailer
202 str = strA;
203 start = str->getStart();
204 pos = getStartXref();
206 // if there was a problem with the 'startxref' position, try to
207 // reconstruct the xref table
208 if (pos == 0) {
209 if (!(ok = constructXRef())) {
210 errCode = errDamaged;
211 return;
214 // read the xref table
215 } else {
216 while (readXRef(&pos)) ;
218 // if there was a problem with the xref table,
219 // try to reconstruct it
220 if (!ok) {
221 if (!(ok = constructXRef())) {
222 errCode = errDamaged;
223 return;
228 // get the root dictionary (catalog) object
229 trailerDict.dictLookupNF("Root", &obj);
230 if (obj.isRef()) {
231 rootNum = obj.getRefNum();
232 rootGen = obj.getRefGen();
233 obj.free();
234 } else {
235 obj.free();
236 if (!(ok = constructXRef())) {
237 errCode = errDamaged;
238 return;
242 // now set the trailer dictionary's xref pointer so we can fetch
243 // indirect objects from it
244 trailerDict.getDict()->setXRef(this);
246 // check for encryption
247 #ifndef NO_DECRYPTION
248 encrypted = gFalse;
249 #endif
250 if (checkEncrypted(ownerPassword, userPassword)) {
251 ok = gFalse;
252 errCode = errEncrypted;
253 return;
257 XRef::~XRef() {
258 gfree(entries);
259 trailerDict.free();
260 if (streamEnds) {
261 gfree(streamEnds);
263 if (objStr) {
264 delete objStr;
268 // Read the 'startxref' position.
269 Guint XRef::getStartXref() {
270 char buf[xrefSearchSize+1];
271 char *p;
272 int c, n, i;
274 // read last xrefSearchSize bytes
275 str->setPos(xrefSearchSize, -1);
276 for (n = 0; n < xrefSearchSize; ++n) {
277 if ((c = str->getChar()) == EOF) {
278 break;
280 buf[n] = c;
282 buf[n] = '\0';
284 // find startxref
285 for (i = n - 9; i >= 0; --i) {
286 if (!strncmp(&buf[i], "startxref", 9)) {
287 break;
290 if (i < 0) {
291 return 0;
293 for (p = &buf[i+9]; isspace(*p); ++p) ;
294 lastXRefPos = strToUnsigned(p);
296 return lastXRefPos;
299 // Read one xref table section. Also reads the associated trailer
300 // dictionary, and returns the prev pointer (if any).
301 GBool XRef::readXRef(Guint *pos) {
302 Parser *parser;
303 Object obj;
304 GBool more;
306 // start up a parser, parse one token
307 obj.initNull();
308 parser = new Parser(NULL,
309 new Lexer(NULL,
310 str->makeSubStream(start + *pos, gFalse, 0, &obj)));
311 parser->getObj(&obj);
313 // parse an old-style xref table
314 if (obj.isCmd("xref")) {
315 obj.free();
316 more = readXRefTable(parser, pos);
318 // parse an xref stream
319 } else if (obj.isInt()) {
320 obj.free();
321 if (!parser->getObj(&obj)->isInt()) {
322 goto err1;
324 obj.free();
325 if (!parser->getObj(&obj)->isCmd("obj")) {
326 goto err1;
328 obj.free();
329 if (!parser->getObj(&obj)->isStream()) {
330 goto err1;
332 more = readXRefStream(obj.getStream(), pos);
333 obj.free();
335 } else {
336 goto err1;
339 delete parser;
340 return more;
342 err1:
343 obj.free();
344 delete parser;
345 ok = gFalse;
346 return gFalse;
349 GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
350 XRefEntry entry;
351 GBool more;
352 Object obj, obj2;
353 Guint pos2;
354 int first, n, newSize, i;
356 while (1) {
357 parser->getObj(&obj);
358 if (obj.isCmd("trailer")) {
359 obj.free();
360 break;
362 if (!obj.isInt()) {
363 goto err1;
365 first = obj.getInt();
366 obj.free();
367 if (!parser->getObj(&obj)->isInt()) {
368 goto err1;
370 n = obj.getInt();
371 obj.free();
372 if (first + n > size) {
373 for (newSize = size ? 2 * size : 1024;
374 first + n > newSize;
375 newSize <<= 1) ;
376 entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
377 for (i = size; i < newSize; ++i) {
378 entries[i].offset = 0xffffffff;
379 entries[i].type = xrefEntryFree;
381 size = newSize;
383 for (i = first; i < first + n; ++i) {
384 if (!parser->getObj(&obj)->isInt()) {
385 goto err1;
387 entry.offset = (Guint)obj.getInt();
388 obj.free();
389 if (!parser->getObj(&obj)->isInt()) {
390 goto err1;
392 entry.gen = obj.getInt();
393 obj.free();
394 parser->getObj(&obj);
395 if (obj.isCmd("n")) {
396 entry.type = xrefEntryUncompressed;
397 } else if (obj.isCmd("f")) {
398 entry.type = xrefEntryFree;
399 } else {
400 goto err1;
402 obj.free();
403 if (entries[i].offset == 0xffffffff) {
404 entries[i] = entry;
405 // PDF files of patents from the IBM Intellectual Property
406 // Network have a bug: the xref table claims to start at 1
407 // instead of 0.
408 if (i == 1 && first == 1 &&
409 entries[1].offset == 0 && entries[1].gen == 65535 &&
410 entries[1].type == xrefEntryFree) {
411 i = first = 0;
412 entries[0] = entries[1];
413 entries[1].offset = 0xffffffff;
419 // read the trailer dictionary
420 if (!parser->getObj(&obj)->isDict()) {
421 goto err1;
424 // get the 'Prev' pointer
425 obj.getDict()->lookupNF("Prev", &obj2);
426 if (obj2.isInt()) {
427 *pos = (Guint)obj2.getInt();
428 more = gTrue;
429 } else if (obj2.isRef()) {
430 // certain buggy PDF generators generate "/Prev NNN 0 R" instead
431 // of "/Prev NNN"
432 *pos = (Guint)obj2.getRefNum();
433 more = gTrue;
434 } else {
435 more = gFalse;
437 obj2.free();
439 // save the first trailer dictionary
440 if (trailerDict.isNone()) {
441 obj.copy(&trailerDict);
444 // check for an 'XRefStm' key
445 if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
446 pos2 = obj2.getInt();
447 readXRef(&pos2);
448 if (!ok) {
449 goto err1;
452 obj2.free();
454 obj.free();
455 return more;
457 err1:
458 obj.free();
459 ok = gFalse;
460 return gFalse;
463 GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
464 Dict *dict;
465 int w[3];
466 GBool more;
467 Object obj, obj2, idx;
468 int newSize, first, n, i;
470 dict = xrefStr->getDict();
472 if (!dict->lookupNF("Size", &obj)->isInt()) {
473 goto err1;
475 newSize = obj.getInt();
476 obj.free();
477 if (newSize > size) {
478 entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
479 for (i = size; i < newSize; ++i) {
480 entries[i].offset = 0xffffffff;
481 entries[i].type = xrefEntryFree;
483 size = newSize;
486 if (!dict->lookupNF("W", &obj)->isArray() ||
487 obj.arrayGetLength() < 3) {
488 goto err1;
490 for (i = 0; i < 3; ++i) {
491 if (!obj.arrayGet(i, &obj2)->isInt()) {
492 obj2.free();
493 goto err1;
495 w[i] = obj2.getInt();
496 obj2.free();
498 obj.free();
500 xrefStr->reset();
501 dict->lookupNF("Index", &idx);
502 if (idx.isArray()) {
503 for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
504 if (!idx.arrayGet(i, &obj)->isInt()) {
505 idx.free();
506 goto err1;
508 first = obj.getInt();
509 obj.free();
510 if (!idx.arrayGet(i+1, &obj)->isInt()) {
511 idx.free();
512 goto err1;
514 n = obj.getInt();
515 obj.free();
516 if (!readXRefStreamSection(xrefStr, w, first, n)) {
517 idx.free();
518 goto err0;
521 } else {
522 if (!readXRefStreamSection(xrefStr, w, 0, size)) {
523 idx.free();
524 goto err0;
527 idx.free();
529 dict->lookupNF("Prev", &obj);
530 if (obj.isInt()) {
531 *pos = (Guint)obj.getInt();
532 more = gTrue;
533 } else {
534 more = gFalse;
536 obj.free();
537 if (trailerDict.isNone()) {
538 trailerDict.initDict(dict);
541 return more;
543 err1:
544 obj.free();
545 err0:
546 ok = gFalse;
547 return gFalse;
550 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
551 Guint offset;
552 int type, gen, c, newSize, i, j;
554 if (first + n > size) {
555 for (newSize = size ? 2 * size : 1024;
556 first + n > newSize;
557 newSize <<= 1) ;
558 entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
559 for (i = size; i < newSize; ++i) {
560 entries[i].offset = 0xffffffff;
561 entries[i].type = xrefEntryFree;
563 size = newSize;
565 for (i = first; i < first + n; ++i) {
566 if (w[0] == 0) {
567 type = 1;
568 } else {
569 for (type = 0, j = 0; j < w[0]; ++j) {
570 if ((c = xrefStr->getChar()) == EOF) {
571 return gFalse;
573 type = (type << 8) + c;
576 for (offset = 0, j = 0; j < w[1]; ++j) {
577 if ((c = xrefStr->getChar()) == EOF) {
578 return gFalse;
580 offset = (offset << 8) + c;
582 for (gen = 0, j = 0; j < w[2]; ++j) {
583 if ((c = xrefStr->getChar()) == EOF) {
584 return gFalse;
586 gen = (gen << 8) + c;
588 switch (type) {
589 case 0:
590 entries[i].offset = offset;
591 entries[i].gen = gen;
592 entries[i].type = xrefEntryFree;
593 break;
594 case 1:
595 entries[i].offset = offset;
596 entries[i].gen = gen;
597 entries[i].type = xrefEntryUncompressed;
598 break;
599 case 2:
600 entries[i].offset = offset;
601 entries[i].gen = gen;
602 entries[i].type = xrefEntryCompressed;
603 break;
604 default:
605 return gFalse;
609 return gTrue;
612 // Attempt to construct an xref table for a damaged file.
613 GBool XRef::constructXRef() {
614 Parser *parser;
615 Object newTrailerDict, obj;
616 char buf[256];
617 Guint pos;
618 int num, gen;
619 int newSize;
620 int streamEndsSize;
621 char *p;
622 int i;
623 GBool gotRoot;
625 gfree(entries);
626 size = 0;
627 entries = NULL;
629 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
630 gotRoot = gFalse;
631 streamEndsLen = streamEndsSize = 0;
633 str->reset();
634 while (1) {
635 pos = str->getPos();
636 if (!str->getLine(buf, 256)) {
637 break;
639 p = buf;
641 // got trailer dictionary
642 if (!strncmp(p, "trailer", 7)) {
643 obj.initNull();
644 parser = new Parser(NULL,
645 new Lexer(NULL,
646 str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
647 parser->getObj(&newTrailerDict);
648 if (newTrailerDict.isDict()) {
649 newTrailerDict.dictLookupNF("Root", &obj);
650 if (obj.isRef()) {
651 rootNum = obj.getRefNum();
652 rootGen = obj.getRefGen();
653 if (!trailerDict.isNone()) {
654 trailerDict.free();
656 newTrailerDict.copy(&trailerDict);
657 gotRoot = gTrue;
659 obj.free();
661 newTrailerDict.free();
662 delete parser;
664 // look for object
665 } else if (isdigit(*p)) {
666 num = atoi(p);
667 do {
668 ++p;
669 } while (*p && isdigit(*p));
670 if (isspace(*p)) {
671 do {
672 ++p;
673 } while (*p && isspace(*p));
674 if (isdigit(*p)) {
675 gen = atoi(p);
676 do {
677 ++p;
678 } while (*p && isdigit(*p));
679 if (isspace(*p)) {
680 do {
681 ++p;
682 } while (*p && isspace(*p));
683 if (!strncmp(p, "obj", 3)) {
684 if (num >= size) {
685 newSize = (num + 1 + 255) & ~255;
686 entries = (XRefEntry *)
687 grealloc(entries, newSize * sizeof(XRefEntry));
688 for (i = size; i < newSize; ++i) {
689 entries[i].offset = 0xffffffff;
690 entries[i].type = xrefEntryFree;
692 size = newSize;
694 if (entries[num].type == xrefEntryFree ||
695 gen >= entries[num].gen) {
696 entries[num].offset = pos - start;
697 entries[num].gen = gen;
698 entries[num].type = xrefEntryUncompressed;
705 } else if (!strncmp(p, "endstream", 9)) {
706 if (streamEndsLen == streamEndsSize) {
707 streamEndsSize += 64;
708 streamEnds = (Guint *)grealloc(streamEnds,
709 streamEndsSize * sizeof(int));
711 streamEnds[streamEndsLen++] = pos;
715 if (gotRoot)
716 return gTrue;
718 error(-1, "Couldn't find trailer dictionary");
719 return gFalse;
722 #ifndef NO_DECRYPTION
723 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
724 Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
725 Object ownerKey, userKey, permissions, fileID, fileID1;
726 GBool encrypted1;
727 GBool ret;
729 keyLength = 0;
730 encVersion = encRevision = 0;
731 ret = gFalse;
733 permFlags = defPermFlags;
734 ownerPasswordOk = gFalse;
735 trailerDict.dictLookup("Encrypt", &encrypt);
736 if ((encrypted1 = encrypt.isDict())) {
737 ret = gTrue;
738 encrypt.dictLookup("Filter", &filterObj);
739 if (filterObj.isName("Standard")) {
740 encrypt.dictLookup("V", &versionObj);
741 encrypt.dictLookup("R", &revisionObj);
742 encrypt.dictLookup("Length", &lengthObj);
743 encrypt.dictLookup("O", &ownerKey);
744 encrypt.dictLookup("U", &userKey);
745 encrypt.dictLookup("P", &permissions);
746 trailerDict.dictLookup("ID", &fileID);
747 if (versionObj.isInt() &&
748 revisionObj.isInt() &&
749 ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
750 userKey.isString() && userKey.getString()->getLength() == 32 &&
751 permissions.isInt() &&
752 fileID.isArray()) {
753 encVersion = versionObj.getInt();
754 encRevision = revisionObj.getInt();
755 if (lengthObj.isInt()) {
756 keyLength = lengthObj.getInt() / 8;
757 } else {
758 keyLength = 5;
760 permFlags = permissions.getInt();
761 if (encVersion >= 1 && encVersion <= 2 &&
762 encRevision >= 2 && encRevision <= 3) {
763 fileID.arrayGet(0, &fileID1);
764 if (fileID1.isString()) {
765 if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
766 ownerKey.getString(), userKey.getString(),
767 permFlags, fileID1.getString(),
768 ownerPassword, userPassword, fileKey,
769 &ownerPasswordOk)) {
770 if (ownerPassword && !ownerPasswordOk) {
771 error(-1, "Incorrect owner password");
773 ret = gFalse;
774 } else {
775 error(-1, "Incorrect password");
777 } else {
778 error(-1, "Weird encryption info");
780 fileID1.free();
781 } else {
782 error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
783 encVersion, encRevision);
785 } else {
786 error(-1, "Weird encryption info");
788 fileID.free();
789 permissions.free();
790 userKey.free();
791 ownerKey.free();
792 lengthObj.free();
793 revisionObj.free();
794 versionObj.free();
795 } else {
796 error(-1, "Unknown security handler '%s'",
797 filterObj.isName() ? filterObj.getName() : "???");
799 filterObj.free();
801 encrypt.free();
803 // this flag has to be set *after* we read the O/U/P strings
804 encrypted = encrypted1;
806 return ret;
808 #else
809 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
810 Object obj;
811 GBool encrypted;
813 trailerDict.dictLookup("Encrypt", &obj);
814 if ((encrypted = !obj.isNull())) {
815 error(-1, "PDF file is encrypted and this version of the Xpdf tools");
816 error(-1, "was built without decryption support.");
818 obj.free();
819 return encrypted;
821 #endif
823 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
824 #ifndef NO_DECRYPTION
825 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
826 #else
827 return gTrue;
828 #endif
831 GBool XRef::okToChange(GBool ignoreOwnerPW) {
832 #ifndef NO_DECRYPTION
833 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
834 #else
835 return gTrue;
836 #endif
839 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
840 #ifndef NO_DECRYPTION
841 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
842 #else
843 return gTrue;
844 #endif
847 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
848 #ifndef NO_DECRYPTION
849 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
850 #else
851 return gTrue;
852 #endif
855 Object *XRef::fetch(int num, int gen, Object *obj) {
856 XRefEntry *e;
857 Parser *parser;
858 Object obj1, obj2, obj3;
860 // check for bogus ref - this can happen in corrupted PDF files
861 if (num < 0 || num >= size) {
862 goto err;
865 e = &entries[num];
866 switch (e->type) {
868 case xrefEntryUncompressed:
869 if (e->gen != gen) {
870 goto err;
872 obj1.initNull();
873 parser = new Parser(this,
874 new Lexer(this,
875 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
876 parser->getObj(&obj1);
877 parser->getObj(&obj2);
878 parser->getObj(&obj3);
879 if (!obj1.isInt() || obj1.getInt() != num ||
880 !obj2.isInt() || obj2.getInt() != gen ||
881 !obj3.isCmd("obj")) {
882 goto err;
884 #ifndef NO_DECRYPTION
885 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
886 num, gen);
887 #else
888 parser->getObj(obj);
889 #endif
890 obj1.free();
891 obj2.free();
892 obj3.free();
893 delete parser;
894 break;
896 case xrefEntryCompressed:
897 if (gen != 0) {
898 goto err;
900 if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
901 if (objStr) {
902 delete objStr;
904 objStr = new ObjectStream(this, e->offset);
906 objStr->getObject(e->gen, num, obj);
907 break;
909 default:
910 goto err;
913 return obj;
915 err:
916 return obj->initNull();
919 Object *XRef::getDocInfo(Object *obj) {
920 return trailerDict.dictLookup("Info", obj);
923 // Added for the pdftex project.
924 Object *XRef::getDocInfoNF(Object *obj) {
925 return trailerDict.dictLookupNF("Info", obj);
928 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
929 int a, b, m;
931 if (streamEndsLen == 0 ||
932 streamStart > streamEnds[streamEndsLen - 1]) {
933 return gFalse;
936 a = -1;
937 b = streamEndsLen - 1;
938 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
939 while (b - a > 1) {
940 m = (a + b) / 2;
941 if (streamStart <= streamEnds[m]) {
942 b = m;
943 } else {
944 a = m;
947 *streamEnd = streamEnds[b];
948 return gTrue;
951 Guint XRef::strToUnsigned(char *s) {
952 Guint x;
953 char *p;
954 int i;
956 x = 0;
957 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
958 x = 10 * x + (*p - '0');
960 return x;