Bringing apdf from vendor into main branch.
[AROS-Contrib.git] / apdf / xpdf / XRef.cc
blob31cca284a40cc73969822344d16492cab6e2c175
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 #include "Error.h"
26 #include "ErrorCodes.h"
27 #include "XRef.h"
28 #include "PDFDoc.h"
29 #include "Hints.h"
30 #include "PartsVec.h"
31 #include "SecurityHandler.h"
33 //------------------------------------------------------------------------
35 #define xrefSearchSize 1024 // read this many bytes at end of file
36 // to look for 'startxref'
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
48 //------------------------------------------------------------------------
49 // JoinStream
50 //------------------------------------------------------------------------
52 class JoinStream: public Stream {
53 public:
55 JoinStream(Object *s1, Object *s2) {
56 s1->copy(&str1);
57 s2->copy(&str2);
58 str = str1.getStream();
60 ~JoinStream() {
61 str1.free();
62 str2.free();
64 virtual StreamKind getKind() { return strWeird; }
65 virtual void reset() {
66 str1.streamReset();
67 if (str2.isStream())
68 str2.streamReset();
69 str = str1.getStream();
71 virtual int getChar() {
72 int c = str->getChar();
73 if (c == EOF && str2.isStream() && str != str2.getStream()) {
74 str = str2.getStream();
75 c = str->getChar();
77 return c;
79 virtual int lookChar() {
80 int c = str->lookChar();
81 if (c == EOF && str2.isStream() && str != str2.getStream()) {
82 c = str2.streamLookChar();
84 return c;
86 virtual int getPos() { return 0; }
87 virtual void setPos(Guint pos1, int) {}
88 virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
89 virtual BaseStream *getBaseStream() { return NULL; }
90 virtual Dict *getDict() { return NULL; }
92 private:
93 Stream *str;
94 Object str1;
95 Object str2;
99 //------------------------------------------------------------------------
100 // ObjectStream
101 //------------------------------------------------------------------------
103 class ObjectStream {
104 public:
106 // Create an object stream, using object number <objStrNum>,
107 // generation 0.
108 ObjectStream(XRef *xref, int objStrNumA);
110 ~ObjectStream();
112 // Return the object number of this object stream.
113 //int getObjStrNum() { return objStrNum; }
115 // Get the <objIdx>th object from this stream, which should be
116 // object number <objNum>, generation 0.
117 Object *getObject(int objIdx, int objNum, Object *obj);
119 // Get the <objIdx>th object from this stream.
120 Object *getObject(int objIdx, Object *obj);
122 private:
123 //int objStrNum; // object number of the object stream
124 int nObjects; // number of objects in the stream
125 Object *objs; // the objects (length = nObjects)
126 int *objNums; // the object numbers (length = nObjects)
129 ObjectStream::ObjectStream(XRef *xref, int objStrNum) {
130 DEBUG_INFO
131 Stream *str;
132 Parser *parser;
133 int *offsets;
134 Object objStr, obj1, obj2;
135 int first, i;
137 //objStrNum = objStrNumA;
138 nObjects = 0;
139 objs = NULL;
140 objNums = NULL;
142 if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
143 goto err1;
146 if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
147 obj1.free();
148 goto err1;
150 nObjects = obj1.getInt();
151 obj1.free();
152 if (nObjects <= 0) {
153 goto err1;
156 if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
157 obj1.free();
158 goto err1;
160 first = obj1.getInt();
161 obj1.free();
162 if (first < 0) {
163 goto err1;
166 objs = new Object[nObjects];
167 objNums = (int *)gmallocn(nObjects, sizeof(int));
168 offsets = (int *)gmallocn(nObjects, sizeof(int));
170 // parse the header: object numbers and offsets
171 objStr.streamReset();
172 obj1.initNull();
173 str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
174 parser = new Parser(xref, new Lexer(xref, str));
175 for (i = 0; i < nObjects; ++i) {
176 parser->getObj(&obj1);
177 parser->getObj(&obj2);
178 if (!obj1.isInt() || !obj2.isInt()) {
179 obj1.free();
180 obj2.free();
181 delete parser;
182 gfree(offsets);
183 goto err1;
185 objNums[i] = obj1.getInt();
186 offsets[i] = obj2.getInt();
187 obj1.free();
188 obj2.free();
189 if (objNums[i] < 0 || offsets[i] < 0 ||
190 (i > 0 && offsets[i] < offsets[i-1])) {
191 delete parser;
192 gfree(offsets);
193 goto err1;
196 while (str->getChar() != EOF) ;
197 delete parser;
199 // skip to the first object - this shouldn't be necessary because
200 // the First key is supposed to be equal to offsets[0], but just in
201 // case...
202 for (i = first; i < offsets[0]; ++i) {
203 objStr.getStream()->getChar();
206 // parse the objects
207 for (i = 0; i < nObjects; ++i) {
208 obj1.initNull();
209 if (i == nObjects - 1) {
210 str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
211 } else {
212 str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
213 offsets[i+1] - offsets[i]);
215 parser = new Parser(xref, new Lexer(xref, str));
216 parser->getObj(&objs[i]);
217 while (str->getChar() != EOF) ;
218 delete parser;
221 gfree(offsets);
223 err1:
224 objStr.free();
225 return;
228 ObjectStream::~ObjectStream() {
229 int i;
231 if (objs) {
232 for (i = 0; i < nObjects; ++i) {
233 objs[i].free();
235 delete[] objs;
237 gfree(objNums);
240 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
241 if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
242 return obj->initNull();
244 return objs[objIdx].copy(obj);
247 Object *ObjectStream::getObject(int objIdx, Object *obj) {
248 if (objIdx < 0 || objIdx >= nObjects) {
249 return obj->initNull();
251 return objs[objIdx].copy(obj);
254 //------------------------------------------------------------------------
255 // ObjectStreamCache
256 //------------------------------------------------------------------------
258 class ObjectStreamCache
260 public:
262 ObjectStreamCache();
263 ~ObjectStreamCache();
265 ObjectStream *getObjectStream(XRef *xref, int objStrNum);
267 private:
268 static const int cacheSize = 16;
269 struct Entry
271 Entry *next;
272 int objStrNum;
273 ObjectStream *objStr;
276 Entry *first;
277 Entry entries[cacheSize];
280 ObjectStreamCache::ObjectStreamCache() {
281 int k;
282 Entry **p = &first;
284 for (k = 0; k < cacheSize; ++k) {
285 *p = &entries[k];
286 p = &entries[k].next;
287 entries[k].objStrNum = -1;
288 entries[k].objStr = NULL;
291 *p = NULL;
294 ObjectStreamCache::~ObjectStreamCache() {
295 int k;
297 for (k = 0; k < cacheSize; ++k) {
298 delete entries[k].objStr;
302 ObjectStream *ObjectStreamCache::getObjectStream(XRef *xref, int objStrNum) {
303 Entry **ptr = &first;
304 Entry *last = NULL;
305 Entry *entry;
307 while ((entry = *ptr) != NULL && entry->objStrNum != objStrNum && entry->objStr) {
308 ptr = &entry->next;
309 last = entry;
312 if (entry == NULL || entry->objStr == NULL) {
313 if (!entry) {
314 entry = last;
315 delete entry->objStr;
318 entry->objStrNum = objStrNum;
319 entry->objStr = new ObjectStream(xref, objStrNum);
320 } else if (entry != first) {
321 *ptr = entry->next;
322 entry->next = first;
323 first = entry;
326 return entry->objStr;
329 //------------------------------------------------------------------------
330 // XRef
331 //------------------------------------------------------------------------
333 XRef::XRef(BaseStream *strA, PDFDoc *doc, GString *ownerPassword, GString *userPassword) {
334 DEBUG_INFO
335 Guint pos;
336 Object obj;
338 ok = gTrue;
339 errCode = errNone;
340 size = 0;
341 entries = NULL;
342 streamEnds = NULL;
343 streamEndsLen = 0;
345 pageOffsetHintTable = NULL;
346 sharedObjectHintTable = NULL;
347 outlinesHintTable = NULL;
348 namedDestsHintTable = NULL;
349 infoHintTable = NULL;
350 formsHintTable = NULL;
351 numFirstPageXRefs = -1;
353 strCache = new ObjectStreamCache;
355 encrypted = gFalse;
356 permFlags = defPermFlags;
357 ownerPasswordOk = gFalse;
359 // read the trailer
360 str = strA;
362 if (doc) {
363 doc->setXRef(this);
366 start = str->getStart();
368 if (doc && getLinearizationInfo()) {
369 str->preRead(0,endOfFirstPageOffset);
370 pos = str->getPos();
371 if (readXRef(&pos)) {
372 if (readTrailerDict()) {
373 Object hintStr;
374 int i;
375 Parser *parser;
377 trailerDict.getDict()->setXRef(this);
379 // check for encryption
380 encrypted = gFalse;
381 if (!checkEncrypted(doc, ownerPassword, userPassword)) {
382 ok = gFalse;
383 errCode = errEncrypted;
384 return;
387 str->preRead(hintTable1Offset, hintTable1Length);
388 if (hintTable2Length)
389 str->preRead(hintTable2Offset, hintTable2Length);
391 // hint streams should have the last object numbers,
392 // but not all files get that right.
393 for (i = size; --i > 0;)
394 if (entries[i].offset == hintTable1Offset)
395 break;
397 if (fetch(i, entries[i].gen, &obj)->isStream())
398 obj.copy(&hintStr);
399 obj.free();
401 if (hintStr.isStream() && hintTable2Length) {
402 for (i = size; --i > 0;)
403 if (entries[i].offset == hintTable2Offset)
404 break;
405 if (fetch(i, entries[i].gen, &obj)->isStream()) {
406 Stream *hintStr2 = new JoinStream(&hintStr, &obj);
407 hintStr.free();
408 hintStr.initStream(hintStr2);
410 obj.free();
413 if (hintStr.isStream() && (hintStr.streamReset(), hintStr.streamLookChar()) != EOF) {
414 Dict *dict = hintStr.streamGetDict();
415 int offset = 0;
417 if (dict->lookup("P", &obj)->isInt())
418 offset = obj.getInt();
419 obj.free();
421 hintStr.streamReset();
422 for (i = 0; i < offset; ++i)
423 hintStr.streamGetChar();
425 pageOffsetHintTable = new PageOffsetHintTable(hintStr.getStream(), numPages,
426 firstPageObjectNum,
427 hintTable1Offset, hintTable1Length);
429 if (dict->lookup("S", &obj)->isInt()) {
430 int pos = obj.getInt();
431 hintStr.streamReset();
432 for (i = 0; i < pos; ++i)
433 hintStr.streamGetChar();
435 obj.free();
436 sharedObjectHintTable = new SharedObjectHintTable(hintStr.getStream(),
437 firstPageObjectNum, pageOffsetHintTable->getPageOffset(firstPageNum),
438 hintTable1Offset, hintTable1Length);
440 if (dict->lookup("O", &obj)->isInt()) {
441 pos = obj.getInt();
442 hintStr.streamReset();
443 for (i = 0; i < pos; ++i)
444 hintStr.streamGetChar();
445 outlinesHintTable = new GenericHintTable(hintStr.getStream());
447 obj.free();
449 if (dict->lookup("E", &obj)->isInt()) {
450 pos = obj.getInt();
451 hintStr.streamReset();
452 for (i = 0; i < pos; ++i)
453 hintStr.streamGetChar();
454 namedDestsHintTable = new GenericHintTable(hintStr.getStream());
456 obj.free();
458 if (dict->lookup("I", &obj)->isInt()) {
459 pos = obj.getInt();
460 hintStr.streamReset();
461 for (i = 0; i < pos; ++i)
462 hintStr.streamGetChar();
463 infoHintTable = new GenericHintTable(hintStr.getStream());
465 obj.free();
467 if (dict->lookup("V", &obj)->isInt()) {
468 pos = obj.getInt();
469 hintStr.streamReset();
470 for (i = 0; i < pos; ++i)
471 hintStr.streamGetChar();
472 formsHintTable = new FormsHintTable(hintStr.getStream());
474 obj.free();
476 hintStr.free();
478 str->setPos(start + mainXRefOffset);
479 str->preRead(start + mainXRefOffset, fileSize - mainXRefOffset);
481 parser = new Parser(NULL,
482 new Lexer(NULL,
483 str->makeSubStream(start + mainXRefOffset, gFalse, 0, &obj)));
484 if (readXRefPart2(parser, 0, size - numFirstPageXRefs)) {
485 delete parser;
486 return;
488 delete parser;
490 delete pageOffsetHintTable;
491 delete sharedObjectHintTable;
492 delete outlinesHintTable;
493 delete namedDestsHintTable;
494 delete infoHintTable;
495 delete formsHintTable;
496 pageOffsetHintTable = NULL;
497 sharedObjectHintTable = NULL;
498 outlinesHintTable = NULL;
499 namedDestsHintTable = NULL;
500 infoHintTable = NULL;
501 formsHintTable = NULL;
503 } else
504 error(-1, "Bad hint stream");
506 } else {
507 obj.free();
509 gfree(entries);
510 entries = NULL;
511 size = 0;
512 trailerDict.free();
514 error(-1, "Bad linearized file");
516 str->removeParts();
518 linearized = gFalse;
520 pos = getStartXref();
522 // if there was a problem with the 'startxref' position, try to
523 // reconstruct the xref table
524 if (pos == 0) {
525 if (!(ok = constructXRef())) {
526 errCode = errDamaged;
527 return;
530 // read the xref table
531 } else {
532 while (readXRef(&pos)) ;
534 // if there was a problem with the xref table,
535 // try to reconstruct it
536 if (!ok) {
537 if (!(ok = constructXRef())) {
538 errCode = errDamaged;
539 return;
544 // get the root dictionary (catalog) object
545 trailerDict.dictLookupNF("Root", &obj);
546 if (obj.isRef()) {
547 rootNum = obj.getRefNum();
548 rootGen = obj.getRefGen();
549 obj.free();
550 } else {
551 obj.free();
552 if (!(ok = constructXRef())) {
553 errCode = errDamaged;
554 return;
558 // now set the trailer dictionary's xref pointer so we can fetch
559 // indirect objects from it
560 trailerDict.getDict()->setXRef(this);
562 // check for encryption
563 if (doc) {
564 encrypted = gFalse;
565 if (!checkEncrypted(doc, ownerPassword, userPassword)) {
566 ok = gFalse;
567 errCode = errEncrypted;
568 return;
573 #if 0
574 XRef::XRef(BaseStream *strA) {
575 DEBUG_INFO
576 Guint pos;
577 Object obj;
579 ok = gTrue;
580 errCode = errNone;
581 size = 0;
582 entries = NULL;
583 streamEnds = NULL;
584 streamEndsLen = 0;
586 pageOffsetHintTable = NULL;
587 sharedObjectHintTable = NULL;
588 outlinesHintTable = NULL;
589 namedDestsHintTable = NULL;
590 infoHintTable = NULL;
591 formsHintTable = NULL;
592 linearized = gFalse;
593 encrypted = gFalse;
595 strCache = new ObjectStreamCache;
597 // read the trailer
598 str = strA;
600 start = str->getStart();
601 pos = getStartXref();
603 // if there was a problem with the 'startxref' position, try to
604 // reconstruct the xref table
605 if (pos == 0) {
606 if (!(ok = constructXRef())) {
607 errCode = errDamaged;
608 return;
611 // read the xref table
612 } else {
613 while (readXRef(&pos)) ;
615 // if there was a problem with the xref table,
616 // try to reconstruct it
617 if (!ok) {
618 if (!(ok = constructXRef())) {
619 errCode = errDamaged;
620 return;
625 // get the root dictionary (catalog) object
626 trailerDict.dictLookupNF("Root", &obj);
627 if (obj.isRef()) {
628 rootNum = obj.getRefNum();
629 rootGen = obj.getRefGen();
630 obj.free();
631 } else {
632 obj.free();
633 if (!(ok = constructXRef())) {
634 errCode = errDamaged;
635 return;
639 // now set the trailer dictionary's xref pointer so we can fetch
640 // indirect objects from it
641 trailerDict.getDict()->setXRef(this);
643 #endif
645 XRef::~XRef() {
646 delete pageOffsetHintTable;
647 delete sharedObjectHintTable;
648 delete outlinesHintTable;
649 delete namedDestsHintTable;
650 delete infoHintTable;
651 delete formsHintTable;
652 delete strCache;
653 gfree(entries);
654 trailerDict.free();
655 if (streamEnds) {
656 gfree(streamEnds);
660 // Read the 'startxref' position.
661 Guint XRef::getStartXref() {
662 char buf[xrefSearchSize+1];
663 char *p;
664 int c, n, i;
666 // read last xrefSearchSize bytes
667 str->setPos(xrefSearchSize, -1);
668 for (n = 0; n < xrefSearchSize; ++n) {
669 if ((c = str->getChar()) == EOF) {
670 break;
672 buf[n] = c;
674 buf[n] = '\0';
676 // find startxref
677 for (i = n - 9; i >= 0; --i) {
678 if (!strncmp(&buf[i], "startxref", 9)) {
679 break;
682 if (i < 0) {
683 return 0;
685 for (p = &buf[i+9]; isspace(*p); ++p) ;
686 lastXRefPos = strToUnsigned(p);
688 return lastXRefPos;
691 // Read one xref table section. Also reads the associated trailer
692 // dictionary, and returns the prev pointer (if any).
693 GBool XRef::readXRef(Guint *pos) {
694 Parser *parser;
695 Object obj;
696 GBool more;
698 // start up a parser, parse one token
699 obj.initNull();
700 parser = new Parser(NULL,
701 new Lexer(NULL,
702 str->makeSubStream(start + *pos, gFalse, 0, &obj)));
703 parser->getObj(&obj);
705 // parse an old-style xref table
706 if (obj.isCmd("xref")) {
707 obj.free();
708 more = readXRefTable(parser, pos);
710 // parse an xref stream
711 } else if (obj.isInt()) {
712 obj.free();
713 if (!parser->getObj(&obj)->isInt()) {
714 goto err1;
716 obj.free();
717 if (!parser->getObj(&obj)->isCmd("obj")) {
718 goto err1;
720 obj.free();
721 if (!parser->getObj(&obj)->isStream()) {
722 goto err1;
724 more = readXRefStream(obj.getStream(), pos);
725 obj.free();
727 } else {
728 goto err1;
731 delete parser;
732 return more;
734 err1:
735 obj.free();
736 delete parser;
737 ok = gFalse;
738 return gFalse;
741 GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
742 XRefEntry entry;
743 GBool more;
744 Object obj, obj2;
745 Guint pos2;
746 int first, n, newSize, i;
748 while (1) {
749 parser->getObj(&obj);
750 if (obj.isCmd("trailer")) {
751 obj.free();
752 break;
754 if (!obj.isInt()) {
755 goto err1;
757 first = obj.getInt();
758 obj.free();
759 if (!parser->getObj(&obj)->isInt()) {
760 goto err1;
762 n = obj.getInt();
763 obj.free();
764 if (first < 0 || n < 0 || first + n < 0) {
765 goto err1;
767 if (first + n > size) {
768 for (newSize = size ? 2 * size : 1024;
769 first + n > newSize && newSize > 0;
770 newSize <<= 1) ;
771 if (newSize < 0) {
772 goto err1;
774 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
775 for (i = size; i < newSize; ++i) {
776 entries[i].offset = 0xffffffff;
777 entries[i].type = xrefEntryFree;
779 size = newSize;
781 if (numFirstPageXRefs == -1) {
782 numFirstPageXRefs = n;
784 if (!readXRefPart(parser, first, n)) {
785 goto err1;
789 // read the trailer dictionary
790 if (!parser->getObj(&obj)->isDict()) {
791 goto err1;
794 // get the 'Prev' pointer
795 obj.getDict()->lookupNF("Prev", &obj2);
796 if (obj2.isInt()) {
797 *pos = (Guint)obj2.getInt();
798 more = gTrue;
799 } else if (obj2.isRef()) {
800 // certain buggy PDF generators generate "/Prev NNN 0 R" instead
801 // of "/Prev NNN"
802 *pos = (Guint)obj2.getRefNum();
803 more = gTrue;
804 } else {
805 more = gFalse;
807 obj2.free();
809 // save the first trailer dictionary
810 if (trailerDict.isNone()) {
811 obj.copy(&trailerDict);
814 // check for an 'XRefStm' key
815 if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
816 pos2 = (Guint)obj2.getInt();
817 readXRef(&pos2);
818 if (!ok) {
819 obj2.free();
820 goto err1;
823 obj2.free();
825 obj.free();
826 return more;
828 err1:
829 obj.free();
830 ok = gFalse;
831 return gFalse;
834 GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
835 Dict *dict;
836 int w[3];
837 GBool more;
838 Object obj, obj2, idx;
839 int newSize, first, n, i;
841 dict = xrefStr->getDict();
843 if (!dict->lookupNF("Size", &obj)->isInt()) {
844 goto err1;
846 newSize = obj.getInt();
847 obj.free();
848 if (newSize < 0) {
849 goto err1;
851 if (newSize > size) {
852 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
853 for (i = size; i < newSize; ++i) {
854 entries[i].offset = 0xffffffff;
855 entries[i].type = xrefEntryFree;
857 size = newSize;
858 if (numFirstPageXRefs == -1) {
859 numFirstPageXRefs = size;
863 if (!dict->lookupNF("W", &obj)->isArray() ||
864 obj.arrayGetLength() < 3) {
865 goto err1;
867 for (i = 0; i < 3; ++i) {
868 if (!obj.arrayGet(i, &obj2)->isInt()) {
869 obj2.free();
870 goto err1;
872 w[i] = obj2.getInt();
873 obj2.free();
874 if (w[i] < 0 || w[i] > 4) {
875 goto err1;
878 obj.free();
880 xrefStr->reset();
881 dict->lookupNF("Index", &idx);
882 if (idx.isArray()) {
883 for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
884 if (!idx.arrayGet(i, &obj)->isInt()) {
885 idx.free();
886 goto err1;
888 first = obj.getInt();
889 obj.free();
890 if (!idx.arrayGet(i+1, &obj)->isInt()) {
891 idx.free();
892 goto err1;
894 n = obj.getInt();
895 obj.free();
896 if (first < 0 || n < 0 ||
897 !readXRefStreamSection(xrefStr, w, first, n)) {
898 idx.free();
899 goto err0;
902 } else {
903 if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
904 idx.free();
905 goto err0;
908 idx.free();
910 dict->lookupNF("Prev", &obj);
911 if (obj.isInt()) {
912 *pos = (Guint)obj.getInt();
913 more = gTrue;
914 } else {
915 more = gFalse;
917 obj.free();
918 if (trailerDict.isNone()) {
919 trailerDict.initDict(dict);
922 return more;
924 err1:
925 obj.free();
926 err0:
927 ok = gFalse;
928 return gFalse;
931 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
932 Guint offset;
933 int type, gen, c, newSize, i, j;
935 if (first + n < 0) {
936 return gFalse;
938 if (first + n > size) {
939 for (newSize = size ? 2 * size : 1024;
940 first + n > newSize && newSize > 0;
941 newSize <<= 1) ;
942 if (newSize < 0) {
943 return gFalse;
945 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
946 for (i = size; i < newSize; ++i) {
947 entries[i].offset = 0xffffffff;
948 entries[i].type = xrefEntryFree;
950 size = newSize;
952 for (i = first; i < first + n; ++i) {
953 if (w[0] == 0) {
954 type = 1;
955 } else {
956 for (type = 0, j = 0; j < w[0]; ++j) {
957 if ((c = xrefStr->getChar()) == EOF) {
958 return gFalse;
960 type = (type << 8) + c;
963 for (offset = 0, j = 0; j < w[1]; ++j) {
964 if ((c = xrefStr->getChar()) == EOF) {
965 return gFalse;
967 offset = (offset << 8) + c;
969 for (gen = 0, j = 0; j < w[2]; ++j) {
970 if ((c = xrefStr->getChar()) == EOF) {
971 return gFalse;
973 gen = (gen << 8) + c;
975 if (entries[i].offset == 0xffffffff) {
976 switch (type) {
977 case 0:
978 entries[i].offset = offset;
979 entries[i].gen = gen;
980 entries[i].type = xrefEntryFree;
981 break;
982 case 1:
983 entries[i].offset = offset;
984 entries[i].gen = gen;
985 entries[i].type = xrefEntryUncompressed;
986 break;
987 case 2:
988 entries[i].offset = offset;
989 entries[i].gen = gen;
990 entries[i].type = xrefEntryCompressed;
991 break;
992 default:
993 return gFalse;
998 return gTrue;
1001 GBool XRef::readXRefPart2(Parser *parser, int first, int n) {
1002 Object obj, obj1;
1003 //GBool more;
1004 Guint pos = str->getPos();
1005 Parser *parser2;
1007 // start up a parser, parse one token
1008 obj1.initNull();
1009 parser2 = new Parser(NULL,
1010 new Lexer(NULL,
1011 str->makeSubStream(start + pos, gFalse, 0, &obj1)));
1013 if (!parser2->getObj(&obj)->isInt()) {
1014 goto err1;
1016 obj.free();
1017 if (!parser2->getObj(&obj)->isInt()) {
1018 goto err1;
1020 obj.free();
1021 if (parser2->getObj(&obj)->isCmd("obj")) {
1022 obj.free();
1023 if (!parser2->getObj(&obj)->isStream()) {
1024 goto err1;
1026 delete parser2;
1027 obj1.free();
1028 /*more =*/ readXRefStream(obj.getStream(), &pos);
1029 obj.free();
1030 } else {
1031 obj.free();
1032 delete parser2;
1033 obj1.free();
1035 return readXRefPart(parser, first, n);
1038 return gTrue/*more*/;
1040 err1:
1041 delete parser2;
1042 obj1.free();
1043 obj.free();
1044 ok = gFalse;
1045 return gFalse;
1048 // Attempt to construct an xref table for a damaged file.
1049 GBool XRef::constructXRef() {
1050 Parser *parser;
1051 Object newTrailerDict, obj;
1052 char buf[256];
1053 Guint pos;
1054 int num, gen;
1055 int newSize;
1056 int streamEndsSize;
1057 char *p;
1058 int i;
1059 GBool gotRoot;
1061 gfree(entries);
1062 size = 0;
1063 entries = NULL;
1065 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
1066 gotRoot = gFalse;
1067 streamEndsLen = streamEndsSize = 0;
1069 str->reset();
1070 while (1) {
1071 pos = str->getPos();
1072 if (!str->getLine(buf, 256)) {
1073 break;
1075 p = buf;
1077 // got trailer dictionary
1078 if (!strncmp(p, "trailer", 7)) {
1079 obj.initNull();
1080 parser = new Parser(NULL,
1081 new Lexer(NULL,
1082 str->makeSubStream(pos + 7, gFalse, 0, &obj)));
1083 parser->getObj(&newTrailerDict);
1084 if (newTrailerDict.isDict()) {
1085 newTrailerDict.dictLookupNF("Root", &obj);
1086 if (obj.isRef()) {
1087 rootNum = obj.getRefNum();
1088 rootGen = obj.getRefGen();
1089 if (!trailerDict.isNone()) {
1090 trailerDict.free();
1092 newTrailerDict.copy(&trailerDict);
1093 gotRoot = gTrue;
1095 obj.free();
1097 newTrailerDict.free();
1098 delete parser;
1100 // look for object
1101 } else if (isdigit(*p)) {
1102 num = atoi(p);
1103 if (num > 0) {
1104 do {
1105 ++p;
1106 } while (*p && isdigit(*p));
1107 if (isspace(*p)) {
1108 do {
1109 ++p;
1110 } while (*p && isspace(*p));
1111 if (isdigit(*p)) {
1112 gen = atoi(p);
1113 do {
1114 ++p;
1115 } while (*p && isdigit(*p));
1116 if (isspace(*p)) {
1117 do {
1118 ++p;
1119 } while (*p && isspace(*p));
1120 if (!strncmp(p, "obj", 3)) {
1121 if (num >= size) {
1122 newSize = (num + 1 + 255) & ~255;
1123 if (newSize < 0) {
1124 error(-1, "Bad object number");
1125 return gFalse;
1127 entries = (XRefEntry *)
1128 greallocn(entries, newSize, sizeof(XRefEntry));
1129 for (i = size; i < newSize; ++i) {
1130 entries[i].offset = 0xffffffff;
1131 entries[i].type = xrefEntryFree;
1133 size = newSize;
1135 if (entries[num].type == xrefEntryFree ||
1136 gen >= entries[num].gen) {
1137 entries[num].offset = pos - start;
1138 entries[num].gen = gen;
1139 entries[num].type = xrefEntryUncompressed;
1147 } else if (!strncmp(p, "endstream", 9)) {
1148 if (streamEndsLen == streamEndsSize) {
1149 streamEndsSize += 64;
1150 streamEnds = (Guint *)greallocn(streamEnds,
1151 streamEndsSize, sizeof(int));
1153 streamEnds[streamEndsLen++] = pos;
1157 if (gotRoot)
1158 return gTrue;
1160 error(-1, "Couldn't find trailer dictionary");
1161 return gFalse;
1164 GBool XRef::checkEncrypted(PDFDoc *doc, GString *ownerPassword, GString *userPassword) {
1165 Object encrypt;
1166 SecurityHandler *secHdlr;
1167 GBool ret;
1169 trailerDict.dictLookup("Encrypt", &encrypt);
1170 if (encrypt.isDict()) {
1171 if ((secHdlr = SecurityHandler::make(doc, &encrypt))) {
1172 if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
1173 // authorization succeeded
1174 setEncryption(secHdlr->getPermissionFlags(),
1175 secHdlr->getOwnerPasswordOk(),
1176 secHdlr->getFileKey(),
1177 secHdlr->getFileKeyLength(),
1178 secHdlr->getEncVersion());
1179 ret = gTrue;
1180 } else {
1181 // authorization failed
1182 ret = gFalse;
1184 delete secHdlr;
1185 } else {
1186 // couldn't find the matching security handler
1187 ret = gFalse;
1189 } else {
1190 // document is not encrypted
1191 ret = gTrue;
1193 encrypt.free();
1195 return ret;
1198 void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
1199 Guchar *fileKeyA, int keyLengthA, int encVersionA) {
1200 int i;
1202 encrypted = gTrue;
1203 permFlags = permFlagsA;
1204 ownerPasswordOk = ownerPasswordOkA;
1205 if (keyLengthA <= 16) {
1206 keyLength = keyLengthA;
1207 } else {
1208 keyLength = 16;
1210 for (i = 0; i < keyLength; ++i) {
1211 fileKey[i] = fileKeyA[i];
1213 encVersion = encVersionA;
1216 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
1217 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
1220 GBool XRef::okToChange(GBool ignoreOwnerPW) {
1221 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
1224 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
1225 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
1228 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
1229 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
1232 Object *XRef::fetch(int num, int gen, Object *obj) {
1233 XRefEntry *e;
1234 Parser *parser;
1235 Object obj1, obj2, obj3;
1237 //printf("fetch(%d,%d)\n",num,gen);
1238 // check for bogus ref - this can happen in corrupted PDF files
1239 if (num < 0 || num >= size) {
1240 goto err;
1243 e = &entries[num];
1244 //printf("-> %d %d %d\n", e->gen, e->offset, e->type);
1245 switch (e->type) {
1247 case xrefEntryUncompressed:
1248 if (e->gen != gen) {
1249 goto err;
1251 obj1.initNull();
1252 parser = new Parser(this,
1253 new Lexer(this,
1254 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
1255 parser->getObj(&obj1);
1256 parser->getObj(&obj2);
1257 parser->getObj(&obj3);
1258 if (!obj1.isInt() || obj1.getInt() != num ||
1259 !obj2.isInt() || obj2.getInt() != gen ||
1260 !obj3.isCmd("obj")) {
1261 obj1.free();
1262 obj2.free();
1263 obj3.free();
1264 delete parser;
1265 goto err;
1267 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
1268 num, gen);
1269 obj1.free();
1270 obj2.free();
1271 obj3.free();
1272 delete parser;
1273 break;
1275 case xrefEntryCompressed:
1276 if (gen != 0) {
1277 goto err;
1279 strCache->getObjectStream(this, (int)e->offset)->getObject(e->gen, num, obj);
1280 break;
1282 default:
1283 goto err;
1286 return obj;
1288 err:
1289 return obj->initNull();
1292 Object *XRef::getDocInfo(Object *obj) {
1293 return trailerDict.dictLookup("Info", obj);
1296 // Added for the pdftex project.
1297 Object *XRef::getDocInfoNF(Object *obj) {
1298 return trailerDict.dictLookupNF("Info", obj);
1301 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
1302 int a, b, m;
1304 if (streamEndsLen == 0 ||
1305 streamStart > streamEnds[streamEndsLen - 1]) {
1306 return gFalse;
1309 a = -1;
1310 b = streamEndsLen - 1;
1311 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
1312 while (b - a > 1) {
1313 m = (a + b) / 2;
1314 if (streamStart <= streamEnds[m]) {
1315 b = m;
1316 } else {
1317 a = m;
1320 *streamEnd = streamEnds[b];
1321 return gTrue;
1324 Guint XRef::strToUnsigned(char *s) {
1325 Guint x;
1326 char *p;
1327 int i;
1329 #undef isdigit // workaround a bug in gcc 2.93's code generator
1330 x = 0;
1331 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
1332 x = 10 * x + (*p - '0');
1334 return x;
1338 GBool XRef::getLinearizationInfo() {
1339 DEBUG_INFO
1340 char buf[256];
1341 int pos = 0;
1342 int num, gen;
1343 char *p;
1344 Object obj;
1346 str->reset();
1348 // start up a parser, parse one token
1349 obj.initNull();
1350 // look for first object
1351 Parser parser(NULL, new Lexer(NULL, str->makeSubStream(start + pos, gFalse, 0, &obj)));
1353 parser.getObj(&obj);
1354 if (!obj.isInt()) {
1355 obj.free();
1356 return gFalse;
1358 obj.free();
1360 parser.getObj(&obj);
1361 if (!obj.isInt()) {
1362 obj.free();
1363 return gFalse;
1365 obj.free();
1367 parser.getObj(&obj);
1368 if (!obj.isCmd("obj")) {
1369 obj.free();
1370 return gFalse;
1372 obj.free();
1374 // read first object
1375 Object linDict;
1376 GBool ok = gFalse;
1377 parser.getObj(&linDict);
1378 if (linDict.isDict()) {
1379 str->setPos(start + parser.getPos() - 4);
1381 linDict.dictLookupNF("Linearized", &obj);
1382 ok = obj.isNum() && (int)obj.getNum() == 1;
1383 obj.free();
1385 if (ok) {
1386 linDict.dictLookupNF("L", &obj);
1387 if (!obj.isInt()/* || obj.getInt() != file->size()*/) {
1388 ok = gFalse;
1389 } else {
1390 fileSize = obj.getInt();
1392 obj.free();
1393 linDict.dictLookupNF("O", &obj);
1394 if (obj.isInt())
1395 firstPageObjectNum = obj.getInt();
1396 else
1397 ok = gFalse;
1398 obj.free();
1399 linDict.dictLookupNF("E", &obj);
1400 if (obj.isInt())
1401 endOfFirstPageOffset = obj.getInt();
1402 else
1403 ok = gFalse;
1404 obj.free();
1405 linDict.dictLookupNF("N", &obj);
1406 if (obj.isInt())
1407 numPages = obj.getInt();
1408 else
1409 ok = gFalse;
1410 obj.free();
1411 linDict.dictLookupNF("T", &obj);
1412 if (obj.isInt())
1413 mainXRefOffset = obj.getInt();
1414 else
1415 ok = gFalse;
1416 obj.free();
1417 linDict.dictLookupNF("P", &obj);
1418 if (obj.isInt())
1419 firstPageNum = obj.getInt();
1420 else
1421 firstPageNum = 0;
1422 obj.free();
1423 linDict.dictLookupNF("H", &obj);
1424 if (obj.isArray() && obj.arrayGetLength() >= 2) {
1425 Object obj2;
1426 if (obj.arrayGetNF(0,&obj2)->isInt())
1427 hintTable1Offset = obj2.getInt();
1428 else
1429 ok = gFalse;
1430 obj2.free();
1431 if (obj.arrayGetNF(1,&obj2)->isInt())
1432 hintTable1Length = obj2.getInt();
1433 else
1434 ok = gFalse;
1435 obj2.free();
1436 if (obj.arrayGetLength() == 4) {
1437 if (obj.arrayGetNF(2,&obj2)->isInt())
1438 hintTable2Offset = obj2.getInt();
1439 else
1440 ok = gFalse;
1441 obj2.free();
1442 if (obj.arrayGetNF(3,&obj2)->isInt())
1443 hintTable2Length = obj2.getInt();
1444 else
1445 ok = gFalse;
1446 obj2.free();
1447 } else {
1448 hintTable2Offset = 0;
1449 hintTable2Length = 0;
1451 } else
1452 ok = gFalse;
1453 obj.free();
1456 linDict.free();
1457 /*printf("O=%d E=%x N=%d T=%x P=%d\nH=[%x %x %x %x] ok=%d\n",
1458 firstPageObjectNum,endOfFirstPageOffset,numPages,
1459 mainXRefOffset,firstPageNum,hintTable1Offset,hintTable1Length,
1460 hintTable2Offset,hintTable2Length,ok);*/
1461 return ok;
1464 // read trailer dict
1465 GBool XRef::readTrailerDict() {
1466 DEBUG_INFO
1467 Object obj;
1468 GBool r = gTrue;
1470 if (trailerDict.isDict()) {
1471 trailerDict.dictLookupNF("Size", &obj);
1472 if (obj.isInt())
1473 size = obj.getInt();
1474 else
1475 r = gFalse;
1476 obj.free();
1477 trailerDict.dictLookupNF("Root", &obj);
1478 if (obj.isRef()) {
1479 rootNum = obj.getRefNum();
1480 rootGen = obj.getRefGen();
1481 } else {
1482 r = gFalse;
1484 obj.free();
1485 } else {
1486 r = gFalse;
1488 return r;
1491 // Read a part of an xref table
1492 GBool XRef::readXRefPart(Parser *parser, int first, int n) {
1493 DEBUG_INFO
1494 XRefEntry entry;
1495 int i;
1496 Object obj;
1498 for (i = first; i < first + n; ++i) {
1499 if (!parser->getObj(&obj)->isInt()) {
1500 return gFalse;
1502 entry.offset = (Guint)obj.getInt();
1503 obj.free();
1504 if (!parser->getObj(&obj)->isInt()) {
1505 return gFalse;
1507 entry.gen = obj.getInt();
1508 obj.free();
1509 parser->getObj(&obj);
1510 if (obj.isCmd("n")) {
1511 entry.type = xrefEntryUncompressed;
1512 } else if (obj.isCmd("f")) {
1513 entry.type = xrefEntryFree;
1514 } else {
1515 return gFalse;
1517 obj.free();
1518 if (entries[i].offset == 0xffffffff) {
1519 //printf("entry[%d]= %d %d %d\n", i, entry.gen, entry.offset, entry.type);
1520 entries[i] = entry;
1521 // PDF files of patents from the IBM Intellectual Property
1522 // Network have a bug: the xref table claims to start at 1
1523 // instead of 0.
1524 if (i == 1 && first == 1 &&
1525 entries[1].offset == 0 && entries[1].gen == 65535 &&
1526 entries[1].type == xrefEntryFree) {
1527 i = first = 0;
1528 entries[0] = entries[1];
1529 entries[1].offset = 0xffffffff;
1534 return gTrue;
1537 void XRef::fetchPage(int n, Object *obj) {
1538 DEBUG_INFO
1539 int offset = pageOffsetHintTable->getPageOffset(n);
1540 int length = pageOffsetHintTable->getPageLength(n);
1541 int numRefs = pageOffsetHintTable->getPageNumRefs(n);
1542 int first = pageOffsetHintTable->getPageFirstObjectNum(n);
1543 int num = pageOffsetHintTable->getPageNumObjects(n);
1544 int maxSharedId = sharedObjectHintTable->getNumIds();
1545 PartsVec parts;
1547 parts.add(offset, length);
1549 for (int i = 0; i < numRefs; ++i) {
1550 int id = pageOffsetHintTable->getPageRefId(n, i);
1551 if (id < maxSharedId) {
1552 int offset2 = sharedObjectHintTable->getGroupOffset(id);
1553 int length2 = sharedObjectHintTable->getGroupLength(id);
1554 parts.add(offset2, length2);
1555 } else if (maxSharedId) {
1556 error(-1, "Bad shared object ID: page %d, ref %d id %d", n, i, id);
1560 num = parts.getNumBlocks();
1561 for (int i = 0; i < num; ++i) {
1562 str->preRead(parts.getBlockOffset(i), parts.getBlockSize(i));
1565 fetch(first, 0, obj);
1568 void XRef::preloadOutlines() {
1569 if (outlinesHintTable)
1570 str->preRead(outlinesHintTable->getOffset(), outlinesHintTable->getLength());
1573 void XRef::preloadNamedDests() {
1574 if (namedDestsHintTable)
1575 str->preRead(namedDestsHintTable->getOffset(), namedDestsHintTable->getLength());
1578 void XRef::preloadInfo() {
1579 if (infoHintTable)
1580 str->preRead(infoHintTable->getOffset(), infoHintTable->getLength());
1583 void XRef::preloadForms() {
1584 DEBUG_INFO
1585 if (formsHintTable) {
1586 int numRefs = formsHintTable->getNumRefs();
1587 int maxSharedId = sharedObjectHintTable->getNumIds();
1588 PartsVec parts;
1589 parts.add(formsHintTable->getOffset(), formsHintTable->getLength());
1590 for (int i = 0; i < numRefs; ++i) {
1591 int id = formsHintTable->getRefId(i);
1592 if (id >= 0 && id < maxSharedId) {
1593 int offset2 = sharedObjectHintTable->getGroupOffset(id);
1594 int length2 = sharedObjectHintTable->getGroupLength(id);
1595 parts.add(offset2, length2);
1596 } else if (maxSharedId) {
1597 error(-1, "Bad shared object ID: form, ref %d id %d", i, id);
1600 int num = parts.getNumBlocks();
1601 for (int i = 0; i < num; ++i)
1602 str->preRead(parts.getBlockOffset(i), parts.getBlockSize(i));