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 pages
= (Page
**)gmalloc(pagesSize
* sizeof(Page
*));
68 pageRefs
= (Ref
*)gmalloc(pagesSize
* sizeof(Ref
));
69 for (i
= 0; i
< pagesSize
; ++i
) {
74 numPages
= readPageTree(pagesDict
.getDict(), NULL
, 0);
75 if (numPages
!= numPages0
) {
76 error(-1, "Page count in top-level pages object is incorrect");
80 // read named destination dictionary
81 catDict
.dictLookup("Dests", &dests
);
83 // read root of named destination tree
84 if (catDict
.dictLookup("Names", &obj
)->isDict())
85 obj
.dictLookup("Dests", &nameTree
);
91 if (catDict
.dictLookup("URI", &obj
)->isDict()) {
92 if (obj
.dictLookup("Base", &obj2
)->isString()) {
93 baseURI
= obj2
.getString()->copy();
99 // get the metadata stream
100 catDict
.dictLookup("Metadata", &metadata
);
102 // get the structure tree root
103 catDict
.dictLookup("StructTreeRoot", &structTreeRoot
);
105 // get the outline dictionary
106 catDict
.dictLookup("Outlines", &outline
);
122 Catalog::~Catalog() {
126 for (i
= 0; i
< pagesSize
; ++i
) {
140 structTreeRoot
.free();
144 GString
*Catalog::readMetadata() {
150 if (!metadata
.isStream()) {
153 dict
= metadata
.streamGetDict();
154 if (!dict
->lookup("Subtype", &obj
)->isName("XML")) {
155 error(-1, "Unknown Metadata type: '%s'",
156 obj
.isName() ? obj
.getName() : "???");
160 metadata
.streamReset();
161 while ((c
= metadata
.streamGetChar()) != EOF
) {
164 metadata
.streamClose();
168 int Catalog::readPageTree(Dict
*pagesDict
, PageAttrs
*attrs
, int start
) {
172 PageAttrs
*attrs1
, *attrs2
;
176 attrs1
= new PageAttrs(attrs
, pagesDict
);
177 pagesDict
->lookup("Kids", &kids
);
178 if (!kids
.isArray()) {
179 error(-1, "Kids object (page %d) is wrong type (%s)",
180 start
+1, kids
.getTypeName());
183 for (i
= 0; i
< kids
.arrayGetLength(); ++i
) {
184 kids
.arrayGet(i
, &kid
);
185 if (kid
.isDict("Page")) {
186 attrs2
= new PageAttrs(attrs1
, kid
.getDict());
187 page
= new Page(xref
, start
+1, kid
.getDict(), attrs2
);
192 if (start
>= pagesSize
) {
194 pages
= (Page
**)grealloc(pages
, pagesSize
* sizeof(Page
*));
195 pageRefs
= (Ref
*)grealloc(pageRefs
, pagesSize
* sizeof(Ref
));
196 for (j
= pagesSize
- 32; j
< pagesSize
; ++j
) {
198 pageRefs
[j
].num
= -1;
199 pageRefs
[j
].gen
= -1;
203 kids
.arrayGetNF(i
, &kidRef
);
204 if (kidRef
.isRef()) {
205 pageRefs
[start
].num
= kidRef
.getRefNum();
206 pageRefs
[start
].gen
= kidRef
.getRefGen();
210 // This should really be isDict("Pages"), but I've seen at least one
211 // PDF file where the /Type entry is missing.
212 } else if (kid
.isDict()) {
213 if ((start
= readPageTree(kid
.getDict(), attrs1
, start
))
217 error(-1, "Kid object (page %d) is wrong type (%s)",
218 start
+1, kid
.getTypeName());
238 int Catalog::findPage(int num
, int gen
) {
241 for (i
= 0; i
< numPages
; ++i
) {
242 if (pageRefs
[i
].num
== num
&& pageRefs
[i
].gen
== gen
)
248 LinkDest
*Catalog::findDest(GString
*name
) {
253 // try named destination dictionary then name tree
255 if (dests
.isDict()) {
256 if (!dests
.dictLookup(name
->getCString(), &obj1
)->isNull())
261 if (!found
&& nameTree
.isDict()) {
262 if (!findDestInTree(&nameTree
, name
, &obj1
)->isNull())
270 // construct LinkDest
272 if (obj1
.isArray()) {
273 dest
= new LinkDest(obj1
.getArray());
274 } else if (obj1
.isDict()) {
275 if (obj1
.dictLookup("D", &obj2
)->isArray())
276 dest
= new LinkDest(obj2
.getArray());
278 error(-1, "Bad named destination value");
281 error(-1, "Bad named destination value");
284 if (dest
&& !dest
->isOk()) {
292 Object
*Catalog::findDestInTree(Object
*tree
, GString
*name
, Object
*obj
) {
294 Object kids
, kid
, limits
, low
, high
;
299 if (tree
->dictLookup("Names", &names
)->isArray()) {
300 done
= found
= gFalse
;
301 for (i
= 0; !done
&& i
< names
.arrayGetLength(); i
+= 2) {
302 if (names
.arrayGet(i
, &name1
)->isString()) {
303 cmp
= name
->cmp(name1
.getString());
305 names
.arrayGet(i
+1, obj
);
308 } else if (cmp
< 0) {
321 // root or intermediate node
323 if (tree
->dictLookup("Kids", &kids
)->isArray()) {
324 for (i
= 0; !done
&& i
< kids
.arrayGetLength(); ++i
) {
325 if (kids
.arrayGet(i
, &kid
)->isDict()) {
326 if (kid
.dictLookup("Limits", &limits
)->isArray()) {
327 if (limits
.arrayGet(0, &low
)->isString() &&
328 name
->cmp(low
.getString()) >= 0) {
329 if (limits
.arrayGet(1, &high
)->isString() &&
330 name
->cmp(high
.getString()) <= 0) {
331 findDestInTree(&kid
, name
, obj
);
345 // name was outside of ranges of all kids