diff options
author | Jeremias Maerki <jeremias@apache.org> | 2007-05-28 14:31:24 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2007-05-28 14:31:24 +0000 |
commit | 8c1aba3f976127d33ec50b67d760f56364c08487 (patch) | |
tree | adfdca730f41c1b9029324bf53535aa25bf16d27 /src/java/org/apache/fop/apps | |
parent | 7ada0a06fe2b9ce0e1867d2c9c47f71ea47a43b2 (diff) | |
download | xmlgraphics-fop-8c1aba3f976127d33ec50b67d760f56364c08487.tar.gz xmlgraphics-fop-8c1aba3f976127d33ec50b67d760f56364c08487.zip |
Bugzilla #41831:
- Add support font auto-detection (easier font configuration) including a font cache to speed up the auto-detection process.
- Refactoring of the configuration code: All Avalon configuration stuff is extracted into separate "Configurator" classes.
- Refactoring of the FOURIResolver.
Submitted by: Adrian Cumiskey <fop-dev.at.cumiskey.com>
Changes to the patch by jeremias during the review:
- Font cache simplified (Java object serialization instead of XML), functionality fixed and moved to the fonts.package.
- Relocated default cache file location to user directory.
- Fixed the font configuration for PDFDocumentGraphics2D/PDFTranscoder that got lost with the patch.
- Fixed a problem with having a non-file URL as font base URL.
- Simplified RendererContextInfo stuff to make it easier to understand.
- Fixed handling of Type 1 fonts in auto-detection.
- Reduced verbosity of font-related log output.
- Updated Jakarta Commons IO to version 1.3.1 (the patch depends on it)
- Various javadocs improvements
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@542237 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/apps')
-rw-r--r-- | src/java/org/apache/fop/apps/FOURIResolver.java | 189 | ||||
-rw-r--r-- | src/java/org/apache/fop/apps/FOUserAgent.java | 43 | ||||
-rw-r--r-- | src/java/org/apache/fop/apps/FopFactory.java | 364 | ||||
-rw-r--r-- | src/java/org/apache/fop/apps/FopFactoryConfigurator.java | 241 |
4 files changed, 510 insertions, 327 deletions
diff --git a/src/java/org/apache/fop/apps/FOURIResolver.java b/src/java/org/apache/fop/apps/FOURIResolver.java index e3ffd22cc..13baeaa56 100644 --- a/src/java/org/apache/fop/apps/FOURIResolver.java +++ b/src/java/org/apache/fop/apps/FOURIResolver.java @@ -29,6 +29,7 @@ import java.net.URL; import java.net.URLConnection; import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamSource; // commons logging @@ -47,9 +48,42 @@ import org.apache.xmlgraphics.util.io.Base64EncodeStream; public class FOURIResolver implements javax.xml.transform.URIResolver { + // log private Log log = LogFactory.getLog("FOP"); + + // true if exceptions are to be thrown if the URIs cannot be resolved. + private boolean throwExceptions = false; + + /** + * Default constructor + */ + public FOURIResolver() { + this(false); + } /** + * Additional constructor + * @param throwExceptions true if exceptions are to be thrown if the URIs cannot be + * resolved. + */ + public FOURIResolver(boolean throwExceptions) { + this.throwExceptions = throwExceptions; + } + + /** + * Handles resolve exceptions appropriately. + * @param errorStr error string + * @param strict strict user config + */ + private void handleException(Exception e, String errorStr, boolean strict) + throws TransformerException { + if (strict) { + throw new TransformerException(errorStr, e); + } + log.error(e.getMessage()); + } + + /** * Called by the processor through {@link FOUserAgent} when it encounters an * uri in an external-graphic element. * (see also {@link javax.xml.transform.URIResolver#resolve(String, String)} @@ -70,31 +104,25 @@ public class FOURIResolver * @throws javax.xml.transform.TransformerException Never thrown by this implementation. * @see javax.xml.transform.URIResolver#resolve(String, String) */ - public Source resolve(String href, String base) - throws javax.xml.transform.TransformerException { - - //data URLs can be quite long so don't try to build a File (can lead to problems) + public Source resolve(String href, String base) throws TransformerException { + // data URLs can be quite long so don't try to build a File (can lead to problems) if (href.startsWith("data:")) { return parseDataURI(href); } - + URL absoluteURL = null; - File f = new File(href); - if (f.exists()) { + File file = new File(href); + if (file.canRead() && file.isFile()) { try { - absoluteURL = f.toURL(); + absoluteURL = file.toURL(); } catch (MalformedURLException mfue) { - log.error("Could not convert filename to URL: " + mfue.getMessage()); + handleException(mfue, + "Could not convert filename '" + href + "' to URL", throwExceptions); } } else { - URL baseURL = null; - try { - baseURL = toBaseURL(base); - } catch (MalformedURLException mfue) { - log.error("Error with base URL \"" + base + "\"): " + mfue.getMessage()); - } - if (baseURL == null) { - // We don't have a valid baseURL just use the URL as given + // no base provided + if (base == null) { + // We don't have a valid file protocol based URL try { absoluteURL = new URL(href); } catch (MalformedURLException mue) { @@ -103,64 +131,78 @@ public class FOURIResolver // the href contains only a path then file: is assumed absoluteURL = new URL("file:" + href); } catch (MalformedURLException mfue) { - log.error("Error with URL '" + href + "': " + mue.getMessage()); - return null; + handleException(mfue, + "Error with URL '" + href + "'", throwExceptions); } } + + // try and resolve from context of base } else { + URL baseURL = null; try { - /* - This piece of code is based on the following statement in - RFC2396 section 5.2: - - 3) If the scheme component is defined, indicating that the reference - starts with a scheme name, then the reference is interpreted as an - absolute URI and we are done. Otherwise, the reference URI's - scheme is inherited from the base URI's scheme component. - - Due to a loophole in prior specifications [RFC1630], some parsers - allow the scheme name to be present in a relative URI if it is the - same as the base URI scheme. Unfortunately, this can conflict - with the correct parsing of non-hierarchical URI. For backwards - compatibility, an implementation may work around such references - by removing the scheme if it matches that of the base URI and the - scheme is known to always use the <hier_part> syntax. - - The URL class does not implement this work around, so we do. - */ + baseURL = new URL(base); + } catch (MalformedURLException mfue) { + handleException(mfue, "Error with base URL '" + base + "'", throwExceptions); + } - String scheme = baseURL.getProtocol() + ":"; - if (href.startsWith(scheme)) { - href = href.substring(scheme.length()); - if ("file:".equals(scheme)) { - int colonPos = href.indexOf(':'); - int slashPos = href.indexOf('/'); - if (slashPos >= 0 && colonPos >= 0 && colonPos < slashPos) { - href = "/" + href; //Absolute file URL doesn't have a leading slash - } + /* + * This piece of code is based on the following statement in + * RFC2396 section 5.2: + * + * 3) If the scheme component is defined, indicating that the + * reference starts with a scheme name, then the reference is + * interpreted as an absolute URI and we are done. Otherwise, + * the reference URI's scheme is inherited from the base URI's + * scheme component. + * + * Due to a loophole in prior specifications [RFC1630], some + * parsers allow the scheme name to be present in a relative URI + * if it is the same as the base URI scheme. Unfortunately, this + * can conflict with the correct parsing of non-hierarchical + * URI. For backwards compatibility, an implementation may work + * around such references by removing the scheme if it matches + * that of the base URI and the scheme is known to always use + * the <hier_part> syntax. + * + * The URL class does not implement this work around, so we do. + */ + String scheme = baseURL.getProtocol() + ":"; + if (href.startsWith(scheme)) { + href = href.substring(scheme.length()); + if ("file:".equals(scheme)) { + int colonPos = href.indexOf(':'); + int slashPos = href.indexOf('/'); + if (slashPos >= 0 && colonPos >= 0 && colonPos < slashPos) { + href = "/" + href; // Absolute file URL doesn't + // have a leading slash } } + } + try { absoluteURL = new URL(baseURL, href); } catch (MalformedURLException mfue) { - log.error("Error with URL '" + href + "': " + mfue.getMessage()); - return null; + handleException(mfue, + "Error with URL; base '" + base + "' " + "href '" + href + "'", + throwExceptions); } } } - String effURL = absoluteURL.toExternalForm(); - try { - URLConnection connection = absoluteURL.openConnection(); - connection.setAllowUserInteraction(false); - connection.setDoInput(true); - updateURLConnection(connection, href); - connection.connect(); - return new StreamSource(connection.getInputStream(), effURL); - } catch (FileNotFoundException fnfe) { - //Note: This is on "debug" level since the caller is supposed to handle this - log.debug("File not found: " + effURL); - } catch (java.io.IOException ioe) { - log.error("Error with opening URL '" + effURL + "': " + ioe.getMessage()); + if (absoluteURL != null) { + String effURL = absoluteURL.toExternalForm(); + try { + URLConnection connection = absoluteURL.openConnection(); + connection.setAllowUserInteraction(false); + connection.setDoInput(true); + updateURLConnection(connection, href); + connection.connect(); + return new StreamSource(connection.getInputStream(), effURL); + } catch (FileNotFoundException fnfe) { + //Note: This is on "debug" level since the caller is supposed to handle this + log.debug("File not found: " + effURL); + } catch (java.io.IOException ioe) { + log.error("Error with opening URL '" + effURL + "': " + ioe.getMessage()); + } } return null; } @@ -201,29 +243,6 @@ public class FOURIResolver } /** - * Returns the base URL as a java.net.URL. - * If the base URL is not set a default URL pointing to the - * current directory is returned. - * @param baseURL the base URL - * @returns the base URL as java.net.URL - */ - private URL toBaseURL(String base) throws MalformedURLException { - if (base == null) { - return new java.io.File("").toURL(); - } - if (!base.endsWith("/")) { - // The behavior described by RFC 3986 regarding resolution of relative - // references may be misleading for normal users: - // file://path/to/resources + myResource.res -> file://path/to/myResource.res - // file://path/to/resources/ + myResource.res -> file://path/to/resources/myResource.res - // We assume that even when the ending slash is missing, users have the second - // example in mind - base += "/"; - } - return new URL(base); - } - - /** * Parses inline data URIs as generated by MS Word's XML export and FO stylesheet. * @see <a href="http://www.ietf.org/rfc/rfc2397">RFC 2397</a> */ diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index f15ddcc31..24df2f75b 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -63,19 +63,25 @@ import org.apache.fop.render.pdf.PDFRenderer; public class FOUserAgent { /** Defines the default target resolution (72dpi) for FOP */ - public static final float DEFAULT_TARGET_RESOLUTION = FopFactory.DEFAULT_TARGET_RESOLUTION; + public static final float DEFAULT_TARGET_RESOLUTION = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; private static Log log = LogFactory.getLog("FOP"); private FopFactory factory; - /** The base URL for all URL resolutions, especially for external-graphics */ - private String baseURL; - + /** + * The base URL for all URL resolutions, especially for + * external-graphics. + */ + private String base = null; + + /** The base URL for all font URL resolutions. */ + private String fontBase = null; + /** A user settable URI Resolver */ private URIResolver uriResolver = null; - private float targetResolution = DEFAULT_TARGET_RESOLUTION; + private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; private Map rendererOptions = new java.util.HashMap(); private File outputFile = null; private Renderer rendererOverride = null; @@ -105,7 +111,6 @@ public class FOUserAgent { /** * Default constructor - * @throws FOPException * @see org.apache.fop.apps.FopFactory * @deprecated Provided for compatibility only. Please use the methods from * FopFactory to construct FOUserAgent instances! @@ -126,6 +131,7 @@ public class FOUserAgent { } this.factory = factory; setBaseURL(factory.getBaseURL()); + setFontBaseURL(factory.getFontBaseURL()); setTargetResolution(factory.getTargetResolution()); } @@ -277,10 +283,18 @@ public class FOUserAgent { /** * Sets the base URL. - * @param baseURL base URL + * @param baseUrl base URL */ - public void setBaseURL(String baseURL) { - this.baseURL = baseURL; + public void setBaseURL(String baseUrl) { + this.base = baseUrl; + } + + /** + * sets font base URL + * @param fontBaseUrl font base URL + */ + public void setFontBaseURL(String fontBaseUrl) { + this.fontBase = fontBaseUrl; } /** @@ -288,7 +302,7 @@ public class FOUserAgent { * @return the base URL */ public String getBaseURL() { - return this.baseURL; + return this.base; } /** @@ -410,8 +424,10 @@ public class FOUserAgent { */ public void setTargetResolution(float dpi) { this.targetResolution = dpi; - log.info("target-resolution set to: " + targetResolution - + "dpi (px2mm=" + getTargetPixelUnitToMillimeter() + ")"); + if (log.isDebugEnabled()) { + log.debug("target-resolution set to: " + targetResolution + + "dpi (px2mm=" + getTargetPixelUnitToMillimeter() + ")"); + } } /** @@ -429,8 +445,7 @@ public class FOUserAgent { /** @return the font base URL */ public String getFontBaseURL() { - String fontBaseURL = getFactory().getFontBaseURL(); - return fontBaseURL != null ? fontBaseURL : getBaseURL(); + return fontBase != null ? fontBase : getBaseURL(); } /** diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index f7a3bd251..1e7ab2a62 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; +import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -39,14 +40,13 @@ import javax.xml.transform.stream.StreamSource; import org.xml.sax.SAXException; import org.apache.avalon.framework.configuration.Configuration; -import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; +import org.apache.fop.fonts.FontCache; import org.apache.fop.hyphenation.HyphenationTreeResolver; import org.apache.fop.image.ImageFactory; import org.apache.fop.layoutmgr.LayoutManagerMaker; @@ -62,27 +62,6 @@ import org.apache.fop.util.ContentHandlerFactoryRegistry; */ public class FopFactory { - /** Defines the default target resolution (72dpi) for FOP */ - public static final float DEFAULT_TARGET_RESOLUTION = 72.0f; //dpi - - /** Defines the default source resolution (72dpi) for FOP */ - private static final float DEFAULT_SOURCE_RESOLUTION = 72.0f; //dpi - - /** Defines the default page-height */ - private static final String DEFAULT_PAGE_HEIGHT = "11in"; - - /** Defines the default page-width */ - private static final String DEFAULT_PAGE_WIDTH = "8.26in"; - - /** Defines if FOP should use strict validation for FO and user config */ - private static final boolean DEFAULT_STRICT_FO_VALIDATION = true; - - /** Defines if FOP should validate the user config strictly */ - private static final boolean DEFAULT_STRICT_USERCONFIG_VALIDATION = true; - - /** Defines if FOP should use an alternative rule to determine text indents */ - private static final boolean DEFAULT_BREAK_INDENT_INHERITANCE = false; - /** logger instance */ private static Log log = LogFactory.getLog(FopFactory.class); @@ -100,7 +79,7 @@ public class FopFactory { = new ContentHandlerFactoryRegistry(); /** Our default resolver if none is set */ - private URIResolver foURIResolver = new FOURIResolver(); + private URIResolver foURIResolver = null; /** A user settable URI Resolver */ private URIResolver uriResolver = null; @@ -108,22 +87,23 @@ public class FopFactory { /** The resolver for user-supplied hyphenation patterns */ private HyphenationTreeResolver hyphResolver; + /** Image factory for creating fop image objects */ private ImageFactory imageFactory = new ImageFactory(); - /** user configuration */ - private Configuration userConfig = null; - + /** Configuration layer used to configure fop */ + private FopFactoryConfigurator config = null; + /** * The base URL for all URL resolutions, especially for * external-graphics. */ - private String baseURL; + private String base = null; /** The base URL for all font URL resolutions. */ - private String fontBaseURL; + private String fontBase = null; /** The base URL for all hyphen URL resolutions. */ - private String hyphenBaseURL; + private String hyphenBase = null; /** * FOP has the ability, for some FO's, to continue processing even if the @@ -131,32 +111,36 @@ public class FopFactory { * behavior for FOP. However, this flag, if set, provides the user the * ability for FOP to halt on all content model violations if desired. */ - private boolean strictFOValidation = DEFAULT_STRICT_FO_VALIDATION; + private boolean strictFOValidation = FopFactoryConfigurator.DEFAULT_STRICT_FO_VALIDATION; /** * FOP will validate the contents of the user configuration strictly * (e.g. base-urls and font urls/paths). */ - private boolean strictUserConfigValidation = DEFAULT_STRICT_USERCONFIG_VALIDATION; - + private boolean strictUserConfigValidation + = FopFactoryConfigurator.DEFAULT_STRICT_USERCONFIG_VALIDATION; + + /** Font cache to speed up auto-font configuration (null if disabled) */ + private FontCache fontCache = null; + /** Allows enabling kerning on the base 14 fonts, default is false */ private boolean enableBase14Kerning = false; /** Source resolution in dpi */ - private float sourceResolution = DEFAULT_SOURCE_RESOLUTION; + private float sourceResolution = FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION; /** Target resolution in dpi */ - private float targetResolution = DEFAULT_TARGET_RESOLUTION; + private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; /** Page height */ - private String pageHeight = DEFAULT_PAGE_HEIGHT; + private String pageHeight = FopFactoryConfigurator.DEFAULT_PAGE_HEIGHT; /** Page width */ - private String pageWidth = DEFAULT_PAGE_WIDTH; + private String pageWidth = FopFactoryConfigurator.DEFAULT_PAGE_WIDTH; /** @see #setBreakIndentInheritanceOnReferenceAreaBoundary(boolean) */ private boolean breakIndentInheritanceOnReferenceAreaBoundary - = DEFAULT_BREAK_INDENT_INHERITANCE; + = FopFactoryConfigurator.DEFAULT_BREAK_INDENT_INHERITANCE; /** Optional overriding LayoutManagerMaker */ private LayoutManagerMaker lmMakerOverride = null; @@ -165,14 +149,17 @@ public class FopFactory { /** Map with cached ICC based ColorSpace objects. */ private Map colorSpaceMap = null; - + /** * Main constructor. */ protected FopFactory() { + this.config = new FopFactoryConfigurator(this); this.elementMappingRegistry = new ElementMappingRegistry(this); + this.foURIResolver = new FOURIResolver(validateUserConfigStrictly()); // Use a synchronized Map - I am not really sure this is needed, but better safe than sorry. this.colorSpaceMap = Collections.synchronizedMap(new java.util.HashMap()); + setUseCache(FopFactoryConfigurator.DEFAULT_USE_CACHE); } /** @@ -336,11 +323,42 @@ public class FopFactory { } /** + * cleans the base url + * @param base + * @return + * @throws MalformedURLException + * @throws URISyntaxException + */ + private String checkBaseURL(String base) throws MalformedURLException { + if (!base.endsWith("/")) { + // The behavior described by RFC 3986 regarding resolution of relative + // references may be misleading for normal users: + // file://path/to/resources + myResource.res -> file://path/to/myResource.res + // file://path/to/resources/ + myResource.res -> file://path/to/resources/myResource.res + // We assume that even when the ending slash is missing, users have the second + // example in mind + base += "/"; + } + File dir = new File(base); + try { + base = (dir.isDirectory() ? dir.toURL() : new URL(base)).toExternalForm(); + } catch (MalformedURLException mfue) { + if (strictUserConfigValidation) { + throw mfue; + } + log.error(mfue.getMessage()); + } + return base; + } + + /** * Sets the base URL. - * @param baseURL base URL + * @param base base URL + * @throws MalformedURLException + * @throws URISyntaxException */ - void setBaseURL(String baseURL) { - this.baseURL = baseURL; + public void setBaseURL(String base) throws MalformedURLException { + this.base = checkBaseURL(base); } /** @@ -348,42 +366,46 @@ public class FopFactory { * @return the base URL */ public String getBaseURL() { - return this.baseURL; + return this.base; } - + /** * Sets the font base URL. - * @param fontBaseURL font base URL + * @param fontBase font base URL + * @throws MalformedURLException + * @throws URISyntaxException */ - public void setFontBaseURL(String fontBaseURL) { - this.fontBaseURL = fontBaseURL; + public void setFontBaseURL(String fontBase) throws MalformedURLException { + this.fontBase = checkBaseURL(fontBase); } /** @return the font base URL */ public String getFontBaseURL() { - return this.fontBaseURL; + return this.fontBase; } /** @return the hyphen base URL */ public String getHyphenBaseURL() { - return hyphenBaseURL; + return this.hyphenBase; } /** * Sets the hyphen base URL. - * @param hyphenBaseURL hythen base URL - */ - public void setHyphenBaseURL(final String hyphenBaseURL) { - if (hyphenBaseURL != null) { + * @param hyphenBase hythen base URL + * @throws MalformedURLException + * @throws URISyntaxException + * */ + public void setHyphenBaseURL(final String hyphenBase) throws MalformedURLException { + if (hyphenBase != null) { this.hyphResolver = new HyphenationTreeResolver() { public Source resolve(String href) { - return resolveURI(href, hyphenBaseURL); + return resolveURI(href, hyphenBase); } }; } - this.hyphenBaseURL = hyphenBaseURL; + this.hyphenBase = checkBaseURL(hyphenBase); } - + /** * Sets the URI Resolver. It is used for resolving factory-level URIs like hyphenation * patterns and as backup for URI resolution performed during a rendering run. @@ -483,8 +505,10 @@ public class FopFactory { */ public void setSourceResolution(float dpi) { this.sourceResolution = dpi; - log.info("source-resolution set to: " + sourceResolution - + "dpi (px2mm=" + getSourcePixelUnitToMillimeter() + ")"); + if (log.isDebugEnabled()) { + log.debug("source-resolution set to: " + sourceResolution + + "dpi (px2mm=" + getSourcePixelUnitToMillimeter() + ")"); + } } /** @return the resolution for resolution-dependant output */ @@ -538,7 +562,9 @@ public class FopFactory { */ public void setPageHeight(String pageHeight) { this.pageHeight = pageHeight; - log.info("Default page-height set to: " + pageHeight); + if (log.isDebugEnabled()) { + log.debug("Default page-height set to: " + pageHeight); + } } /** @@ -559,7 +585,9 @@ public class FopFactory { */ public void setPageWidth(String pageWidth) { this.pageWidth = pageWidth; - log.info("Default page-width set to: " + pageWidth); + if (log.isDebugEnabled()) { + log.debug("Default page-width set to: " + pageWidth); + } } /** @@ -595,7 +623,7 @@ public class FopFactory { public Set getIgnoredNamespace() { return Collections.unmodifiableSet(this.ignoredNamespaces); } - + //------------------------------------------- Configuration stuff /** @@ -605,14 +633,9 @@ public class FopFactory { * @throws SAXException if a parsing error occurs */ public void setUserConfig(File userConfigFile) throws SAXException, IOException { - try { - DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); - setUserConfig(cfgBuilder.buildFromFile(userConfigFile)); - } catch (ConfigurationException e) { - throw new FOPException(e); - } + config.setUserConfig(userConfigFile); } - + /** * Set the user configuration from an URI. * @param uri the URI to the configuration file @@ -620,12 +643,7 @@ public class FopFactory { * @throws SAXException if a parsing error occurs */ public void setUserConfig(String uri) throws SAXException, IOException { - try { - DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); - setUserConfig(cfgBuilder.build(uri)); - } catch (ConfigurationException e) { - throw new FOPException(e); - } + config.setUserConfig(uri); } /** @@ -634,12 +652,7 @@ public class FopFactory { * @throws FOPException if a configuration problem occurs */ public void setUserConfig(Configuration userConfig) throws FOPException { - this.userConfig = userConfig; - try { - configure(userConfig); - } catch (ConfigurationException e) { - throw new FOPException(e); - } + config.setUserConfig(userConfig); } /** @@ -647,162 +660,54 @@ public class FopFactory { * @return the user configuration */ public Configuration getUserConfig() { - return userConfig; + return config.getUserConfig(); } /** - * Returns the configuration subtree for a specific renderer. - * @param mimeType MIME type of the renderer - * @return the requested configuration subtree, null if there's no configuration + * Is the user configuration to be validated? + * @param strictUserConfigValidation strict user config validation */ - public Configuration getUserRendererConfig(String mimeType) { - if (userConfig == null || mimeType == null) { - return null; - } - - Configuration userRendererConfig = null; - - Configuration[] cfgs - = userConfig.getChild("renderers").getChildren("renderer"); - for (int i = 0; i < cfgs.length; ++i) { - Configuration child = cfgs[i]; - try { - if (child.getAttribute("mime").equals(mimeType)) { - userRendererConfig = child; - break; - } - } catch (ConfigurationException e) { - // silently pass over configurations without mime type - } - } - log.debug((userRendererConfig == null ? "No u" : "U") - + "ser configuration found for MIME type " + mimeType); - return userRendererConfig; + public void setStrictUserConfigValidation(boolean strictUserConfigValidation) { + this.strictUserConfigValidation = strictUserConfigValidation; } /** - * Initializes user agent settings from the user configuration - * file, if present: baseURL, resolution, default page size,... - * - * @throws ConfigurationException when there is an entry that - * misses the required attribute - * Configures the FopFactory. - * @param cfg Avalon Configuration Object - * @see org.apache.avalon.framework.configuration.Configurable - */ - public void configure(Configuration cfg) throws ConfigurationException { - log.info("Initializing FopFactory Configuration"); - - if (cfg.getChild("strict-configuration", false) != null) { - this.strictUserConfigValidation - = cfg.getChild("strict-configuration").getValueAsBoolean(); - } - if (cfg.getChild("strict-validation", false) != null) { - this.strictFOValidation = cfg.getChild("strict-validation").getValueAsBoolean(); - } - if (cfg.getChild("base", false) != null) { - try { - setBaseURL(getBaseURLfromConfig(cfg, "base")); - } catch (ConfigurationException e) { - if (strictUserConfigValidation) { - throw e; - } - log.error(e.getMessage()); - } - } - if (cfg.getChild("font-base", false) != null) { - try { - setFontBaseURL(getBaseURLfromConfig(cfg, "font-base")); - } catch (ConfigurationException e) { - if (strictUserConfigValidation) { - throw e; - } - log.error(e.getMessage()); - } - } - if (cfg.getChild("hyphenation-base", false) != null) { - try { - setHyphenBaseURL(getBaseURLfromConfig(cfg, "hyphenation-base")); - } catch (ConfigurationException e) { - if (strictUserConfigValidation) { - throw e; - } - log.error(e.getMessage()); - } - } - if (cfg.getChild("source-resolution", false) != null) { - setSourceResolution( - cfg.getChild("source-resolution").getValueAsFloat(DEFAULT_SOURCE_RESOLUTION)); - } - if (cfg.getChild("target-resolution", false) != null) { - setTargetResolution( - cfg.getChild("target-resolution").getValueAsFloat(DEFAULT_TARGET_RESOLUTION)); - } - if (cfg.getChild("break-indent-inheritance", false) != null) { - setBreakIndentInheritanceOnReferenceAreaBoundary( - cfg.getChild("break-indent-inheritance").getValueAsBoolean()); - } - Configuration pageConfig = cfg.getChild("default-page-settings"); - if (pageConfig.getAttribute("height", null) != null) { - setPageHeight(pageConfig.getAttribute("height", DEFAULT_PAGE_HEIGHT)); - } - if (pageConfig.getAttribute("width", null) != null) { - setPageWidth(pageConfig.getAttribute("width", DEFAULT_PAGE_WIDTH)); - } + * Is the user configuration to be validated? + * @return if the user configuration should be validated + */ + public boolean validateUserConfigStrictly() { + return this.strictUserConfigValidation; } + //------------------------------------------- Cache related stuff + /** - * Retrieves and verifies a base URL. - * @param cfg The Configuration object to retrieve the base URL from - * @param name the element name for the base URL - * @return the requested base URL or null if not available - * @throws ConfigurationException - */ - public static String getBaseURLfromConfig(Configuration cfg, String name) - throws ConfigurationException { - if (cfg.getChild(name, false) != null) { - try { - String cfgBasePath = cfg.getChild(name).getValue(null); - if (cfgBasePath != null) { - // Is the path a dirname? - File dir = new File(cfgBasePath); -// if (!dir.exists()) { -// throw new ConfigurationException("Base URL '" + name -// + "' references non-existent resource '" -// + cfgBasePath + "'"); -// } else if (dir.isDirectory()) { - if (dir.isDirectory()) { - // Yes, convert it into a URL - cfgBasePath = dir.toURL().toExternalForm(); - } - // Otherwise, this is already a URL - } - log.info(name + " set to: " + cfgBasePath); - return cfgBasePath; - } catch (MalformedURLException mue) { - throw new ConfigurationException("Base URL '" + name - + "' in user config is malformed!"); + * Whether or not to cache results of font triplet detection/auto-config + * @param useCache use cache or not + */ + public void setUseCache(boolean useCache) { + if (useCache) { + this.fontCache = FontCache.load(); + if (this.fontCache == null) { + this.fontCache = new FontCache(); } + } else { + this.fontCache = null; } - return null; } /** - * Is the user configuration to be validated? - * @param strictUserConfigValidation strict user config validation + * Cache results of font triplet detection/auto-config? + * @return whether this factory is uses the cache */ - public void setStrictUserConfigValidation(boolean strictUserConfigValidation) { - this.strictUserConfigValidation = strictUserConfigValidation; + public boolean useCache() { + return (this.fontCache != null); } - /** - * Is the user configuration to be validated? - * @return if the user configuration should be validated - */ - public boolean validateUserConfigStrictly() { - return this.strictUserConfigValidation; + public FontCache getFontCache() { + return this.fontCache; } - + //------------------------------------------- URI resolution /** @@ -810,26 +715,29 @@ public class FopFactory { * Will use the configured resolver and if not successful fall back * to the default resolver. * @param uri URI to access - * @param base the base URI to resolve against + * @param baseUri the base URI to resolve against * @return A {@link javax.xml.transform.Source} object, or null if the URI * cannot be resolved. * @see org.apache.fop.apps.FOURIResolver */ - public Source resolveURI(String uri, String base) { + public Source resolveURI(String uri, String baseUri) { Source source = null; //RFC 2397 data URLs don't need to be resolved, just decode them. boolean bypassURIResolution = uri.startsWith("data:"); if (!bypassURIResolution && uriResolver != null) { try { - source = uriResolver.resolve(uri, base); + source = uriResolver.resolve(uri, baseUri); } catch (TransformerException te) { log.error("Attempt to resolve URI '" + uri + "' failed: ", te); + if (validateUserConfigStrictly()) { + return null; + } } } if (source == null) { // URI Resolver not configured or returned null, use default resolver try { - source = foURIResolver.resolve(uri, base); + source = foURIResolver.resolve(uri, baseUri); } catch (TransformerException te) { log.error("Attempt to resolve URI '" + uri + "' failed: ", te); } @@ -846,18 +754,18 @@ public class FopFactory { * The FOP URI resolver is used to try and locate the ICC file. * If that fails null is returned. * - * @param base a base URI to resolve relative URIs + * @param baseUri a base URI to resolve relative URIs * @param iccProfileSrc ICC Profile source to return a ColorSpace for * @return ICC ColorSpace object or null if ColorSpace could not be created */ - public ColorSpace getColorSpace(String base, String iccProfileSrc) { + public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) { ColorSpace colorSpace = null; - if (!this.colorSpaceMap.containsKey(base + iccProfileSrc)) { + if (!this.colorSpaceMap.containsKey(baseUri + iccProfileSrc)) { try { ICC_Profile iccProfile = null; // First attempt to use the FOP URI resolver to locate the ICC // profile - Source src = this.resolveURI(iccProfileSrc, base); + Source src = this.resolveURI(iccProfileSrc, baseUri); if (src != null && src instanceof StreamSource) { // FOP URI resolver found ICC profile - create ICC profile // from the Source @@ -882,16 +790,16 @@ public class FopFactory { if (colorSpace != null) { // Put in cache (not when VM resolved it as we can't control - this.colorSpaceMap.put(base + iccProfileSrc, colorSpace); + this.colorSpaceMap.put(baseUri + iccProfileSrc, colorSpace); } else { // TODO To avoid an excessive amount of warnings perhaps // register a null ColorMap in the colorSpaceMap log.warn("Color profile '" + iccProfileSrc + "' not found."); } } else { - colorSpace = (ColorSpace) this.colorSpaceMap.get(base + colorSpace = (ColorSpace) this.colorSpaceMap.get(baseUri + iccProfileSrc); } return colorSpace; - } + } } diff --git a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java new file mode 100644 index 000000000..d708cd989 --- /dev/null +++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java @@ -0,0 +1,241 @@ +/* + * 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.apps; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.util.LogUtil; +import org.xml.sax.SAXException; + +/** + * FopFactory configurator + */ +public class FopFactoryConfigurator { + + /** Defines if FOP should use an alternative rule to determine text indents */ + public static final boolean DEFAULT_BREAK_INDENT_INHERITANCE = false; + + /** Defines if FOP should validate the user config strictly */ + public static final boolean DEFAULT_STRICT_USERCONFIG_VALIDATION = true; + + /** Defines if FOP should use strict validation for FO and user config */ + public static final boolean DEFAULT_STRICT_FO_VALIDATION = true; + + /** Defines the default page-width */ + public static final String DEFAULT_PAGE_WIDTH = "8.26in"; + + /** Defines the default page-height */ + public static final String DEFAULT_PAGE_HEIGHT = "11in"; + + /** Defines the default source resolution (72dpi) for FOP */ + public static final float DEFAULT_SOURCE_RESOLUTION = 72.0f; //dpi + + /** Defines the default target resolution (72dpi) for FOP */ + public static final float DEFAULT_TARGET_RESOLUTION = 72.0f; //dpi + + /** Use cache (record previously detected font triplet info) */ + public static final boolean DEFAULT_USE_CACHE = true; + + /** logger instance */ + private final Log log = LogFactory.getLog(FopFactoryConfigurator.class); + + /** Fop factory */ + private FopFactory factory = null; + + /** Fop factory configuration */ + private Configuration cfg = null; + + /** + * Default constructor + * @param factory fop factory + */ + public FopFactoryConfigurator(FopFactory factory) { + super(); + this.factory = factory; + } + + /** + * Initializes user agent settings from the user configuration + * file, if present: baseURL, resolution, default page size,... + * @param factory fop factory + * @throws FOPException fop exception + */ + public void configure(FopFactory factory) throws FOPException { + if (log.isDebugEnabled()) { + log.debug("Initializing FopFactory Configuration"); + } + + // strict configuration + if (cfg.getChild("strict-configuration", false) != null) { + try { + factory.setStrictUserConfigValidation( + cfg.getChild("strict-configuration").getValueAsBoolean()); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, false); + } + } + boolean strict = factory.validateUserConfigStrictly(); + + // strict fo validation + if (cfg.getChild("strict-validation", false) != null) { + try { + factory.setStrictValidation( + cfg.getChild("strict-validation").getValueAsBoolean()); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + } + } + + // base definitions for relative path resolution + if (cfg.getChild("base", false) != null) { + try { + factory.setBaseURL( + cfg.getChild("base").getValue(null)); + } catch (MalformedURLException mfue) { + LogUtil.handleException(log, mfue, strict); + } + } + if (cfg.getChild("font-base", false) != null) { + try { + factory.setFontBaseURL( + cfg.getChild("font-base").getValue(null)); + } catch (MalformedURLException mfue) { + LogUtil.handleException(log, mfue, strict); + } + } + if (cfg.getChild("hyphenation-base", false) != null) { + try { + factory.setHyphenBaseURL( + cfg.getChild("hyphenation-base").getValue(null)); + } catch (MalformedURLException mfue) { + LogUtil.handleException(log, mfue, strict); + } + } + + // renderer options + if (cfg.getChild("source-resolution", false) != null) { + factory.setSourceResolution( + cfg.getChild("source-resolution").getValueAsFloat( + FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION)); + if (log.isDebugEnabled()) { + log.debug("source-resolution set to: " + factory.getSourceResolution() + + "dpi (px2mm=" + factory.getSourcePixelUnitToMillimeter() + ")"); + } + } + if (cfg.getChild("target-resolution", false) != null) { + factory.setTargetResolution( + cfg.getChild("target-resolution").getValueAsFloat( + FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)); + if (log.isDebugEnabled()) { + log.debug("target-resolution set to: " + factory.getTargetResolution() + + "dpi (px2mm=" + factory.getTargetPixelUnitToMillimeter() + + ")"); + } + } + if (cfg.getChild("break-indent-inheritance", false) != null) { + try { + factory.setBreakIndentInheritanceOnReferenceAreaBoundary( + cfg.getChild("break-indent-inheritance").getValueAsBoolean()); + } catch (ConfigurationException e) { + LogUtil.handleException(log, e, strict); + } + } + Configuration pageConfig = cfg.getChild("default-page-settings"); + if (pageConfig.getAttribute("height", null) != null) { + factory.setPageHeight( + pageConfig.getAttribute("height", FopFactoryConfigurator.DEFAULT_PAGE_HEIGHT)); + if (log.isInfoEnabled()) { + log.info("Default page-height set to: " + factory.getPageHeight()); + } + } + if (pageConfig.getAttribute("width", null) != null) { + factory.setPageWidth( + pageConfig.getAttribute("width", FopFactoryConfigurator.DEFAULT_PAGE_WIDTH)); + if (log.isInfoEnabled()) { + log.info("Default page-width set to: " + factory.getPageWidth()); + } + } + + // caching (fonts) + if (cfg.getChild("use-cache", false) != null) { + try { + factory.setUseCache( + cfg.getChild("use-cache").getValueAsBoolean()); + } catch (ConfigurationException mfue) { + LogUtil.handleException(log, mfue, strict); + } + } + } + + /** + * Set the user configuration. + * @param userConfigFile the configuration file + * @throws IOException if an I/O error occurs + * @throws SAXException if a parsing error occurs + */ + public void setUserConfig(File userConfigFile) throws SAXException, IOException { + try { + DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); + setUserConfig(cfgBuilder.buildFromFile(userConfigFile)); + } catch (ConfigurationException e) { + throw new FOPException(e); + } + } + + /** + * Set the user configuration from an URI. + * @param uri the URI to the configuration file + * @throws IOException if an I/O error occurs + * @throws SAXException if a parsing error occurs + */ + public void setUserConfig(String uri) throws SAXException, IOException { + try { + DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); + setUserConfig(cfgBuilder.build(uri)); + } catch (ConfigurationException e) { + throw new FOPException(e); + } + } + + /** + * Set the user configuration. + * @param cfg avalon configuration + * @throws FOPException if a configuration problem occurs + */ + public void setUserConfig(Configuration cfg) throws FOPException { + this.cfg = cfg; + configure(this.factory); + } + + /** + * Get the avalon user configuration. + * @return the user configuration + */ + public Configuration getUserConfig() { + return this.cfg; + } +} |