upgrade to xpdf 3.00.
[swftools.git] / pdf2swf / xpdf / Catalog.cc
blobc645fd001be0719f67cd52f722a49ecdf91308ec
1 //========================================================================
2 //
3 // Catalog.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <aconf.h>
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
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"
26 //------------------------------------------------------------------------
27 // Catalog
28 //------------------------------------------------------------------------
30 Catalog::Catalog(XRef *xrefA) {
31 Object catDict, pagesDict;
32 Object obj, obj2;
33 int numPages0;
34 int i;
36 ok = gTrue;
37 xref = xrefA;
38 pages = NULL;
39 pageRefs = NULL;
40 numPages = pagesSize = 0;
41 baseURI = NULL;
43 xref->getCatalog(&catDict);
44 if (!catDict.isDict()) {
45 error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
46 goto err1;
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;
58 pagesDict.dictLookup("Count", &obj);
59 // some PDF files actually use real numbers here ("/Count 9.0")
60 if (!obj.isNum()) {
61 error(-1, "Page count in top-level pages object is wrong type (%s)",
62 obj.getTypeName());
63 goto err3;
65 pagesSize = numPages0 = (int)obj.getNum();
66 obj.free();
67 pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
68 pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
69 for (i = 0; i < pagesSize; ++i) {
70 pages[i] = NULL;
71 pageRefs[i].num = -1;
72 pageRefs[i].gen = -1;
74 numPages = readPageTree(pagesDict.getDict(), NULL, 0);
75 if (numPages != numPages0) {
76 error(-1, "Page count in top-level pages object is incorrect");
78 pagesDict.free();
80 // read named destination dictionary
81 catDict.dictLookup("Dests", &dests);
83 // read root of named destination tree
84 if (catDict.dictLookup("Names", &obj)->isDict())
85 obj.dictLookup("Dests", &nameTree);
86 else
87 nameTree.initNull();
88 obj.free();
90 // read base URI
91 if (catDict.dictLookup("URI", &obj)->isDict()) {
92 if (obj.dictLookup("Base", &obj2)->isString()) {
93 baseURI = obj2.getString()->copy();
95 obj2.free();
97 obj.free();
99 // get the metadata stream
100 catDict.dictLookup("Metadata", &metadata);
102 // get the structure tree root
103 catDict.dictLookup("StructTreeRoot", &structTreeRoot);
105 // get the outline dictionary
106 catDict.dictLookup("Outlines", &outline);
108 catDict.free();
109 return;
111 err3:
112 obj.free();
113 err2:
114 pagesDict.free();
115 err1:
116 catDict.free();
117 dests.initNull();
118 nameTree.initNull();
119 ok = gFalse;
122 Catalog::~Catalog() {
123 int i;
125 if (pages) {
126 for (i = 0; i < pagesSize; ++i) {
127 if (pages[i]) {
128 delete pages[i];
131 gfree(pages);
132 gfree(pageRefs);
134 dests.free();
135 nameTree.free();
136 if (baseURI) {
137 delete baseURI;
139 metadata.free();
140 structTreeRoot.free();
141 outline.free();
144 GString *Catalog::readMetadata() {
145 GString *s;
146 Dict *dict;
147 Object obj;
148 int c;
150 if (!metadata.isStream()) {
151 return NULL;
153 dict = metadata.streamGetDict();
154 if (!dict->lookup("Subtype", &obj)->isName("XML")) {
155 error(-1, "Unknown Metadata type: '%s'",
156 obj.isName() ? obj.getName() : "???");
158 obj.free();
159 s = new GString();
160 metadata.streamReset();
161 while ((c = metadata.streamGetChar()) != EOF) {
162 s->append(c);
164 metadata.streamClose();
165 return s;
168 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
169 Object kids;
170 Object kid;
171 Object kidRef;
172 PageAttrs *attrs1, *attrs2;
173 Page *page;
174 int i, j;
176 attrs1 = new PageAttrs(attrs, pagesDict);
177 pagesDict->lookup("Kids", &kids);
178 if (!kids.isArray()) {
179 error(-1, "Kids object (page %d) is wrong type (%s)",
180 start+1, kids.getTypeName());
181 goto err1;
183 for (i = 0; i < kids.arrayGetLength(); ++i) {
184 kids.arrayGet(i, &kid);
185 if (kid.isDict("Page")) {
186 attrs2 = new PageAttrs(attrs1, kid.getDict());
187 page = new Page(xref, start+1, kid.getDict(), attrs2);
188 if (!page->isOk()) {
189 ++start;
190 goto err3;
192 if (start >= pagesSize) {
193 pagesSize += 32;
194 pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
195 pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
196 for (j = pagesSize - 32; j < pagesSize; ++j) {
197 pages[j] = NULL;
198 pageRefs[j].num = -1;
199 pageRefs[j].gen = -1;
202 pages[start] = page;
203 kids.arrayGetNF(i, &kidRef);
204 if (kidRef.isRef()) {
205 pageRefs[start].num = kidRef.getRefNum();
206 pageRefs[start].gen = kidRef.getRefGen();
208 kidRef.free();
209 ++start;
210 // This should really be isDict("Pages"), but I've seen at least one
211 // PDF file where the /Type entry is missing.
212 } else if (kid.isDict()) {
213 if ((start = readPageTree(kid.getDict(), attrs1, start))
214 < 0)
215 goto err2;
216 } else {
217 error(-1, "Kid object (page %d) is wrong type (%s)",
218 start+1, kid.getTypeName());
219 goto err2;
221 kid.free();
223 delete attrs1;
224 kids.free();
225 return start;
227 err3:
228 delete page;
229 err2:
230 kid.free();
231 err1:
232 kids.free();
233 delete attrs1;
234 ok = gFalse;
235 return -1;
238 int Catalog::findPage(int num, int gen) {
239 int i;
241 for (i = 0; i < numPages; ++i) {
242 if (pageRefs[i].num == num && pageRefs[i].gen == gen)
243 return i + 1;
245 return 0;
248 LinkDest *Catalog::findDest(GString *name) {
249 LinkDest *dest;
250 Object obj1, obj2;
251 GBool found;
253 // try named destination dictionary then name tree
254 found = gFalse;
255 if (dests.isDict()) {
256 if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
257 found = gTrue;
258 else
259 obj1.free();
261 if (!found && nameTree.isDict()) {
262 if (!findDestInTree(&nameTree, name, &obj1)->isNull())
263 found = gTrue;
264 else
265 obj1.free();
267 if (!found)
268 return NULL;
270 // construct LinkDest
271 dest = NULL;
272 if (obj1.isArray()) {
273 dest = new LinkDest(obj1.getArray());
274 } else if (obj1.isDict()) {
275 if (obj1.dictLookup("D", &obj2)->isArray())
276 dest = new LinkDest(obj2.getArray());
277 else
278 error(-1, "Bad named destination value");
279 obj2.free();
280 } else {
281 error(-1, "Bad named destination value");
283 obj1.free();
284 if (dest && !dest->isOk()) {
285 delete dest;
286 dest = NULL;
289 return dest;
292 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
293 Object names, name1;
294 Object kids, kid, limits, low, high;
295 GBool done, found;
296 int cmp, i;
298 // leaf node
299 if (tree->dictLookup("Names", &names)->isArray()) {
300 done = found = gFalse;
301 for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
302 if (names.arrayGet(i, &name1)->isString()) {
303 cmp = name->cmp(name1.getString());
304 if (cmp == 0) {
305 names.arrayGet(i+1, obj);
306 found = gTrue;
307 done = gTrue;
308 } else if (cmp < 0) {
309 done = gTrue;
312 name1.free();
314 names.free();
315 if (!found)
316 obj->initNull();
317 return obj;
319 names.free();
321 // root or intermediate node
322 done = gFalse;
323 if (tree->dictLookup("Kids", &kids)->isArray()) {
324 for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
325 if (kids.arrayGet(i, &kid)->isDict()) {
326 if (kid.dictLookup("Limits", &limits)->isArray()) {
327 if (limits.arrayGet(0, &low)->isString() &&
328 name->cmp(low.getString()) >= 0) {
329 if (limits.arrayGet(1, &high)->isString() &&
330 name->cmp(high.getString()) <= 0) {
331 findDestInTree(&kid, name, obj);
332 done = gTrue;
334 high.free();
336 low.free();
338 limits.free();
340 kid.free();
343 kids.free();
345 // name was outside of ranges of all kids
346 if (!done)
347 obj->initNull();
349 return obj;