Initial revision
[swftools.git] / pdf2swf / xpdf / Catalog.cc
1 //========================================================================
2 //
3 // Catalog.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stddef.h>
14 #include "gmem.h"
15 #include "Object.h"
16 #include "Array.h"
17 #include "Dict.h"
18 #include "Page.h"
19 #include "Error.h"
20 #include "Link.h"
21 #include "Catalog.h"
22
23 //------------------------------------------------------------------------
24 // Catalog
25 //------------------------------------------------------------------------
26
27 Catalog::Catalog(Object *catDict) {
28   Object pagesDict;
29   Object obj, obj2;
30   int numPages0;
31   int i;
32
33   ok = gTrue;
34   pages = NULL;
35   pageRefs = NULL;
36   numPages = pagesSize = 0;
37
38   if (!catDict->isDict()) {
39     error(-1, "Catalog object is wrong type (%s)", catDict->getTypeName());
40     goto err1;
41   }
42
43   // read page tree
44   catDict->dictLookup("Pages", &pagesDict);
45   // This should really be isDict("Pages"), but I've seen at least one
46   // PDF file where the /Type entry is missing.
47   if (!pagesDict.isDict()) {
48     error(-1, "Top-level pages object is wrong type (%s)",
49           pagesDict.getTypeName());
50     goto err2;
51   }
52   pagesDict.dictLookup("Count", &obj);
53   if (!obj.isInt()) {
54     error(-1, "Page count in top-level pages object is wrong type (%s)",
55           obj.getTypeName());
56     goto err3;
57   }
58   pagesSize = numPages0 = obj.getInt();
59   obj.free();
60   pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
61   pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
62   for (i = 0; i < pagesSize; ++i) {
63     pages[i] = NULL;
64     pageRefs[i].num = -1;
65     pageRefs[i].gen = -1;
66   }
67   numPages = readPageTree(pagesDict.getDict(), NULL, 0);
68   if (numPages != numPages0) {
69     error(-1, "Page count in top-level pages object is incorrect");
70   }
71   pagesDict.free();
72
73   // read named destination dictionary
74   catDict->dictLookup("Dests", &dests);
75
76   // read root of named destination tree
77   if (catDict->dictLookup("Names", &obj)->isDict())
78     obj.dictLookup("Dests", &nameTree);
79   else
80     nameTree.initNull();
81   obj.free();
82
83   // read base URI
84   baseURI = NULL;
85   if (catDict->dictLookup("URI", &obj)->isDict()) {
86     if (obj.dictLookup("Base", &obj2)->isString()) {
87       baseURI = obj2.getString()->copy();
88     }
89     obj2.free();
90   }
91   obj.free();
92
93   return;
94
95  err3:
96   obj.free();
97  err2:
98   pagesDict.free();
99  err1:
100   dests.initNull();
101   nameTree.initNull();
102   ok = gFalse;
103 }
104
105 Catalog::~Catalog() {
106   int i;
107
108   if (pages) {
109     for (i = 0; i < pagesSize; ++i) {
110       if (pages[i]) {
111         delete pages[i];
112       }
113     }
114     gfree(pages);
115     gfree(pageRefs);
116   }
117   dests.free();
118   nameTree.free();
119   if (baseURI) {
120     delete baseURI;
121   }
122 }
123
124 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
125   Object kids;
126   Object kid;
127   Object kidRef;
128   PageAttrs *attrs1, *attrs2;
129   Page *page;
130   int i, j;
131
132   attrs1 = new PageAttrs(attrs, pagesDict);
133   pagesDict->lookup("Kids", &kids);
134   if (!kids.isArray()) {
135     error(-1, "Kids object (page %d) is wrong type (%s)",
136           start+1, kids.getTypeName());
137     goto err1;
138   }
139   for (i = 0; i < kids.arrayGetLength(); ++i) {
140     kids.arrayGet(i, &kid);
141     if (kid.isDict("Page")) {
142       attrs2 = new PageAttrs(attrs1, kid.getDict());
143       page = new Page(start+1, kid.getDict(), attrs2);
144       if (!page->isOk()) {
145         ++start;
146         goto err3;
147       }
148       if (start >= pagesSize) {
149         pagesSize += 32;
150         pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
151         pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
152         for (j = pagesSize - 32; j < pagesSize; ++j) {
153           pages[j] = NULL;
154           pageRefs[j].num = -1;
155           pageRefs[j].gen = -1;
156         }
157       }
158       pages[start] = page;
159       kids.arrayGetNF(i, &kidRef);
160       if (kidRef.isRef()) {
161         pageRefs[start].num = kidRef.getRefNum();
162         pageRefs[start].gen = kidRef.getRefGen();
163       }
164       kidRef.free();
165       ++start;
166     // This should really be isDict("Pages"), but I've seen at least one
167     // PDF file where the /Type entry is missing.
168     } else if (kid.isDict()) {
169       if ((start = readPageTree(kid.getDict(), attrs1, start)) < 0)
170         goto err2;
171     } else {
172       error(-1, "Kid object (page %d) is wrong type (%s)",
173             start+1, kid.getTypeName());
174       goto err2;
175     }
176     kid.free();
177   }
178   delete attrs1;
179   kids.free();
180   return start;
181
182  err3:
183   delete page;
184  err2:
185   kid.free();
186  err1:
187   kids.free();
188   delete attrs1;
189   ok = gFalse;
190   return -1;
191 }
192
193 int Catalog::findPage(int num, int gen) {
194   int i;
195
196   for (i = 0; i < numPages; ++i) {
197     if (pageRefs[i].num == num && pageRefs[i].gen == gen)
198       return i + 1;
199   }
200   return 0;
201 }
202
203 LinkDest *Catalog::findDest(GString *name) {
204   LinkDest *dest;
205   Object obj1, obj2;
206   GBool found;
207
208   // try named destination dictionary then name tree
209   found = gFalse;
210   if (dests.isDict()) {
211     if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
212       found = gTrue;
213     else
214       obj1.free();
215   }
216   if (!found && nameTree.isDict()) {
217     if (!findDestInTree(&nameTree, name, &obj1)->isNull())
218       found = gTrue;
219     else
220       obj1.free();
221   }
222   if (!found)
223     return NULL;
224
225   // construct LinkDest
226   dest = NULL;
227   if (obj1.isArray()) {
228     dest = new LinkDest(obj1.getArray(), gTrue);
229   } else if (obj1.isDict()) {
230     if (obj1.dictLookup("D", &obj2)->isArray())
231       dest = new LinkDest(obj2.getArray(), gTrue);
232     else
233       error(-1, "Bad named destination value");
234     obj2.free();
235   } else {
236     error(-1, "Bad named destination value");
237   }
238   obj1.free();
239
240   return dest;
241 }
242
243 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
244   Object names, name1;
245   Object kids, kid, limits, low, high;
246   GBool done, found;
247   int cmp, i;
248
249   // leaf node
250   if (tree->dictLookup("Names", &names)->isArray()) {
251     done = found = gFalse;
252     for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
253       if (names.arrayGet(i, &name1)->isString()) {
254         cmp = name->cmp(name1.getString());
255         if (cmp == 0) {
256           names.arrayGet(i+1, obj);
257           found = gTrue;
258           done = gTrue;
259         } else if (cmp < 0) {
260           done = gTrue;
261         }
262         name1.free();
263       }
264     }
265     names.free();
266     if (!found)
267       obj->initNull();
268     return obj;
269   }
270   names.free();
271
272   // root or intermediate node
273   done = gFalse;
274   if (tree->dictLookup("Kids", &kids)->isArray()) {
275     for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
276       if (kids.arrayGet(i, &kid)->isDict()) {
277         if (kid.dictLookup("Limits", &limits)->isArray()) {
278           if (limits.arrayGet(0, &low)->isString() &&
279               name->cmp(low.getString()) >= 0) {
280             if (limits.arrayGet(1, &high)->isString() &&
281                 name->cmp(high.getString()) <= 0) {
282               findDestInTree(&kid, name, obj);
283               done = gTrue;
284             }
285             high.free();
286           }
287           low.free();
288         }
289         limits.free();
290       }
291       kid.free();
292     }
293   }
294   kids.free();
295
296   // name was outside of ranges of all kids
297   if (!done)
298     obj->initNull();
299
300   return obj;
301 }