My current project requires the generation of PDF reports in a web-based J2EE app, using free tools. Everyone knows about Jasper and iText, but our requirements are for a small number of somewhat simple reports, so I thought those would be overkill. Not to mention the fact that my preference is to use presentation technology that is familiar without any custom coding.
We are using Spring Web Flow, so I decided to try and use Velocity templates to build the reports and let SWF render them through its MVC architecture. Instead of rendering that view directly to the browser in HTML, I made sure that the output was in XHTML and then passed it to Apache FOP to do an XSL-FO transform and generate a PDF, based on a slightly modified freely available XHTML-to-FO stylesheet I found on Antenna House's website.
It turns out, this all worked pretty well. The only struggle I faced was that most of the examples of using Apache FOP out there on the internet are for version 0.20. The current version, 0.90, is drastically different. However, after I read a bit of the documentation, it was easy enough to figure out. In order to get SWF to do this, I had to write my own ViewResolver and View classes and then wire it all up in Spring. Below is a snippet from View class and then the method that actually does the parsing:
public class VelocityToPdfView extends VelocityView {
private static String PDF_MIMETYPE = "application/pdf";
private static String XSL_STYLESHEET_CLASSPATH = "/xhtml2fo.xsl";
@Override
protected void mergeTemplate(Template template,
Context context, HttpServletResponse response)
throws Exception {
try
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(stream);
template.merge(context, pw);
pw.flush();
InputStream inputStream =
new ByteArrayInputStream(stream.toByteArray());
response.setContentType(PDF_MIMETYPE);
Html2Pdf.htmlToPdf(
inputStream,
(OutputStream)response.getOutputStream(),
XSL_STYLESHEET_CLASSPATH);
}
catch (MethodInvocationException ex) {
throw new NestedServletException(
"Method invocation failed during rendering " +
"of Velocity view with name '" +
getBeanName() +
"': " + ex.getMessage() +
"; reference [" + ex.getReferenceName() +
"], method '" + ex.getMethodName() + "'",
ex.getWrappedThrowable());
}
}
}
html2Pdf method:
public static void htmlToPdf(InputStream input,
OutputStream pdfOutputStream,String styleSheet) {
try {
// Setup a buffer to obtain the content length
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Setup FOP
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
// Setup Transformer
Source xsltSrc = new StreamSource(
Html2Pdf.class.getResourceAsStream(styleSheet));
Transformer transformer = tFactory.newTransformer(xsltSrc);
// Make sure the XSL transformation's result is piped
// through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Setup input
Source src = new StreamSource(input);
// Start the transformation and rendering process
transformer.transform(src, res);
pdfOutputStream.write(out.toByteArray());
pdfOutputStream.flush();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
Comments
Post new comment