Intro
FOP http://xml.apache.org/fop/index.html
is an XSLT driven print formatter capable of rendering to different output types such as PDF or SVG.
It's pretty simple to extend the Spring view classes to implement a FOP view and so enable dynamic print format output from your web applications.
Below is an example of how to do so using the PDF output generated from one of the examples included in the FOP distribution.
Implementation
Generation of the PDF from the combination of a raw XML Node (your model) and an XSLT stylesheet involves two distinct phases of operation:
- the XML is transformed to FO (another XML dialect) using your stylesheet and a normal XSLT processor.
- the resultant FO tree is processed by the FOP driver into the desired output format and directed to the output stream. In practice, this distinction is blurred slightly by sending the transformation results directly to the FOP processor which operates by responding to SAX events.
Let's look at some code which explains it with ease. First, here's a generic subclass of AbstractXsltView that can be used for all of your FOP transforms and which was added to the Spring sandbox prior to the 1.1.2 release:
package org.springframework.web.servlet.view.xslt;
import java.io.BufferedOutputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import org.apache.fop.apps.Driver;
import org.w3c.dom.Node;
/**
* Convenient superclass for views rendered to PDF (or other FOP output format)
* using XSLT-FO stylesheet.
*/
public abstract class AbstractXslFoView extends AbstractXsltView {
/** default renderer will be PDF unless overridden */
private static final int DEFAULT_RENDERER = Driver.RENDER_PDF;
private int renderer = DEFAULT_RENDERER;
Driver driver;
/**
* Perform the actual transformation, writing to the HTTP response via the FOP
* Driver.
*/
protected void doTransform(
Map model,
Node dom,
HttpServletRequest request,
HttpServletResponse response
) throws Exception {
driver = new Driver();
driver.setRenderer(renderer);
driver.setOutputStream(response.getOutputStream());
Result result = new SAXResult(driver.getContentHandler());
doTransform(dom, getParameters(request), result, response.getCharacterEncoding());
}
/**
* Sets the renderer to use for this FOP transformation. See the available
* types in org.apache.fop.apps.Driver. Defaults to Driver.RENDER_PDF
*
* @param renderer the type of renderer
*/
public void setRenderer(int renderer) {
this.renderer = renderer;
}
}
As is typical with XSLT views in Spring, we must create an application specific class to generate the 'DOMified' model passed from the controller. In this example, I simply ignore the controller model and read in the XML from a file on disk. The file is supplied as part of the FOP distribution. I use JDOM here as I'm more familiar with it than other XML API's, but as long as a W3C Node is returned, it's an implementation detail.
package view;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jdom.Document;
import org.jdom.input.BuilderErrorHandler;
import org.jdom.input.SAXBuilder;
import org.jdom.output.DOMOutputter;
import org.springframework.web.servlet.view.xslt.AbstractXslFoView;
import org.w3c.dom.Node;
public class FopTestView extends AbstractXslFoView {
protected Node createDomNode(
Map model,
String rootName,
HttpServletRequest req,
HttpServletResponse res
) throws Exception {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(
"D:/fop-0.20.5/examples/embedding/xml/xml/projectteam.xml"
);
return new DOMOutputter().output(doc);
}
}
Lastly, in views.properties (or equivalent) we specify the properties of our FOP view.
Dependencies
Your web app will need to have fop.jar, batik.jar and the avalon framework jar in its classpath (WEB-INF/lib or whatever). The avalon requirement is unfortunate but necessary due to the hardwiring of an avalon logger into the FOP driver. All 3 jars are available in the FOP distribution.
Summary
That's it. I'm omitting any controller code or config in the assumption that you know what to do anyway. Return a view name of "fop" in your ModelAndView and a PDF should load in the client showing the output of the XSL:FO transformation.
Happy FOPping 
The AbstractXslView appears to have changed in Spring 1.2.1 - the following change fixes things.
Change the doTransform method to: