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