/*
* 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: AFPRendererConfigurator.java 1339442 2012-05-17 01:42:56Z gadams $ */
package org.apache.fop.render.afp;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.AFPResourceLevel;
import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.fonts.AFPFont;
import org.apache.fop.afp.fonts.AFPFontCollection;
import org.apache.fop.afp.fonts.AFPFontInfo;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.fonts.CharacterSetBuilder;
import org.apache.fop.afp.fonts.CharacterSetType;
import org.apache.fop.afp.fonts.DoubleByteFont;
import org.apache.fop.afp.fonts.OutlineFont;
import org.apache.fop.afp.fonts.RasterFont;
import org.apache.fop.afp.util.DefaultFOPResourceAccessor;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fonts.FontCollection;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.fonts.FontManagerConfigurator;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.PrintRendererConfigurator;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.util.LogUtil;
/**
* AFP Renderer configurator
*/
public class AFPRendererConfigurator extends PrintRendererConfigurator
implements IFDocumentHandlerConfigurator {
private final AFPEventProducer eventProducer;
/**
* Default constructor
*
* @param userAgent user agent
*/
public AFPRendererConfigurator(FOUserAgent userAgent) {
super(userAgent);
eventProducer = AFPEventProducer.Provider.get(userAgent.getEventBroadcaster());
}
private AFPFontInfo buildFont(Configuration fontCfg, String fontPath)
throws ConfigurationException {
FontManager fontManager = this.userAgent.getFactory().getFontManager();
Configuration[] triple = fontCfg.getChildren("font-triplet");
List<FontTriplet> tripletList = new ArrayList<FontTriplet>();
if (triple.length == 0) {
eventProducer.fontConfigMissing(this, "<font-triplet...", fontCfg.getLocation());
return null;
}
for (Configuration config : triple) {
int weight = FontUtil.parseCSS2FontWeight(config.getAttribute("weight"));
FontTriplet triplet = new FontTriplet(config.getAttribute("name"),
config.getAttribute("style"),
weight);
tripletList.add(triplet);
}
//build the fonts
Configuration[] config = fontCfg.getChildren("afp-font");
if (config.length == 0) {
eventProducer.fontConfigMissing(this, "<afp-font...", fontCfg.getLocation());
return null;
}
Configuration afpFontCfg = config[0];
URI baseURI = null;
String uri = afpFontCfg.getAttribute("base-uri", fontPath);
if (uri == null) {
//Fallback for old attribute which only supports local filenames
String path = afpFontCfg.getAttribute("path", fontPath);
if (path != null) {
File f = new File(path);
baseURI = f.toURI();
}
} else {
try {
baseURI = new URI(uri);
} catch (URISyntaxException e) {
eventProducer.invalidConfiguration(this, e);
return null;
}
}
ResourceAccessor accessor = new DefaultFOPResourceAccessor(
this.userAgent,
fontManager.getFontBaseURL(),
baseURI);
AFPFont font = null;
try {
String type = afpFontCfg.getAttribute("type");
if (type == null) {
eventProducer.fontConfigMissing(this, "type attribute", fontCfg.getLocation());
return null;
}
String codepage = afpFontCfg.getAttribute("codepage");
if (codepage == null) {
eventProducer.fontConfigMissing(this, "codepage attribute",
fontCfg.getLocation());
return null;
}
String encoding = afpFontCfg.getAttribute("encoding");
if (encoding == null) {
eventProducer.fontConfigMissing(this, "encoding attribute",
fontCfg.getLocation());
return null;
}
font = fontFromType(type, codepage, encoding, accessor, afpFontCfg);
} catch (ConfigurationException ce) {
eventProducer.invalidConfiguration(this, ce);
} catch (IOException ioe) {
eventProducer.invalidConfiguration(this, ioe);
} catch (IllegalArgumentException iae) {
eventProducer.invalidConfiguration(this, iae);
}
return font != null ? new AFPFontInfo(font, tripletList) : null;
}
/**
* Create the AFPFont based on type and type-dependent configuration.
*
* @param type font type e.g. 'raster', 'outline'
* @param codepage codepage file
* @param encoding character encoding e.g. 'Cp500', 'UnicodeBigUnmarked'
* @param accessor
* @param afpFontCfg
* @return the created AFPFont
* @throws ConfigurationException
*/
private AFPFont fontFromType(String type, String codepage, String encoding,
ResourceAccessor accessor, Configuration afpFontCfg) throws ConfigurationException,
IOException {
if ("raster".equalsIgnoreCase(type)) {
String name = afpFontCfg.getAttribute("name", "Unknown");
// Create a new font object
RasterFont font = new RasterFont(name);
Configuration[] rasters = afpFontCfg.getChildren("afp-raster-font");
if (rasters.length == 0) {
eventProducer.fontConfigMissing(this, "<afp-raster-font...",
afpFontCfg.getLocation());
return null;
}
for (int j = 0; j < rasters.length; j++) {
Configuration rasterCfg = rasters[j];
String characterset = rasterCfg.getAttribute("characterset");
if (characterset == null) {
eventProducer.fontConfigMissing(this, "characterset attribute",
afpFontCfg.getLocation());
return null;
}
float size = rasterCfg.getAttributeAsFloat("size");
int sizeMpt = (int) (size * 1000);
String base14 = rasterCfg.getAttribute("base14-font", null);
if (base14 != null) {
try {
Class<? extends Typeface> clazz = Class.forName(
"org.apache.fop.fonts.base14." + base14).asSubclass(Typeface.class);
try {
Typeface tf = clazz.newInstance();
font.addCharacterSet(sizeMpt,
CharacterSetBuilder.getSingleByteInstance()
.build(characterset, codepage, encoding, tf,
eventProducer));
} catch (Exception ie) {
String msg = "The base 14 font class " + clazz.getName()
+ " could not be instantiated";
log.error(msg);
}
} catch (ClassNotFoundException cnfe) {
String msg = "The base 14 font class for " + characterset
+ " could not be found";
log.error(msg);
}
} else {
font.addCharacterSet(sizeMpt, CharacterSetBuilder.getSingleByteInstance()
.buildSBCS(characterset, codepage, encoding, accessor, eventProducer));
}
}
return font;
} else if ("outline".equalsIgnoreCase(type)) {
String characterset = afpFontCfg.getAttribute("characterset");
if (characterset == null) {
eventProducer.fontConfigMissing(this, "characterset attribute",
afpFontCfg.getLocation());
return null;
}
String name = afpFontCfg.getAttribute("name", characterset);
CharacterSet characterSet = null;
String base14 = afpFontCfg.getAttribute("base14-font", null);
if (base14 != null) {
try {
Class<? extends Typeface> clazz = Class.forName("org.apache.fop.fonts.base14."
+ base14).asSubclass(Typeface.class);
try {
Typeface tf = clazz.newInstance();
characterSet = CharacterSetBuilder.getSingleByteInstance()
.build(characterset, codepage, encoding, tf, eventProducer);
} catch (Exception ie) {
String msg = "The base 14 font class " + clazz.getName()
+ " could not be instantiated";
log.error(msg);
}
} catch (ClassNotFoundException cnfe) {
String msg = "The base 14 font class for " + characterset
+ " could not be found";
log.error(msg);
}
} else {
characterSet = CharacterSetBuilder.getSingleByteInstance().buildSBCS(
characterset, codepage, encoding, accessor, eventProducer);
}
// Return new font object
return new OutlineFont(name, characterSet);
} else if ("CIDKeyed".equalsIgnoreCase(type)) {
String characterset = afpFontCfg.getAttribute("characterset");
if (characterset == null) {
eventProducer.fontConfigMissing(this, "characterset attribute",
afpFontCfg.getLocation());
return null;
}
String name = afpFontCfg.getAttribute("name", characterset);
CharacterSet characterSet = null;
CharacterSetType charsetType = afpFontCfg.getAttributeAsBoolean("ebcdic-dbcs", false)
? CharacterSetType.DOUBLE_BYTE_LINE_DATA : CharacterSetType.DOUBLE_BYTE;
characterSet = CharacterSetBuilder.getDoubleByteInstance().buildDBCS(characterset,
codepage, encoding, charsetType, accessor, eventProducer);
// Create a new font object
DoubleByteFont font = new DoubleByteFont(name, characterSet);
return font;
} else {
log.error("No or incorrect type attribute: " + type);
}
return null;
}
/**
* Builds a list of AFPFontInfo objects for use with the setup() method.
*
* @param cfg Configuration object
* @param eventProducer for AFP font related events
* @return List the newly created list of fonts
* @throws ConfigurationException if something's wrong with the config data
*/
private List<AFPFontInfo> buildFontListFromConfiguration(Configuration cfg,
AFPEventProducer eventProducer) throws FOPException, ConfigurationException {
Configuration fonts = cfg.getChild("fonts");
FontManager fontManager = this.userAgent.getFactory().getFontManager();
// General matcher
FontTriplet.Matcher referencedFontsMatcher = fontManager.getReferencedFontsMatcher();
// Renderer-specific matcher
FontTriplet.Matcher localMatcher = null;
// Renderer-specific referenced fonts
Configuration referencedFontsCfg = fonts.getChild("referenced-fonts", false);
if (referencedFontsCfg != null) {
localMatcher = FontManagerConfigurator.createFontsMatcher(
referencedFontsCfg, this.userAgent.getFactory().validateUserConfigStrictly());
}
List<AFPFontInfo> fontList = new java.util.ArrayList<AFPFontInfo>();
Configuration[] font = fonts.getChildren("font");
final String fontPath = null;
for (int i = 0; i < font.length; i++) {
AFPFontInfo afi = buildFont(font[i], fontPath);
if (afi != null) {
if (log.isDebugEnabled()) {
log.debug("Adding font " + afi.getAFPFont().getFontName());
}
List<FontTriplet> fontTriplets = afi.getFontTriplets();
for (int j = 0; j < fontTriplets.size(); ++j) {
FontTriplet triplet = fontTriplets.get(j);
if (log.isDebugEnabled()) {
log.debug(" Font triplet "
+ triplet.getName() + ", "
+ triplet.getStyle() + ", "
+ triplet.getWeight());
}
if ((referencedFontsMatcher != null && referencedFontsMatcher.matches(triplet))
|| (localMatcher != null && localMatcher.matches(triplet))) {
afi.getAFPFont().setEmbeddable(false);
break;
}
}
fontList.add(afi);
}
}
return fontList;
}
/** images are converted to grayscale bitmapped IOCA */
private static final String IMAGES_MODE_GRAYSCALE = "b+w";
/** images are converted to color bitmapped IOCA */
private static final String IMAGES_MODE_COLOR = "color";
/**
* Throws an UnsupportedOperationException.
*
* @param renderer not used
*/
@Override
public void configure(Renderer renderer) {
throw new UnsupportedOperationException();
}
private void configure(AFPCustomizable customizable, Configuration cfg) throws FOPException {
// image information
Configuration imagesCfg = cfg.getChild("images");
// default to grayscale images
String imagesMode = imagesCfg.getAttribute("mode", IMAGES_MODE_GRAYSCALE);
if (IMAGES_MODE_COLOR.equals(imagesMode)) {
customizable.setColorImages(true);
boolean cmyk = imagesCfg.getAttributeAsBoolean("cmyk", false);
customizable.setCMYKImagesSupported(cmyk);
} else {
customizable.setColorImages(false);
// default to 8 bits per pixel
int bitsPerPixel = imagesCfg.getAttributeAsInteger("bits-per-pixel", 8);
customizable.setBitsPerPixel(bitsPerPixel);
}
String dithering = imagesCfg.getAttribute("dithering-quality", "medium");
float dq = 0.5f;
if (dithering.startsWith("min")) {
dq = 0.0f;
} else if (dithering.startsWith("max")) {
dq = 1.0f;
} else {
try {
dq = Float.parseFloat(dithering);
} catch (NumberFormatException nfe) {
//ignore and leave the default above
}
}
customizable.setDitheringQuality(dq);
// native image support
boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
customizable.setNativeImagesSupported(nativeImageSupport);
Configuration jpegConfig = imagesCfg.getChild("jpeg");
boolean allowEmbedding = false;
float ieq = 1.0f;
if (jpegConfig != null) {
allowEmbedding = jpegConfig.getAttributeAsBoolean("allow-embedding", false);
String bitmapEncodingQuality = jpegConfig.getAttribute("bitmap-encoding-quality", null);
if (bitmapEncodingQuality != null) {
try {
ieq = Float.parseFloat(bitmapEncodingQuality);
} catch (NumberFormatException nfe) {
//ignore and leave the default above
}
}
}
customizable.canEmbedJpeg(allowEmbedding);
customizable.setBitmapEncodingQuality(ieq);
//FS11 and FS45 page segment wrapping
boolean pSeg = imagesCfg.getAttributeAsBoolean("pseg", false);
customizable.setWrapPSeg(pSeg);
//FS45 image forcing
boolean fs45 = imagesCfg.getAttributeAsBoolean("fs45", false);
customizable.setFS45(fs45);
// shading (filled rectangles)
Configuration shadingCfg = cfg.getChild("shading");
AFPShadingMode shadingMode = AFPShadingMode.valueOf(
shadingCfg.getValue(AFPShadingMode.COLOR.getName()));
customizable.setShadingMode(shadingMode);
// GOCA Support
Configuration gocaCfg = cfg.getChild("goca");
boolean gocaEnabled = gocaCfg.getAttributeAsBoolean(
"enabled", customizable.isGOCAEnabled());
customizable.setGOCAEnabled(gocaEnabled);
String gocaText = gocaCfg.getAttribute(
"text", customizable.isStrokeGOCAText() ? "stroke" : "default");
customizable.setStrokeGOCAText("stroke".equalsIgnoreCase(gocaText)
|| "shapes".equalsIgnoreCase(gocaText));
// renderer resolution
Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false);
if (rendererResolutionCfg != null) {
customizable.setResolution(rendererResolutionCfg.getValueAsInteger(240));
}
// renderer resolution
Configuration lineWidthCorrectionCfg = cfg.getChild("line-width-correction", false);
if (lineWidthCorrectionCfg != null) {
customizable.setLineWidthCorrection(lineWidthCorrectionCfg
.getValueAsFloat(AFPConstants.LINE_WIDTH_CORRECTION));
}
// a default external resource group file setting
Configuration resourceGroupFileCfg
= cfg.getChild("resource-group-file", false);
if (resourceGroupFileCfg != null) {
String resourceGroupDest = null;
try {
resourceGroupDest = resourceGroupFileCfg.getValue();
if (resourceGroupDest != null) {
File resourceGroupFile = new File(resourceGroupDest);
boolean created = resourceGroupFile.createNewFile();
if (created && resourceGroupFile.canWrite()) {
customizable.setDefaultResourceGroupFilePath(resourceGroupDest);
} else {
log.warn("Unable to write to default external resource group file '"
+ resourceGroupDest + "'");
}
}
} catch (ConfigurationException e) {
LogUtil.handleException(log, e,
userAgent.getFactory().validateUserConfigStrictly());
} catch (IOException ioe) {
throw new FOPException("Could not create default external resource group file"
, ioe);
}
}
Configuration defaultResourceLevelCfg = cfg.getChild("default-resource-levels", false);
if (defaultResourceLevelCfg != null) {
AFPResourceLevelDefaults defaults = new AFPResourceLevelDefaults();
String[] types = defaultResourceLevelCfg.getAttributeNames();
for (int i = 0, c = types.length; i < c; i++) {
String type = types[i];
try {
String level = defaultResourceLevelCfg.getAttribute(type);
defaults.setDefaultResourceLevel(type, AFPResourceLevel.valueOf(level));
} catch (IllegalArgumentException iae) {
LogUtil.handleException(log, iae,
userAgent.getFactory().validateUserConfigStrictly());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e,
userAgent.getFactory().validateUserConfigStrictly());
}
}
customizable.setResourceLevelDefaults(defaults);
}
}
/** {@inheritDoc} */
@Override
public void configure(IFDocumentHandler documentHandler) throws FOPException {
Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
if (cfg != null) {
AFPDocumentHandler afpDocumentHandler = (AFPDocumentHandler) documentHandler;
configure(afpDocumentHandler, cfg);
}
}
/** {@inheritDoc} */
@Override
public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
throws FOPException {
FontManager fontManager = userAgent.getFactory().getFontManager();
List<AFPFontCollection> fontCollections = new ArrayList<AFPFontCollection>();
Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
if (cfg != null) {
try {
List<AFPFontInfo> fontList = buildFontListFromConfiguration(cfg, eventProducer);
fontCollections.add(new AFPFontCollection(
userAgent.getEventBroadcaster(), fontList));
} catch (ConfigurationException e) {
eventProducer.invalidConfiguration(this, e);
LogUtil.handleException(log, e,
userAgent.getFactory().validateUserConfigStrictly());
}
} else {
fontCollections.add(new AFPFontCollection(userAgent.getEventBroadcaster(), null));
}
fontManager.setup(fontInfo,
fontCollections.toArray(
new FontCollection[fontCollections.size()]));
documentHandler.setFontInfo(fontInfo);
}
}