| <?xml version="1.0" standalone="no"?> |
| <!-- |
| Copyright 1999-2004 The Apache Software Foundation |
| |
| Licensed 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$ --> |
| <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" |
| "http://cvs.apache.org/viewcvs.cgi/*checkout*/xml-forrest/src/core/context/resources/schema/dtd/document-v12.dtd"> |
| |
| |
| <!-- Embedding FOP --> |
| <document> |
| <header> |
| <title>FOP: Embedding</title> |
| <subtitle>How to Embed FOP in a Java application</subtitle> |
| <version>$Revision$</version> |
| </header> |
| |
| <body> |
| <section id="overview"> |
| <title>Overview</title> |
| <p>Review <link href="running.html">Running FOP</link> for important information that applies to embedded applications as well as command-line use, such as options and performance. |
| </p> |
| <p>To embed FOP in your application, instantiate org.apache.fop.apps.Driver. |
| Once this class is |
| instantiated, methods are called to set the |
| Renderer to use |
| and the OutputStream to use to output the results of the |
| rendering (where applicable). In the case of the Renderer and |
| ElementMapping(s), the Driver may be supplied either with the |
| object itself, or the name of the class, in which case Driver will |
| instantiate the class itself. The advantage of the latter is it |
| enables runtime determination of Renderer and ElementMapping(s). |
| </p> |
| </section> |
| <section id="basics"> |
| <title>Basics</title> |
| <p>The simplest way to use Driver is to instantiate it with the |
| InputSource and OutputStream, then set the renderer desired and |
| call the run method. |
| </p> |
| <p>Here is an example use of Driver which outputs PDF: |
| </p> |
| <source><![CDATA[ |
| import org.apache.fop.apps.Driver; |
| |
| /*..*/ |
| |
| Driver driver = new Driver(new InputSource(args[0]), |
| new FileOutputStream(args[1])); |
| driver.setRenderer(Driver.RENDER_PDF); |
| driver.run();]]></source> |
| <p> |
| In the example above, args[0] contains the path to an XSL-FO file, while |
| args[1] contains a path for the target PDF file. |
| </p> |
| <section id="basic-logging"> |
| <title>Logging</title> |
| <p> |
| You also need to set up logging. Global logging for all FOP |
| processes is managed by MessageHandler. Per-instance logging |
| is handled by Driver. You want to set both using an implementation |
| of org.apache.avalon.framework.logger.Logger. See |
| <jump href="#logging">below</jump> for more information. |
| </p> |
| <p> |
| Call <code>setLogger(Logger)</code> always immediately after |
| instantiating the Driver object. See here: |
| </p> |
| <source><![CDATA[ |
| import org.apache.avalon.framework.logger.Logger; |
| import org.apache.avalon.framework.logger.ConsoleLogger; |
| |
| /*..*/ |
| |
| Driver driver = new Driver(); |
| Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO); |
| MessageHandler.setScreenLogger(logger); |
| driver.setLogger(logger);]]></source> |
| </section> |
| |
| <section id="basic-logging-new-version"> |
| <title>Logging (Upcoming FOP 1.0 Version only)</title> |
| <p> |
| Logging is handled automatically via Jakarta Commons-Logging, which uses |
| JDK logging by default. No special driver configuration is needed. |
| For specialized configuration of Commons-Logging (e.g. to use a |
| different logger or to change logging levels), please see the |
| <fork href="http://jakarta.apache.org/commons/logging/">Jakarta Commons-Logging</fork> |
| site. |
| </p> |
| </section> |
| |
| <section id="render"> |
| <title>Processing XSL-FO</title> |
| <p> |
| Once the Driver is set up, one of the <code>render()</code> methods |
| is called. Depending on whether DOM or an InputSource is being used, the |
| invocation of the method is either <code>render(Document)</code> or |
| <code>render(Parser, InputSource)</code> respectively. |
| </p> |
| <p> |
| <strong>Another possibility may be used to build the FO Tree: You can |
| call <code>getContentHandler()</code> and fire the SAX events yourself. |
| </strong> |
| You don't have to call <code>run()</code> or <code>render()</code> on the |
| Driver object if you use <code>getContentHandler()</code>. |
| </p> |
| <p>Here is an example use of Driver:</p> |
| <source><![CDATA[ |
| Driver driver = new Driver(); |
| //Setup logging here: driver.setLogger(... |
| driver.setRenderer(Driver.RENDER_PDF); |
| driver.setInputSource(new FileInputSource(args[0])); |
| driver.setOutputStream(new FileOutputStream(args[1])); |
| driver.run();]]></source> |
| </section> |
| |
| <section id="render-with-xslt"> |
| <title>Processing XSL-FO generated from XML+XSLT</title> |
| <p> |
| If you want to process XSL-FO generated from XML using XSLT we recommend |
| using standard JAXP to do the XSLT part and piping the generated SAX |
| events directly through to FOP. Here's how this would look like: |
| </p> |
| <source><![CDATA[ |
| Driver driver = new Driver(); |
| //Setup logging here: driver.setLogger(... |
| driver.setRenderer(Driver.RENDER_PDF); |
| |
| //Setup the OutputStream for FOP |
| driver.setOutputStream(new java.io.FileOutputStream(outFile)); |
| |
| //Make sure the XSL transformation's result is piped through to FOP |
| Result res = new SAXResult(driver.getContentHandler()); |
| |
| //Setup XML input |
| Source src = new StreamSource(xmlFile); |
| |
| //Setup Transformer |
| Source xsltSrc = new StreamSource(xslFile); |
| TransformerFactory transformerFactory = TransformerFactory.newInstance(); |
| Transformer transformer = transformerFactory.newTransformer(xsltSrc); |
| |
| //Start the transformation and rendering process |
| transformer.transform(src, res);]]></source> |
| <note>There's no need to call <code>run()</code> or <code>render()</code>.</note> |
| <p> |
| This may look complicated at first, but it's really just the combination of an |
| XSL transformation and a FOP run. It's also easy to comment out the FOP part |
| for debugging purposes, for example when you're tracking down a bug in your |
| stylesheet. You can easily write the XSL-FO output from the XSL transformation |
| to a file to check if that part generates the expected output. |
| </p> |
| <p> |
| For fully working examples of the above and hints to some interesting |
| possibilities, see the <link href="#examples">examples section</link> below. |
| </p> |
| </section> |
| </section> |
| <section id="logging"> |
| <title>Controlling logging</title> |
| <p> |
| Current FOP 0.20.x production uses the |
| <fork href="http://avalon.apache.org/framework/api/org/apache/avalon/framework/logger/package-summary.html">Logger package</fork> |
| from Apache Avalon Framework to do logging. See the |
| <fork href="http://avalon.apache.org/framework/">Apache Avalon Framework</fork> |
| for more information. |
| </p> |
| <p> |
| Per default FOP uses the SimpleLog which logs to System.out. If you want to do logging using a |
| logging framework (such as LogKit, Log4J or JDK 1.4 Logging) you can set a |
| different Logger implementation on the Driver object. Here's an example how you would use LogKit: |
| </p> |
| <source><![CDATA[ |
| Hierarchy hierarchy = Hierarchy.getDefaultHierarchy(); |
| PatternFormatter formatter = new PatternFormatter( |
| "[%{priority}]: %{message}\n%{throwable}" ); |
| |
| LogTarget target = null; |
| target = new StreamTarget(System.out, formatter); |
| |
| hierarchy.setDefaultLogTarget(target); |
| log = hierarchy.getLoggerFor("fop"); |
| log.setPriority(Priority.INFO); |
| |
| driver.setLogger(new org.apache.avalon.framework.logger.LogKitLogger(log));]]></source> |
| <p> |
| The LogKitLogger class implements the Logger interface so all logging calls are being redirected to LogKit. |
| More information on Jakarta LogKit can be found <fork href="http://jakarta.apache.org/avalon/logkit/index.html">here</fork>. |
| </p> |
| <p> |
| Similar implementations exist for Log4J (org.apache.avalon.framework.logger.Log4JLogger) and |
| JDK 1.4 logging (org.apache.avalon.framework.logger.Jdk14Logger). |
| </p> |
| <p> |
| If you want FOP to be totally silent you can also set an org.apache.avalon.framework.logger.NullLogger instance. |
| </p> |
| <p> |
| If you want to use yet another logging facility you simply have to create a class that |
| implements org.apache.avalon.framework.logging.Logger and set it on the Driver object. |
| See the existing implementations in Avalon Framework for examples. |
| </p> |
| </section> |
| <section id="input"> |
| <title>Input Sources</title> |
| <p> |
| The input XSL-FO document is always handled internally as SAX (see the |
| <link href="design/parsing.html">Parsing Design Document</link> for the rationale). |
| However, the input itself can be provided in a variety of ways to FOP, |
| which normalizes the input (if necessary) into SAX events: |
| </p> |
| <ul> |
| <li><strong>SAX Events through SAX Handler</strong>: <code>FOTreeBuilder</code> is the SAX Handler which is obtained through <code>getContentHandler</code> on <code>Driver</code>.</li> |
| <li><strong>DOM (which is converted into SAX Events)</strong>: The conversion of a DOM tree is done via the <code>render(Document)</code> method on <code>Driver</code>.</li> |
| <li><strong>Data Source (which is parsed and converted into SAX Events)</strong>: The <code>Driver</code> can take an <code>InputSource</code> as input. |
| This can use a <code>Stream</code>, <code>String</code> etc.</li> |
| <li><strong>XML+XSLT Transformation</strong> (which is transformed using an XSLT Processor and the result is fired as SAX Events: <code>XSLTInputHandler</code> is used as an <code>InputSource</code> in the render(<code>XMLReader</code>, <code>InputSource</code>) method on <code>Driver</code>.</li> |
| </ul> |
| <p> |
| There are a variety of upstream data manipulations possible. |
| For example, you may have a DOM and an XSL stylesheet; or you may want to |
| set variables in the stylesheet. Interface documentation and some cookbook |
| solutions to these situations are provided in |
| <fork href="http://xml.apache.org/xalan-j/usagepatterns.html">Xalan Basic Usage Patterns</fork>. |
| </p> |
| <p> |
| See the <link href="#examples">Examples</link> for some variations on input. |
| </p> |
| </section> |
| <section id="config-external"> |
| <title>Using a Configuration File</title> |
| <p> |
| To access an external configuration: |
| </p> |
| <source><![CDATA[ |
| import org.apache.fop.apps.Options; |
| |
| /*..*/ |
| |
| userConfigFile = new File(userConfig); |
| options = new Options(userConfigFile);]]></source> |
| <note> |
| This is all you need to do, it sets up a static configuration class. |
| </note> |
| <p> |
| No further reference to the <code>options</code> variable is necessary. |
| The "options = " is actually not even necessary. |
| </p> |
| <p> |
| See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment. |
| </p> |
| </section> |
| <section id="config-internal"> |
| <title>Setting the Configuration Programmatically</title> |
| <p> |
| If you wish to set configuration options from within your embedded application, use the <code>Configuration.put</code> method. Here is an example that sets the "baseDir" configuration in a Unix environment: |
| </p> |
| <source>org.apache.fop.configuration.Configuration.put("baseDir","/my/base/dir");</source> |
| <p> |
| Here is another that sets baseDir in a Windows environment: |
| </p> |
| <source>org.apache.fop.configuration.Configuration.put("baseDir","C:\my\base\dir");</source> |
| <p> |
| See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment. |
| </p> |
| </section> |
| <section id="hints"> |
| <title>Hints</title> |
| <section id="object-reuse"> |
| <title>Object reuse</title> |
| <p> |
| If FOP is going to be used multiple times within your application |
| it may be useful to reuse certain objects to save time. |
| </p> |
| <p> |
| The renderers and the driver can both be reused. A renderer is reusable |
| once the previous render has been completed. The driver is reuseable |
| after the rendering is complete and the <code>reset()</code> method is called. |
| You will need to setup the driver again with a new OutputStream, |
| IntputStream and renderer. |
| </p> |
| </section> |
| <section id="awt"> |
| <title>AWT issues</title> |
| <p> |
| If your XSL-FO files contain SVG then Batik will be used. When Batik is |
| initialised it uses certain classes in <code>java.awt</code> that |
| intialises the java AWT classes. This means that a daemon thread |
| is created by the JVM and on Unix it will need to connect to a |
| DISPLAY. |
| </p> |
| <p> |
| The thread means that the Java application may not automatically quit |
| when finished, you will need to call <code>System.exit()</code>. These |
| issues should be fixed in the upcoming JDK 1.4. |
| </p> |
| <p> |
| If you run into trouble running FOP on a head-less server, please see the |
| <link href="graphics.html#batik">notes on Batik</link>. |
| </p> |
| </section> |
| <section id="render-info"> |
| <title>Getting information on the rendering process</title> |
| <p> |
| To get the number of pages that were rendered by FOP you can call |
| <code>Driver.getResults()</code>. This returns a FormattingResults object |
| where you can lookup the number of pages produced. It also gives you the |
| page-sequences that were produced along with their id attribute and their |
| number of pages. This is particularly useful if you render multiple |
| documents (each enclosed by a page-sequence) and have to know the number of |
| pages of each document. |
| </p> |
| </section> |
| </section> |
| <section id="performance"> |
| <title>Improving performance</title> |
| <p> |
| There are several options to consider: |
| </p> |
| <ul> |
| <li> |
| Whenever possible, try to use SAX to couple the individual components involved |
| (parser, XSL transformer, SQL datasource etc.). |
| </li> |
| <li> |
| Depending on the target OutputStream (in case of an FileOutputStream, but not |
| for a ByteArrayOutputStream, for example) it may improve performance considerably |
| if you buffer the OutputStream using a BufferedOutputStream: |
| <code>driver.setOutputStream(new java.io.BufferedOutputStream(out));</code> |
| <br/> |
| Make sure you properly close the OutputStream when FOP is finished. |
| </li> |
| <li> |
| Cache the stylesheet. If you use the same stylesheet multiple times |
| you can setup a JAXP <code>Templates</code> object and reuse it each time you do |
| the XSL transformation. (More information can be found |
| <fork href="http://www.javaworld.com/javaworld/jw-05-2003/jw-0502-xsl.html">here</fork>.) |
| </li> |
| <li> |
| Use an XSLT compiler like <fork href="http://xml.apache.org/xalan-j/xsltc_usage.html">XSLTC</fork> |
| that comes with Xalan-J. |
| </li> |
| </ul> |
| </section> |
| <section id="multithreading"> |
| <title>Multithreading FOP</title> |
| <p> |
| FOP is not currently completely thread safe. |
| Although the relevant methods of the Driver object are synchronized, FOP uses static |
| variables for configuration data and loading images. |
| Here are some tips to mitigate these problems: |
| </p> |
| <ul> |
| <li> |
| To avoid having your threads blocked, create a Driver object for each thread. |
| </li> |
| <li> |
| If possible, do not change the configuration data while there is a Driver object rendering. |
| Setup the configuration only once, preferably in the <code>init()</code> method of the servlet. |
| </li> |
| <li> |
| If you must change the configuration data more often, or if you have multiple |
| servlets within the same webapp using FOP, consider implementing a singleton |
| class to encapsulate the configuration settings and to run FOP in synchronized methods. |
| </li> |
| </ul> |
| <p>There is also a known issue with fonts being jumbled between threads when using the AWT renderer (which is used by the -awt and -print output options). |
| In general, you cannot safely run multiple threads through the AWT renderer.</p> |
| </section> |
| <section id="examples"> |
| <title>Examples</title> |
| <p> |
| The directory "{fop-dir}/examples/embedding" contains several working examples. |
| In contrast to the examples above the examples here primarily use JAXP for |
| XML access. This may be easier to understand for people familiar with JAXP. |
| </p> |
| <section id="ExampleFO2PDF"> |
| <title>ExampleFO2PDF.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleFO2PDF.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleFO2PDF.java?view=markup"> |
| (future 1.0dev)</fork> |
| demonstrates the basic usage pattern to transform an XSL-FO |
| file to PDF using FOP. |
| </p> |
| <figure src="images/EmbeddingExampleFO2PDF.png" alt="Example XSL-FO to PDF"/> |
| </section> |
| <section id="ExampleXML2FO"> |
| <title>ExampleXML2FO.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleXML2FO.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleXML2FO.java?view=markup"> |
| (future 1.0dev)</fork> |
| has nothing to do with FOP. It is there to show you how an XML |
| file can be converted to XSL-FO using XSLT. The JAXP API is used to do the |
| transformation. Make sure you've got a JAXP-compliant XSLT processor in your |
| classpath (ex. <fork href="http://xml.apache.org/xalan-j">Xalan</fork>). |
| </p> |
| <figure src="images/EmbeddingExampleXML2FO.png" alt="Example XML to XSL-FO"/> |
| </section> |
| <section id="ExampleXML2PDF"> |
| <title>ExampleXML2PDF.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleXML2PDF.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleXML2PDF.java?view=markup"> |
| (future 1.0dev)</fork> |
| demonstrates how you can convert an arbitrary XML file to PDF |
| using XSLT and XSL-FO/FOP. It is a combination of the first two examples |
| above. The example uses JAXP to transform the XML file to XSL-FO and FOP to |
| transform the XSL-FO to PDF. |
| </p> |
| <figure src="images/EmbeddingExampleXML2PDF.png" alt="Example XML to PDF (via XSL-FO)"/> |
| <p> |
| The output (XSL-FO) from the XSL transformation is piped through to FOP using |
| SAX events. This is the most efficient way to do this because the |
| intermediate result doesn't have to be saved somewhere. Often, novice users |
| save the intermediate result in a file, a byte array or a DOM tree. We |
| strongly discourage you to do this if it isn't absolutely necessary. The |
| performance is significantly higher with SAX. |
| </p> |
| </section> |
| <section id="ExampleObj2XML"> |
| <title>ExampleObj2XML.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleObj2XML.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleObj2XML.java?view=markup"> |
| (future 1.0dev)</fork> |
| is a preparatory example for the next one. It's an example that |
| shows how an arbitrary Java object can be converted to XML. It's an often |
| needed task to do this. Often people create a DOM tree from a Java object and |
| use that. This is pretty straightforward. The example here however shows how |
| to do this using SAX which will probably be faster and not even more |
| complicated once you know how this works. |
| </p> |
| <figure src="images/EmbeddingExampleObj2XML.png" alt="Example Java object to XML"/> |
| <p> |
| For this example we've created two classes: ProjectTeam and ProjectMember |
| (found in xml-fop/examples/embedding/java/embedding/model). They represent |
| the same data structure found in |
| xml-fop/examples/embedding/xml/xml/projectteam.xml. We want to serialize a |
| project team with several members which exist as Java objects to XML. |
| Therefore we created the two classes: ProjectTeamInputSource and |
| ProjectTeamXMLReader (in the same place as ProjectTeam above). |
| </p> |
| <p> |
| The XMLReader implementation (regard it as a special kind of XML parser)is |
| responsible for creating SAX events from the Java object. The InputSource |
| class is only used to hold the ProjectTeam object to be used. |
| </p> |
| <p> |
| Have a look at the source of ExampleObj2XML.java to find out how this is |
| used. For more detailed information see other resources on JAXP (ex. |
| <fork href="http://java.sun.com/xml/jaxp/dist/1.1/docs/tutorial/xslt/3_generate.html">An older JAXP tutorial</fork>). |
| </p> |
| </section> |
| <section id="ExampleObj2PDF"> |
| <title>ExampleObj2PDF.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleObj2PDF.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleObj2PDF.java?view=markup"> |
| (future 1.0dev)</fork> |
| combines the previous and the third to demonstrate |
| how you can transform a Java object to a PDF directly in one smooth run |
| by generating SAX events from the Java object that get fed to an XSL |
| transformation. The result of the transformation is then converted to PDF |
| using FOP as before. |
| </p> |
| <figure src="images/EmbeddingExampleObj2PDF.png" alt="Example Java object to PDF (via XML and XSL-FO)"/> |
| </section> |
| <section id="ExampleDOM2PDF"> |
| <title>ExampleDOM2PDF.java</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/tags/fop-0_20_5/examples/embedding/java/embedding/ExampleDOM2PDF.java?view=markup"> |
| (current 0.20.5)</fork> |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleDOM2PDF.java?view=markup"> |
| (future 1.0dev)</fork> |
| has FOP use a DOMSource instead of a StreamSource in order to |
| use a DOM tree as input for an XSL transformation. |
| </p> |
| </section> |
| <section id="ExampleSVG2PDF"> |
| <title>ExampleSVG2PDF.java (PDF Transcoder example)</title> |
| <p>This example |
| <fork href="http://svn.apache.org/viewcvs.cgi/xmlgraphics/fop/trunk/examples/embedding/java/embedding/ExampleSVG2PDF.java?view=markup"> |
| (applies to 0.20.5 and future 1.0dev)</fork> |
| shows use of the PDF Transcoder, a sub-application within FOP. |
| It is used to generate a PDF document from an SVG file. |
| </p> |
| </section> |
| <section id="example-notes"> |
| <title>Final notes</title> |
| <p> |
| These examples should give you an idea of what's possible. It should be easy |
| to adjust these examples to your needs. Also, if you have other examples that you |
| think should be added here, please let us know via either the FOP-USER or FOP-DEV |
| mailing lists. Finally, for more help please send your questions to the FOP-USER |
| mailing list. |
| </p> |
| </section> |
| </section> |
| </body> |
| </document> |
| |