/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.core.pobjects;
import org.icepdf.core.pobjects.acroform.InteractiveForm;
import org.icepdf.core.util.Library;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>The <code>Catalog</code> object represents the root of a PDF document's
* object heirarchy. The <code>Catalog</code> is located by means of the
* <b>Root</b> entry in the trailer of the PDF file. The catalog contains
* references to other objects defining the document's contents, outline, names,
* destinations, and other attributes.</p>
* <br>
* <p>The <code>Catalog</code> class can be accessed from the {@link Document}
* class for convenience, but can also be accessed via the {@link PTrailer} class.
* Useful information about the document can be extracted from the Catalog
* Dictionary, such as PDF version information and Viewer Preferences. All
* Catalog dictionary properties can be accesed via the getEntries method.
* See section 3.6.1 of the PDF Reference version 1.6 for more information on
* the properties available in the Catalog Object. </p>
*
* @since 1.0
*/
public class Catalog extends Dictionary {
private static final Logger logger =
Logger.getLogger(Catalog.class.toString());
public static final Name TYPE = new Name("Catalog");
public static final Name DESTS_KEY = new Name("Dests");
public static final Name VIEWERPREFERENCES_KEY = new Name("ViewerPreferences");
public static final Name NAMES_KEY = new Name("Names");
public static final Name OUTLINES_KEY = new Name("Outlines");
public static final Name OCPROPERTIES_KEY = new Name("OCProperties");
public static final Name PAGES_KEY = new Name("Pages");
public static final Name PAGELAYOUT_KEY = new Name("PageLayout");
public static final Name PAGEMODE_KEY = new Name("PageMode");
public static final Name ACRO_FORM_KEY = new Name("AcroForm");
public static final Name COLLECTION_KEY = new Name("Collection");
public static final Name METADATA_KEY = new Name("Metadata");
public static final Name PERMS_KEY = new Name("Perms");
public static final Name PAGE_MODE_USE_NONE_VALUE = new Name("UseNone");
public static final Name PAGE_MODE_USE_OUTLINES_VALUE = new Name("UseOutlines");
public static final Name PAGE_MODE_USE_THUMBS_VALUE = new Name("UseThumbs");
public static final Name PAGE_MODE_FULL_SCREEN_VALUE = new Name("FullScreen");
public static final Name PAGE_MODE_OPTIONAL_CONTENT_VALUE = new Name("UseOC");
public static final Name PAGE_MODE_USE_ATTACHMENTS_VALUE = new Name("UseAttachments");
private PageTree pageTree;
private Outlines outlines;
private Names names;
private OptionalContent optionalContent;
private NamedDestinations dests;
private ViewerPreferences viewerPref;
private InteractiveForm interactiveForm;
private boolean outlinesInited = false;
private boolean namesTreeInited = false;
private boolean destsInited = false;
private boolean viewerPrefInited = false;
private boolean optionalContentInited = false;
// Announce ICEpdf Core
static {
if (logger.isLoggable(Level.INFO)) {
logger.info("ICEsoft ICEpdf Core " + Document.getLibraryVersion());
}
}
/**
* Creates a new instance of a Catalog.
*
* @param l document library.
* @param h Catalog dictionary entries.
*/
public Catalog(Library l, HashMap<Object, Object> h) {
super(l, h);
}
/**
* Initiate the PageTree.
*/
public synchronized void init() throws InterruptedException {
Object tmp = library.getObject(entries, PAGES_KEY);
pageTree = null;
if (tmp instanceof PageTree) {
pageTree = (PageTree) tmp;
}
// malformed core corner case, pages must not be references, but we
// have a couple cases that break the spec.
else if (tmp instanceof HashMap) {
pageTree = new PageTree(library, (HashMap) tmp);
}
// malformed corner case, just have a page object, instead of tree.
else if (tmp instanceof Page) {
Page tmpPage = (Page) tmp;
HashMap<String, Object> tmpPages = new HashMap<String, Object>();
List<Reference> kids = new ArrayList<Reference>();
kids.add(tmpPage.getPObjectReference());
tmpPages.put("Kids", kids);
tmpPages.put("Count", 1);
pageTree = new PageTree(library, tmpPages);
}
// let any exception bubble up.
if (pageTree != null) {
pageTree.init();
}
// check for the collections dictionary for the presence of a portable collection
tmp = library.getObject(entries, NAMES_KEY);
if (tmp != null) {
names = new Names(library, (HashMap) tmp);
names.init();
}
// load the Acroform data.
tmp = library.getObject(entries, ACRO_FORM_KEY);
if (tmp instanceof HashMap) {
interactiveForm = new InteractiveForm(library, (HashMap) tmp);
interactiveForm.init();
}
// todo namesTree contains forms javascript, might need to be initialized here
}
/**
* Gets PageTree node that is the root of the document's page tree.
* The PageTree can be traversed to access child PageTree and Page objects.
*
* @return Catalogs PageTree.
* {@link Page}
*/
public PageTree getPageTree() {
return pageTree;
}
/**
* Gets the Outlines Dictionary that is the root of the document's outline
* hierarchy. The Outline can be traversed to access child OutlineItems.
*
* @return Outlines object if one exists; null, otherwise.
* {@link OutlineItem}
*/
public Outlines getOutlines() {
if (!outlinesInited) {
outlinesInited = true;
Object o = library.getObject(entries, OUTLINES_KEY);
if (o != null)
outlines = new Outlines(library, (HashMap) o);
}
return outlines;
}
/**
* A collection dictionary that a conforming reader shall use to enhance the presentation of file attachments
* stored in the PDF document.
*
* @return collection dictionary.
*/
public HashMap getCollection() {
return library.getDictionary(entries, COLLECTION_KEY);
}
/**
* A name object specifying how the document shall be displayed when opened:
*
* @return one of the PageMode value contants, default is Default value: UseNone.
*/
public Name getPageMode() {
Name name = library.getName(entries, PAGEMODE_KEY);
if (name == null) {
return PAGE_MODE_USE_NONE_VALUE;
} else {
return name;
}
}
/**
* Gets the document's Names dictionary. The Names dictionary contains
* a category of objects in a PDF file which can be referred to by name
* rather than by object reference.
*
* @return names object entry. If no names entries exists null
* is returned.
*/
public Names getNames() {
return names;
}
/**
* Gets the Names object's embedded files name tree if present. The root node is also check to make sure
* the tree has values.
*
* @return A name tree mapping name strings to file specifications for embedded
* file streams.
*/
public NameTree getEmbeddedFilesNameTree() {
if (names != null) {
NameTree nameTree = names.getEmbeddedFilesNameTree();
if (nameTree != null && nameTree.getRoot() != null) {
return nameTree;
}
}
return null;
}
/**
* Gets a dictionary of names and corresponding destinations.
*
* @return A Dictionary of Destinations; if none, null is returned.
*/
@SuppressWarnings("unchecked")
public NamedDestinations getDestinations() {
if (!destsInited) {
destsInited = true;
Object o = library.getObject(entries, DESTS_KEY);
if (o != null) {
dests = new NamedDestinations(library, (HashMap<Object, Object>) o);
}
}
return dests;
}
/**
* Gets a dictionary of keys and corresponding viewer preferences
* This can be used to pull information based on the PDF specification,
* such as HideToolbar or FitWindow
*
* @return the constructed ViewerPreferences object
*/
public ViewerPreferences getViewerPreferences() {
if (!viewerPrefInited) {
viewerPrefInited = true;
Object o = library.getObject(entries, VIEWERPREFERENCES_KEY);
if (o != null) {
viewerPref = new ViewerPreferences(library, (HashMap) o);
viewerPref.init();
}
}
return viewerPref;
}
/**
* Gets the the optional content properties dictionary if present.
*
* @return OptionalContent dictionary, null if none exists.
*/
public OptionalContent getOptionalContent() {
if (!optionalContentInited) {
optionalContentInited = true;
Object o = library.getObject(entries, OCPROPERTIES_KEY);
if (o != null && o instanceof HashMap) {
optionalContent = new OptionalContent(library, ((HashMap) o));
optionalContent.init();
} else {
optionalContent = new OptionalContent(library, new HashMap());
optionalContent.init();
}
}
return optionalContent;
}
/**
* A metadata stream that shall contain metadata for the document. To
* access the metadata stream data make a call to getMetData().getDecodedStreamBytes()
* which can be used to create a String or open an InputStream.
*
* @return metadata stream if define, otherwise null.
*/
public Stream getMetaData() {
Object o = library.getObject(entries, METADATA_KEY);
if (o != null && o instanceof Stream) {
return (Stream) o;
}
return null;
}
/**
* Gets the permissions of the catalog if present. Perms key.
*
* @return permissions if present, otherwise false.
*/
public Permissions getPermissions() {
HashMap hashMap = library.getDictionary(entries, PERMS_KEY);
if (hashMap != null) {
return new Permissions(library, hashMap);
} else {
return null;
}
}
/**
* Gets the interactive form object that contains the form widgets for the given PDF.
*
* @return interactive form object, null if no forms are pressent.
*/
public InteractiveForm getInteractiveForm() {
return interactiveForm;
}
/**
* Returns a summary of the Catalog dictionary values.
*
* @return dictionary values.
*/
public String toString() {
return "CATALOG= " + entries.toString();
}
}