]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XRef.cc
c725a4a3c74d3b9c9d5bb38ee070336a49343cb3
[evince.git] / pdf / xpdf / XRef.cc
1 //========================================================================
2 //
3 // XRef.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include "gmem.h"
18 #include "Object.h"
19 #include "Stream.h"
20 #include "Lexer.h"
21 #include "Parser.h"
22 #include "Dict.h"
23 #include "Error.h"
24 #include "XRef.h"
25
26 //------------------------------------------------------------------------
27
28 #define xrefSearchSize 1024     // read this many bytes at end of file
29                                 //   to look for 'startxref'
30
31 //------------------------------------------------------------------------
32 // The global xref table
33 //------------------------------------------------------------------------
34
35 XRef *xref = NULL;
36
37 //------------------------------------------------------------------------
38 // XRef
39 //------------------------------------------------------------------------
40
41 XRef::XRef(Stream *str1) {
42   XRef *oldXref;
43   int pos;
44   int i;
45
46   ok = gTrue;
47   size = 0;
48   entries = NULL;
49
50   // get rid of old xref (otherwise it will try to fetch the Root object
51   // in the new document, using the old xref)
52   oldXref = xref;
53   xref = NULL;
54
55   // read the trailer
56   str = str1;
57   start = str->getStart();
58   pos = readTrailer(str);
59
60   // if there was a problem with the trailer,
61   // try to reconstruct the xref table
62   if (pos == 0) {
63     if (!(ok = constructXRef(str))) {
64       xref = oldXref;
65       return;
66     }
67
68   // trailer is ok - read the xref table
69   } else {
70     entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
71     for (i = 0; i < size; ++i) {
72       entries[i].offset = -1;
73       entries[i].used = gFalse;
74     }
75     while (readXRef(str, &pos)) ;
76
77     // if there was a problem with the xref table,
78     // try to reconstruct it
79     if (!ok) {
80       gfree(entries);
81       size = 0;
82       entries = NULL;
83       if (!(ok = constructXRef(str))) {
84         xref = oldXref;
85         return;
86       }
87     }
88   }
89
90   // set up new xref table
91   xref = this;
92
93   // check for encryption
94   if (checkEncrypted()) {
95     ok = gFalse;
96     xref = oldXref;
97     return;
98   }
99 }
100
101 XRef::~XRef() {
102   gfree(entries);
103   trailerDict.free();
104 }
105
106 // Read startxref position, xref table size, and root.  Returns
107 // first xref position.
108 int XRef::readTrailer(Stream *str) {
109   Parser *parser;
110   Object obj;
111   char buf[xrefSearchSize+1];
112   int n, pos, pos1;
113   char *p;
114   int c;
115   int i;
116
117   // read last xrefSearchSize bytes
118   str->setPos(-xrefSearchSize);
119   for (n = 0; n < xrefSearchSize; ++n) {
120     if ((c = str->getChar()) == EOF)
121       break;
122     buf[n] = c;
123   }
124   buf[n] = '\0';
125
126   // find startxref
127   for (i = n - 9; i >= 0; --i) {
128     if (!strncmp(&buf[i], "startxref", 9))
129       break;
130   }
131   if (i < 0)
132     return 0;
133   for (p = &buf[i+9]; isspace(*p); ++p) ;
134   pos = atoi(p);
135
136   // find trailer dict by looking after first xref table
137   // (NB: we can't just use the trailer dict at the end of the file --
138   // this won't work for linearized files.)
139   str->setPos(start + pos);
140   for (i = 0; i < 4; ++i)
141     buf[i] = str->getChar();
142   if (strncmp(buf, "xref", 4))
143     return 0;
144   pos1 = pos + 4;
145   while (1) {
146     str->setPos(start + pos1);
147     for (i = 0; i < 35; ++i) {
148       if ((c = str->getChar()) == EOF)
149         return 0;
150       buf[i] = c;
151     }
152     if (!strncmp(buf, "trailer", 7))
153       break;
154     p = buf;
155     while (isspace(*p)) ++p;
156     while ('0' <= *p && *p <= '9') ++p;
157     while (isspace(*p)) ++p;
158     n = atoi(p);
159     while ('0' <= *p && *p <= '9') ++p;
160     while (isspace(*p)) ++p;
161     if (p == buf)
162       return 0;
163     pos1 += (p - buf) + n * 20;
164   }
165   pos1 += 7;
166
167   // read trailer dict
168   obj.initNull();
169   parser = new Parser(new Lexer(str->subStream (start + pos1, -1, &obj)));
170   parser->getObj(&trailerDict);
171   if (trailerDict.isDict()) {
172     trailerDict.dictLookupNF("Size", &obj);
173     if (obj.isInt())
174       size = obj.getInt();
175     else
176       pos = 0;
177     obj.free();
178     trailerDict.dictLookupNF("Root", &obj);
179     if (obj.isRef()) {
180       rootNum = obj.getRefNum();
181       rootGen = obj.getRefGen();
182     } else {
183       pos = 0;
184     }
185     obj.free();
186   } else {
187     pos = 0;
188   }
189   delete parser;
190
191   // return first xref position
192   return pos;
193 }
194
195 // Read an xref table and the prev pointer from the trailer.
196 GBool XRef::readXRef(Stream *str, int *pos) {
197   Parser *parser;
198   Object obj, obj2;
199   char s[20];
200   GBool more;
201   int first, n, i, j;
202   int c;
203
204   // seek to xref in stream
205   str->setPos(start + *pos);
206
207   // make sure it's an xref table
208   while ((c = str->getChar()) != EOF && isspace(c)) ;
209   s[0] = (char)c;
210   s[1] = (char)str->getChar();
211   s[2] = (char)str->getChar();
212   s[3] = (char)str->getChar();
213   if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f'))
214     goto err2;
215
216   // read xref
217   while (1) {
218     while ((c = str->lookChar()) != EOF && isspace(c))
219       str->getChar();
220     if (c == 't')
221       break;
222     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
223       s[i] = (char)c;
224     if (i == 0)
225       goto err2;
226     s[i] = '\0';
227     first = atoi(s);
228     while ((c = str->lookChar()) != EOF && isspace(c))
229       str->getChar();
230     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
231       s[i] = (char)c;
232     if (i == 0)
233       goto err2;
234     s[i] = '\0';
235     n = atoi(s);
236     while ((c = str->lookChar()) != EOF && isspace(c))
237       str->getChar();
238     for (i = first; i < first + n; ++i) {
239       for (j = 0; j < 20; ++j) {
240         if ((c = str->getChar()) == EOF)
241           goto err2;
242         s[j] = (char)c;
243       }
244       if (entries[i].offset < 0) {
245         s[10] = '\0';
246         entries[i].offset = atoi(s);
247         s[16] = '\0';
248         entries[i].gen = atoi(&s[11]);
249         if (s[17] == 'n')
250           entries[i].used = gTrue;
251         else if (s[17] == 'f')
252           entries[i].used = gFalse;
253         else
254           goto err2;
255       }
256     }
257   }
258
259   // read prev pointer from trailer dictionary
260   obj.initNull();
261   parser = new Parser(new Lexer(str->subStream (str->getPos(), -1, &obj)));
262   parser->getObj(&obj);
263   if (!obj.isCmd("trailer"))
264     goto err1;
265   obj.free();
266   parser->getObj(&obj);
267   if (!obj.isDict())
268     goto err1;
269   obj.getDict()->lookupNF("Prev", &obj2);
270   if (obj2.isInt()) {
271     *pos = obj2.getInt();
272     more = gTrue;
273   } else {
274     more = gFalse;
275   }
276   obj.free();
277   obj2.free();
278
279   delete parser;
280   return more;
281
282  err1:
283   obj.free();
284  err2:
285   ok = gFalse;
286   return gFalse;
287 }
288
289 // Attempt to construct an xref table for a damaged file.
290 GBool XRef::constructXRef(Stream *str) {
291   Parser *parser;
292   Object obj;
293   char buf[256];
294   int pos;
295   int num, gen;
296   int newSize;
297   char *p;
298   int i;
299   GBool gotRoot;
300
301   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
302   gotRoot = gFalse;
303
304   str->reset();
305   while (1) {
306     pos = str->getPos();
307     if (!str->getLine(buf, 256))
308       break;
309     p = buf;
310
311     // got trailer dictionary
312     if (!strncmp(p, "trailer", 7)) {
313       obj.initNull();
314       parser = new Parser(new Lexer(str->subStream(start + pos + 8, -1, &obj)));
315       if (!trailerDict.isNone())
316         trailerDict.free();
317       parser->getObj(&trailerDict);
318       if (trailerDict.isDict()) {
319         trailerDict.dictLookupNF("Root", &obj);
320         if (obj.isRef()) {
321           rootNum = obj.getRefNum();
322           rootGen = obj.getRefGen();
323           gotRoot = gTrue;
324         }
325         obj.free();
326       } else {
327         pos = 0;
328       }
329       delete parser;
330
331     // look for object
332     } else if (isdigit(*p)) {
333       num = atoi(p);
334       do {
335         ++p;
336       } while (*p && isdigit(*p));
337       if (isspace(*p)) {
338         do {
339           ++p;
340         } while (*p && isspace(*p));
341         if (isdigit(*p)) {
342           gen = atoi(p);
343           do {
344             ++p;
345           } while (*p && isdigit(*p));
346           if (isspace(*p)) {
347             do {
348               ++p;
349             } while (*p && isspace(*p));
350             if (!strncmp(p, "obj", 3)) {
351               if (num >= size) {
352                 newSize = (num + 1 + 255) & ~255;
353                 entries = (XRefEntry *)
354                             grealloc(entries, newSize * sizeof(XRefEntry));
355                 for (i = size; i < newSize; ++i) {
356                   entries[i].offset = -1;
357                   entries[i].used = gFalse;
358                 }
359                 size = newSize;
360               }
361               if (!entries[num].used || gen >= entries[num].gen) {
362                 entries[num].offset = pos - start;
363                 entries[num].gen = gen;
364                 entries[num].used = gTrue;
365               }
366             }
367           }
368         }
369       }
370     }
371   }
372
373   if (gotRoot)
374     return gTrue;
375
376   error(-1, "Couldn't find trailer dictionary");
377   return gFalse;
378 }
379
380 GBool XRef::checkEncrypted() {
381   Object obj;
382   GBool encrypted;
383
384   trailerDict.dictLookup("Encrypt", &obj);
385   if ((encrypted = !obj.isNull())) {
386     error(-1, "PDF file is encrypted and cannot be displayed");
387     error(-1, "* Decryption support is currently not included in xpdf");
388     error(-1, "* due to legal restrictions: the U.S.A. still has bogus");
389     error(-1, "* export controls on cryptography software.");
390   }
391   obj.free();
392   return encrypted;
393 }
394
395 GBool XRef::okToPrint() {
396   return gTrue;
397 }
398
399 GBool XRef::okToCopy() {
400   return gTrue;
401 }
402
403 Object *XRef::fetch(int num, int gen, Object *obj) {
404   XRefEntry *e;
405   Parser *parser;
406   Object obj1, obj2, obj3;
407
408   // check for bogus ref - this can happen in corrupted PDF files
409   if (num < 0 || num >= size) {
410     obj->initNull();
411     return obj;
412   }
413
414   e = &entries[num];
415   if (e->gen == gen && e->offset >= 0) {
416     obj1.initNull();
417     parser = new Parser(new Lexer(str->subStream(start + e->offset, -1, &obj1)));
418     parser->getObj(&obj1);
419     parser->getObj(&obj2);
420     parser->getObj(&obj3);
421     if (obj1.isInt() && obj1.getInt() == num &&
422         obj2.isInt() && obj2.getInt() == gen &&
423         obj3.isCmd("obj")) {
424       parser->getObj(obj);
425     } else {
426       obj->initNull();
427     }
428     obj1.free();
429     obj2.free();
430     obj3.free();
431     delete parser;
432   } else {
433     obj->initNull();
434   }
435   return obj;
436 }
437
438 Object *XRef::getDocInfo(Object *obj) {
439   return trailerDict.dictLookup("Info", obj);
440 }