1 //========================================================================
5 // Copyright 1996-2002 Glyph & Cog, LLC
7 //========================================================================
10 #pragma implementation
25 //------------------------------------------------------------------------
27 //------------------------------------------------------------------------
29 Catalog::Catalog(XRef *xrefA, GBool printCommands) {
30 Object catDict, pagesDict;
39 numPages = pagesSize = 0;
42 xref->getCatalog(&catDict);
43 if (!catDict.isDict()) {
44 error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
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());
57 pagesDict.dictLookup("Count", &obj);
59 error(-1, "Page count in top-level pages object is wrong type (%s)",
63 pagesSize = numPages0 = obj.getInt();
65 pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
66 pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
67 for (i = 0; i < pagesSize; ++i) {
72 numPages = readPageTree(pagesDict.getDict(), NULL, 0, printCommands);
73 if (numPages != numPages0) {
74 error(-1, "Page count in top-level pages object is incorrect");
78 // read named destination dictionary
79 catDict.dictLookup("Dests", &dests);
81 // read root of named destination tree
82 if (catDict.dictLookup("Names", &obj)->isDict())
83 obj.dictLookup("Dests", &nameTree);
89 if (catDict.dictLookup("URI", &obj)->isDict()) {
90 if (obj.dictLookup("Base", &obj2)->isString()) {
91 baseURI = obj2.getString()->copy();
97 // get the metadata stream
98 catDict.dictLookup("Metadata", &metadata);
100 // get the structure tree root
101 catDict.dictLookup("StructTreeRoot", &structTreeRoot);
117 Catalog::~Catalog() {
121 for (i = 0; i < pagesSize; ++i) {
135 structTreeRoot.free();
138 GString *Catalog::readMetadata() {
144 if (!metadata.isStream()) {
147 dict = metadata.streamGetDict();
148 if (!dict->lookup("Subtype", &obj)->isName("XML")) {
149 error(-1, "Unknown Metadata type: '%s'",
150 obj.isName() ? obj.getName() : "???");
154 metadata.streamReset();
155 while ((c = metadata.streamGetChar()) != EOF) {
158 metadata.streamClose();
162 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
163 GBool printCommands) {
167 PageAttrs *attrs1, *attrs2;
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());
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);
187 if (start >= pagesSize) {
189 pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
190 pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
191 for (j = pagesSize - 32; j < pagesSize; ++j) {
193 pageRefs[j].num = -1;
194 pageRefs[j].gen = -1;
198 kids.arrayGetNF(i, &kidRef);
199 if (kidRef.isRef()) {
200 pageRefs[start].num = kidRef.getRefNum();
201 pageRefs[start].gen = kidRef.getRefGen();
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))
212 error(-1, "Kid object (page %d) is wrong type (%s)",
213 start+1, kid.getTypeName());
233 int Catalog::findPage(int num, int gen) {
236 for (i = 0; i < numPages; ++i) {
237 if (pageRefs[i].num == num && pageRefs[i].gen == gen)
243 LinkDest *Catalog::findDest(GString *name) {
248 // try named destination dictionary then name tree
250 if (dests.isDict()) {
251 if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
256 if (!found && nameTree.isDict()) {
257 if (!findDestInTree(&nameTree, name, &obj1)->isNull())
265 // construct LinkDest
267 if (obj1.isArray()) {
268 dest = new LinkDest(obj1.getArray());
269 } else if (obj1.isDict()) {
270 if (obj1.dictLookup("D", &obj2)->isArray())
271 dest = new LinkDest(obj2.getArray());
273 error(-1, "Bad named destination value");
276 error(-1, "Bad named destination value");
283 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
285 Object kids, kid, limits, low, high;
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());
296 names.arrayGet(i+1, obj);
299 } else if (cmp < 0) {
312 // root or intermediate node
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);
336 // name was outside of ranges of all kids