]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/Catalog.cc
Reused eog HIG dialog in GPdf.
[evince.git] / pdf / xpdf / Catalog.cc
1 //========================================================================
2 //
3 // Catalog.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 <stddef.h>
16 #include "gmem.h"
17 #include "Object.h"
18 #include "XRef.h"
19 #include "Array.h"
20 #include "Dict.h"
21 #include "Page.h"
22 #include "Error.h"
23 #include "Link.h"
24 #include "Catalog.h"
25
26 //------------------------------------------------------------------------
27 // Catalog
28 //------------------------------------------------------------------------
29
30 Catalog::Catalog(XRef *xrefA) {
31   Object catDict, pagesDict;
32   Object obj, obj2;
33   int numPages0;
34   int i;
35
36   ok = gTrue;
37   xref = xrefA;
38   pages = NULL;
39   pageRefs = NULL;
40   numPages = pagesSize = 0;
41   baseURI = NULL;
42
43   xref->getCatalog(&catDict);
44   if (!catDict.isDict()) {
45     error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
46     goto err1;
47   }
48
49   // read page tree
50   catDict.dictLookup("Pages", &pagesDict);
51   // This should really be isDict("Pages"), but I've seen at least one
52   // PDF file where the /Type entry is missing.
53   if (!pagesDict.isDict()) {
54     error(-1, "Top-level pages object is wrong type (%s)",
55           pagesDict.getTypeName());
56     goto err2;
57   }
58   pagesDict.dictLookup("Count", &obj);
59   if (!obj.isInt()) {
60     error(-1, "Page count in top-level pages object is wrong type (%s)",
61           obj.getTypeName());
62     goto err3;
63   }
64   pagesSize = numPages0 = obj.getInt();
65   obj.free();
66   pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
67   pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
68   for (i = 0; i < pagesSize; ++i) {
69     pages[i] = NULL;
70     pageRefs[i].num = -1;
71     pageRefs[i].gen = -1;
72   }
73   numPages = readPageTree(pagesDict.getDict(), NULL, 0);
74   if (numPages != numPages0) {
75     error(-1, "Page count in top-level pages object is incorrect");
76   }
77   pagesDict.free();
78
79   // read named destination dictionary
80   catDict.dictLookup("Dests", &dests);
81
82   // read root of named destination tree
83   if (catDict.dictLookup("Names", &obj)->isDict())
84     obj.dictLookup("Dests", &nameTree);
85   else
86     nameTree.initNull();
87   obj.free();
88
89   // read base URI
90   if (catDict.dictLookup("URI", &obj)->isDict()) {
91     if (obj.dictLookup("Base", &obj2)->isString()) {
92       baseURI = obj2.getString()->copy();
93     }
94     obj2.free();
95   }
96   obj.free();
97
98   // get the metadata stream
99   catDict.dictLookup("Metadata", &metadata);
100
101   // get the structure tree root
102   catDict.dictLookup("StructTreeRoot", &structTreeRoot);
103
104   // get the outline dictionary
105   catDict.dictLookup("Outlines", &outline);
106
107   catDict.free();
108   return;
109
110  err3:
111   obj.free();
112  err2:
113   pagesDict.free();
114  err1:
115   catDict.free();
116   dests.initNull();
117   nameTree.initNull();
118   ok = gFalse;
119 }
120
121 Catalog::~Catalog() {
122   int i;
123
124   if (pages) {
125     for (i = 0; i < pagesSize; ++i) {
126       if (pages[i]) {
127         delete pages[i];
128       }
129     }
130     gfree(pages);
131     gfree(pageRefs);
132   }
133   dests.free();
134   nameTree.free();
135   if (baseURI) {
136     delete baseURI;
137   }
138   metadata.free();
139   structTreeRoot.free();
140   outline.free();
141 }
142
143 GString *Catalog::readMetadata() {
144   GString *s;
145   Dict *dict;
146   Object obj;
147   int c;
148
149   if (!metadata.isStream()) {
150     return NULL;
151   }
152   dict = metadata.streamGetDict();
153   if (!dict->lookup("Subtype", &obj)->isName("XML")) {
154     error(-1, "Unknown Metadata type: '%s'",
155           obj.isName() ? obj.getName() : "???");
156   }
157   obj.free();
158   s = new GString();
159   metadata.streamReset();
160   while ((c = metadata.streamGetChar()) != EOF) {
161     s->append(c);
162   }
163   metadata.streamClose();
164   return s;
165 }
166
167 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
168   Object kids;
169   Object kid;
170   Object kidRef;
171   PageAttrs *attrs1, *attrs2;
172   Page *page;
173   int i, j;
174
175   attrs1 = new PageAttrs(attrs, pagesDict);
176   pagesDict->lookup("Kids", &kids);
177   if (!kids.isArray()) {
178     error(-1, "Kids object (page %d) is wrong type (%s)",
179           start+1, kids.getTypeName());
180     goto err1;
181   }
182   for (i = 0; i < kids.arrayGetLength(); ++i) {
183     kids.arrayGet(i, &kid);
184     if (kid.isDict("Page")) {
185       attrs2 = new PageAttrs(attrs1, kid.getDict());
186       page = new Page(xref, start+1, kid.getDict(), attrs2);
187       if (!page->isOk()) {
188         ++start;
189         goto err3;
190       }
191       if (start >= pagesSize) {
192         pagesSize += 32;
193         pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
194         pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
195         for (j = pagesSize - 32; j < pagesSize; ++j) {
196           pages[j] = NULL;
197           pageRefs[j].num = -1;
198           pageRefs[j].gen = -1;
199         }
200       }
201       pages[start] = page;
202       kids.arrayGetNF(i, &kidRef);
203       if (kidRef.isRef()) {
204         pageRefs[start].num = kidRef.getRefNum();
205         pageRefs[start].gen = kidRef.getRefGen();
206       }
207       kidRef.free();
208       ++start;
209     // This should really be isDict("Pages"), but I've seen at least one
210     // PDF file where the /Type entry is missing.
211     } else if (kid.isDict()) {
212       if ((start = readPageTree(kid.getDict(), attrs1, start))
213           < 0)
214         goto err2;
215     } else {
216       error(-1, "Kid object (page %d) is wrong type (%s)",
217             start+1, kid.getTypeName());
218       goto err2;
219     }
220     kid.free();
221   }
222   delete attrs1;
223   kids.free();
224   return start;
225
226  err3:
227   delete page;
228  err2:
229   kid.free();
230  err1:
231   kids.free();
232   delete attrs1;
233   ok = gFalse;
234   return -1;
235 }
236
237 int Catalog::findPage(int num, int gen) {
238   int i;
239
240   for (i = 0; i < numPages; ++i) {
241     if (pageRefs[i].num == num && pageRefs[i].gen == gen)
242       return i + 1;
243   }
244   return 0;
245 }
246
247 LinkDest *Catalog::findDest(GString *name) {
248   LinkDest *dest;
249   Object obj1, obj2;
250   GBool found;
251
252   // try named destination dictionary then name tree
253   found = gFalse;
254   if (dests.isDict()) {
255     if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
256       found = gTrue;
257     else
258       obj1.free();
259   }
260   if (!found && nameTree.isDict()) {
261     if (!findDestInTree(&nameTree, name, &obj1)->isNull())
262       found = gTrue;
263     else
264       obj1.free();
265   }
266   if (!found)
267     return NULL;
268
269   // construct LinkDest
270   dest = NULL;
271   if (obj1.isArray()) {
272     dest = new LinkDest(obj1.getArray());
273   } else if (obj1.isDict()) {
274     if (obj1.dictLookup("D", &obj2)->isArray())
275       dest = new LinkDest(obj2.getArray());
276     else
277       error(-1, "Bad named destination value");
278     obj2.free();
279   } else {
280     error(-1, "Bad named destination value");
281   }
282   obj1.free();
283   if (dest && !dest->isOk()) {
284     delete dest;
285     dest = NULL;
286   }
287
288   return dest;
289 }
290
291 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
292   Object names, name1;
293   Object kids, kid, limits, low, high;
294   GBool done, found;
295   int cmp, i;
296
297   // leaf node
298   if (tree->dictLookup("Names", &names)->isArray()) {
299     done = found = gFalse;
300     for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
301       if (names.arrayGet(i, &name1)->isString()) {
302         cmp = name->cmp(name1.getString());
303         if (cmp == 0) {
304           names.arrayGet(i+1, obj);
305           found = gTrue;
306           done = gTrue;
307         } else if (cmp < 0) {
308           done = gTrue;
309         }
310         name1.free();
311       }
312     }
313     names.free();
314     if (!found)
315       obj->initNull();
316     return obj;
317   }
318   names.free();
319
320   // root or intermediate node
321   done = gFalse;
322   if (tree->dictLookup("Kids", &kids)->isArray()) {
323     for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
324       if (kids.arrayGet(i, &kid)->isDict()) {
325         if (kid.dictLookup("Limits", &limits)->isArray()) {
326           if (limits.arrayGet(0, &low)->isString() &&
327               name->cmp(low.getString()) >= 0) {
328             if (limits.arrayGet(1, &high)->isString() &&
329                 name->cmp(high.getString()) <= 0) {
330               findDestInTree(&kid, name, obj);
331               done = gTrue;
332             }
333             high.free();
334           }
335           low.free();
336         }
337         limits.free();
338       }
339       kid.free();
340     }
341   }
342   kids.free();
343
344   // name was outside of ranges of all kids
345   if (!done)
346     obj->initNull();
347
348   return obj;
349 }