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