/* * Copyright 2006-2012 ICEsoft Technologies Inc. * * 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.graphics.*; import org.icepdf.core.util.Library; import java.awt.*; import java.util.Enumeration; import java.util.Hashtable; import java.util.Set; import java.util.logging.Logger; import java.util.logging.Level; /** * A resource is a dictionary type as defined by the PDF specification. It * can contain fonts, xobjects, colorspaces, patterns, shading and * external graphic states. * * @since 1.0 */ public class Resources extends Dictionary { // shared resource counter. private static int uniqueCounter = 0; private static synchronized int getUniqueId() { return uniqueCounter++; } private static final Logger logger = Logger.getLogger(Resources.class.toString()); Hashtable fonts; Hashtable xobjects; Hashtable colorspaces; Hashtable patterns; Hashtable shading; Hashtable extGStates; // reference count to keep track of how many objects reference this resource. private int referenceCount; /** * @param l * @param h */ public Resources(Library l, Hashtable h) { super(l, h); colorspaces = library.getDictionary(entries, "ColorSpace"); fonts = library.getDictionary(entries, "Font"); xobjects = library.getDictionary(entries, "XObject"); patterns = library.getDictionary(entries, "Pattern"); shading = library.getDictionary(entries, "Shading"); extGStates = library.getDictionary(entries, "ExtGState"); referenceCount = 0; } /** * Increments the refernce count, meaning that at least one object is * depending on this reference. * * @param referer object doing the reference, used for debug purposes. */ public void addReference(Object referer) { synchronized(this) { referenceCount++; ////System.out.println("Resources.addReference() " + getPObjectReference() + " " + uniqueId + " count: " + referenceCount); ////System.out.println("Resources.addReference() referer: " + referer.getPObjectReference() + " " + referer.getClass().getSimpleName()); //if (referer instanceof Page) { // Thread.dumpStack(); //} } } public void removeReference(Object referer) { synchronized(this) { referenceCount--; ////System.out.println("Resources.removeReference() " + getPObjectReference() + " " + uniqueId + " count: " + referenceCount); ////System.out.println("Resources.removeReference() referer: " + referer.getPObjectReference() + " " + referer.getClass().getSimpleName()); } } /** * Disposes this classes resources if an only if no other PObject * is also using this oject. If no other PObject references this instance * then we can dispose of image, stream and xform objects. * * @param cache true to cache image streams, false otherwise. * @param referer only used for debuggin, can be null otherwise. */ public boolean dispose(boolean cache, Dictionary referer) { synchronized(this) { referenceCount--; ////System.out.println("Resources.dispose() " + getPObjectReference() + " " + uniqueId + " count: " + referenceCount + " cache: " + cache); ////System.out.println("Resources.dispose() referer: " + referer.getPObjectReference() + " " + referer.getClass().getSimpleName()); // we have a reference so we can't dispose. if (referenceCount > 0) { ////System.out.println("Resources.dispose() REDUNDANT"); return false; } } // NOTE: Make sure not to clear fonts, color spaces, pattern, // or extGStat's as this hold reverences to object not the actual // object. The only images contain object with a lot of memory if (xobjects != null) { Enumeration xobjectContent = xobjects.elements(); while (xobjectContent.hasMoreElements()) { Object tmp = xobjectContent.nextElement(); if (tmp instanceof Stream) { Stream stream = (Stream) tmp; stream.dispose(cache); } if (tmp instanceof Reference) { Object reference = library.getObject((Reference) tmp); if (reference instanceof Form) { Form form = (Form) reference; form.dispose(cache); } if (reference instanceof Stream) { Stream stream = (Stream) reference; stream.dispose(cache); } } } } // remove refernces from library clearResource(colorspaces); clearResource(fonts); clearResource(xobjects); clearResource(patterns); clearResource(shading); clearResource(extGStates); return true; } private void clearResource(Hashtable resource){ if (resource != null){ Set keys = resource.keySet(); Object value; for (Object key : keys){ value = resource.get(key); if (value instanceof Reference){ library.removeObject((Reference)value); } } } } /** * @param o * @return */ public PColorSpace getColorSpace(Object o) { if (o == null) { return null; } Object tmp; // every resource has a color space entry and o can be tmp in it. if (colorspaces != null && colorspaces.get(o) != null) { tmp = colorspaces.get(o); PColorSpace cs = PColorSpace.getColorSpace(library, tmp); if (cs != null) { cs.init(); } return cs; } // look for our name in the pattern dictionary if (patterns != null && patterns.get(o) != null) { tmp = patterns.get(o); PColorSpace cs = PColorSpace.getColorSpace(library, tmp); if (cs != null) { cs.init(); } return cs; } // if its not in color spaces or pattern then its a plain old // named colour space. PColorSpace cs = PColorSpace.getColorSpace(library, o); if (cs != null) { cs.init(); } return cs; } /** * @param s * @return */ public org.icepdf.core.pobjects.fonts.Font getFont(String s) { org.icepdf.core.pobjects.fonts.Font font = null; if (fonts != null) { Object ob = fonts.get(s); // check to make sure the library contains a font if (ob instanceof org.icepdf.core.pobjects.fonts.Font) { font = (org.icepdf.core.pobjects.fonts.Font) ob; } // the default value is most likely Reference else if (ob instanceof Reference) { font = (org.icepdf.core.pobjects.fonts.Font) library.getObject((Reference) ob); } } if (font != null) { font.init(); } return font; } /** * @param s * @param fill * @return */ public Image getImage(String s, Color fill) { // check xobjects for stream Stream st = (Stream) library.getObject(xobjects, s); if (st == null) { return null; } // return null if the xobject is not an image if (!st.isImageSubtype()) { return null; } // lastly return the images. Image image = null; try { image = st.getImage(fill, this, true); // clean up the stream's resources. st.dispose(true); } catch (Exception e) { logger.log(Level.FINE, "Error getting image by name: " + s, e); } return image; } /** * @param s * @return */ public boolean isForm(String s) { Object o = library.getObject(xobjects, s); return o instanceof Form; } /** * Gets the Form XObject specified by the named reference. * * @param nameReference name of resourse to retreive. * @return if the named reference is found return it, otherwise return null; */ public Form getForm(String nameReference) { Form formXObject = null; Object tempForm = library.getObject(xobjects, nameReference); if (tempForm instanceof Form) { formXObject = (Form) tempForm; } return formXObject; } /** * Retrieves a Pattern object given the named resource. This can be * call for a fill, text fill or do image mask. * * @param name of object to find. * @return tiling or shading type pattern object. If not constructor is * found, then null is returned. */ public Pattern getPattern(String name) { if (patterns != null) { Object attribute = library.getObject(patterns, name); // An instance of TilingPattern will always have a stream if (attribute != null && attribute instanceof TilingPattern) { return (TilingPattern) attribute; } else if (attribute != null && attribute instanceof Stream) { return new TilingPattern((Stream)attribute); } // ShaddingPatterns will not have a stream but still need to parsed else if (attribute != null && attribute instanceof Hashtable) { return ShadingPattern.getShadingPattern(library, (Hashtable) attribute); } } return null; } /** * Gets the shadding pattern based on a shading dictionary name, similar * to getPattern but is only called for the 'sh' token. * * @param name name of shading dictionary * @return associated shading pattern if any. */ public ShadingPattern getShading(String name) { // look for pattern name in the shading dictionary, used by 'sh' tokens if (shading != null) { Object shadingDictionary = library.getObject(shading, name); if (shadingDictionary != null && shadingDictionary instanceof Hashtable) { return ShadingPattern.getShadingPattern(library, entries, (Hashtable) shadingDictionary); } // else if (shadingDictionary != null && shadingDictionary instanceof Stream) { // System.out.println("Found Type 6 shading pattern.... returning empty pattern data. "); // todo: alter parser to take into account stream shading types... // return new ShadingType6Pattern(library, null); // return null; // } } return null; } /** * Returns the ExtGState object which has the specified reference name. * * @param namedReference name of ExtGState object to try and find. * @return ExtGState which contains the named references ExtGState attrbutes, * if the namedReference could not be found null is returned. */ public ExtGState getExtGState(String namedReference) { ExtGState gsState = null; if (extGStates != null) { Object attribute = library.getObject(extGStates, namedReference); if (attribute instanceof Hashtable) { gsState = new ExtGState(library, (Hashtable) attribute); } else if (attribute instanceof Reference) { gsState = new ExtGState(library, (Hashtable) library.getObject( (Reference) attribute)); } } return gsState; } }