/* * 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.awt.color.ColorSpace; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; import java.util.Collection; import java.util.Collections; import java.util.Set; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import org.xml.sax.SAXException; import org.apache.avalon.framework.configuration.Configuration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.java2d.color.RenderingIntent; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fonts.FontCache; import org.apache.fop.fonts.FontManager; import org.apache.fop.hyphenation.HyphenationTreeResolver; import org.apache.fop.layoutmgr.LayoutManagerMaker; import org.apache.fop.render.ImageHandlerRegistry; import org.apache.fop.render.RendererFactory; import org.apache.fop.render.XMLHandlerRegistry; import org.apache.fop.util.ColorSpaceCache; import org.apache.fop.util.ContentHandlerFactoryRegistry; /** * Factory class which instantiates new Fop and FOUserAgent instances. This * class also holds environmental information and configuration used by FOP. * Information that may potentially be different for each rendering run can be * found and managed in the FOUserAgent. */ public class FopFactory implements ImageContext { /** logger instance */ private static Log log = LogFactory.getLog(FopFactory.class); /** Factory for Renderers and FOEventHandlers */ private RendererFactory rendererFactory; /** Registry for XML handlers */ private XMLHandlerRegistry xmlHandlers; /** Registry for image handlers */ private ImageHandlerRegistry imageHandlers; /** The registry for ElementMapping instances */ private ElementMappingRegistry elementMappingRegistry; /** The registry for ContentHandlerFactory instance */ private ContentHandlerFactoryRegistry contentHandlerFactoryRegistry = new ContentHandlerFactoryRegistry(); /** The resolver for user-supplied hyphenation patterns */ private HyphenationTreeResolver hyphResolver = null; private ColorSpaceCache colorSpaceCache = null; /** Image manager for loading and caching image objects */ private ImageManager imageManager; /** Font manager for font substitution, autodetection and caching **/ private FontManager fontManager; /** Configuration layer used to configure fop */ private FopFactoryConfigurator config = null; /** * The base URL for all URL resolutions, especially for * external-graphics. */ private String base = null; /** * Controls if accessibility is turned on or off */ private boolean accessibility = false; /** The base URL for all hyphen URL resolutions. */ private String hyphenBase = null; /** * FOP has the ability, for some FO's, to continue processing even if the * input XSL violates that FO's content model. This is the default * 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 = 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 = FopFactoryConfigurator.DEFAULT_STRICT_USERCONFIG_VALIDATION; /** Source resolution in dpi */ private float sourceResolution = FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION; /** Target resolution in dpi */ private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; /** Page height */ private String pageHeight = FopFactoryConfigurator.DEFAULT_PAGE_HEIGHT; /** Page width */ private String pageWidth = FopFactoryConfigurator.DEFAULT_PAGE_WIDTH; /** @see #setBreakIndentInheritanceOnReferenceAreaBoundary(boolean) */ private boolean breakIndentInheritanceOnReferenceAreaBoundary = FopFactoryConfigurator.DEFAULT_BREAK_INDENT_INHERITANCE; /** Optional overriding LayoutManagerMaker */ private LayoutManagerMaker lmMakerOverride = null; private Set ignoredNamespaces; private FOURIResolver foURIResolver; /** * Main constructor. */ protected FopFactory() { this.config = new FopFactoryConfigurator(this); this.elementMappingRegistry = new ElementMappingRegistry(this); this.foURIResolver = new FOURIResolver(validateUserConfigStrictly()); this.fontManager = new FontManager() { /** {@inheritDoc} */ @Override public void setFontBaseURL(String fontBase) throws MalformedURLException { super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase)); } }; this.colorSpaceCache = new ColorSpaceCache(foURIResolver); this.imageManager = new ImageManager(this); this.rendererFactory = new RendererFactory(); this.xmlHandlers = new XMLHandlerRegistry(); this.imageHandlers = new ImageHandlerRegistry(); this.ignoredNamespaces = new java.util.HashSet(); } /** * Returns a new FopFactory instance. * @return the requested FopFactory instance. */ public static FopFactory newInstance() { return new FopFactory(); } /** * Returns a new FOUserAgent instance. Use the FOUserAgent to configure special values that * are particular to a rendering run. Don't reuse instances over multiple rendering runs but * instead create a new one each time and reuse the FopFactory. * @return the newly created FOUserAgent instance initialized with default values * @throws FOPException */ public FOUserAgent newFOUserAgent() { FOUserAgent userAgent = new FOUserAgent(this); return userAgent; } /** * Sets accessibility support. * * @param value true to enable accessibility, false otherwise */ void setAccessibility(boolean value) { this.accessibility = value; } boolean isAccessibilityEnabled() { return accessibility; } /** * Returns a new {@link Fop} instance. FOP will be configured with a default user agent * instance. *

* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can * use the constants defined in {@link MimeConstants}. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @return the new Fop instance * @throws FOPException when the constructor fails */ public Fop newFop(String outputFormat) throws FOPException { return newFop(outputFormat, newFOUserAgent()); } /** * Returns a new {@link Fop} instance. Use this factory method if you want to configure this * very rendering run, i.e. if you want to set some metadata like the title and author of the * document you want to render. In that case, create a new {@link FOUserAgent} * instance using {@link #newFOUserAgent()}. *

* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can * use the constants defined in {@link MimeConstants}. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @param userAgent the user agent that will be used to control the rendering run * @return the new Fop instance * @throws FOPException when the constructor fails */ public Fop newFop(String outputFormat, FOUserAgent userAgent) throws FOPException { return newFop(outputFormat, userAgent, null); } /** * Returns a new {@link Fop} instance. FOP will be configured with a default user agent * instance. Use this factory method if your output type requires an output stream. *

* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can * use the constants defined in {@link MimeConstants}. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @param stream the output stream * @return the new Fop instance * @throws FOPException when the constructor fails */ public Fop newFop(String outputFormat, OutputStream stream) throws FOPException { return newFop(outputFormat, newFOUserAgent(), stream); } /** * Returns a new {@link Fop} instance. Use this factory method if your output type * requires an output stream and you want to configure this very rendering run, * i.e. if you want to set some metadata like the title and author of the document * you want to render. In that case, create a new {@link FOUserAgent} instance * using {@link #newFOUserAgent()}. *

* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can * use the constants defined in {@link MimeConstants}. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @param userAgent the user agent that will be used to control the rendering run * @param stream the output stream * @return the new Fop instance * @throws FOPException when the constructor fails */ public Fop newFop(String outputFormat, FOUserAgent userAgent, OutputStream stream) throws FOPException { if (userAgent == null) { throw new NullPointerException("The userAgent parameter must not be null!"); } return new Fop(outputFormat, userAgent, stream); } /** * Returns a new {@link Fop} instance. Use this factory method if you want to supply your * own {@link org.apache.fop.render.Renderer Renderer} or * {@link org.apache.fop.fo.FOEventHandler FOEventHandler} * instance instead of the default ones created internally by FOP. * @param userAgent the user agent that will be used to control the rendering run * @return the new Fop instance * @throws FOPException when the constructor fails */ public Fop newFop(FOUserAgent userAgent) throws FOPException { if (userAgent.getRendererOverride() == null && userAgent.getFOEventHandlerOverride() == null && userAgent.getDocumentHandlerOverride() == null) { throw new IllegalStateException("An overriding renderer," + " FOEventHandler or IFDocumentHandler must be set on the user agent" + " when this factory method is used!"); } return newFop(null, userAgent); } /** @return the RendererFactory */ public RendererFactory getRendererFactory() { return this.rendererFactory; } /** @return the XML handler registry */ public XMLHandlerRegistry getXMLHandlerRegistry() { return this.xmlHandlers; } /** @return the image handler registry */ public ImageHandlerRegistry getImageHandlerRegistry() { return this.imageHandlers; } /** @return the element mapping registry */ public ElementMappingRegistry getElementMappingRegistry() { return this.elementMappingRegistry; } /** @return the content handler factory registry */ public ContentHandlerFactoryRegistry getContentHandlerFactoryRegistry() { return this.contentHandlerFactoryRegistry; } /** * Returns the image manager. * @return the image manager */ public ImageManager getImageManager() { return this.imageManager; } /** * Add the element mapping with the given class name. * @param elementMapping the class name representing the element mapping. */ public void addElementMapping(ElementMapping elementMapping) { this.elementMappingRegistry.addElementMapping(elementMapping); } /** * Sets an explicit LayoutManagerMaker instance which overrides the one * defined by the AreaTreeHandler. * @param lmMaker the LayoutManagerMaker instance */ public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) { this.lmMakerOverride = lmMaker; } /** * Returns the overriding LayoutManagerMaker instance, if any. * @return the overriding LayoutManagerMaker or null */ public LayoutManagerMaker getLayoutManagerMakerOverride() { return this.lmMakerOverride; } /** * Sets the base URL. * @param base the base URL * @throws MalformedURLException if there's a problem with a file URL */ public void setBaseURL(String base) throws MalformedURLException { this.base = foURIResolver.checkBaseURL(base); } /** * Returns the base URL. * @return the base URL */ public String getBaseURL() { return this.base; } /** * Sets the font base URL. * @param fontBase font base URL * @throws MalformedURLException if there's a problem with a file URL * @deprecated use getFontManager().setFontBaseURL(fontBase) instead */ @Deprecated public void setFontBaseURL(String fontBase) throws MalformedURLException { getFontManager().setFontBaseURL(fontBase); } /** * @return the font base URL * @deprecated use getFontManager().setFontBaseURL(fontBase) instead */ @Deprecated public String getFontBaseURL() { return getFontManager().getFontBaseURL(); } /** @return the hyphen base URL */ public String getHyphenBaseURL() { return this.hyphenBase; } /** * Sets the hyphen base URL. * @param hyphenBase hythen base URL * @throws MalformedURLException if there's a problem with a file URL * */ public void setHyphenBaseURL(final String hyphenBase) throws MalformedURLException { if (hyphenBase != null) { setHyphenationTreeResolver( new HyphenationTreeResolver() { public Source resolve(String href) { return resolveURI(href, hyphenBase); } }); } this.hyphenBase = foURIResolver.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. * @param uriResolver the new URI resolver */ public void setURIResolver(URIResolver uriResolver) { foURIResolver.setCustomURIResolver(uriResolver); } /** * Returns the URI Resolver. * @return the URI Resolver */ public URIResolver getURIResolver() { return foURIResolver; } /** * Returns the FO URI Resolver. * @return the FO URI Resolver */ public FOURIResolver getFOURIResolver() { return foURIResolver; } /** @return the HyphenationTreeResolver for resolving user-supplied hyphenation patterns. */ public HyphenationTreeResolver getHyphenationTreeResolver() { return this.hyphResolver; } /** * Sets the HyphenationTreeResolver to be used for resolving user-supplied hyphenation files. * @param hyphResolver the HyphenationTreeResolver instance */ public void setHyphenationTreeResolver(HyphenationTreeResolver hyphResolver) { this.hyphResolver = hyphResolver; } /** * Activates strict XSL content model validation for FOP * Default is false (FOP will continue processing where it can) * @param validateStrictly true to turn on strict validation */ public void setStrictValidation(boolean validateStrictly) { this.strictFOValidation = validateStrictly; } /** * Returns whether FOP is strictly validating input XSL * @return true of strict validation turned on, false otherwise */ public boolean validateStrictly() { return strictFOValidation; } /** * @return true if the indent inheritance should be broken when crossing reference area * boundaries (for more info, see the javadoc for the relative member variable) */ public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() { return breakIndentInheritanceOnReferenceAreaBoundary; } /** * Controls whether to enable a feature that breaks indent inheritance when crossing * reference area boundaries. *

* This flag controls whether FOP will enable special code that breaks property * inheritance for start-indent and end-indent when the evaluation of the inherited * value would cross a reference area. This is described under * http://wiki.apache.org/xmlgraphics-fop/IndentInheritance as is intended to * improve interoperability with commercial FO implementations and to produce * results that are more in line with the expectation of unexperienced FO users. * Note: Enabling this features violates the XSL specification! * @param value true to enable the feature */ public void setBreakIndentInheritanceOnReferenceAreaBoundary(boolean value) { this.breakIndentInheritanceOnReferenceAreaBoundary = value; } /** * @return true if kerning on base 14 fonts is enabled * @deprecated use getFontManager().isBase14KerningEnabled() instead */ @Deprecated public boolean isBase14KerningEnabled() { return getFontManager().isBase14KerningEnabled(); } /** * Controls whether kerning is activated on base 14 fonts. * @param value true if kerning should be activated * @deprecated use getFontManager().setBase14KerningEnabled(boolean) instead */ @Deprecated public void setBase14KerningEnabled(boolean value) { getFontManager().setBase14KerningEnabled(value); } /** @return the resolution for resolution-dependant input */ public float getSourceResolution() { return this.sourceResolution; } /** * Returns the conversion factor from pixel units to millimeters. This * depends on the desired source resolution. * @return float conversion factor * @see #getSourceResolution() */ public float getSourcePixelUnitToMillimeter() { return 25.4f / getSourceResolution(); } /** * Sets the source resolution in dpi. This value is used to interpret the pixel size * of source documents like SVG images and bitmap images without resolution information. * @param dpi resolution in dpi */ public void setSourceResolution(float dpi) { this.sourceResolution = dpi; if (log.isDebugEnabled()) { log.debug("source-resolution set to: " + sourceResolution + "dpi (px2mm=" + getSourcePixelUnitToMillimeter() + ")"); } } /** @return the resolution for resolution-dependant output */ public float getTargetResolution() { return this.targetResolution; } /** * Returns the conversion factor from pixel units to millimeters. This * depends on the desired target resolution. * @return float conversion factor * @see #getTargetResolution() */ public float getTargetPixelUnitToMillimeter() { return 25.4f / this.targetResolution; } /** * Sets the source resolution in dpi. This value is used to interpret the pixel size * of source documents like SVG images and bitmap images without resolution information. * @param dpi resolution in dpi */ public void setTargetResolution(float dpi) { this.targetResolution = dpi; } /** * Sets the source resolution in dpi. This value is used to interpret the pixel size * of source documents like SVG images and bitmap images without resolution information. * @param dpi resolution in dpi */ public void setSourceResolution(int dpi) { setSourceResolution((float)dpi); } /** * Gets the default page-height to use as fallback, * in case page-height="auto" * * @return the page-height, as a String */ public String getPageHeight() { return this.pageHeight; } /** * Sets the page-height to use as fallback, in case * page-height="auto" * * @param pageHeight page-height as a String */ public void setPageHeight(String pageHeight) { this.pageHeight = pageHeight; if (log.isDebugEnabled()) { log.debug("Default page-height set to: " + pageHeight); } } /** * Gets the default page-width to use as fallback, * in case page-width="auto" * * @return the page-width, as a String */ public String getPageWidth() { return this.pageWidth; } /** * Sets the page-width to use as fallback, in case * page-width="auto" * * @param pageWidth page-width as a String */ public void setPageWidth(String pageWidth) { this.pageWidth = pageWidth; if (log.isDebugEnabled()) { log.debug("Default page-width set to: " + pageWidth); } } /** * Adds a namespace to the set of ignored namespaces. * If FOP encounters a namespace which it cannot handle, it issues a warning except if this * namespace is in the ignored set. * @param namespaceURI the namespace URI */ public void ignoreNamespace(String namespaceURI) { this.ignoredNamespaces.add(namespaceURI); } /** * Adds a collection of namespaces to the set of ignored namespaces. * If FOP encounters a namespace which it cannot handle, it issues a warning except if this * namespace is in the ignored set. * @param namespaceURIs the namespace URIs */ public void ignoreNamespaces(Collection namespaceURIs) { this.ignoredNamespaces.addAll(namespaceURIs); } /** * Indicates whether a namespace URI is on the ignored list. * @param namespaceURI the namespace URI * @return true if the namespace is ignored by FOP */ public boolean isNamespaceIgnored(String namespaceURI) { return this.ignoredNamespaces.contains(namespaceURI); } /** @return the set of namespaces that are ignored by FOP */ public Set getIgnoredNamespace() { return Collections.unmodifiableSet(this.ignoredNamespaces); } //------------------------------------------- Configuration stuff /** * 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 { config.setUserConfig(userConfigFile); } /** * 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 { config.setUserConfig(uri); } /** * Set the user configuration. * @param userConfig configuration * @throws FOPException if a configuration problem occurs */ public void setUserConfig(Configuration userConfig) throws FOPException { config.setUserConfig(userConfig); } /** * Get the user configuration. * @return the user configuration */ public Configuration getUserConfig() { return config.getUserConfig(); } /** * Is the user configuration to be validated? * @param strictUserConfigValidation strict user config validation */ public void setStrictUserConfigValidation(boolean strictUserConfigValidation) { this.strictUserConfigValidation = strictUserConfigValidation; this.foURIResolver.setThrowExceptions(strictUserConfigValidation); } /** * Is the user configuration to be validated? * @return if the user configuration should be validated */ public boolean validateUserConfigStrictly() { return this.strictUserConfigValidation; } //------------------------------------------- Font related stuff /** * Whether or not to cache results of font triplet detection/auto-config * @param useCache use cache or not * @deprecated use getFontManager().setUseCache(boolean) instead */ @Deprecated public void setUseCache(boolean useCache) { getFontManager().setUseCache(useCache); } /** * Cache results of font triplet detection/auto-config? * @return whether this factory is uses the cache * @deprecated use getFontManager().useCache() instead */ @Deprecated public boolean useCache() { return getFontManager().useCache(); } /** * Returns the font cache instance used by this factory. * @return the font cache * @deprecated use getFontManager().getFontCache() instead */ @Deprecated public FontCache getFontCache() { return getFontManager().getFontCache(); } /** * Returns the font manager. * @return the font manager */ public FontManager getFontManager() { return this.fontManager; } /** * Attempts to resolve the given URI. * Will use the configured resolver and if not successful fall back * to the default resolver. * @param href URI to access * @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 href, String baseUri) { Source source = null; try { source = foURIResolver.resolve(href, baseUri); } catch (TransformerException e) { log.error("Attempt to resolve URI '" + href + "' failed: ", e); } return source; } /** * Create (if needed) and return an ICC ColorSpace instance. *

* The ICC profile source is taken from the src attribute of the color-profile FO element. * If the ICC ColorSpace is not yet in the cache a new one is created and stored in the cache. *

* The FOP URI resolver is used to try and locate the ICC file. * If that fails null is returned. *

* Note: this method should not be considered as part of FOP's external API. * @param profileName the profile name * @param baseUri a base URI to resolve relative URIs * @param iccProfileSrc ICC Profile source to return a ColorSpace for * @param renderingIntent overriding rendering intent * @return ICC ColorSpace object or null if ColorSpace could not be created */ public ColorSpace getColorSpace(String profileName, String baseUri, String iccProfileSrc, RenderingIntent renderingIntent) { return colorSpaceCache.get(profileName, baseUri, iccProfileSrc, renderingIntent); } }