blob: 0f0acd60d679f35098e31aa03ba1f9140f2def58 [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.render;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.CachedFontInfo;
import org.apache.fop.fonts.EmbedFontInfo;
import org.apache.fop.fonts.FontCache;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontResolver;
import org.apache.fop.fonts.FontSetup;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.autodetect.FontFileFinder;
import org.apache.fop.fonts.autodetect.FontInfoFinder;
import org.apache.fop.util.LogUtil;
import org.apache.xmlgraphics.util.ClasspathResource;
/**
* Base Print renderer configurator (mostly handles font configuration)
*/
public class PrintRendererConfigurator extends AbstractRendererConfigurator
implements RendererConfigurator {
/** logger instance */
protected static Log log = LogFactory.getLog(PrintRendererConfigurator.class);
/**
* Default constructor
* @param userAgent user agent
*/
public PrintRendererConfigurator(FOUserAgent userAgent) {
super(userAgent);
}
/**
* Builds a list of EmbedFontInfo objects for use with the setup() method.
*
* @param renderer print renderer
* @throws FOPException if something's wrong with the config data
*/
public void configure(Renderer renderer) throws FOPException {
Configuration cfg = getRendererConfig(renderer);
if (cfg == null) {
return;
}
PrintRenderer printRenderer = (PrintRenderer)renderer;
FontResolver fontResolver = printRenderer.getFontResolver();
if (fontResolver == null) {
//Ensure that we have minimal font resolution capabilities
fontResolver = FontSetup.createMinimalFontResolver();
}
FopFactory factory = userAgent.getFactory();
boolean strict = factory.validateUserConfigStrictly();
FontCache fontCache = factory.getFontCache();
List fontInfoList = buildFontListFromConfiguration(cfg,
userAgent.getFontBaseURL(), fontResolver, strict,
fontCache);
if (fontCache != null && fontCache.hasChanged()) {
fontCache.save();
}
printRenderer.addFontList(fontInfoList);
}
/**
* Builds a list of EmbedFontInfo objects for use with the setup() method.
*
* @param cfg Configuration object
* @param fontBaseURL the base URL to resolve relative font URLs with
* @param fontResolver the FontResolver to use
* @param strict true if an Exception should be thrown if an error is found.
* @param fontCache the font cache (or null if it is disabled)
* @return a List of EmbedFontInfo objects.
* @throws FOPException If an error occurs while processing the configuration
*/
public static List buildFontListFromConfiguration(Configuration cfg,
String fontBaseURL, FontResolver fontResolver,
boolean strict, FontCache fontCache) throws FOPException {
List fontInfoList = new java.util.ArrayList();
Configuration fonts = cfg.getChild("fonts", false);
if (fonts != null) {
long start = 0;
if (log.isDebugEnabled()) {
log.debug("Starting font configuration...");
start = System.currentTimeMillis();
}
// native o/s search (autodetect) configuration
boolean autodetectFonts = (fonts.getChild("auto-detect", false) != null);
if (autodetectFonts) {
// search in font base if it is defined and
// is a directory but don't recurse
FontFileFinder fontFileFinder = new FontFileFinder();
if (fontBaseURL != null) {
try {
File fontBase = FileUtils.toFile(new URL(fontBaseURL));
if (fontBase != null) {
//Can only use the font base URL if it's a file URL
addFontInfoListFromFileList(
fontFileFinder.find(fontBase.getAbsolutePath()),
fontInfoList,
fontResolver,
fontCache
);
}
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}
}
// native o/s font directory finder
try {
addFontInfoListFromFileList(
fontFileFinder.find(),
fontInfoList,
fontResolver,
fontCache
);
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}
// load fonts from classpath
addFontInfoListFromFileList(ClasspathResource.getInstance()
.listResourcesOfMimeType("application/x-font"),
fontInfoList, fontResolver, fontCache);
addFontInfoListFromFileList(
ClasspathResource.getInstance()
.listResourcesOfMimeType(
"application/x-font-truetype"),
fontInfoList, fontResolver, fontCache);
}
// directory (multiple font) configuration
Configuration[] directories = fonts.getChildren("directory");
for (int i = 0; i < directories.length; i++) {
boolean recursive = directories[i].getAttributeAsBoolean("recursive", false);
String directory = null;
try {
directory = directories[i].getValue();
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
continue;
}
if (directory == null) {
LogUtil.handleException(log,
new FOPException("directory defined without value"), strict);
continue;
}
FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1);
try {
addFontInfoListFromFileList(
fontFileFinder.find(directory),
fontInfoList,
fontResolver,
fontCache
);
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}
}
// font file (singular) configuration
Configuration[] font = fonts.getChildren("font");
for (int i = 0; i < font.length; i++) {
EmbedFontInfo fontInfo = getFontInfoFromConfiguration(
font[i], fontResolver, strict, fontCache);
if (fontInfo != null) {
fontInfoList.add(fontInfo);
}
}
if (log.isDebugEnabled()) {
log.debug("Finished font configuration in "
+ (System.currentTimeMillis() - start) + "ms");
}
}
return fontInfoList;
}
/**
* Iterates over font file list adding font info to list
* @param fontFileList font file list
* @param fontInfoList font info list
* @param resolver font resolver
*/
private static void addFontInfoListFromFileList(
List fontFileList, List fontInfoList, FontResolver resolver, FontCache fontCache) {
for (Iterator iter = fontFileList.iterator(); iter.hasNext();) {
URL fontUrl = (URL)iter.next();
// parse font to ascertain font info
FontInfoFinder finder = new FontInfoFinder();
EmbedFontInfo fontInfo = finder.find(fontUrl, resolver, fontCache);
if (fontInfo != null) {
fontInfoList.add(fontInfo);
}
}
}
private static void closeSource(Source src) {
if (src instanceof StreamSource) {
StreamSource streamSource = (StreamSource)src;
IOUtils.closeQuietly(streamSource.getInputStream());
IOUtils.closeQuietly(streamSource.getReader());
}
}
/**
* Returns a font info from a font node Configuration definition
*
* @param fontCfg Configuration object (font node)
* @param fontResolver font resolver used to resolve font
* @param strict validate configuration strictly
* @param fontCache the font cache (or null if it is disabled)
* @return font info
* @throws FOPException if something's wrong with the config data
*/
public static EmbedFontInfo getFontInfoFromConfiguration(
Configuration fontCfg, FontResolver fontResolver, boolean strict, FontCache fontCache)
throws FOPException {
String metricsUrl = fontCfg.getAttribute("metrics-url", null);
String embedUrl = fontCfg.getAttribute("embed-url", null);
if (metricsUrl == null && embedUrl == null) {
LogUtil.handleError(log, "Font configuration without metric-url or embed-url", strict);
return null;
}
if (strict) {
//This section just checks early whether the URIs can be resolved
//Stream are immediately closed again since they will never be used anyway
if (embedUrl != null) {
Source source = fontResolver.resolve(embedUrl);
closeSource(source);
if (source == null) {
LogUtil.handleError(log,
"Failed to resolve font with embed-url '" + embedUrl + "'", strict);
return null;
}
}
if (metricsUrl != null) {
Source source = fontResolver.resolve(metricsUrl);
closeSource(source);
if (source == null) {
LogUtil.handleError(log,
"Failed to resolve font with metric-url '" + metricsUrl + "'", strict);
return null;
}
}
}
boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
EmbedFontInfo fontInfo = null;
Configuration[] tripletCfg = fontCfg.getChildren("font-triplet");
// no font triplet info
if (tripletCfg.length == 0) {
LogUtil.handleError(log, "font without font-triplet", strict);
// if not strict try to determine font info from the embed/metrics url
File fontFile = CachedFontInfo.getFileFromUrls(new String[] {embedUrl, metricsUrl});
URL fontUrl;
try {
fontUrl = fontFile.toURI().toURL();
} catch (MalformedURLException e) {
// Should never happen
log.debug("Malformed Url: " + e.getMessage());
return null;
}
if (fontFile != null) {
FontInfoFinder finder = new FontInfoFinder();
return finder.find(fontUrl, fontResolver, fontCache);
} else {
return null;
}
} else {
List tripleList = new java.util.ArrayList();
for (int j = 0; j < tripletCfg.length; j++) {
try {
String name = tripletCfg[j].getAttribute("name");
if (name == null) {
LogUtil.handleError(log, "font-triplet without name", strict);
continue;
}
String weightStr = tripletCfg[j].getAttribute("weight");
if (weightStr == null) {
LogUtil.handleError(log, "font-triplet without weight", strict);
continue;
}
int weight = FontUtil.parseCSS2FontWeight(weightStr);
String style = tripletCfg[j].getAttribute("style");
if (style == null) {
LogUtil.handleError(log, "font-triplet without style", strict);
continue;
}
tripleList.add(FontInfo.createFontKey(name, style, weight));
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}
fontInfo = new EmbedFontInfo(metricsUrl, useKerning, tripleList, embedUrl);
if (fontCache != null) {
if (!fontCache.containsFont(fontInfo)) {
fontCache.addFont(fontInfo);
}
}
if (log.isDebugEnabled()) {
log.debug("Adding font " + fontInfo.getEmbedFile()
+ ", metric file " + fontInfo.getMetricsFile());
for (int j = 0; j < tripleList.size(); ++j) {
FontTriplet triplet = (FontTriplet) tripleList.get(j);
log.debug(" Font triplet "
+ triplet.getName() + ", "
+ triplet.getStyle() + ", "
+ triplet.getWeight());
}
}
}
return fontInfo;
}
}