/* * 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.fonts.FontFactory; import org.icepdf.core.pobjects.graphics.*; import org.icepdf.core.util.Library; import java.awt.*; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * 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 { public static final Name COLORSPACE_KEY = new Name("ColorSpace"); public static final Name FONT_KEY = new Name("Font"); public static final Name XOBJECT_KEY = new Name("XObject"); public static final Name PATTERN_KEY = new Name("Pattern"); public static final Name SHADING_KEY = new Name("Shading"); public static final Name EXTGSTATE_KEY = new Name("ExtGState"); public static final Name PROPERTIES_KEY = new Name("Properties"); // 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()); HashMap fonts; HashMap xobjects; HashMap colorspaces; HashMap patterns; HashMap shading; HashMap extGStates; HashMap properties; /** * @param l * @param h */ public Resources(Library l, HashMap h) { super(l, h); colorspaces = library.getDictionary(entries, COLORSPACE_KEY); fonts = library.getDictionary(entries, FONT_KEY); xobjects = library.getDictionary(entries, XOBJECT_KEY); patterns = library.getDictionary(entries, PATTERN_KEY); shading = library.getDictionary(entries, SHADING_KEY); extGStates = library.getDictionary(entries, EXTGSTATE_KEY); properties = library.getDictionary(entries, PROPERTIES_KEY); } public HashMap getFonts() { return fonts; } /** * @param o * @return */ public PColorSpace getColorSpace(Object o) { if (o == null) { return null; } try { 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; } catch (InterruptedException e) { logger.fine("Colorspace parsing was interrupted"); } return null; } /** * @param s * @return */ public org.icepdf.core.pobjects.fonts.Font getFont(Name 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; } // corner case where font is just a inline dictionary. else if (ob instanceof HashMap) { font = FontFactory.getInstance().getFont(library, (HashMap) ob); } // the default value is most likely Reference else if (ob instanceof Reference) { Reference ref = (Reference) ob; ob = library.getObject((Reference) ob); if (ob instanceof PObject) { ob = ((PObject) ob).getObject(); } if (ob instanceof org.icepdf.core.pobjects.fonts.Font) { font = (org.icepdf.core.pobjects.fonts.Font) ob; } else { font = FontFactory.getInstance().getFont(library, (HashMap) ob); } // cache the font for later use. if (font != null) { library.addObject(font, ref); font.setPObjectReference(ref); } } // if still null do a deeper search checking the base font name of // each font for a match to the needed font name. We have a few // malformed documents that don't refer to a font by the base name // and not the font name found in the resource table. if (font == null) { for (Object tmp : fonts.values()) { if (tmp instanceof Reference) { ob = library.getObject((Reference) tmp); if (ob instanceof PObject) { ob = ((PObject) ob).getObject(); } if (ob instanceof org.icepdf.core.pobjects.fonts.Font) { font = (org.icepdf.core.pobjects.fonts.Font) ob; String baseFont = font.getBaseFont(); if (s.getName().equals(baseFont) || baseFont.contains(s.getName())) { // cache the font for later use. library.addObject(font, (Reference) tmp); font.setPObjectReference((Reference) tmp); break; } else { font = null; } } } } } } if (font != null) { try { font.setParentResource(this); font.init(); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "Error initializing font, falling back to font substitution."); } else { logger.log(Level.FINER, "Error initializing font, falling back to font substitution. " + font); } } } return font; } /** * @param s * @param graphicsState * @return */ public Image getImage(Name s, GraphicsState graphicsState) { // check xobjects for stream ImageStream st = (ImageStream) 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(graphicsState, this); } catch (Exception e) { logger.log(Level.FINE, "Error getting image by name: " + s, e); } return image; } public ImageStream getImageStream(Name s) { // check xobjects for stream Object st = library.getObject(xobjects, s); if (st instanceof ImageStream) { return (ImageStream) st; } return null; } public Object getXObject(Name s) { return library.getObject(xobjects, s); } /** * Gets a rough count of the images resources associated with this page. Does * not include inline images. * * @return rough count of images resources. */ public int getImageCount() { int count = 0; if (xobjects != null) { for (Object tmp : xobjects.values()) { if (tmp instanceof Reference) { tmp = library.getObject((Reference) tmp); if (tmp instanceof ImageStream) { count++; } } } } return count; } /** * @param s * @return */ public boolean isForm(Name 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(Name 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(Name 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 HashMap) { return ShadingPattern.getShadingPattern(library, (HashMap) 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(Name 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 HashMap) { return ShadingPattern.getShadingPattern(library, entries, (HashMap) shadingDictionary); } else if (shadingDictionary != null && shadingDictionary instanceof Stream) { return ShadingPattern.getShadingPattern(library, null, (Stream) shadingDictionary); } } 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 attributes, * if the namedReference could not be found null is returned. */ public ExtGState getExtGState(Name namedReference) { ExtGState gsState = null; if (extGStates != null) { Object attribute = library.getObject(extGStates, namedReference); if (attribute instanceof HashMap) { gsState = new ExtGState(library, (HashMap) attribute); } else if (attribute instanceof Reference) { gsState = new ExtGState(library, (HashMap) library.getObject( (Reference) attribute)); } } return gsState; } /** * Looks for the specified key in the Properties dictionary. If the dictionary * and corresponding value is found the object is returned otherwise null. * * @param key key to find a value of in the Properties dictionary. * @return key value if found, null otherwise. */ public OptionalContents getPropertyEntry(Name key) { if (properties != null) { Object object = library.getObject(properties.get(key)); if (object instanceof OptionalContents) { return (OptionalContents) library.getObject(properties.get(key)); } } return null; } /** * Checks to see if the Shading key has value in this resource dictionary. * * @return true if there are shading values, false otherwise. */ public boolean isShading() { return shading != null; } }