1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
26 //------------------------------------------------------------------------
28 //------------------------------------------------------------------------
30 Catalog::Catalog(XRef *xrefA) {
31 Object catDict, pagesDict;
40 numPages = pagesSize = 0;
43 xref->getCatalog(&catDict);
44 if (!catDict.isDict()) {
45 error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
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());
58 pagesDict.dictLookup("Count", &obj);
59 // some PDF files actually use real numbers here ("/Count 9.0")
61 error(-1, "Page count in top-level pages object is wrong type (%s)",
65 pagesSize = numPages0 = (int)obj.getNum();
67 // The gcc doesnt optimize this away, so this check is ok,
68 // even if it looks like a pagesSize != pagesSize check
69 if (pagesSize*(int)sizeof(Page *)/sizeof(Page *) != pagesSize ||
70 pagesSize*(int)sizeof(Ref)/sizeof(Ref) != pagesSize) {
71 error(-1, "Invalid 'pagesSize'");
76 pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
77 pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
78 for (i = 0; i < pagesSize; ++i) {
83 numPages = readPageTree(pagesDict.getDict(), NULL, 0);
84 if (numPages != numPages0) {
85 error(-1, "Page count in top-level pages object is incorrect");
89 // read named destination dictionary
90 catDict.dictLookup("Dests", &dests);
92 // read root of named destination tree
93 if (catDict.dictLookup("Names", &obj)->isDict())
94 obj.dictLookup("Dests", &nameTree);
100 if (catDict.dictLookup("URI", &obj)->isDict()) {
101 if (obj.dictLookup("Base", &obj2)->isString()) {
102 baseURI = obj2.getString()->copy();
108 // get the metadata stream
109 catDict.dictLookup("Metadata", &metadata);
111 // get the structure tree root
112 catDict.dictLookup("StructTreeRoot", &structTreeRoot);
114 // get the outline dictionary
115 catDict.dictLookup("Outlines", &outline);
131 Catalog::~Catalog() {
135 for (i = 0; i < pagesSize; ++i) {
149 structTreeRoot.free();
153 GString *Catalog::readMetadata() {
159 if (!metadata.isStream()) {
162 dict = metadata.streamGetDict();
163 if (!dict->lookup("Subtype", &obj)->isName("XML")) {
164 error(-1, "Unknown Metadata type: '%s'",
165 obj.isName() ? obj.getName() : "???");
169 metadata.streamReset();
170 while ((c = metadata.streamGetChar()) != EOF) {
173 metadata.streamClose();
177 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
181 PageAttrs *attrs1, *attrs2;
185 attrs1 = new PageAttrs(attrs, pagesDict);
186 pagesDict->lookup("Kids", &kids);
187 if (!kids.isArray()) {
188 error(-1, "Kids object (page %d) is wrong type (%s)",
189 start+1, kids.getTypeName());
192 for (i = 0; i < kids.arrayGetLength(); ++i) {
193 kids.arrayGet(i, &kid);
194 if (kid.isDict("Page")) {
195 attrs2 = new PageAttrs(attrs1, kid.getDict());
196 page = new Page(xref, start+1, kid.getDict(), attrs2);
201 if (start >= pagesSize) {
203 if (pagesSize*(int)sizeof(Page *)/sizeof(Page *) != pagesSize ||
204 pagesSize*(int)sizeof(Ref)/sizeof(Ref) != pagesSize) {
205 error(-1, "Invalid 'pagesSize' parameter.");
208 pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
209 pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
210 for (j = pagesSize - 32; j < pagesSize; ++j) {
212 pageRefs[j].num = -1;
213 pageRefs[j].gen = -1;
217 kids.arrayGetNF(i, &kidRef);
218 if (kidRef.isRef()) {
219 pageRefs[start].num = kidRef.getRefNum();
220 pageRefs[start].gen = kidRef.getRefGen();
224 // This should really be isDict("Pages"), but I've seen at least one
225 // PDF file where the /Type entry is missing.
226 } else if (kid.isDict()) {
227 if ((start = readPageTree(kid.getDict(), attrs1, start))
231 error(-1, "Kid object (page %d) is wrong type (%s)",
232 start+1, kid.getTypeName());
252 int Catalog::findPage(int num, int gen) {
255 for (i = 0; i < numPages; ++i) {
256 if (pageRefs[i].num == num && pageRefs[i].gen == gen)
262 LinkDest *Catalog::findDest(GString *name) {
267 // try named destination dictionary then name tree
269 if (dests.isDict()) {
270 if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
275 if (!found && nameTree.isDict()) {
276 if (!findDestInTree(&nameTree, name, &obj1)->isNull())
284 // construct LinkDest
286 if (obj1.isArray()) {
287 dest = new LinkDest(obj1.getArray());
288 } else if (obj1.isDict()) {
289 if (obj1.dictLookup("D", &obj2)->isArray())
290 dest = new LinkDest(obj2.getArray());
292 error(-1, "Bad named destination value");
295 error(-1, "Bad named destination value");
298 if (dest && !dest->isOk()) {
306 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
308 Object kids, kid, limits, low, high;
313 if (tree->dictLookup("Names", &names)->isArray()) {
314 done = found = gFalse;
315 for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
316 if (names.arrayGet(i, &name1)->isString()) {
317 cmp = name->cmp(name1.getString());
319 names.arrayGet(i+1, obj);
322 } else if (cmp < 0) {
335 // root or intermediate node
337 if (tree->dictLookup("Kids", &kids)->isArray()) {
338 for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
339 if (kids.arrayGet(i, &kid)->isDict()) {
340 if (kid.dictLookup("Limits", &limits)->isArray()) {
341 if (limits.arrayGet(0, &low)->isString() &&
342 name->cmp(low.getString()) >= 0) {
343 if (limits.arrayGet(1, &high)->isString() &&
344 name->cmp(high.getString()) <= 0) {
345 findDestInTree(&kid, name, obj);
359 // name was outside of ranges of all kids