Introduction
This article describes how to create a PDF document using XSLFO and Apache FOP.
Background
While developing a JSP based web application, I came through a requirement to create a PDF document and export it on a button click. There are lot of tutorials available for exporting to Excel, Word, etc. I decided to use XSLFO and FOP for creation of PDF document.
XSLFO is XSL Formatting Objects and can be used for formatting XML data. More information is available here and here.
Apache FOP (Formatting Objects Processor) is a Java application that reads a formatting objects tree and renders the resulting pages to a specified output (here, in our case, PDF). More information is available here.
How It Works
We have an XML that holds data and an XSLT that creates an XML containing formatting objects by taking data from the first XML. This resultant XML is de-serialized into Java objects. FOP creates a PDF file using these Java objects.
Hello World XSLFO
Here is a sample XML. This XML contains name and a list of friends with contact numbers.
="1.0"="iso-8859-1"
<root>
<name>shyam</name>
<friend>
<name>Abc</name>
<phNo>90909090909</phNo>
</friend>
<friend>
<name>Xyz</name>
<phNo>32323232323</phNo>
</friend>
</root>
Save it as Hello.xml.
Here is the sample xslfo style sheet. This first takes a name from the above XML. Then this xslt renders a table that contains details of friends available in the above XML.
="1.0"="iso-8859-1"
<xsl:stylesheet version="1.1"
xmlns:xsl=http://www.w3.org/1999/XSL/Transform
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="fo">
<xsl:template match="root">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Hello, <xsl:value-of select="name" />!</fo:block>
<fo:block>
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
No.
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Name
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Phone Number
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:for-each select="./friend">
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="position()" />
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="name" />
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="phNo" />
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Save it as HelloWorld.xsl.
Java Code
Here is the code that creates PDF:
File xsltfile = new File("HelloWorld.xsl");
StreamSource source = new StreamSource(new File("Hello.xml"));
StreamSource transformSource = new StreamSource(xsltfile);
FopFactory fopFactory = FopFactory.newInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Transformer xslfoTransformer;
try
{
xslfoTransformer = getTransformer(transformSource);
Fop fop;
try
{
fop = fopFactory.newFop
(MimeConstants.MIME_PDF, foUserAgent, outStream);
Result res = new SAXResult(fop.getDefaultHandler());
try
{
xslfoTransformer.transform(source, res);
byte[] pdfBytes = outStream.toByteArray();
response.setContentLength(pdfBytes.length);
response.setContentType("application/pdf");
response.addHeader("Content-Disposition",
"attachment;filename=pdffile.pdf");
response.getOutputStream().write(pdfBytes);
response.getOutputStream().flush();
}
catch (TransformerException e) {
throw e;
}
}
catch (FOPException e) {
throw e;
}
}
catch (TransformerConfigurationException e)
{
throw e;
}
catch (TransformerFactoryConfigurationError e)
{
throw e;
}
The 'getTransformer
' function. I used xslt2 processor for advanced XSLT processing. A simple XSLT processor is enough here.
private Transformer getTransformer(StreamSource streamSource)
{
net.sf.saxon.TransformerFactoryImpl impl =
new net.sf.saxon.TransformerFactoryImpl();
try {
return impl.newTransformer(streamSource);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
return null;
}
Following is the list of the main namespaces used in the above code:
import java.io.File;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import java.io.ByteArrayOutputStream;
import javax.xml.transform.Transformer;
import javax.xml.transform.Result;
How It Works
First normal XML-XSLT transformation occurs which creates XSLFO XML. That is Hello.xml transformed by HelloWorld.xsl creates the following XML.
="1.0"="utf-8"
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="1in" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Hello, shyam!</fo:block>
<fo:block>
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
No.
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Name
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Phone Number
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>1</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>Abc</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>90909090909</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>2</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>Xyz</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>32323232323</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
This XML is a serialized FO objects tree. This XML is de-serialized to FO objects and by using these objects, FOP draws a PDF file. All these processes happen in the transform;
function, we don't need to worry about it!!!
Points of Interest
FOP is a great utility available to us for the creation of PDF. I hope this bit of code will help you to create PDF programmatically. Thank you.
History
- Initial version on 24 June 2009