| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.pdf; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil; |
| |
| import org.apache.fop.fonts.FontDescriptor; |
| import org.apache.fop.fonts.FontInfo; |
| import org.apache.fop.fonts.Typeface; |
| import org.apache.fop.fonts.base14.Symbol; |
| import org.apache.fop.fonts.base14.ZapfDingbats; |
| |
| /** |
| * Class representing a /Resources object. |
| * |
| * /Resources object contain a list of references to the fonts, patterns, |
| * shadings, etc., for the document. |
| */ |
| public class PDFResources extends PDFDictionary { |
| |
| /** |
| * /Font objects keyed by their internal name |
| */ |
| protected Map<String, PDFDictionary> fonts = new LinkedHashMap<String, PDFDictionary>(); |
| |
| /** |
| * Set of XObjects |
| */ |
| protected Set<PDFXObject> xObjects = new LinkedHashSet<PDFXObject>(); |
| /** Map of color spaces (key: color space name) */ |
| protected Map<LazyName, PDFColorSpace> colorSpaces = new LinkedHashMap<LazyName, PDFColorSpace>(); |
| |
| /** Map of ICC color spaces (key: ICC profile description) */ |
| protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>(); |
| |
| private PDFResources parent; |
| private PDFDictionary fontsObj; |
| private Map<String, PDFDictionary> fontsObjDict = new LinkedHashMap<String, PDFDictionary>(); |
| |
| /** Named properties */ |
| protected Map<String, PDFReference> properties = new LinkedHashMap<String, PDFReference>(); |
| |
| protected Set<PDFResourceContext> contexts = new LinkedHashSet<PDFResourceContext>(); |
| |
| /** |
| * create a /Resources object. |
| */ |
| public PDFResources(PDFDocument doc) { |
| /* generic creation of object */ |
| super(); |
| setObjectNumber(doc); |
| } |
| |
| public void addContext(PDFResourceContext c) { |
| contexts.add(c); |
| } |
| |
| public void setParentResources(PDFResources p) { |
| parent = p; |
| } |
| |
| public PDFResources getParentResources() { |
| return parent; |
| } |
| |
| /** |
| * add font object to resources list. |
| * |
| * @param font the PDFFont to add |
| */ |
| public void addFont(PDFFont font) { |
| addFont(font.getName(), font); |
| } |
| |
| public void addFont(String name, PDFDictionary font) { |
| if (fontsObj != null) { |
| fontsObj.put(name, font); |
| fontsObjDict.put(name, font); |
| } else { |
| fonts.put(name, font); |
| } |
| } |
| |
| public void createFontsAsObj() { |
| fontsObj = new PDFDictionary(); |
| getDocument().registerTrailerObject(fontsObj); |
| put("Font", fontsObj); |
| } |
| |
| /** |
| * Add the fonts in the font info to this PDF document's Font Resources. |
| * |
| * @param doc PDF document to add fonts to |
| * @param fontInfo font info object to get font information from |
| */ |
| public void addFonts(PDFDocument doc, FontInfo fontInfo) { |
| Map<String, Typeface> usedFonts = fontInfo.getUsedFonts(); |
| for (Map.Entry<String, Typeface> e : usedFonts.entrySet()) { |
| String f = e.getKey(); |
| Typeface font = e.getValue(); |
| |
| //Check if the font actually had any mapping operations. If not, it is an indication |
| //that it has never actually been used and therefore doesn't have to be embedded. |
| if (font.hadMappingOperations()) { |
| FontDescriptor desc = null; |
| if (font instanceof FontDescriptor) { |
| desc = (FontDescriptor)font; |
| } |
| String encoding = font.getEncodingName(); |
| if (font instanceof Symbol || font instanceof ZapfDingbats) { |
| encoding = null; //Symbolic fonts shouldn't specify an encoding value in PDF |
| } |
| addFont(doc.getFactory().makeFont( |
| f, font.getEmbedFontName(), encoding, font, desc)); |
| } |
| } |
| } |
| |
| /** |
| * Add an XObject to the resources. |
| * |
| * @param xObject the XObject to add |
| */ |
| public void addXObject(PDFXObject xObject) { |
| this.xObjects.add(xObject); |
| } |
| |
| /** |
| * Add a ColorSpace dictionary to the resources. |
| * @param colorSpace the color space |
| */ |
| public void addColorSpace(PDFColorSpace colorSpace) { |
| this.colorSpaces.put(new LazyName(colorSpace), colorSpace); |
| if (colorSpace instanceof PDFICCBasedColorSpace) { |
| PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace; |
| String desc = ColorProfileUtil.getICCProfileDescription( |
| icc.getICCStream().getICCProfile()); |
| this.iccColorSpaces.put(desc, icc); |
| } |
| } |
| |
| static class LazyName { |
| private PDFColorSpace colorSpace; |
| public LazyName(PDFColorSpace colorSpace) { |
| this.colorSpace = colorSpace; |
| } |
| public PDFName getName() { |
| return new PDFName(colorSpace.getName()); |
| } |
| } |
| |
| /** |
| * Returns a ICCBased color space by profile name. |
| * @param desc the name of the color space |
| * @return the requested color space or null if it wasn't found |
| */ |
| public PDFICCBasedColorSpace getICCColorSpaceByProfileName(String desc) { |
| PDFICCBasedColorSpace cs = this.iccColorSpaces.get(desc); |
| return cs; |
| } |
| |
| /** |
| * Returns a color space by name. |
| * @param name the name of the color space |
| * @return the requested color space or null if it wasn't found |
| */ |
| public PDFColorSpace getColorSpace(PDFName name) { |
| for (Map.Entry<LazyName, PDFColorSpace> x : colorSpaces.entrySet()) { |
| if (x.getKey().getName().equals(name)) { |
| return x.getValue(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Add a named property. |
| * |
| * @param name name of property |
| * @param property reference to property value |
| */ |
| public void addProperty(String name, PDFReference property) { |
| this.properties.put(name, property); |
| } |
| |
| /** |
| * Get a named property. |
| * |
| * @param name name of property |
| */ |
| public PDFReference getProperty(String name) { |
| return this.properties.get(name); |
| } |
| |
| @Override |
| public int output(OutputStream stream) throws IOException { |
| populateDictionary(); |
| return super.output(stream); |
| } |
| |
| private void populateDictionary() { |
| if (parent != null && parent.fontsObj != null) { |
| put("Font", parent.fontsObj); |
| } |
| if (!this.fonts.isEmpty() || (parent != null && !parent.fonts.isEmpty())) { |
| PDFDictionary dict = new PDFDictionary(this); |
| /* construct PDF dictionary of font object references */ |
| for (Map.Entry<String, PDFDictionary> entry : fonts.entrySet()) { |
| dict.put(entry.getKey(), entry.getValue()); |
| } |
| if (parent != null) { |
| for (Map.Entry<String, PDFDictionary> entry : parent.fonts.entrySet()) { |
| dict.put(entry.getKey(), entry.getValue()); |
| } |
| for (Map.Entry<String, PDFDictionary> entry : parent.fontsObjDict.entrySet()) { |
| dict.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| put("Font", dict); |
| } |
| |
| Set<PDFPattern> patterns = new LinkedHashSet<PDFPattern>(); |
| Set<PDFShading> shadings = new LinkedHashSet<PDFShading>(); |
| Set<PDFGState> gstates = new LinkedHashSet<PDFGState>(); |
| for (PDFResourceContext c : contexts) { |
| xObjects.addAll(c.getXObjects()); |
| patterns.addAll(c.getPatterns()); |
| shadings.addAll(c.getShadings()); |
| gstates.addAll(c.getGStates()); |
| } |
| if (parent != null) { |
| xObjects.addAll(parent.xObjects); |
| for (PDFResourceContext c : parent.contexts) { |
| patterns.addAll(c.getPatterns()); |
| shadings.addAll(c.getShadings()); |
| gstates.addAll(c.getGStates()); |
| } |
| } |
| |
| if (!shadings.isEmpty()) { |
| PDFDictionary dict = (PDFDictionary) get("Shading"); |
| if (dict == null) { |
| dict = new PDFDictionary(this); |
| } |
| for (PDFShading shading : shadings) { |
| dict.put(shading.getName(), shading); |
| } |
| put("Shading", dict); |
| } |
| |
| if (!patterns.isEmpty()) { |
| PDFDictionary dict = (PDFDictionary) get("Pattern"); |
| if (dict == null) { |
| dict = new PDFDictionary(this); |
| } |
| for (PDFPattern pattern : patterns) { |
| dict.put(pattern.getName(), pattern); |
| } |
| put("Pattern", dict); |
| } |
| |
| PDFArray procset = new PDFArray(this); |
| procset.add(new PDFName("PDF")); |
| procset.add(new PDFName("ImageB")); |
| procset.add(new PDFName("ImageC")); |
| procset.add(new PDFName("Text")); |
| put("ProcSet", procset); |
| |
| if (!xObjects.isEmpty()) { |
| PDFDictionary dict = (PDFDictionary) get("XObject"); |
| if (dict == null) { |
| dict = new PDFDictionary(this); |
| } |
| for (PDFXObject xObject : xObjects) { |
| dict.put(xObject.getName().toString(), xObject); |
| } |
| put("XObject", dict); |
| } |
| |
| if (!gstates.isEmpty()) { |
| PDFDictionary dict = (PDFDictionary) get("ExtGState"); |
| if (dict == null) { |
| dict = new PDFDictionary(this); |
| } |
| for (PDFGState gstate : gstates) { |
| dict.put(gstate.getName(), gstate); |
| } |
| put("ExtGState", dict); |
| } |
| |
| if (!this.colorSpaces.isEmpty() || (parent != null && !parent.colorSpaces.isEmpty())) { |
| PDFDictionary dict = (PDFDictionary)this.get("ColorSpace"); |
| if (dict == null) { |
| dict = new PDFDictionary(this); |
| } |
| if (parent != null) { |
| for (PDFColorSpace colorSpace : parent.colorSpaces.values()) { |
| dict.put(colorSpace.getName(), colorSpace); |
| } |
| } |
| for (PDFColorSpace colorSpace : colorSpaces.values()) { |
| dict.put(colorSpace.getName(), colorSpace); |
| } |
| put("ColorSpace", dict); |
| } |
| |
| if (!properties.isEmpty()) { |
| PDFDictionary dict = new PDFDictionary(this); |
| for (Map.Entry<String, PDFReference> stringPDFReferenceEntry : properties.entrySet()) { |
| dict.put(stringPDFReferenceEntry.getKey(), stringPDFReferenceEntry.getValue()); |
| } |
| put("Properties", dict); |
| } |
| } |
| |
| @Override |
| public void getChildren(Set<PDFObject> children) { |
| getChildren(children, false); |
| } |
| |
| private void getChildren(Set<PDFObject> children, boolean isParent) { |
| super.getChildren(children); |
| for (PDFDictionary f : fonts.values()) { |
| children.add(f); |
| f.getChildren(children); |
| } |
| for (PDFResourceContext c : contexts) { |
| for (PDFXObject x : c.getXObjects()) { |
| children.add(x); |
| x.getChildren(children); |
| } |
| for (PDFPattern x : c.getPatterns()) { |
| children.add(x); |
| x.getChildren(children); |
| } |
| for (PDFShading x : c.getShadings()) { |
| children.add(x); |
| x.getChildren(children); |
| } |
| for (PDFGState x : c.getGStates()) { |
| children.add(x); |
| x.getChildren(children); |
| } |
| } |
| if (!isParent) { |
| for (PDFColorSpace x : colorSpaces.values()) { |
| children.add((PDFObject)x); |
| ((PDFObject)x).getChildren(children); |
| } |
| } |
| if (parent != null) { |
| parent.getChildren(children, true); |
| } |
| } |
| } |