beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / Catalog.cc
bloba8c96ac479fb16dffadc15ba67a1bbdef1e88353
1 //========================================================================
2 //
3 // Catalog.cc
4 //
5 // Copyright 1996-2007 Glyph & Cog, LLC
6 //
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17 // Copyright (C) 2005-2013, 2015 Albert Astals Cid <aacid@kde.org>
18 // Copyright (C) 2005 Jeff Muizelaar <jrmuizel@nit.ca>
19 // Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com>
20 // Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
21 // Copyright (C) 2005, 2006, 2008 Brad Hards <bradh@frogmouth.net>
22 // Copyright (C) 2006, 2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
23 // Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
24 // Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
25 // Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
26 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
27 // Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
28 // Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
29 // Copyright (C) 2013 Julien Nabet <serval2412@yahoo.fr>
30 // Copyright (C) 2013 Adrian Perez de Castro <aperez@igalia.com>
31 // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
32 // Copyright (C) 2013 José Aliste <jaliste@src.gnome.org>
33 // Copyright (C) 2014 Ed Porras <ed@moto-research.com>
34 // Copyright (C) 2015 Even Rouault <even.rouault@spatialys.com>
36 // To see a description of the changes please see the Changelog file that
37 // came with your tarball or type make ChangeLog if you are building from git
39 //========================================================================
41 #include <config.h>
43 #ifdef USE_GCC_PRAGMAS
44 #pragma implementation
45 #endif
47 #include <stddef.h>
48 #include <stdlib.h>
49 #include "goo/gmem.h"
50 #include "Object.h"
51 #include "PDFDoc.h"
52 #include "XRef.h"
53 #include "Array.h"
54 #include "Dict.h"
55 #include "Page.h"
56 #include "Error.h"
57 #include "Link.h"
58 #include "PageLabelInfo.h"
59 #include "Catalog.h"
60 #include "Form.h"
61 #include "OptionalContent.h"
62 #include "ViewerPreferences.h"
63 #include "FileSpec.h"
64 #include "StructTreeRoot.h"
66 #if MULTITHREADED
67 # define catalogLocker() MutexLocker locker(&mutex)
68 #else
69 # define catalogLocker()
70 #endif
71 //------------------------------------------------------------------------
72 // Catalog
73 //------------------------------------------------------------------------
75 Catalog::Catalog(PDFDoc *docA) {
76 Object catDict, pagesDict, pagesDictRef;
77 Object obj, obj2;
78 Object optContentProps;
80 #if MULTITHREADED
81 gInitMutex(&mutex);
82 #endif
83 ok = gTrue;
84 doc = docA;
85 xref = doc->getXRef();
86 pages = NULL;
87 pageRefs = NULL;
88 numPages = -1;
89 pagesSize = 0;
90 baseURI = NULL;
91 pageLabelInfo = NULL;
92 form = NULL;
93 optContent = NULL;
94 pageMode = pageModeNull;
95 pageLayout = pageLayoutNull;
96 destNameTree = NULL;
97 embeddedFileNameTree = NULL;
98 jsNameTree = NULL;
99 viewerPrefs = NULL;
100 structTreeRoot = NULL;
102 pagesList = NULL;
103 pagesRefList = NULL;
104 attrsList = NULL;
105 kidsIdxList = NULL;
106 lastCachedPage = 0;
107 markInfo = markInfoNull;
109 xref->getCatalog(&catDict);
110 if (!catDict.isDict()) {
111 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
112 goto err1;
114 // get the AcroForm dictionary
115 catDict.dictLookup("AcroForm", &acroForm);
117 // read base URI
118 if (catDict.dictLookup("URI", &obj)->isDict()) {
119 if (obj.dictLookup("Base", &obj2)->isString()) {
120 baseURI = obj2.getString()->copy();
122 obj2.free();
124 obj.free();
126 // get the Optional Content dictionary
127 if (catDict.dictLookup("OCProperties", &optContentProps)->isDict()) {
128 optContent = new OCGs(&optContentProps, xref);
129 if (!optContent->isOk ()) {
130 delete optContent;
131 optContent = NULL;
134 optContentProps.free();
136 // actions
137 catDict.dictLookupNF("AA", &additionalActions);
139 // get the ViewerPreferences dictionary
140 catDict.dictLookup("ViewerPreferences", &viewerPreferences);
141 catDict.free();
142 return;
144 err1:
145 catDict.free();
146 ok = gFalse;
149 Catalog::~Catalog() {
150 delete kidsIdxList;
151 if (attrsList) {
152 std::vector<PageAttrs *>::iterator it;
153 for (it = attrsList->begin() ; it != attrsList->end(); ++it ) {
154 delete *it;
156 delete attrsList;
158 delete pagesRefList;
159 if (pagesList) {
160 std::vector<Dict *>::iterator it;
161 for (it = pagesList->begin() ; it != pagesList->end(); ++it ) {
162 if (!(*it)->decRef()) {
163 delete *it;
166 delete pagesList;
168 if (pages) {
169 for (int i = 0; i < pagesSize; ++i) {
170 if (pages[i]) {
171 delete pages[i];
174 gfree(pages);
176 gfree(pageRefs);
177 names.free();
178 dests.free();
179 delete destNameTree;
180 delete embeddedFileNameTree;
181 delete jsNameTree;
182 if (baseURI) {
183 delete baseURI;
185 delete pageLabelInfo;
186 delete form;
187 delete optContent;
188 delete viewerPrefs;
189 delete structTreeRoot;
190 metadata.free();
191 outline.free();
192 acroForm.free();
193 viewerPreferences.free();
194 additionalActions.free();
195 #if MULTITHREADED
196 gDestroyMutex(&mutex);
197 #endif
200 GooString *Catalog::readMetadata() {
201 GooString *s;
202 Dict *dict;
203 Object obj;
205 catalogLocker();
206 if (metadata.isNone()) {
207 Object catDict;
209 xref->getCatalog(&catDict);
210 if (catDict.isDict()) {
211 catDict.dictLookup("Metadata", &metadata);
212 } else {
213 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
214 metadata.initNull();
216 catDict.free();
219 if (!metadata.isStream()) {
220 return NULL;
222 dict = metadata.streamGetDict();
223 if (!dict->lookup("Subtype", &obj)->isName("XML")) {
224 error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'",
225 obj.isName() ? obj.getName() : "???");
227 obj.free();
228 s = new GooString();
229 metadata.getStream()->fillGooString(s);
230 metadata.streamClose();
231 return s;
234 Page *Catalog::getPage(int i)
236 if (i < 1) return NULL;
238 catalogLocker();
239 if (i > lastCachedPage) {
240 GBool cached = cachePageTree(i);
241 if ( cached == gFalse) {
242 return NULL;
245 return pages[i-1];
248 Ref *Catalog::getPageRef(int i)
250 if (i < 1) return NULL;
252 catalogLocker();
253 if (i > lastCachedPage) {
254 GBool cached = cachePageTree(i);
255 if ( cached == gFalse) {
256 return NULL;
259 return &pageRefs[i-1];
262 GBool Catalog::cachePageTree(int page)
264 Dict *pagesDict;
266 if (pagesList == NULL) {
268 Object catDict;
269 Ref pagesRef;
271 xref->getCatalog(&catDict);
273 if (catDict.isDict()) {
274 Object pagesDictRef;
275 if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() &&
276 pagesDictRef.getRefNum() >= 0 &&
277 pagesDictRef.getRefNum() < xref->getNumObjects()) {
278 pagesRef = pagesDictRef.getRef();
279 pagesDictRef.free();
280 } else {
281 error(errSyntaxError, -1, "Catalog dictionary does not contain a valid \"Pages\" entry");
282 pagesDictRef.free();
283 catDict.free();
284 return gFalse;
286 } else {
287 error(errSyntaxError, -1, "Could not find catalog dictionary");
288 catDict.free();
289 return gFalse;
292 Object obj;
293 catDict.dictLookup("Pages", &obj);
294 catDict.free();
295 // This should really be isDict("Pages"), but I've seen at least one
296 // PDF file where the /Type entry is missing.
297 if (obj.isDict()) {
298 obj.getDict()->incRef();
299 pagesDict = obj.getDict();
300 obj.free();
302 else {
303 error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", obj.getTypeName());
304 obj.free();
305 return gFalse;
308 pagesSize = getNumPages();
309 pages = (Page **)gmallocn_checkoverflow(pagesSize, sizeof(Page *));
310 pageRefs = (Ref *)gmallocn_checkoverflow(pagesSize, sizeof(Ref));
311 if (pages == NULL || pageRefs == NULL ) {
312 error(errSyntaxError, -1, "Cannot allocate page cache");
313 pagesDict->decRef();
314 pagesSize = 0;
315 return gFalse;
317 for (int i = 0; i < pagesSize; ++i) {
318 pages[i] = NULL;
319 pageRefs[i].num = -1;
320 pageRefs[i].gen = -1;
323 pagesList = new std::vector<Dict *>();
324 pagesList->push_back(pagesDict);
325 pagesRefList = new std::vector<Ref>();
326 pagesRefList->push_back(pagesRef);
327 attrsList = new std::vector<PageAttrs *>();
328 attrsList->push_back(new PageAttrs(NULL, pagesDict));
329 kidsIdxList = new std::vector<int>();
330 kidsIdxList->push_back(0);
331 lastCachedPage = 0;
335 while(1) {
337 if (page <= lastCachedPage) return gTrue;
339 if (pagesList->empty()) return gFalse;
341 pagesDict = pagesList->back();
342 Object kids;
343 pagesDict->lookup("Kids", &kids);
344 if (!kids.isArray()) {
345 error(errSyntaxError, -1, "Kids object (page {0:d}) is wrong type ({1:s})",
346 lastCachedPage+1, kids.getTypeName());
347 kids.free();
348 return gFalse;
351 int kidsIdx = kidsIdxList->back();
352 if (kidsIdx >= kids.arrayGetLength()) {
353 if (!pagesList->back()->decRef()) {
354 delete pagesList->back();
356 pagesList->pop_back();
357 pagesRefList->pop_back();
358 delete attrsList->back();
359 attrsList->pop_back();
360 kidsIdxList->pop_back();
361 if (!kidsIdxList->empty()) kidsIdxList->back()++;
362 kids.free();
363 continue;
366 Object kidRef;
367 kids.arrayGetNF(kidsIdx, &kidRef);
368 if (!kidRef.isRef()) {
369 error(errSyntaxError, -1, "Kid object (page {0:d}) is not an indirect reference ({1:s})",
370 lastCachedPage+1, kidRef.getTypeName());
371 kidRef.free();
372 kids.free();
373 return gFalse;
376 GBool loop = gFalse;;
377 for (size_t i = 0; i < pagesRefList->size(); i++) {
378 if (((*pagesRefList)[i]).num == kidRef.getRefNum()) {
379 loop = gTrue;
380 break;
383 if (loop) {
384 error(errSyntaxError, -1, "Loop in Pages tree");
385 kidRef.free();
386 kids.free();
387 kidsIdxList->back()++;
388 continue;
391 Object kid;
392 kids.arrayGet(kidsIdx, &kid);
393 kids.free();
394 if (kid.isDict("Page") || (kid.isDict() && !kid.getDict()->hasKey("Kids"))) {
395 PageAttrs *attrs = new PageAttrs(attrsList->back(), kid.getDict());
396 Page *p = new Page(doc, lastCachedPage+1, kid.getDict(),
397 kidRef.getRef(), attrs, form);
398 if (!p->isOk()) {
399 error(errSyntaxError, -1, "Failed to create page (page {0:d})", lastCachedPage+1);
400 delete p;
401 kidRef.free();
402 kid.free();
403 return gFalse;
406 if (lastCachedPage >= numPages) {
407 error(errSyntaxError, -1, "Page count in top-level pages object is incorrect");
408 kidRef.free();
409 kid.free();
410 return gFalse;
413 pages[lastCachedPage] = p;
414 pageRefs[lastCachedPage].num = kidRef.getRefNum();
415 pageRefs[lastCachedPage].gen = kidRef.getRefGen();
417 lastCachedPage++;
418 kidsIdxList->back()++;
420 // This should really be isDict("Pages"), but I've seen at least one
421 // PDF file where the /Type entry is missing.
422 } else if (kid.isDict()) {
423 attrsList->push_back(new PageAttrs(attrsList->back(), kid.getDict()));
424 pagesRefList->push_back(kidRef.getRef());
425 kid.getDict()->incRef();
426 pagesList->push_back(kid.getDict());
427 kidsIdxList->push_back(0);
428 } else {
429 error(errSyntaxError, -1, "Kid object (page {0:d}) is wrong type ({1:s})",
430 lastCachedPage+1, kid.getTypeName());
431 kidsIdxList->back()++;
433 kidRef.free();
434 kid.free();
438 return gFalse;
441 int Catalog::findPage(int num, int gen) {
442 int i;
444 for (i = 0; i < getNumPages(); ++i) {
445 Ref *ref = getPageRef(i+1);
446 if (ref != NULL && ref->num == num && ref->gen == gen)
447 return i + 1;
449 return 0;
452 LinkDest *Catalog::findDest(GooString *name) {
453 LinkDest *dest;
454 Object obj1, obj2;
455 GBool found;
457 // try named destination dictionary then name tree
458 found = gFalse;
459 if (getDests()->isDict()) {
460 if (!getDests()->dictLookup(name->getCString(), &obj1)->isNull())
461 found = gTrue;
462 else
463 obj1.free();
465 if (!found) {
466 catalogLocker();
467 if (getDestNameTree()->lookup(name, &obj1))
468 found = gTrue;
469 else
470 obj1.free();
472 if (!found)
473 return NULL;
475 // construct LinkDest
476 dest = NULL;
477 if (obj1.isArray()) {
478 dest = new LinkDest(obj1.getArray());
479 } else if (obj1.isDict()) {
480 if (obj1.dictLookup("D", &obj2)->isArray())
481 dest = new LinkDest(obj2.getArray());
482 else
483 error(errSyntaxWarning, -1, "Bad named destination value");
484 obj2.free();
485 } else {
486 error(errSyntaxWarning, -1, "Bad named destination value");
488 obj1.free();
489 if (dest && !dest->isOk()) {
490 delete dest;
491 dest = NULL;
494 return dest;
497 FileSpec *Catalog::embeddedFile(int i)
499 Object efDict;
500 Object obj;
501 catalogLocker();
502 obj = getEmbeddedFileNameTree()->getValue(i);
503 FileSpec *embeddedFile = 0;
504 if (obj.isRef()) {
505 Object fsDict;
506 embeddedFile = new FileSpec(obj.fetch(xref, &fsDict));
507 fsDict.free();
508 } else if (obj.isDict()) {
509 embeddedFile = new FileSpec(&obj);
510 } else {
511 Object null;
512 embeddedFile = new FileSpec(&null);
514 return embeddedFile;
517 GooString *Catalog::getJS(int i)
519 Object obj;
520 // getJSNameTree()->getValue(i) returns a shallow copy of the object so we
521 // do not need to free it
522 catalogLocker();
523 getJSNameTree()->getValue(i).fetch(xref, &obj);
525 if (!obj.isDict()) {
526 obj.free();
527 return 0;
529 Object obj2;
530 if (!obj.dictLookup("S", &obj2)->isName()) {
531 obj2.free();
532 obj.free();
533 return 0;
535 if (strcmp(obj2.getName(), "JavaScript")) {
536 obj2.free();
537 obj.free();
538 return 0;
540 obj2.free();
541 obj.dictLookup("JS", &obj2);
542 GooString *js = 0;
543 if (obj2.isString()) {
544 js = new GooString(obj2.getString());
546 else if (obj2.isStream()) {
547 Stream *stream = obj2.getStream();
548 js = new GooString();
549 stream->fillGooString(js);
551 obj2.free();
552 obj.free();
553 return js;
556 Catalog::PageMode Catalog::getPageMode() {
558 catalogLocker();
559 if (pageMode == pageModeNull) {
561 Object catDict, obj;
563 pageMode = pageModeNone;
565 xref->getCatalog(&catDict);
566 if (!catDict.isDict()) {
567 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
568 catDict.free();
569 return pageMode;
572 if (catDict.dictLookup("PageMode", &obj)->isName()) {
573 if (obj.isName("UseNone"))
574 pageMode = pageModeNone;
575 else if (obj.isName("UseOutlines"))
576 pageMode = pageModeOutlines;
577 else if (obj.isName("UseThumbs"))
578 pageMode = pageModeThumbs;
579 else if (obj.isName("FullScreen"))
580 pageMode = pageModeFullScreen;
581 else if (obj.isName("UseOC"))
582 pageMode = pageModeOC;
583 else if (obj.isName("UseAttachments"))
584 pageMode = pageModeAttach;
586 obj.free();
587 catDict.free();
589 return pageMode;
592 Catalog::PageLayout Catalog::getPageLayout() {
594 catalogLocker();
595 if (pageLayout == pageLayoutNull) {
597 Object catDict, obj;
599 pageLayout = pageLayoutNone;
601 xref->getCatalog(&catDict);
602 if (!catDict.isDict()) {
603 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
604 catDict.free();
605 return pageLayout;
608 pageLayout = pageLayoutNone;
609 if (catDict.dictLookup("PageLayout", &obj)->isName()) {
610 if (obj.isName("SinglePage"))
611 pageLayout = pageLayoutSinglePage;
612 if (obj.isName("OneColumn"))
613 pageLayout = pageLayoutOneColumn;
614 if (obj.isName("TwoColumnLeft"))
615 pageLayout = pageLayoutTwoColumnLeft;
616 if (obj.isName("TwoColumnRight"))
617 pageLayout = pageLayoutTwoColumnRight;
618 if (obj.isName("TwoPageLeft"))
619 pageLayout = pageLayoutTwoPageLeft;
620 if (obj.isName("TwoPageRight"))
621 pageLayout = pageLayoutTwoPageRight;
623 obj.free();
624 catDict.free();
626 return pageLayout;
629 NameTree::NameTree()
631 size = 0;
632 length = 0;
633 entries = NULL;
636 NameTree::~NameTree()
638 int i;
640 for (i = 0; i < length; i++)
641 delete entries[i];
643 gfree(entries);
646 NameTree::Entry::Entry(Array *array, int index) {
647 if (!array->getString(index, &name) || !array->getNF(index + 1, &value)) {
648 Object aux;
649 array->get(index, &aux);
650 if (aux.isString() && array->getNF(index + 1, &value) )
652 name.append(aux.getString());
654 else
655 error(errSyntaxError, -1, "Invalid page tree");
659 NameTree::Entry::~Entry() {
660 value.free();
663 void NameTree::addEntry(Entry *entry)
665 if (length == size) {
666 if (length == 0) {
667 size = 8;
668 } else {
669 size *= 2;
671 entries = (Entry **) grealloc (entries, sizeof (Entry *) * size);
674 entries[length] = entry;
675 ++length;
678 int NameTree::Entry::cmpEntry(const void *voidEntry, const void *voidOtherEntry)
680 Entry *entry = *(NameTree::Entry **) voidEntry;
681 Entry *otherEntry = *(NameTree::Entry **) voidOtherEntry;
683 return entry->name.cmp(&otherEntry->name);
686 void NameTree::init(XRef *xrefA, Object *tree) {
687 xref = xrefA;
688 parse(tree);
689 if (entries && length > 0) {
690 qsort(entries, length, sizeof(Entry *), Entry::cmpEntry);
694 void NameTree::parse(Object *tree) {
695 Object names;
696 Object kids, kid;
697 int i;
699 if (!tree->isDict())
700 return;
702 // leaf node
703 if (tree->dictLookup("Names", &names)->isArray()) {
704 for (i = 0; i < names.arrayGetLength(); i += 2) {
705 NameTree::Entry *entry;
707 entry = new Entry(names.getArray(), i);
708 addEntry(entry);
711 names.free();
713 // root or intermediate node
714 if (tree->dictLookup("Kids", &kids)->isArray()) {
715 for (i = 0; i < kids.arrayGetLength(); ++i) {
716 if (kids.arrayGet(i, &kid)->isDict())
717 parse(&kid);
718 kid.free();
721 kids.free();
724 int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry)
726 GooString *key = (GooString *) voidKey;
727 Entry *entry = *(NameTree::Entry **) voidEntry;
729 return key->cmp(&entry->name);
732 GBool NameTree::lookup(GooString *name, Object *obj)
734 Entry **entry;
736 entry = (Entry **) bsearch(name, entries,
737 length, sizeof(Entry *), Entry::cmp);
738 if (entry != NULL) {
739 (*entry)->value.fetch(xref, obj);
740 return gTrue;
741 } else {
742 error(errSyntaxError, -1, "failed to look up ({0:s})", name->getCString());
743 obj->initNull();
744 return gFalse;
748 Object NameTree::getValue(int index)
750 if (index < length) {
751 return entries[index]->value;
752 } else {
753 return Object();
757 GooString *NameTree::getName(int index)
759 if (index < length) {
760 return &entries[index]->name;
761 } else {
762 return NULL;
766 GBool Catalog::labelToIndex(GooString *label, int *index)
768 char *end;
770 PageLabelInfo *pli = getPageLabelInfo();
771 if (pli != NULL) {
772 if (!pli->labelToIndex(label, index))
773 return gFalse;
774 } else {
775 *index = strtol(label->getCString(), &end, 10) - 1;
776 if (*end != '\0')
777 return gFalse;
780 if (*index < 0 || *index >= getNumPages())
781 return gFalse;
783 return gTrue;
786 GBool Catalog::indexToLabel(int index, GooString *label)
788 char buffer[32];
790 if (index < 0 || index >= getNumPages())
791 return gFalse;
793 PageLabelInfo *pli = getPageLabelInfo();
794 if (pli != NULL) {
795 return pli->indexToLabel(index, label);
796 } else {
797 snprintf(buffer, sizeof (buffer), "%d", index + 1);
798 label->append(buffer);
799 return gTrue;
803 int Catalog::getNumPages()
805 catalogLocker();
806 if (numPages == -1)
808 Object catDict, pagesDict, obj;
810 xref->getCatalog(&catDict);
811 if (!catDict.isDict()) {
812 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
813 catDict.free();
814 return 0;
816 catDict.dictLookup("Pages", &pagesDict);
818 // This should really be isDict("Pages"), but I've seen at least one
819 // PDF file where the /Type entry is missing.
820 if (!pagesDict.isDict()) {
821 error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})",
822 pagesDict.getTypeName());
823 pagesDict.free();
824 catDict.free();
825 return 0;
828 pagesDict.dictLookup("Count", &obj);
829 // some PDF files actually use real numbers here ("/Count 9.0")
830 if (!obj.isNum()) {
831 if (pagesDict.dictIs("Page")) {
832 Object pageRootRef;
833 catDict.dictLookupNF("Pages", &pageRootRef);
835 error(errSyntaxError, -1, "Pages top-level is a single Page. The document is malformed, trying to recover...");
837 Dict *pageDict = pagesDict.getDict();
838 if (pageRootRef.isRef()) {
839 const Ref pageRef = pageRootRef.getRef();
840 Page *p = new Page(doc, 1, pageDict, pageRef, new PageAttrs(NULL, pageDict), form);
841 if (p->isOk()) {
842 pages = (Page **)gmallocn(1, sizeof(Page *));
843 pageRefs = (Ref *)gmallocn(1, sizeof(Ref));
845 pages[0] = p;
846 pageRefs[0].num = pageRef.num;
847 pageRefs[0].gen = pageRef.gen;
849 numPages = 1;
850 lastCachedPage = 1;
851 pagesSize = 1;
852 } else {
853 delete p;
854 numPages = 0;
856 } else {
857 numPages = 0;
859 } else {
860 error(errSyntaxError, -1, "Page count in top-level pages object is wrong type ({0:s})",
861 obj.getTypeName());
862 numPages = 0;
864 } else {
865 numPages = (int)obj.getNum();
866 if (numPages <= 0) {
867 error(errSyntaxError, -1,
868 "Invalid page count {0:d}", numPages);
869 numPages = 0;
870 } else if (numPages > xref->getNumObjects()) {
871 error(errSyntaxError, -1,
872 "Page count ({0:d}) larger than number of objects ({1:d})",
873 numPages, xref->getNumObjects());
874 numPages = 0;
879 catDict.free();
880 obj.free();
881 pagesDict.free();
884 return numPages;
887 PageLabelInfo *Catalog::getPageLabelInfo()
889 catalogLocker();
890 if (!pageLabelInfo) {
891 Object catDict;
892 Object obj;
894 xref->getCatalog(&catDict);
895 if (!catDict.isDict()) {
896 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
897 catDict.free();
898 return NULL;
901 if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
902 pageLabelInfo = new PageLabelInfo(&obj, getNumPages());
904 obj.free();
905 catDict.free();
908 return pageLabelInfo;
911 StructTreeRoot *Catalog::getStructTreeRoot()
913 catalogLocker();
914 if (!structTreeRoot) {
915 Object catalog;
916 Object root;
918 xref->getCatalog(&catalog);
919 if (!catalog.isDict()) {
920 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catalog.getTypeName());
921 catalog.free();
922 return NULL;
925 if (catalog.dictLookup("StructTreeRoot", &root)->isDict("StructTreeRoot")) {
926 structTreeRoot = new StructTreeRoot(doc, root.getDict());
929 root.free();
930 catalog.free();
932 return structTreeRoot;
935 Guint Catalog::getMarkInfo()
937 if (markInfo == markInfoNull) {
938 markInfo = 0;
940 Object catDict;
941 catalogLocker();
942 xref->getCatalog(&catDict);
944 if (catDict.isDict()) {
945 Object markInfoDict;
946 catDict.dictLookup("MarkInfo", &markInfoDict);
947 if (markInfoDict.isDict()) {
948 Object value;
950 if (markInfoDict.dictLookup("Marked", &value)->isBool() && value.getBool())
951 markInfo |= markInfoMarked;
952 else if (!value.isNull())
953 error(errSyntaxError, -1, "Marked object is wrong type ({0:s})", value.getTypeName());
954 value.free();
956 if (markInfoDict.dictLookup("Suspects", &value)->isBool() && value.getBool())
957 markInfo |= markInfoSuspects;
958 else if (!value.isNull())
959 error(errSyntaxError, -1, "Suspects object is wrong type ({0:s})", value.getTypeName());
960 value.free();
962 if (markInfoDict.dictLookup("UserProperties", &value)->isBool() && value.getBool())
963 markInfo |= markInfoUserProperties;
964 else if (!value.isNull())
965 error(errSyntaxError, -1, "UserProperties object is wrong type ({0:s})", value.getTypeName());
966 value.free();
967 } else if (!markInfoDict.isNull()) {
968 error(errSyntaxError, -1, "MarkInfo object is wrong type ({0:s})", markInfoDict.getTypeName());
970 markInfoDict.free();
971 } else {
972 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
974 catDict.free();
976 return markInfo;
979 Object *Catalog::getOutline()
981 catalogLocker();
982 if (outline.isNone())
984 Object catDict;
986 xref->getCatalog(&catDict);
987 if (catDict.isDict()) {
988 catDict.dictLookup("Outlines", &outline);
989 } else {
990 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
991 outline.initNull();
993 catDict.free();
996 return &outline;
999 Object *Catalog::getDests()
1001 catalogLocker();
1002 if (dests.isNone())
1004 Object catDict;
1006 xref->getCatalog(&catDict);
1007 if (catDict.isDict()) {
1008 catDict.dictLookup("Dests", &dests);
1009 } else {
1010 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
1011 dests.initNull();
1013 catDict.free();
1016 return &dests;
1019 Catalog::FormType Catalog::getFormType()
1021 Object xfa;
1022 FormType res = NoForm;
1024 if (acroForm.isDict()) {
1025 acroForm.dictLookup("XFA", &xfa);
1026 if (xfa.isStream() || xfa.isArray()) {
1027 res = XfaForm;
1028 } else {
1029 res = AcroForm;
1031 xfa.free();
1034 return res;
1037 Form *Catalog::getForm()
1039 catalogLocker();
1040 if (!form) {
1041 if (acroForm.isDict()) {
1042 form = new Form(doc, &acroForm);
1043 // perform form-related loading after all widgets have been loaded
1044 form->postWidgetsLoad();
1048 return form;
1051 ViewerPreferences *Catalog::getViewerPreferences()
1053 catalogLocker();
1054 if (!viewerPrefs) {
1055 if (viewerPreferences.isDict()) {
1056 viewerPrefs = new ViewerPreferences(viewerPreferences.getDict());
1060 return viewerPrefs;
1063 Object *Catalog::getNames()
1065 if (names.isNone())
1067 Object catDict;
1069 xref->getCatalog(&catDict);
1070 if (catDict.isDict()) {
1071 catDict.dictLookup("Names", &names);
1072 } else {
1073 error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName());
1074 names.initNull();
1076 catDict.free();
1079 return &names;
1082 NameTree *Catalog::getDestNameTree()
1084 if (!destNameTree) {
1086 destNameTree = new NameTree();
1088 if (getNames()->isDict()) {
1089 Object obj;
1091 getNames()->dictLookup("Dests", &obj);
1092 destNameTree->init(xref, &obj);
1093 obj.free();
1098 return destNameTree;
1101 NameTree *Catalog::getEmbeddedFileNameTree()
1103 if (!embeddedFileNameTree) {
1105 embeddedFileNameTree = new NameTree();
1107 if (getNames()->isDict()) {
1108 Object obj;
1110 getNames()->dictLookup("EmbeddedFiles", &obj);
1111 embeddedFileNameTree->init(xref, &obj);
1112 obj.free();
1117 return embeddedFileNameTree;
1120 NameTree *Catalog::getJSNameTree()
1122 if (!jsNameTree) {
1124 jsNameTree = new NameTree();
1126 if (getNames()->isDict()) {
1127 Object obj;
1129 getNames()->dictLookup("JavaScript", &obj);
1130 jsNameTree->init(xref, &obj);
1131 obj.free();
1136 return jsNameTree;
1139 LinkAction* Catalog::getAdditionalAction(DocumentAdditionalActionsType type) {
1140 Object additionalActionsObject;
1141 LinkAction *linkAction = NULL;
1143 if (additionalActions.fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
1144 const char *key = (type == actionCloseDocument ? "WC" :
1145 type == actionSaveDocumentStart ? "WS" :
1146 type == actionSaveDocumentFinish ? "DS" :
1147 type == actionPrintDocumentStart ? "WP" :
1148 type == actionPrintDocumentFinish ? "DP" : NULL);
1150 Object actionObject;
1152 if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
1153 linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
1154 actionObject.free();
1157 additionalActionsObject.free();
1159 return linkAction;