package com.tom_roush.pdfbox.pdmodel; import com.tom_roush.pdfbox.cos.COSBase; import com.tom_roush.pdfbox.cos.COSDictionary; import com.tom_roush.pdfbox.cos.COSName; import com.tom_roush.pdfbox.cos.COSObject; import com.tom_roush.pdfbox.pdmodel.common.COSObjectable; import com.tom_roush.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import com.tom_roush.pdfbox.pdmodel.font.PDFont; import com.tom_roush.pdfbox.pdmodel.font.PDFontFactory; import com.tom_roush.pdfbox.pdmodel.graphics.PDXObject; import com.tom_roush.pdfbox.pdmodel.graphics.color.PDColorSpace; import com.tom_roush.pdfbox.pdmodel.graphics.form.PDFormXObject; import com.tom_roush.pdfbox.pdmodel.graphics.image.PDImageXObject; import com.tom_roush.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; import com.tom_roush.pdfbox.pdmodel.graphics.pattern.PDAbstractPattern; import com.tom_roush.pdfbox.pdmodel.graphics.shading.PDShading; import com.tom_roush.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import java.io.IOException; import java.util.Collections; /** * A set of resources available at the page/pages/stream level. * * @author Ben Litchfield * @author John Hewson */ public final class PDResources implements COSObjectable { private final COSDictionary resources; private final ResourceCache cache; /** * Constructor for embedding. */ public PDResources() { resources = new COSDictionary(); cache = null; } /** * Constructor for reading. * * @param resourceDictionary The cos dictionary for this resource. */ public PDResources(COSDictionary resourceDictionary) { if (resourceDictionary == null) { throw new IllegalArgumentException("resourceDictionary is null"); } resources = resourceDictionary; cache = null; } /** * Constructor for reading. * * @param resourceDictionary The cos dictionary for this resource. * @param resourceCache The document's resource cache, may be null. */ public PDResources(COSDictionary resourceDictionary, ResourceCache resourceCache) { if (resourceDictionary == null) { throw new IllegalArgumentException("resourceDictionary is null"); } resources = resourceDictionary; cache = resourceCache; } /** * Returns the underlying dictionary. */ public COSDictionary getCOSObject() { return resources; } /** * Returns the font resource with the given name, or null if none exists. * * @param name Name of the font resource. * @throws java.io.IOException if something went wrong. */ public PDFont getFont(COSName name) throws IOException { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDFont cached = cache.getFont(indirect); if (cached != null) { return cached; } } PDFont font = null; COSDictionary dict = (COSDictionary) get(COSName.FONT, name); if (dict != null) { font = PDFontFactory.createFont(dict); } if (cache != null) { cache.put(indirect, font); } return font; } /** * Returns the color space resource with the given name, or null if none exists. * * @param name Name of the color space resource. * @throws java.io.IOException if something went wrong. */ public PDColorSpace getColorSpace(COSName name) throws IOException { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDColorSpace cached = cache.getColorSpace(indirect); if (cached != null) { return cached; } } // get the instance PDColorSpace colorSpace; COSBase object = get(COSName.COLORSPACE, name); if (object != null) { colorSpace = PDColorSpace.create(object, this); } else { colorSpace = PDColorSpace.create(name, this); } if (cache != null) { cache.put(indirect, colorSpace); } return colorSpace; } /** * Returns true if the given color space name exists in these resources. * * @param name Name of the color space resource. */ public boolean hasColorSpace(COSName name) { return get(COSName.COLORSPACE, name) != null; } /** * Returns the external graphics state resource with the given name, or null * if none exists. * * @param name Name of the graphics state resource. */ public PDExtendedGraphicsState getExtGState(COSName name) { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDExtendedGraphicsState cached = cache.getExtGState(indirect); if (cached != null) { return cached; } } // get the instance PDExtendedGraphicsState extGState = null; COSDictionary dict = (COSDictionary) get(COSName.EXT_G_STATE, name); if (dict != null) { extGState = new PDExtendedGraphicsState(dict); } if (cache != null) { cache.put(indirect, extGState); } return extGState; } /** * Returns the shading resource with the given name, or null if none exists. * * @param name Name of the shading resource. * @throws java.io.IOException if something went wrong. */ public PDShading getShading(COSName name) throws IOException { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDShading cached = cache.getShading(indirect); if (cached != null) { return cached; } } // get the instance PDShading shading = null; COSDictionary dict = (COSDictionary) get(COSName.SHADING, name); if (dict != null) { shading = PDShading.create(dict); } if (cache != null) { cache.put(indirect, shading); } return shading; } /** * Returns the pattern resource with the given name, or null if none exists. * * @param name Name of the pattern resource. * @throws java.io.IOException if something went wrong. */ public PDAbstractPattern getPattern(COSName name) throws IOException { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDAbstractPattern cached = cache.getPattern(indirect); if (cached != null) { return cached; } } // get the instance PDAbstractPattern pattern = null; COSDictionary dict = (COSDictionary) get(COSName.PATTERN, name); if (dict != null) { pattern = PDAbstractPattern.create(dict); } if (cache != null) { cache.put(indirect, pattern); } return pattern; } /** * Returns the property list resource with the given name, or null if none exists. * * @param name Name of the property list resource. */ public PDPropertyList getProperties(COSName name) { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDPropertyList cached = cache.getProperties(indirect); if (cached != null) { return cached; } } // get the instance PDPropertyList propertyList = null; COSDictionary dict = (COSDictionary) get(COSName.PROPERTIES, name); if (dict != null) { propertyList = PDPropertyList.create(dict); } if (cache != null) { cache.put(indirect, propertyList); } return propertyList; } /** * Returns the XObject resource with the given name, or null if none exists. * * @param name Name of the XObject resource. * @throws java.io.IOException if something went wrong. */ public PDXObject getXObject(COSName name) throws IOException { COSObject indirect = getIndirect(COSName.FONT, name); if (cache != null && indirect != null) { PDXObject cached = cache.getXObject(indirect); if (cached != null) { return cached; } } // get the instance PDXObject xobject; COSBase value = get(COSName.XOBJECT, name); if (value == null) { xobject = null; } else if (value instanceof COSObject) { xobject = PDXObject.createXObject(((COSObject) value).getObject(), this); } else { xobject = PDXObject.createXObject(value, this); } if (cache != null) { cache.put(indirect, xobject); } return xobject; } /** * Returns the resource with the given name and kind as an indirect object, or null. */ private COSObject getIndirect(COSName kind, COSName name) { COSDictionary dict = (COSDictionary) resources.getDictionaryObject(kind); if (dict == null) { return null; } COSBase base = dict.getItem(name); if (base instanceof COSObject) { return (COSObject) base; } return null; } /** * Returns the resource with the given name and kind, or null. */ private COSBase get(COSName kind, COSName name) { COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind); if (dict == null) { return null; } return dict.getDictionaryObject(name); } /** * Returns the names of the color space resources, if any. */ public Iterable<COSName> getColorSpaceNames() { return getNames(COSName.COLORSPACE); } /** * Returns the names of the XObject resources, if any. */ public Iterable<COSName> getXObjectNames() { return getNames(COSName.XOBJECT); } /** * Returns the names of the font resources, if any. */ public Iterable<COSName> getFontNames() { return getNames(COSName.FONT); } /** * Returns the names of the property list resources, if any. */ public Iterable<COSName> getPropertiesNames() { return getNames(COSName.PROPERTIES); } /** * Returns the names of the shading resources, if any. */ public Iterable<COSName> getShadingNames() { return getNames(COSName.SHADING); } /** * Returns the names of the pattern resources, if any. */ public Iterable<COSName> getPatternNames() { return getNames(COSName.PATTERN); } /** * Returns the names of the extended graphics state resources, if any. */ public Iterable<COSName> getExtGStateNames() { return getNames(COSName.EXT_G_STATE); } /** * Returns the resource names of the given kind. */ private Iterable<COSName> getNames(COSName kind) { COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind); if (dict == null) { return Collections.emptySet(); } return dict.keySet(); } /** * Adds the given font to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param font the font to add * @return the name of the resource in the resources dictionary */ public COSName add(PDFont font) { return add(COSName.FONT, "F", font); } /** * Adds the given color space to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param colorSpace the color space to add * @return the name of the resource in the resources dictionary */ public COSName add(PDColorSpace colorSpace) { return add(COSName.COLORSPACE, "cs", colorSpace); } /** * Adds the given extended graphics state to the resources of the current page and returns the * name for the new resources. Returns the existing resource name if the given item already exists. * * @param extGState the extended graphics state to add * @return the name of the resource in the resources dictionary */ public COSName add(PDExtendedGraphicsState extGState) { return add(COSName.EXT_G_STATE, "gs", extGState); } /** * Adds the given shading to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param shading the shading to add * @return the name of the resource in the resources dictionary */ public COSName add(PDShading shading) { return add(COSName.SHADING, "sh", shading); } /** * Adds the given pattern to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param pattern the pattern to add * @return the name of the resource in the resources dictionary */ public COSName add(PDAbstractPattern pattern) { return add(COSName.PATTERN, "p", pattern); } /** * Adds the given property list to the resources of the current page and returns the name for * the new resources. Returns the existing resource name if the given item already exists. * * @param properties the property list to add * @return the name of the resource in the resources dictionary */ public COSName add(PDPropertyList properties) { if (properties instanceof PDOptionalContentGroup) { return add(COSName.PROPERTIES, "oc", properties); } else { return add(COSName.PROPERTIES, "Prop", properties); } } /** * Adds the given image to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param image the image to add * @return the name of the resource in the resources dictionary */ public COSName add(PDImageXObject image) { return add(COSName.XOBJECT, "Im", image); } /** * Adds the given form to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param form the form to add * @return the name of the resource in the resources dictionary */ public COSName add(PDFormXObject form) { return add(COSName.XOBJECT, "Form", form); } /** * Adds the given XObject to the resources of the current page and returns the name for the * new resources. Returns the existing resource name if the given item already exists. * * @param xobject the XObject to add * @param prefix the prefix to be used when creating the resource name * @return the name of the resource in the resources dictionary */ public COSName add(PDXObject xobject, String prefix) { return add(COSName.XOBJECT, prefix, xobject); } /** * Adds the given resource if it does not already exist. */ private COSName add(COSName kind, String prefix, COSObjectable object) { // return the existing key if the item exists already COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind); if (dict != null && dict.containsValue(object.getCOSObject())) { return dict.getKeyForValue(object.getCOSObject()); } // add the item with a new key COSName name = createKey(kind, prefix); put(kind, name, object); return name; } /** * Returns a unique key for a new resource. */ private COSName createKey(COSName kind, String prefix) { COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind); if (dict == null) { return COSName.getPDFName(prefix + 1); } // find a unique key String key; int n = dict.keySet().size(); do { ++n; key = prefix + n; } while (dict.containsKey(key)); return COSName.getPDFName(key); } /** * Sets the value of a given named resource. */ private void put(COSName kind, COSName name, COSObjectable object) { COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind); if (dict == null) { dict = new COSDictionary(); resources.setItem(kind, dict); } dict.setItem(name, object); } /** * Sets the font resource with the given name. * * @param name the name of the resource * @param font the font to be added */ public void put(COSName name, PDFont font) { put(COSName.FONT, name, font); } /** * Sets the color space resource with the given name. * * @param name the name of the resource * @param colorSpace the color space to be added */ public void put(COSName name, PDColorSpace colorSpace) throws IOException { put(COSName.COLORSPACE, name, colorSpace); } /** * Sets the extended graphics state resource with the given name. * * @param name the name of the resource * @param extGState the extended graphics state to be added */ public void put(COSName name, PDExtendedGraphicsState extGState) { put(COSName.EXT_G_STATE, name, extGState); } /** * Sets the shading resource with the given name. * * @param name the name of the resource * @param shading the shading to be added */ public void put(COSName name, PDShading shading) { put(COSName.SHADING, name, shading); } /** * Sets the pattern resource with the given name. * * @param name the name of the resource * @param pattern the pattern to be added */ public void put(COSName name, PDAbstractPattern pattern) { put(COSName.PATTERN, name, pattern); } /** * Sets the property list resource with the given name. * * @param name the name of the resource * @param properties the property list to be added */ public void put(COSName name, PDPropertyList properties) { put(COSName.PROPERTIES, name, properties); } /** * Sets the XObject resource with the given name. * * @param name the name of the resource * @param xobject the XObject to be added */ public void put(COSName name, PDXObject xobject) { put(COSName.XOBJECT, name, xobject); } /** * Returns the resource cache associated with the Resources, or null if there is none. */ public ResourceCache getResourceCache() { return cache; } }