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/FopFactory.java | |
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/FopFactory.java')
-rw-r--r-- | src/java/org/apache/fop/apps/FopFactory.java | 364 |
1 files changed, 136 insertions, 228 deletions
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; - } + } } |