blob: f98263fcbf17b167aea09e8161b276159bf8d664 [file] [log] [blame]
/*
* 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);
}
}
}