e0d82d23c62ef46ddff1a8c771ceeb576c436758
[swftools.git] / XRef.cc
1 //========================================================================
2 //
3 // XRef.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
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"
31
32 //------------------------------------------------------------------------
33
34 #define xrefSearchSize 1024     // read this many bytes at end of file
35                                 //   to look for 'startxref'
36
37 #ifndef NO_DECRYPTION
38 //------------------------------------------------------------------------
39 // Permission bits
40 //------------------------------------------------------------------------
41
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
48
49 //------------------------------------------------------------------------
50 // ObjectStream
51 //------------------------------------------------------------------------
52
53 class ObjectStream {
54 public:
55
56   // Create an object stream, using object number <objStrNum>,
57   // generation 0.
58   ObjectStream(XRef *xref, int objStrNumA);
59
60   ~ObjectStream();
61
62   // Return the object number of this object stream.
63   int getObjStrNum() { return objStrNum; }
64
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);
68
69 private:
70
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)
75 };
76
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;
83
84   objStrNum = objStrNumA;
85   nObjects = 0;
86   objs = NULL;
87   objNums = NULL;
88
89   if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
90     goto err1;
91   }
92
93   if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
94     obj1.free();
95     goto err1;
96   }
97   nObjects = obj1.getInt();
98   obj1.free();
99   if (nObjects == 0) {
100     goto err1;
101   }
102
103   if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
104     obj1.free();
105     goto err1;
106   }
107   first = obj1.getInt();
108   obj1.free();
109
110   objs = new Object[nObjects];
111   objNums = (int *)gmalloc(nObjects * sizeof(int));
112   offsets = (int *)gmalloc(nObjects * sizeof(int));
113
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;
128     }
129     objNums[i] = obj1.getInt();
130     offsets[i] = obj2.getInt();
131     obj1.free();
132     obj2.free();
133   }
134   while (str->getChar() != EOF) ;
135   delete parser;
136
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();
142   }
143
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]);
152     }
153     parser = new Parser(xref, new Lexer(xref, str));
154     parser->getObj(&objs[i]);
155     while (str->getChar() != EOF) ;
156     delete parser;
157   }
158
159   gfree(offsets);
160
161  err1:
162   objStr.free();
163   return;
164 }
165
166 ObjectStream::~ObjectStream() {
167   int i;
168
169   if (objs) {
170     for (i = 0; i < nObjects; ++i) {
171       objs[i].free();
172     }
173     delete[] objs;
174   }
175   gfree(objNums);
176 }
177
178 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
179   if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
180     return obj->initNull();
181   }
182   return objs[objIdx].copy(obj);
183 }
184
185 //------------------------------------------------------------------------
186 // XRef
187 //------------------------------------------------------------------------
188
189 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
190   Guint pos;
191   Object obj;
192
193   ok = gTrue;
194   errCode = errNone;
195   size = 0;
196   entries = NULL;
197   streamEnds = NULL;
198   streamEndsLen = 0;
199   objStr = NULL;
200
201   // read the trailer
202   str = strA;
203   start = str->getStart();
204   pos = getStartXref();
205
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;
212     }
213
214   // read the xref table
215   } else {
216     while (readXRef(&pos)) ;
217
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;
224       }
225     }
226   }
227
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;
239     }
240   }
241
242   // now set the trailer dictionary's xref pointer so we can fetch
243   // indirect objects from it
244   trailerDict.getDict()->setXRef(this);
245
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;
254   }
255 }
256
257 XRef::~XRef() {
258   gfree(entries);
259   trailerDict.free();
260   if (streamEnds) {
261     gfree(streamEnds);
262   }
263   if (objStr) {
264     delete objStr;
265   }
266 }
267
268 // Read the 'startxref' position.
269 Guint XRef::getStartXref() {
270   char buf[xrefSearchSize+1];
271   char *p;
272   int c, n, i;
273
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;
279     }
280     buf[n] = c;
281   }
282   buf[n] = '\0';
283
284   // find startxref
285   for (i = n - 9; i >= 0; --i) {
286     if (!strncmp(&buf[i], "startxref", 9)) {
287       break;
288     }
289   }
290   if (i < 0) {
291     return 0;
292   }
293   for (p = &buf[i+9]; isspace(*p); ++p) ;
294   lastXRefPos = strToUnsigned(p);
295
296   return lastXRefPos;
297 }
298
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;
305
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);
312
313   // parse an old-style xref table
314   if (obj.isCmd("xref")) {
315     obj.free();
316     more = readXRefTable(parser, pos);
317
318   // parse an xref stream
319   } else if (obj.isInt()) {
320     obj.free();
321     if (!parser->getObj(&obj)->isInt()) {
322       goto err1;
323     }
324     obj.free();
325     if (!parser->getObj(&obj)->isCmd("obj")) {
326       goto err1;
327     }
328     obj.free();
329     if (!parser->getObj(&obj)->isStream()) {
330       goto err1;
331     }
332     more = readXRefStream(obj.getStream(), pos);
333     obj.free();
334
335   } else {
336     goto err1;
337   }
338
339   delete parser;
340   return more;
341
342  err1:
343   obj.free();
344   delete parser;
345   ok = gFalse;
346   return gFalse;
347 }
348
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;
355
356   while (1) {
357     parser->getObj(&obj);
358     if (obj.isCmd("trailer")) {
359       obj.free();
360       break;
361     }
362     if (!obj.isInt()) {
363       goto err1;
364     }
365     first = obj.getInt();
366     obj.free();
367     if (!parser->getObj(&obj)->isInt()) {
368       goto err1;
369     }
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;
380       }
381       size = newSize;
382     }
383     for (i = first; i < first + n; ++i) {
384       if (!parser->getObj(&obj)->isInt()) {
385         goto err1;
386       }
387       entry.offset = (Guint)obj.getInt();
388       obj.free();
389       if (!parser->getObj(&obj)->isInt()) {
390         goto err1;
391       }
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;
401       }
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;
414         }
415       }
416     }
417   }
418
419   // read the trailer dictionary
420   if (!parser->getObj(&obj)->isDict()) {
421     goto err1;
422   }
423
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;
436   }
437   obj2.free();
438
439   // save the first trailer dictionary
440   if (trailerDict.isNone()) {
441     obj.copy(&trailerDict);
442   }
443
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;
450     }
451   }
452   obj2.free();
453
454   obj.free();
455   return more;
456
457  err1:
458   obj.free();
459   ok = gFalse;
460   return gFalse;
461 }
462
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;
469
470   dict = xrefStr->getDict();
471
472   if (!dict->lookupNF("Size", &obj)->isInt()) {
473     goto err1;
474   }
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;
482     }
483     size = newSize;
484   }
485
486   if (!dict->lookupNF("W", &obj)->isArray() ||
487       obj.arrayGetLength() < 3) {
488     goto err1;
489   }
490   for (i = 0; i < 3; ++i) {
491     if (!obj.arrayGet(i, &obj2)->isInt()) {
492       obj2.free();
493       goto err1;
494     }
495     w[i] = obj2.getInt();
496     obj2.free();
497   }
498   obj.free();
499
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;
507       }
508       first = obj.getInt();
509       obj.free();
510       if (!idx.arrayGet(i+1, &obj)->isInt()) {
511         idx.free();
512         goto err1;
513       }
514       n = obj.getInt();
515       obj.free();
516       if (!readXRefStreamSection(xrefStr, w, first, n)) {
517         idx.free();
518         goto err0;
519       }
520     }
521   } else {
522     if (!readXRefStreamSection(xrefStr, w, 0, size)) {
523       idx.free();
524       goto err0;
525     }
526   }
527   idx.free();
528
529   dict->lookupNF("Prev", &obj);
530   if (obj.isInt()) {
531     *pos = (Guint)obj.getInt();
532     more = gTrue;
533   } else {
534     more = gFalse;
535   }
536   obj.free();
537   if (trailerDict.isNone()) {
538     trailerDict.initDict(dict);
539   }
540
541   return more;
542
543  err1:
544   obj.free();
545  err0:
546   ok = gFalse;
547   return gFalse;
548 }
549
550 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
551   Guint offset;
552   int type, gen, c, newSize, i, j;
553
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;
562     }
563     size = newSize;
564   }
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;
572         }
573         type = (type << 8) + c;
574       }
575     }
576     for (offset = 0, j = 0; j < w[1]; ++j) {
577       if ((c = xrefStr->getChar()) == EOF) {
578         return gFalse;
579       }
580       offset = (offset << 8) + c;
581     }
582     for (gen = 0, j = 0; j < w[2]; ++j) {
583       if ((c = xrefStr->getChar()) == EOF) {
584         return gFalse;
585       }
586       gen = (gen << 8) + c;
587     }
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;
606     }
607   }
608
609   return gTrue;
610 }
611
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;
624
625   gfree(entries);
626   size = 0;
627   entries = NULL;
628
629   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
630   gotRoot = gFalse;
631   streamEndsLen = streamEndsSize = 0;
632
633   str->reset();
634   while (1) {
635     pos = str->getPos();
636     if (!str->getLine(buf, 256)) {
637       break;
638     }
639     p = buf;
640
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();
655           }
656           newTrailerDict.copy(&trailerDict);
657           gotRoot = gTrue;
658         }
659         obj.free();
660       }
661       newTrailerDict.free();
662       delete parser;
663
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;
691                 }
692                 size = newSize;
693               }
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;
699               }
700             }
701           }
702         }
703       }
704
705     } else if (!strncmp(p, "endstream", 9)) {
706       if (streamEndsLen == streamEndsSize) {
707         streamEndsSize += 64;
708         streamEnds = (Guint *)grealloc(streamEnds,
709                                        streamEndsSize * sizeof(int));
710       }
711       streamEnds[streamEndsLen++] = pos;
712     }
713   }
714
715   if (gotRoot)
716     return gTrue;
717
718   error(-1, "Couldn't find trailer dictionary");
719   return gFalse;
720 }
721
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;
728
729   keyLength = 0;
730   encVersion = encRevision = 0;
731   ret = gFalse;
732
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;
759         }
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");
772               }
773               ret = gFalse;
774             } else {
775               error(-1, "Incorrect password");
776             }
777           } else {
778             error(-1, "Weird encryption info");
779           }
780           fileID1.free();
781         } else {
782           error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
783                 encVersion, encRevision);
784         }
785       } else {
786         error(-1, "Weird encryption info");
787       }
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() : "???");
798     }
799     filterObj.free();
800   }
801   encrypt.free();
802
803   // this flag has to be set *after* we read the O/U/P strings
804   encrypted = encrypted1;
805
806   return ret;
807 }
808 #else
809 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
810   Object obj;
811   GBool encrypted;
812
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.");
817   }
818   obj.free();
819   return encrypted;
820 }
821 #endif
822
823 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
824 #ifndef NO_DECRYPTION
825   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
826 #else
827   return gTrue;
828 #endif
829 }
830
831 GBool XRef::okToChange(GBool ignoreOwnerPW) {
832 #ifndef NO_DECRYPTION
833   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
834 #else
835   return gTrue;
836 #endif
837 }
838
839 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
840 #ifndef NO_DECRYPTION
841   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
842 #else
843   return gTrue;
844 #endif
845 }
846
847 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
848 #ifndef NO_DECRYPTION
849   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
850 #else
851   return gTrue;
852 #endif
853 }
854
855 Object *XRef::fetch(int num, int gen, Object *obj) {
856   XRefEntry *e;
857   Parser *parser;
858   Object obj1, obj2, obj3;
859
860   // check for bogus ref - this can happen in corrupted PDF files
861   if (num < 0 || num >= size) {
862     goto err;
863   }
864
865   e = &entries[num];
866   switch (e->type) {
867
868   case xrefEntryUncompressed:
869     if (e->gen != gen) {
870       goto err;
871     }
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;
883     }
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;
895
896   case xrefEntryCompressed:
897     if (gen != 0) {
898       goto err;
899     }
900     if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
901       if (objStr) {
902         delete objStr;
903       }
904       objStr = new ObjectStream(this, e->offset);
905     }
906     objStr->getObject(e->gen, num, obj);
907     break;
908
909   default:
910     goto err;
911   }
912
913   return obj;
914
915  err:
916   return obj->initNull();
917 }
918
919 Object *XRef::getDocInfo(Object *obj) {
920   return trailerDict.dictLookup("Info", obj);
921 }
922
923 // Added for the pdftex project.
924 Object *XRef::getDocInfoNF(Object *obj) {
925   return trailerDict.dictLookupNF("Info", obj);
926 }
927
928 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
929   int a, b, m;
930
931   if (streamEndsLen == 0 ||
932       streamStart > streamEnds[streamEndsLen - 1]) {
933     return gFalse;
934   }
935
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;
945     }
946   }
947   *streamEnd = streamEnds[b];
948   return gTrue;
949 }
950
951 Guint XRef::strToUnsigned(char *s) {
952   Guint x;
953   char *p;
954   int i;
955
956   x = 0;
957   for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
958     x = 10 * x + (*p - '0');
959   }
960   return x;
961 }