Embedding FOP How to Embed FOP in a Java application
Overview

Review Running FOP for important information that applies to embedded applications as well as command-line use, such as options and performance.

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).

Basics

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.

Here is an example use of Driver which outputs PDF:

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.

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 below for more information.

To setup the user config file you can do the following

This is all you need to do, it sets up a static configuration class.

Once the Driver is set up, the render method is called. Depending on whether DOM or SAX is being used, the invocation of the method is either render(Document) or render(Parser, InputSource) respectively.

Another possibility may be used to build the FO Tree. You can call getContentHandler() and fire the SAX events yourself.

Once the FO Tree is built, the format() and render() methods may be called in that order.

Here is an example use of Driver:

You can also specify an xml and xsl file for the input.

Here is an example use of Driver with the XSLTInputHandler:

Have a look at the classes CommandLineStarter or FopServlet for complete examples. Also, have a look at the examples at the bottom of this page.

If your XSL-FO files contain SVG then Batik will be used. When Batik is initialised it uses certain classes in java.awt 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. The thread means that the Java application will not automatically quit when finished, you will need to call System.exit(). These issues should be fixed in the upcoming JDK 1.4
Controlling logging

FOP uses Jakarta Avalon's Logger interface to do logging. See the Jakarta Avalon project for more information.

Per default FOP uses the ConsoleLogger 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:

The LogKitLogger class implements the Logger interface so all logging calls are being redirected to LogKit. More information on Jakarta LogKit can be found here.

Similar implementations exist for Log4J (org.apache.avalon.framework.logger.Log4JLogger) and JDK 1.4 logging (org.apache.avalon.framework.logger.Jdk14Logger).

If you want FOP to be totally silent you can also set an org.apache.avalon.framework.logger.NullLogger instance.

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.

Input Sources

The input XSL-FO document is always handled internally as SAX (see the Parsing Design Document 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:

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 Xalan Basic Usage Patterns.

See the Examples for some variations on input.

Hints
Object reuse

If FOP is going to be used multiple times within your application it may be useful to reuse certain objects to save time.

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 reset method is called. You will need to setup the driver again with a new OutputStream, IntputStream and renderer.

Getting information on the rendering process

To get the number of pages that were rendered by FOP you can call Driver.getResults(). 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.

Using FOP in a Servlet

Here is a minimal code snippet to demonstrate the basics:

response.setContentType("application/pdf"); Driver driver=new Driver( new InputSource("foo.fo"), response.getOutputStream()); driver.setRenderer(Driver.RENDER_PDF); driver.run();

There are numerous problems with the code snippet above. Its purpose is only to demonstrate the basic concepts. See xml-fop/examples/servlet for a working example of FOP used in a servlet. After building the servlet, drop the fop.war into the webapps directory of Tomcat. Then access a URL as follows:

http://localhost:8080/fop/fop?fo=/home/path/to/fofile.fo

http://localhost:8080/fop/fop?xml=/home/path/to/xmlfile.xml&xsl=/home/path/to/xslfile.xsl

The source code for the servlet can be found under xml-fop/examples/servlet/src/FopServlet.java.

Some versions of Internet Explorer will not automatically show the PDF. This is well-known to be a limitation of Internet Explorer, and is not a problem with the servlet. However, Internet Explorer can still be used to download the PDF so that it can be viewed later. Also, appending ".pdf" to the end of the URL may help.
Using FOP in a Servlet with an XSLT Transformation

If both the source XML and XSL are read from files, use the TraxInputHandler:

response.setContentType("application/pdf"); XSLTInputHandler input =new XSLTInputHandler(new File("foo.xml"), new File("foo.xsl")); Driver driver=new Driver(); driver.setOutputStream(response.getOutputStream()); driver.setRenderer(Driver.RENDER_PDF); driver.render(input.getParser(), input.getInputSource());

This code snippet has the same problems as the one from the section above.

If your source XML is generated on the fly (for example from a database, a web service, or another servlet), create a transformer object explicitly, and use a SAX event stream to feed the transformation result into FOP:

response.setContentType("application/pdf"); Driver driver =new Driver(); driver.setOutputStream(response.getOutputStream()); driver.setRenderer(Driver.RENDER_PDF); Transformer transformer=TransformerFactory.newInstance() .newTransformer(new StreamSource("foo.xsl")); transformer.transform(xmlsource, new SAXResult(driver.getContentHandler()));

You don't have to call run() or render() on the driver object.

The xmlsource is a placeholder for your actual XML source. If you have to read the XML from a string, supply a new StreamSource(new StringReader(xmlstring)). Constructing and reparsing an XML string is generally less desirable than using a SAXSource if you generate your XML. You can alternatively supply a DOMSource as well. You may also use dynamically generated XSL if you like.

Because you have an explicit transformer object, you can also use it to explicitly set parameters for the transformation run.

Using a Configuration File

To access an external configuration:

org.apache.fop.apps.Options options = new Options(new File("userconfig.xml"));

No further reference to the options variable is necessary.

See Multithreading FOP for issues related to changing configuration in a multithreaded environment.

Setting the Configuration Programmatically

If you wish to set configuration options from within your embedded application, use the Configuration.put method. Here is an example that sets the "baseDir" configuration in a Unix environment:

org.apache.fop.configuration.Configuration.put("baseDir","/my/base/dir");

Here is another that sets baseDir in a Windows environment:

org.apache.fop.configuration.Configuration.put("baseDir","C:\my\base\dir");

See Multithreading FOP for issues related to changing configuration in a multithreaded environment.

Multithreading FOP

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:

Servlet Engines

When using a servlet engine, there are potential CLASSPATH issues, and potential conflicts with existing XML/XSLT libraries. Servlet containers also often use their own classloaders for loading webapps, which can cause bugs and security problems.

Tomcat

Check Tomcat's documentation for detailed instructions about installing FOP and Cocoon. There are known bugs that must be addressed, particularly for Tomcat 4.0.3.

WebSphere 3.5

Put a copy of a working parser in some directory where WebSphere can access it. For example, if /usr/webapps/yourapp/servlets is the CLASSPATH for your servlets, copy the Xerces jar into it (any other directory would also be fine). Do not add the jar to the servlet CLASSPATH, but add it to the CLASSPATH of the application server which contains your web application. In the WebSphere administration console, click on the "environment" button in the "general" tab. In the "variable name" box, enter "CLASSPATH". In the "value" box, enter the correct path to the parser jar file (/usr/webapps/yourapp/servlets/Xerces.jar in our example here). Press "OK", then apply the change and restart the application server.

Examples

The directory "xml-fop/examples/embedding" contains several working examples. In contrast of the examples above the examples here primarily use JAXP for XML access. This may be easier to understand for people familiar with JAXP.

ExampleFO2PDF.java

This example demonstrates the basic usage pattern to transform an XSL-FO file to PDF using FOP.

ExampleXML2FO.java

This example 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. Xalan).

ExampleXML2PDF.java

This example 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.

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.

ExampleObj2XML.java

This example 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.

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).

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.

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. An older JAXP tutorial).

ExampleObj2PDF.java

The last example here 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.

Final notes

These examples should give you an idea of what's possible. It should be easy to adjust these examples to your needs. For examples, you can use a DOMSource instead of a StreamSource to feed a DOM tree as input for an XSL transformation.

If you think you have a decent example that should be here, contact us via one of the mailing lists and we'll see to it that it gets added. Also, if you can't find the solution to your particular problem drop us a message on the fop-user mailing list.