1 //========================================================================
5 // Copyright 1996-2007 Glyph & Cog, LLC
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 //========================================================================
43 #ifdef USE_GCC_PRAGMAS
44 #pragma implementation
58 #include "PageLabelInfo.h"
61 #include "OptionalContent.h"
62 #include "ViewerPreferences.h"
64 #include "StructTreeRoot.h"
67 # define catalogLocker() MutexLocker locker(&mutex)
69 # define catalogLocker()
71 //------------------------------------------------------------------------
73 //------------------------------------------------------------------------
75 Catalog::Catalog(PDFDoc
*docA
) {
76 Object catDict
, pagesDict
, pagesDictRef
;
78 Object optContentProps
;
85 xref
= doc
->getXRef();
94 pageMode
= pageModeNull
;
95 pageLayout
= pageLayoutNull
;
97 embeddedFileNameTree
= NULL
;
100 structTreeRoot
= NULL
;
107 markInfo
= markInfoNull
;
109 xref
->getCatalog(&catDict
);
110 if (!catDict
.isDict()) {
111 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
114 // get the AcroForm dictionary
115 catDict
.dictLookup("AcroForm", &acroForm
);
118 if (catDict
.dictLookup("URI", &obj
)->isDict()) {
119 if (obj
.dictLookup("Base", &obj2
)->isString()) {
120 baseURI
= obj2
.getString()->copy();
126 // get the Optional Content dictionary
127 if (catDict
.dictLookup("OCProperties", &optContentProps
)->isDict()) {
128 optContent
= new OCGs(&optContentProps
, xref
);
129 if (!optContent
->isOk ()) {
134 optContentProps
.free();
137 catDict
.dictLookupNF("AA", &additionalActions
);
139 // get the ViewerPreferences dictionary
140 catDict
.dictLookup("ViewerPreferences", &viewerPreferences
);
149 Catalog::~Catalog() {
152 std::vector
<PageAttrs
*>::iterator it
;
153 for (it
= attrsList
->begin() ; it
!= attrsList
->end(); ++it
) {
160 std::vector
<Dict
*>::iterator it
;
161 for (it
= pagesList
->begin() ; it
!= pagesList
->end(); ++it
) {
162 if (!(*it
)->decRef()) {
169 for (int i
= 0; i
< pagesSize
; ++i
) {
180 delete embeddedFileNameTree
;
185 delete pageLabelInfo
;
189 delete structTreeRoot
;
193 viewerPreferences
.free();
194 additionalActions
.free();
196 gDestroyMutex(&mutex
);
200 GooString
*Catalog::readMetadata() {
206 if (metadata
.isNone()) {
209 xref
->getCatalog(&catDict
);
210 if (catDict
.isDict()) {
211 catDict
.dictLookup("Metadata", &metadata
);
213 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
219 if (!metadata
.isStream()) {
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() : "???");
229 metadata
.getStream()->fillGooString(s
);
230 metadata
.streamClose();
234 Page
*Catalog::getPage(int i
)
236 if (i
< 1) return NULL
;
239 if (i
> lastCachedPage
) {
240 GBool cached
= cachePageTree(i
);
241 if ( cached
== gFalse
) {
248 Ref
*Catalog::getPageRef(int i
)
250 if (i
< 1) return NULL
;
253 if (i
> lastCachedPage
) {
254 GBool cached
= cachePageTree(i
);
255 if ( cached
== gFalse
) {
259 return &pageRefs
[i
-1];
262 GBool
Catalog::cachePageTree(int page
)
266 if (pagesList
== NULL
) {
271 xref
->getCatalog(&catDict
);
273 if (catDict
.isDict()) {
275 if (catDict
.dictLookupNF("Pages", &pagesDictRef
)->isRef() &&
276 pagesDictRef
.getRefNum() >= 0 &&
277 pagesDictRef
.getRefNum() < xref
->getNumObjects()) {
278 pagesRef
= pagesDictRef
.getRef();
281 error(errSyntaxError
, -1, "Catalog dictionary does not contain a valid \"Pages\" entry");
287 error(errSyntaxError
, -1, "Could not find catalog dictionary");
293 catDict
.dictLookup("Pages", &obj
);
295 // This should really be isDict("Pages"), but I've seen at least one
296 // PDF file where the /Type entry is missing.
298 obj
.getDict()->incRef();
299 pagesDict
= obj
.getDict();
303 error(errSyntaxError
, -1, "Top-level pages object is wrong type ({0:s})", obj
.getTypeName());
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");
317 for (int i
= 0; i
< pagesSize
; ++i
) {
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);
337 if (page
<= lastCachedPage
) return gTrue
;
339 if (pagesList
->empty()) return gFalse
;
341 pagesDict
= pagesList
->back();
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());
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()++;
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());
376 GBool loop
= gFalse
;;
377 for (size_t i
= 0; i
< pagesRefList
->size(); i
++) {
378 if (((*pagesRefList
)[i
]).num
== kidRef
.getRefNum()) {
384 error(errSyntaxError
, -1, "Loop in Pages tree");
387 kidsIdxList
->back()++;
392 kids
.arrayGet(kidsIdx
, &kid
);
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
);
399 error(errSyntaxError
, -1, "Failed to create page (page {0:d})", lastCachedPage
+1);
406 if (lastCachedPage
>= numPages
) {
407 error(errSyntaxError
, -1, "Page count in top-level pages object is incorrect");
413 pages
[lastCachedPage
] = p
;
414 pageRefs
[lastCachedPage
].num
= kidRef
.getRefNum();
415 pageRefs
[lastCachedPage
].gen
= kidRef
.getRefGen();
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);
429 error(errSyntaxError
, -1, "Kid object (page {0:d}) is wrong type ({1:s})",
430 lastCachedPage
+1, kid
.getTypeName());
431 kidsIdxList
->back()++;
441 int Catalog::findPage(int num
, int gen
) {
444 for (i
= 0; i
< getNumPages(); ++i
) {
445 Ref
*ref
= getPageRef(i
+1);
446 if (ref
!= NULL
&& ref
->num
== num
&& ref
->gen
== gen
)
452 LinkDest
*Catalog::findDest(GooString
*name
) {
457 // try named destination dictionary then name tree
459 if (getDests()->isDict()) {
460 if (!getDests()->dictLookup(name
->getCString(), &obj1
)->isNull())
467 if (getDestNameTree()->lookup(name
, &obj1
))
475 // construct LinkDest
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());
483 error(errSyntaxWarning
, -1, "Bad named destination value");
486 error(errSyntaxWarning
, -1, "Bad named destination value");
489 if (dest
&& !dest
->isOk()) {
497 FileSpec
*Catalog::embeddedFile(int i
)
502 obj
= getEmbeddedFileNameTree()->getValue(i
);
503 FileSpec
*embeddedFile
= 0;
506 embeddedFile
= new FileSpec(obj
.fetch(xref
, &fsDict
));
508 } else if (obj
.isDict()) {
509 embeddedFile
= new FileSpec(&obj
);
512 embeddedFile
= new FileSpec(&null
);
517 GooString
*Catalog::getJS(int i
)
520 // getJSNameTree()->getValue(i) returns a shallow copy of the object so we
521 // do not need to free it
523 getJSNameTree()->getValue(i
).fetch(xref
, &obj
);
530 if (!obj
.dictLookup("S", &obj2
)->isName()) {
535 if (strcmp(obj2
.getName(), "JavaScript")) {
541 obj
.dictLookup("JS", &obj2
);
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
);
556 Catalog::PageMode
Catalog::getPageMode() {
559 if (pageMode
== pageModeNull
) {
563 pageMode
= pageModeNone
;
565 xref
->getCatalog(&catDict
);
566 if (!catDict
.isDict()) {
567 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
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
;
592 Catalog::PageLayout
Catalog::getPageLayout() {
595 if (pageLayout
== pageLayoutNull
) {
599 pageLayout
= pageLayoutNone
;
601 xref
->getCatalog(&catDict
);
602 if (!catDict
.isDict()) {
603 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
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
;
636 NameTree::~NameTree()
640 for (i
= 0; i
< length
; i
++)
646 NameTree::Entry::Entry(Array
*array
, int index
) {
647 if (!array
->getString(index
, &name
) || !array
->getNF(index
+ 1, &value
)) {
649 array
->get(index
, &aux
);
650 if (aux
.isString() && array
->getNF(index
+ 1, &value
) )
652 name
.append(aux
.getString());
655 error(errSyntaxError
, -1, "Invalid page tree");
659 NameTree::Entry::~Entry() {
663 void NameTree::addEntry(Entry
*entry
)
665 if (length
== size
) {
671 entries
= (Entry
**) grealloc (entries
, sizeof (Entry
*) * size
);
674 entries
[length
] = entry
;
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
) {
689 if (entries
&& length
> 0) {
690 qsort(entries
, length
, sizeof(Entry
*), Entry::cmpEntry
);
694 void NameTree::parse(Object
*tree
) {
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
);
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())
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
)
736 entry
= (Entry
**) bsearch(name
, entries
,
737 length
, sizeof(Entry
*), Entry::cmp
);
739 (*entry
)->value
.fetch(xref
, obj
);
742 error(errSyntaxError
, -1, "failed to look up ({0:s})", name
->getCString());
748 Object
NameTree::getValue(int index
)
750 if (index
< length
) {
751 return entries
[index
]->value
;
757 GooString
*NameTree::getName(int index
)
759 if (index
< length
) {
760 return &entries
[index
]->name
;
766 GBool
Catalog::labelToIndex(GooString
*label
, int *index
)
770 PageLabelInfo
*pli
= getPageLabelInfo();
772 if (!pli
->labelToIndex(label
, index
))
775 *index
= strtol(label
->getCString(), &end
, 10) - 1;
780 if (*index
< 0 || *index
>= getNumPages())
786 GBool
Catalog::indexToLabel(int index
, GooString
*label
)
790 if (index
< 0 || index
>= getNumPages())
793 PageLabelInfo
*pli
= getPageLabelInfo();
795 return pli
->indexToLabel(index
, label
);
797 snprintf(buffer
, sizeof (buffer
), "%d", index
+ 1);
798 label
->append(buffer
);
803 int Catalog::getNumPages()
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());
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());
828 pagesDict
.dictLookup("Count", &obj
);
829 // some PDF files actually use real numbers here ("/Count 9.0")
831 if (pagesDict
.dictIs("Page")) {
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
);
842 pages
= (Page
**)gmallocn(1, sizeof(Page
*));
843 pageRefs
= (Ref
*)gmallocn(1, sizeof(Ref
));
846 pageRefs
[0].num
= pageRef
.num
;
847 pageRefs
[0].gen
= pageRef
.gen
;
860 error(errSyntaxError
, -1, "Page count in top-level pages object is wrong type ({0:s})",
865 numPages
= (int)obj
.getNum();
867 error(errSyntaxError
, -1,
868 "Invalid page count {0:d}", numPages
);
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());
887 PageLabelInfo
*Catalog::getPageLabelInfo()
890 if (!pageLabelInfo
) {
894 xref
->getCatalog(&catDict
);
895 if (!catDict
.isDict()) {
896 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
901 if (catDict
.dictLookup("PageLabels", &obj
)->isDict()) {
902 pageLabelInfo
= new PageLabelInfo(&obj
, getNumPages());
908 return pageLabelInfo
;
911 StructTreeRoot
*Catalog::getStructTreeRoot()
914 if (!structTreeRoot
) {
918 xref
->getCatalog(&catalog
);
919 if (!catalog
.isDict()) {
920 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catalog
.getTypeName());
925 if (catalog
.dictLookup("StructTreeRoot", &root
)->isDict("StructTreeRoot")) {
926 structTreeRoot
= new StructTreeRoot(doc
, root
.getDict());
932 return structTreeRoot
;
935 Guint
Catalog::getMarkInfo()
937 if (markInfo
== markInfoNull
) {
942 xref
->getCatalog(&catDict
);
944 if (catDict
.isDict()) {
946 catDict
.dictLookup("MarkInfo", &markInfoDict
);
947 if (markInfoDict
.isDict()) {
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());
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());
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());
967 } else if (!markInfoDict
.isNull()) {
968 error(errSyntaxError
, -1, "MarkInfo object is wrong type ({0:s})", markInfoDict
.getTypeName());
972 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
979 Object
*Catalog::getOutline()
982 if (outline
.isNone())
986 xref
->getCatalog(&catDict
);
987 if (catDict
.isDict()) {
988 catDict
.dictLookup("Outlines", &outline
);
990 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
999 Object
*Catalog::getDests()
1006 xref
->getCatalog(&catDict
);
1007 if (catDict
.isDict()) {
1008 catDict
.dictLookup("Dests", &dests
);
1010 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
1019 Catalog::FormType
Catalog::getFormType()
1022 FormType res
= NoForm
;
1024 if (acroForm
.isDict()) {
1025 acroForm
.dictLookup("XFA", &xfa
);
1026 if (xfa
.isStream() || xfa
.isArray()) {
1037 Form
*Catalog::getForm()
1041 if (acroForm
.isDict()) {
1042 form
= new Form(doc
, &acroForm
);
1043 // perform form-related loading after all widgets have been loaded
1044 form
->postWidgetsLoad();
1051 ViewerPreferences
*Catalog::getViewerPreferences()
1055 if (viewerPreferences
.isDict()) {
1056 viewerPrefs
= new ViewerPreferences(viewerPreferences
.getDict());
1063 Object
*Catalog::getNames()
1069 xref
->getCatalog(&catDict
);
1070 if (catDict
.isDict()) {
1071 catDict
.dictLookup("Names", &names
);
1073 error(errSyntaxError
, -1, "Catalog object is wrong type ({0:s})", catDict
.getTypeName());
1082 NameTree
*Catalog::getDestNameTree()
1084 if (!destNameTree
) {
1086 destNameTree
= new NameTree();
1088 if (getNames()->isDict()) {
1091 getNames()->dictLookup("Dests", &obj
);
1092 destNameTree
->init(xref
, &obj
);
1098 return destNameTree
;
1101 NameTree
*Catalog::getEmbeddedFileNameTree()
1103 if (!embeddedFileNameTree
) {
1105 embeddedFileNameTree
= new NameTree();
1107 if (getNames()->isDict()) {
1110 getNames()->dictLookup("EmbeddedFiles", &obj
);
1111 embeddedFileNameTree
->init(xref
, &obj
);
1117 return embeddedFileNameTree
;
1120 NameTree
*Catalog::getJSNameTree()
1124 jsNameTree
= new NameTree();
1126 if (getNames()->isDict()) {
1129 getNames()->dictLookup("JavaScript", &obj
);
1130 jsNameTree
->init(xref
, &obj
);
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();