blob: 907895c9964bcff65d714beaed5c7d09b71dff06 [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.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.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} */
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 <code>true</code> to enable accessibility, <code>false</code> 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.
* <p>
* 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()}.
* <p>
* 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.
* <p>
* 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()}.
* <p>
* 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
*/
public void setFontBaseURL(String fontBase) throws MalformedURLException {
getFontManager().setFontBaseURL(fontBase);
}
/**
* @return the font base URL
* @deprecated use getFontManager().setFontBaseURL(fontBase) instead
*/
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.
* <p>
* 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
*/
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
*/
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
*/
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
*/
public boolean useCache() {
return getFontManager().useCache();
}
/**
* Returns the font cache instance used by this factory.
* @return the font cache
* @deprecated use getFontManager().getFontCache() instead
*/
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.
*
* @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 baseUri, String iccProfileSrc) {
return colorSpaceCache.get(baseUri, iccProfileSrc);
}
}