1
//========================================================================
5
// Copyright 1996-2007 Glyph & Cog, LLC
7
//========================================================================
11
#ifdef USE_GCC_PRAGMAS
12
#pragma implementation
26
//------------------------------------------------------------------------
28
//------------------------------------------------------------------------
30
Catalog::Catalog(XRef *xrefA) {
31
Object catDict, pagesDict, pagesDictRef;
41
numPages = pagesSize = 0;
44
xref->getCatalog(&catDict);
45
if (!catDict.isDict()) {
46
error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
51
catDict.dictLookup("Pages", &pagesDict);
52
// This should really be isDict("Pages"), but I've seen at least one
53
// PDF file where the /Type entry is missing.
54
if (!pagesDict.isDict()) {
55
error(-1, "Top-level pages object is wrong type (%s)",
56
pagesDict.getTypeName());
59
pagesDict.dictLookup("Count", &obj);
60
// some PDF files actually use real numbers here ("/Count 9.0")
62
error(-1, "Page count in top-level pages object is wrong type (%s)",
66
pagesSize = numPages0 = (int)obj.getNum();
68
pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
69
pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
70
for (i = 0; i < pagesSize; ++i) {
75
alreadyRead = (char *)gmalloc(xref->getNumObjects());
76
memset(alreadyRead, 0, xref->getNumObjects());
77
if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() &&
78
pagesDictRef.getRefNum() >= 0 &&
79
pagesDictRef.getRefNum() < xref->getNumObjects()) {
80
alreadyRead[pagesDictRef.getRefNum()] = 1;
83
numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead);
85
if (numPages != numPages0) {
86
error(-1, "Page count in top-level pages object is incorrect");
90
// read named destination dictionary
91
catDict.dictLookup("Dests", &dests);
93
// read root of named destination tree
94
if (catDict.dictLookup("Names", &obj)->isDict())
95
obj.dictLookup("Dests", &nameTree);
101
if (catDict.dictLookup("URI", &obj)->isDict()) {
102
if (obj.dictLookup("Base", &obj2)->isString()) {
103
baseURI = obj2.getString()->copy();
109
// get the metadata stream
110
catDict.dictLookup("Metadata", &metadata);
112
// get the structure tree root
113
catDict.dictLookup("StructTreeRoot", &structTreeRoot);
115
// get the outline dictionary
116
catDict.dictLookup("Outlines", &outline);
118
// get the AcroForm dictionary
119
catDict.dictLookup("AcroForm", &acroForm);
135
Catalog::~Catalog() {
139
for (i = 0; i < pagesSize; ++i) {
153
structTreeRoot.free();
158
GString *Catalog::readMetadata() {
164
if (!metadata.isStream()) {
167
dict = metadata.streamGetDict();
168
if (!dict->lookup("Subtype", &obj)->isName("XML")) {
169
error(-1, "Unknown Metadata type: '%s'",
170
obj.isName() ? obj.getName() : "???");
174
metadata.streamReset();
175
while ((c = metadata.streamGetChar()) != EOF) {
178
metadata.streamClose();
182
int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
187
PageAttrs *attrs1, *attrs2;
191
attrs1 = new PageAttrs(attrs, pagesDict);
192
pagesDict->lookup("Kids", &kids);
193
if (!kids.isArray()) {
194
error(-1, "Kids object (page %d) is wrong type (%s)",
195
start+1, kids.getTypeName());
198
for (i = 0; i < kids.arrayGetLength(); ++i) {
199
kids.arrayGetNF(i, &kidRef);
200
if (kidRef.isRef() &&
201
kidRef.getRefNum() >= 0 &&
202
kidRef.getRefNum() < xref->getNumObjects()) {
203
if (alreadyRead[kidRef.getRefNum()]) {
204
error(-1, "Loop in Pages tree");
208
alreadyRead[kidRef.getRefNum()] = 1;
210
kids.arrayGet(i, &kid);
211
if (kid.isDict("Page")) {
212
attrs2 = new PageAttrs(attrs1, kid.getDict());
213
page = new Page(xref, start+1, kid.getDict(), attrs2);
218
if (start >= pagesSize) {
220
pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
221
pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
222
for (j = pagesSize - 32; j < pagesSize; ++j) {
224
pageRefs[j].num = -1;
225
pageRefs[j].gen = -1;
229
if (kidRef.isRef()) {
230
pageRefs[start].num = kidRef.getRefNum();
231
pageRefs[start].gen = kidRef.getRefGen();
234
// This should really be isDict("Pages"), but I've seen at least one
235
// PDF file where the /Type entry is missing.
236
} else if (kid.isDict()) {
237
if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead))
241
error(-1, "Kid object (page %d) is wrong type (%s)",
242
start+1, kid.getTypeName());
262
int Catalog::findPage(int num, int gen) {
265
for (i = 0; i < numPages; ++i) {
266
if (pageRefs[i].num == num && pageRefs[i].gen == gen)
272
LinkDest *Catalog::findDest(GString *name) {
277
// try named destination dictionary then name tree
279
if (dests.isDict()) {
280
if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
285
if (!found && nameTree.isDict()) {
286
if (!findDestInTree(&nameTree, name, &obj1)->isNull())
294
// construct LinkDest
296
if (obj1.isArray()) {
297
dest = new LinkDest(obj1.getArray());
298
} else if (obj1.isDict()) {
299
if (obj1.dictLookup("D", &obj2)->isArray())
300
dest = new LinkDest(obj2.getArray());
302
error(-1, "Bad named destination value");
305
error(-1, "Bad named destination value");
308
if (dest && !dest->isOk()) {
316
Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
318
Object kids, kid, limits, low, high;
323
if (tree->dictLookup("Names", &names)->isArray()) {
324
done = found = gFalse;
325
for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
326
if (names.arrayGet(i, &name1)->isString()) {
327
cmp = name->cmp(name1.getString());
329
names.arrayGet(i+1, obj);
332
} else if (cmp < 0) {
345
// root or intermediate node
347
if (tree->dictLookup("Kids", &kids)->isArray()) {
348
for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
349
if (kids.arrayGet(i, &kid)->isDict()) {
350
if (kid.dictLookup("Limits", &limits)->isArray()) {
351
if (limits.arrayGet(0, &low)->isString() &&
352
name->cmp(low.getString()) >= 0) {
353
if (limits.arrayGet(1, &high)->isString() &&
354
name->cmp(high.getString()) <= 0) {
355
findDestInTree(&kid, name, obj);
369
// name was outside of ranges of all kids