1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
26 #include "ErrorCodes.h"
31 #include "SecurityHandler.h"
33 //------------------------------------------------------------------------
35 #define xrefSearchSize 1024 // read this many bytes at end of file
36 // 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
48 //------------------------------------------------------------------------
50 //------------------------------------------------------------------------
52 class JoinStream
: public Stream
{
55 JoinStream(Object
*s1
, Object
*s2
) {
58 str
= str1
.getStream();
64 virtual StreamKind
getKind() { return strWeird
; }
65 virtual void reset() {
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();
79 virtual int lookChar() {
80 int c
= str
->lookChar();
81 if (c
== EOF
&& str2
.isStream() && str
!= str2
.getStream()) {
82 c
= str2
.streamLookChar();
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
; }
99 //------------------------------------------------------------------------
101 //------------------------------------------------------------------------
106 // Create an object stream, using object number <objStrNum>,
108 ObjectStream(XRef
*xref
, int objStrNumA
);
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
);
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
) {
134 Object objStr
, obj1
, obj2
;
137 //objStrNum = objStrNumA;
142 if (!xref
->fetch(objStrNum
, 0, &objStr
)->isStream()) {
146 if (!objStr
.streamGetDict()->lookup("N", &obj1
)->isInt()) {
150 nObjects
= obj1
.getInt();
156 if (!objStr
.streamGetDict()->lookup("First", &obj1
)->isInt()) {
160 first
= obj1
.getInt();
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();
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()) {
185 objNums
[i
] = obj1
.getInt();
186 offsets
[i
] = obj2
.getInt();
189 if (objNums
[i
] < 0 || offsets
[i
] < 0 ||
190 (i
> 0 && offsets
[i
] < offsets
[i
-1])) {
196 while (str
->getChar() != EOF
) ;
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
202 for (i
= first
; i
< offsets
[0]; ++i
) {
203 objStr
.getStream()->getChar();
207 for (i
= 0; i
< nObjects
; ++i
) {
209 if (i
== nObjects
- 1) {
210 str
= new EmbedStream(objStr
.getStream(), &obj1
, gFalse
, 0);
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
) ;
228 ObjectStream::~ObjectStream() {
232 for (i
= 0; i
< nObjects
; ++i
) {
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 //------------------------------------------------------------------------
256 //------------------------------------------------------------------------
258 class ObjectStreamCache
263 ~ObjectStreamCache();
265 ObjectStream
*getObjectStream(XRef
*xref
, int objStrNum
);
268 static const int cacheSize
= 16;
273 ObjectStream
*objStr
;
277 Entry entries
[cacheSize
];
280 ObjectStreamCache::ObjectStreamCache() {
284 for (k
= 0; k
< cacheSize
; ++k
) {
286 p
= &entries
[k
].next
;
287 entries
[k
].objStrNum
= -1;
288 entries
[k
].objStr
= NULL
;
294 ObjectStreamCache::~ObjectStreamCache() {
297 for (k
= 0; k
< cacheSize
; ++k
) {
298 delete entries
[k
].objStr
;
302 ObjectStream
*ObjectStreamCache::getObjectStream(XRef
*xref
, int objStrNum
) {
303 Entry
**ptr
= &first
;
307 while ((entry
= *ptr
) != NULL
&& entry
->objStrNum
!= objStrNum
&& entry
->objStr
) {
312 if (entry
== NULL
|| entry
->objStr
== NULL
) {
315 delete entry
->objStr
;
318 entry
->objStrNum
= objStrNum
;
319 entry
->objStr
= new ObjectStream(xref
, objStrNum
);
320 } else if (entry
!= first
) {
326 return entry
->objStr
;
329 //------------------------------------------------------------------------
331 //------------------------------------------------------------------------
333 XRef::XRef(BaseStream
*strA
, PDFDoc
*doc
, GString
*ownerPassword
, GString
*userPassword
) {
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
;
356 permFlags
= defPermFlags
;
357 ownerPasswordOk
= gFalse
;
366 start
= str
->getStart();
368 if (doc
&& getLinearizationInfo()) {
369 str
->preRead(0,endOfFirstPageOffset
);
371 if (readXRef(&pos
)) {
372 if (readTrailerDict()) {
377 trailerDict
.getDict()->setXRef(this);
379 // check for encryption
381 if (!checkEncrypted(doc
, ownerPassword
, userPassword
)) {
383 errCode
= errEncrypted
;
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
)
397 if (fetch(i
, entries
[i
].gen
, &obj
)->isStream())
401 if (hintStr
.isStream() && hintTable2Length
) {
402 for (i
= size
; --i
> 0;)
403 if (entries
[i
].offset
== hintTable2Offset
)
405 if (fetch(i
, entries
[i
].gen
, &obj
)->isStream()) {
406 Stream
*hintStr2
= new JoinStream(&hintStr
, &obj
);
408 hintStr
.initStream(hintStr2
);
413 if (hintStr
.isStream() && (hintStr
.streamReset(), hintStr
.streamLookChar()) != EOF
) {
414 Dict
*dict
= hintStr
.streamGetDict();
417 if (dict
->lookup("P", &obj
)->isInt())
418 offset
= obj
.getInt();
421 hintStr
.streamReset();
422 for (i
= 0; i
< offset
; ++i
)
423 hintStr
.streamGetChar();
425 pageOffsetHintTable
= new PageOffsetHintTable(hintStr
.getStream(), numPages
,
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();
436 sharedObjectHintTable
= new SharedObjectHintTable(hintStr
.getStream(),
437 firstPageObjectNum
, pageOffsetHintTable
->getPageOffset(firstPageNum
),
438 hintTable1Offset
, hintTable1Length
);
440 if (dict
->lookup("O", &obj
)->isInt()) {
442 hintStr
.streamReset();
443 for (i
= 0; i
< pos
; ++i
)
444 hintStr
.streamGetChar();
445 outlinesHintTable
= new GenericHintTable(hintStr
.getStream());
449 if (dict
->lookup("E", &obj
)->isInt()) {
451 hintStr
.streamReset();
452 for (i
= 0; i
< pos
; ++i
)
453 hintStr
.streamGetChar();
454 namedDestsHintTable
= new GenericHintTable(hintStr
.getStream());
458 if (dict
->lookup("I", &obj
)->isInt()) {
460 hintStr
.streamReset();
461 for (i
= 0; i
< pos
; ++i
)
462 hintStr
.streamGetChar();
463 infoHintTable
= new GenericHintTable(hintStr
.getStream());
467 if (dict
->lookup("V", &obj
)->isInt()) {
469 hintStr
.streamReset();
470 for (i
= 0; i
< pos
; ++i
)
471 hintStr
.streamGetChar();
472 formsHintTable
= new FormsHintTable(hintStr
.getStream());
478 str
->setPos(start
+ mainXRefOffset
);
479 str
->preRead(start
+ mainXRefOffset
, fileSize
- mainXRefOffset
);
481 parser
= new Parser(NULL
,
483 str
->makeSubStream(start
+ mainXRefOffset
, gFalse
, 0, &obj
)));
484 if (readXRefPart2(parser
, 0, size
- numFirstPageXRefs
)) {
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
;
504 error(-1, "Bad hint stream");
514 error(-1, "Bad linearized file");
520 pos
= getStartXref();
522 // if there was a problem with the 'startxref' position, try to
523 // reconstruct the xref table
525 if (!(ok
= constructXRef())) {
526 errCode
= errDamaged
;
530 // read the xref table
532 while (readXRef(&pos
)) ;
534 // if there was a problem with the xref table,
535 // try to reconstruct it
537 if (!(ok
= constructXRef())) {
538 errCode
= errDamaged
;
544 // get the root dictionary (catalog) object
545 trailerDict
.dictLookupNF("Root", &obj
);
547 rootNum
= obj
.getRefNum();
548 rootGen
= obj
.getRefGen();
552 if (!(ok
= constructXRef())) {
553 errCode
= errDamaged
;
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
565 if (!checkEncrypted(doc
, ownerPassword
, userPassword
)) {
567 errCode
= errEncrypted
;
574 XRef::XRef(BaseStream
*strA
) {
586 pageOffsetHintTable
= NULL
;
587 sharedObjectHintTable
= NULL
;
588 outlinesHintTable
= NULL
;
589 namedDestsHintTable
= NULL
;
590 infoHintTable
= NULL
;
591 formsHintTable
= NULL
;
595 strCache
= new ObjectStreamCache
;
600 start
= str
->getStart();
601 pos
= getStartXref();
603 // if there was a problem with the 'startxref' position, try to
604 // reconstruct the xref table
606 if (!(ok
= constructXRef())) {
607 errCode
= errDamaged
;
611 // read the xref table
613 while (readXRef(&pos
)) ;
615 // if there was a problem with the xref table,
616 // try to reconstruct it
618 if (!(ok
= constructXRef())) {
619 errCode
= errDamaged
;
625 // get the root dictionary (catalog) object
626 trailerDict
.dictLookupNF("Root", &obj
);
628 rootNum
= obj
.getRefNum();
629 rootGen
= obj
.getRefGen();
633 if (!(ok
= constructXRef())) {
634 errCode
= errDamaged
;
639 // now set the trailer dictionary's xref pointer so we can fetch
640 // indirect objects from it
641 trailerDict
.getDict()->setXRef(this);
646 delete pageOffsetHintTable
;
647 delete sharedObjectHintTable
;
648 delete outlinesHintTable
;
649 delete namedDestsHintTable
;
650 delete infoHintTable
;
651 delete formsHintTable
;
660 // Read the 'startxref' position.
661 Guint
XRef::getStartXref() {
662 char buf
[xrefSearchSize
+1];
666 // read last xrefSearchSize bytes
667 str
->setPos(xrefSearchSize
, -1);
668 for (n
= 0; n
< xrefSearchSize
; ++n
) {
669 if ((c
= str
->getChar()) == EOF
) {
677 for (i
= n
- 9; i
>= 0; --i
) {
678 if (!strncmp(&buf
[i
], "startxref", 9)) {
685 for (p
= &buf
[i
+9]; isspace(*p
); ++p
) ;
686 lastXRefPos
= strToUnsigned(p
);
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
) {
698 // start up a parser, parse one token
700 parser
= new Parser(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")) {
708 more
= readXRefTable(parser
, pos
);
710 // parse an xref stream
711 } else if (obj
.isInt()) {
713 if (!parser
->getObj(&obj
)->isInt()) {
717 if (!parser
->getObj(&obj
)->isCmd("obj")) {
721 if (!parser
->getObj(&obj
)->isStream()) {
724 more
= readXRefStream(obj
.getStream(), pos
);
741 GBool
XRef::readXRefTable(Parser
*parser
, Guint
*pos
) {
746 int first
, n
, newSize
, i
;
749 parser
->getObj(&obj
);
750 if (obj
.isCmd("trailer")) {
757 first
= obj
.getInt();
759 if (!parser
->getObj(&obj
)->isInt()) {
764 if (first
< 0 || n
< 0 || first
+ n
< 0) {
767 if (first
+ n
> size
) {
768 for (newSize
= size
? 2 * size
: 1024;
769 first
+ n
> newSize
&& newSize
> 0;
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
;
781 if (numFirstPageXRefs
== -1) {
782 numFirstPageXRefs
= n
;
784 if (!readXRefPart(parser
, first
, n
)) {
789 // read the trailer dictionary
790 if (!parser
->getObj(&obj
)->isDict()) {
794 // get the 'Prev' pointer
795 obj
.getDict()->lookupNF("Prev", &obj2
);
797 *pos
= (Guint
)obj2
.getInt();
799 } else if (obj2
.isRef()) {
800 // certain buggy PDF generators generate "/Prev NNN 0 R" instead
802 *pos
= (Guint
)obj2
.getRefNum();
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();
834 GBool
XRef::readXRefStream(Stream
*xrefStr
, Guint
*pos
) {
838 Object obj
, obj2
, idx
;
839 int newSize
, first
, n
, i
;
841 dict
= xrefStr
->getDict();
843 if (!dict
->lookupNF("Size", &obj
)->isInt()) {
846 newSize
= obj
.getInt();
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
;
858 if (numFirstPageXRefs
== -1) {
859 numFirstPageXRefs
= size
;
863 if (!dict
->lookupNF("W", &obj
)->isArray() ||
864 obj
.arrayGetLength() < 3) {
867 for (i
= 0; i
< 3; ++i
) {
868 if (!obj
.arrayGet(i
, &obj2
)->isInt()) {
872 w
[i
] = obj2
.getInt();
874 if (w
[i
] < 0 || w
[i
] > 4) {
881 dict
->lookupNF("Index", &idx
);
883 for (i
= 0; i
+1 < idx
.arrayGetLength(); i
+= 2) {
884 if (!idx
.arrayGet(i
, &obj
)->isInt()) {
888 first
= obj
.getInt();
890 if (!idx
.arrayGet(i
+1, &obj
)->isInt()) {
896 if (first
< 0 || n
< 0 ||
897 !readXRefStreamSection(xrefStr
, w
, first
, n
)) {
903 if (!readXRefStreamSection(xrefStr
, w
, 0, newSize
)) {
910 dict
->lookupNF("Prev", &obj
);
912 *pos
= (Guint
)obj
.getInt();
918 if (trailerDict
.isNone()) {
919 trailerDict
.initDict(dict
);
931 GBool
XRef::readXRefStreamSection(Stream
*xrefStr
, int *w
, int first
, int n
) {
933 int type
, gen
, c
, newSize
, i
, j
;
938 if (first
+ n
> size
) {
939 for (newSize
= size
? 2 * size
: 1024;
940 first
+ n
> newSize
&& newSize
> 0;
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
;
952 for (i
= first
; i
< first
+ n
; ++i
) {
956 for (type
= 0, j
= 0; j
< w
[0]; ++j
) {
957 if ((c
= xrefStr
->getChar()) == EOF
) {
960 type
= (type
<< 8) + c
;
963 for (offset
= 0, j
= 0; j
< w
[1]; ++j
) {
964 if ((c
= xrefStr
->getChar()) == EOF
) {
967 offset
= (offset
<< 8) + c
;
969 for (gen
= 0, j
= 0; j
< w
[2]; ++j
) {
970 if ((c
= xrefStr
->getChar()) == EOF
) {
973 gen
= (gen
<< 8) + c
;
975 if (entries
[i
].offset
== 0xffffffff) {
978 entries
[i
].offset
= offset
;
979 entries
[i
].gen
= gen
;
980 entries
[i
].type
= xrefEntryFree
;
983 entries
[i
].offset
= offset
;
984 entries
[i
].gen
= gen
;
985 entries
[i
].type
= xrefEntryUncompressed
;
988 entries
[i
].offset
= offset
;
989 entries
[i
].gen
= gen
;
990 entries
[i
].type
= xrefEntryCompressed
;
1001 GBool
XRef::readXRefPart2(Parser
*parser
, int first
, int n
) {
1004 Guint pos
= str
->getPos();
1007 // start up a parser, parse one token
1009 parser2
= new Parser(NULL
,
1011 str
->makeSubStream(start
+ pos
, gFalse
, 0, &obj1
)));
1013 if (!parser2
->getObj(&obj
)->isInt()) {
1017 if (!parser2
->getObj(&obj
)->isInt()) {
1021 if (parser2
->getObj(&obj
)->isCmd("obj")) {
1023 if (!parser2
->getObj(&obj
)->isStream()) {
1028 /*more =*/ readXRefStream(obj
.getStream(), &pos
);
1035 return readXRefPart(parser
, first
, n
);
1038 return gTrue
/*more*/;
1048 // Attempt to construct an xref table for a damaged file.
1049 GBool
XRef::constructXRef() {
1051 Object newTrailerDict
, obj
;
1065 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
1067 streamEndsLen
= streamEndsSize
= 0;
1071 pos
= str
->getPos();
1072 if (!str
->getLine(buf
, 256)) {
1077 // got trailer dictionary
1078 if (!strncmp(p
, "trailer", 7)) {
1080 parser
= new Parser(NULL
,
1082 str
->makeSubStream(pos
+ 7, gFalse
, 0, &obj
)));
1083 parser
->getObj(&newTrailerDict
);
1084 if (newTrailerDict
.isDict()) {
1085 newTrailerDict
.dictLookupNF("Root", &obj
);
1087 rootNum
= obj
.getRefNum();
1088 rootGen
= obj
.getRefGen();
1089 if (!trailerDict
.isNone()) {
1092 newTrailerDict
.copy(&trailerDict
);
1097 newTrailerDict
.free();
1101 } else if (isdigit(*p
)) {
1106 } while (*p
&& isdigit(*p
));
1110 } while (*p
&& isspace(*p
));
1115 } while (*p
&& isdigit(*p
));
1119 } while (*p
&& isspace(*p
));
1120 if (!strncmp(p
, "obj", 3)) {
1122 newSize
= (num
+ 1 + 255) & ~255;
1124 error(-1, "Bad object number");
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
;
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
;
1160 error(-1, "Couldn't find trailer dictionary");
1164 GBool
XRef::checkEncrypted(PDFDoc
*doc
, GString
*ownerPassword
, GString
*userPassword
) {
1166 SecurityHandler
*secHdlr
;
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());
1181 // authorization failed
1186 // couldn't find the matching security handler
1190 // document is not encrypted
1198 void XRef::setEncryption(int permFlagsA
, GBool ownerPasswordOkA
,
1199 Guchar
*fileKeyA
, int keyLengthA
, int encVersionA
) {
1203 permFlags
= permFlagsA
;
1204 ownerPasswordOk
= ownerPasswordOkA
;
1205 if (keyLengthA
<= 16) {
1206 keyLength
= keyLengthA
;
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
) {
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
) {
1244 //printf("-> %d %d %d\n", e->gen, e->offset, e->type);
1247 case xrefEntryUncompressed
:
1248 if (e
->gen
!= gen
) {
1252 parser
= new Parser(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")) {
1267 parser
->getObj(obj
, encrypted
? fileKey
: (Guchar
*)NULL
, keyLength
,
1275 case xrefEntryCompressed
:
1279 strCache
->getObjectStream(this, (int)e
->offset
)->getObject(e
->gen
, num
, obj
);
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
) {
1304 if (streamEndsLen
== 0 ||
1305 streamStart
> streamEnds
[streamEndsLen
- 1]) {
1310 b
= streamEndsLen
- 1;
1311 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
1314 if (streamStart
<= streamEnds
[m
]) {
1320 *streamEnd
= streamEnds
[b
];
1324 Guint
XRef::strToUnsigned(char *s
) {
1329 #undef isdigit // workaround a bug in gcc 2.93's code generator
1331 for (p
= s
, i
= 0; *p
&& isdigit(*p
) && i
< 10; ++p
, ++i
) {
1332 x
= 10 * x
+ (*p
- '0');
1338 GBool
XRef::getLinearizationInfo() {
1348 // start up a parser, parse one token
1350 // look for first object
1351 Parser
parser(NULL
, new Lexer(NULL
, str
->makeSubStream(start
+ pos
, gFalse
, 0, &obj
)));
1353 parser
.getObj(&obj
);
1360 parser
.getObj(&obj
);
1367 parser
.getObj(&obj
);
1368 if (!obj
.isCmd("obj")) {
1374 // read first object
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;
1386 linDict
.dictLookupNF("L", &obj
);
1387 if (!obj
.isInt()/* || obj.getInt() != file->size()*/) {
1390 fileSize
= obj
.getInt();
1393 linDict
.dictLookupNF("O", &obj
);
1395 firstPageObjectNum
= obj
.getInt();
1399 linDict
.dictLookupNF("E", &obj
);
1401 endOfFirstPageOffset
= obj
.getInt();
1405 linDict
.dictLookupNF("N", &obj
);
1407 numPages
= obj
.getInt();
1411 linDict
.dictLookupNF("T", &obj
);
1413 mainXRefOffset
= obj
.getInt();
1417 linDict
.dictLookupNF("P", &obj
);
1419 firstPageNum
= obj
.getInt();
1423 linDict
.dictLookupNF("H", &obj
);
1424 if (obj
.isArray() && obj
.arrayGetLength() >= 2) {
1426 if (obj
.arrayGetNF(0,&obj2
)->isInt())
1427 hintTable1Offset
= obj2
.getInt();
1431 if (obj
.arrayGetNF(1,&obj2
)->isInt())
1432 hintTable1Length
= obj2
.getInt();
1436 if (obj
.arrayGetLength() == 4) {
1437 if (obj
.arrayGetNF(2,&obj2
)->isInt())
1438 hintTable2Offset
= obj2
.getInt();
1442 if (obj
.arrayGetNF(3,&obj2
)->isInt())
1443 hintTable2Length
= obj2
.getInt();
1448 hintTable2Offset
= 0;
1449 hintTable2Length
= 0;
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);*/
1464 // read trailer dict
1465 GBool
XRef::readTrailerDict() {
1470 if (trailerDict
.isDict()) {
1471 trailerDict
.dictLookupNF("Size", &obj
);
1473 size
= obj
.getInt();
1477 trailerDict
.dictLookupNF("Root", &obj
);
1479 rootNum
= obj
.getRefNum();
1480 rootGen
= obj
.getRefGen();
1491 // Read a part of an xref table
1492 GBool
XRef::readXRefPart(Parser
*parser
, int first
, int n
) {
1498 for (i
= first
; i
< first
+ n
; ++i
) {
1499 if (!parser
->getObj(&obj
)->isInt()) {
1502 entry
.offset
= (Guint
)obj
.getInt();
1504 if (!parser
->getObj(&obj
)->isInt()) {
1507 entry
.gen
= obj
.getInt();
1509 parser
->getObj(&obj
);
1510 if (obj
.isCmd("n")) {
1511 entry
.type
= xrefEntryUncompressed
;
1512 } else if (obj
.isCmd("f")) {
1513 entry
.type
= xrefEntryFree
;
1518 if (entries
[i
].offset
== 0xffffffff) {
1519 //printf("entry[%d]= %d %d %d\n", i, entry.gen, entry.offset, entry.type);
1521 // PDF files of patents from the IBM Intellectual Property
1522 // Network have a bug: the xref table claims to start at 1
1524 if (i
== 1 && first
== 1 &&
1525 entries
[1].offset
== 0 && entries
[1].gen
== 65535 &&
1526 entries
[1].type
== xrefEntryFree
) {
1528 entries
[0] = entries
[1];
1529 entries
[1].offset
= 0xffffffff;
1537 void XRef::fetchPage(int n
, Object
*obj
) {
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();
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() {
1580 str
->preRead(infoHintTable
->getOffset(), infoHintTable
->getLength());
1583 void XRef::preloadForms() {
1585 if (formsHintTable
) {
1586 int numRefs
= formsHintTable
->getNumRefs();
1587 int maxSharedId
= sharedObjectHintTable
->getNumIds();
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
));