Click here to Skip to main content
Click here to Skip to main content

PDF Generation using XSLFO and FOP

, 26 Jun 2009
Rate this:
Please Sign up or sign in to vote.
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

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 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 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:

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

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:

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

Share

About the Author

Shyam S
Architect
India India
I am a software architect in a reputed software firm. Mainly I am dealing with presentation layer like creation of re-usable framework, controls, pages etc.

Comments and Discussions

 
QuestionWould you provide the complete Java source code please ? Pinmemberssenorita25-Apr-14 18:06 
QuestionSource Code [modified] PinmemberMember 1052652415-Jan-14 10:56 
Questionusing old version fop PinmemberMember 1017873329-Jul-13 18:35 
QuestionThrough Java and xsl-fo we can access database value in PDF Pinmemberpmamamul9-Apr-13 2:02 
Question"response" Pinmembermarkdesign8918-Jun-12 4:54 
AnswerRe: "response" PinmemberShyam S18-Jun-12 18:11 
GeneralMy vote of 5 Pingroupsanjeevchaubey1-Nov-11 22:25 
QuestionFOP & Applet PinmemberMember 235285618-Aug-11 1:53 
AnswerRe: FOP & Applet PinmemberMember 827588228-Sep-11 9:46 
QuestionHow to limit the number pages it creates Pinmembervicky0000011-Jan-10 21:21 
Generalnot bad but... Pinmembercinamon27-Jun-09 2:50 
GeneralRe: not bad but... PinmemberTefik Becirovic27-Jun-09 9:12 
GeneralRe: not bad but... PinmemberMember 827588228-Sep-11 9:55 
QuestionXSL-FO? PinmemberTefik Becirovic27-Jun-09 1:21 
AnswerRe: XSL-FO? PinmemberDon Kackman9-Jul-09 9:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 26 Jun 2009
Article Copyright 2009 by Shyam S
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid