Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java / JavaSE

PDF Generation using XSLFO and FOP

4.70/5 (7 votes)
26 Jun 2009CPOL2 min read 181.3K  
An article describes how to create PDF document programmatically using XSLFO and FOP

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

Image 1

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.

XML
<?xml version="1.0" encoding="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.

XML
<?xml version="1.0" encoding="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:

Java
// the XSL FO file
File xsltfile = new File("HelloWorld.xsl");
// the XML file from which we take the name
StreamSource source = new StreamSource(new File("Hello.xml"));
// creation of transform source
StreamSource transformSource = new StreamSource(xsltfile);
// create an instance of fop factory
FopFactory fopFactory = FopFactory.newInstance();
// a user agent is needed for transformation
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// to store output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();

Transformer xslfoTransformer;
try
{
	xslfoTransformer = getTransformer(transformSource);
	// Construct fop with desired output format
        Fop fop;
	try
	{
		fop = fopFactory.newFop
			(MimeConstants.MIME_PDF, foUserAgent, outStream);
		// Resulting SAX events (the generated FO) 
		// must be piped through to FOP
                Result res = new SAXResult(fop.getDefaultHandler());

		// Start XSLT transformation and FOP processing
		try
		{
		        // everything will happen here..
			xslfoTransformer.transform(source, res);
			// if you want to get the PDF bytes, use the following code
			//return outStream.toByteArray();

			// if you want to save PDF file use the following code
			/*File pdffile = new File("Result.pdf");
			OutputStream out = new java.io.FileOutputStream(pdffile);
                        out = new java.io.BufferedOutputStream(out);
                        FileOutputStream str = new FileOutputStream(pdffile);
                        str.write(outStream.toByteArray());
                        str.close();
                        out.close();*/

			// to write the content to out put stream
			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.

Java
private Transformer getTransformer(StreamSource streamSource)
{
	// setup the xslt transformer
	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:

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

XML
<?xml version="1.0" encoding="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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)