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

PDF Generation using XSLFO and FOP

By , 26 Jun 2009
 

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)

About the Author

Shyam S
Architect
India India
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionThrough Java and xsl-fo we can access database value in PDFmemberpmamamul9 Apr '13 - 2:02 
Is there any idea Through Java and xsl-fo we can access database value in PDF
Question"response"membermarkdesign8918 Jun '12 - 4:54 
Sorry but Eclipse give me an error for the object "response":
 
response.setContentLength(pdfBytes.length);
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment;filename=pdffile.pdf");
response.getOutputStream().write(pdfBytes);
response.getOutputStream().flush();
 
Why?
AnswerRe: "response"memberShyam S18 Jun '12 - 18:11 
Here response is normail http response, you get it in a servlet or struts action class. This code is an example of downloading pdf in a web application. You can try saving the file to file system - the code is supplied.
GeneralMy vote of 5groupsanjeevchaubey1 Nov '11 - 22:25 
realy a nice article for apache xsl fo
 
thanks,
QuestionFOP & AppletmemberMember 235285618 Aug '11 - 1:53 
Great article!!!
 
This code works well as a stand alone module but within an applet it breaks (see below):
 
------------------------------------------------------------------------------------------------------
javax.xml.transform.TransformerConfigurationException: Failed to compile stylesheet. 1 error detected.
at net.sf.saxon.PreparedStylesheet.prepare(PreparedStylesheet.java:124)
at net.sf.saxon.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:125)
at net.sf.saxon.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:80)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet.getTransformer(Unknown Source)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet.genereateClientPDF(Unknown Source)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet.access$1200(Unknown Source)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet$4.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
java.lang.NullPointerException
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet.genereateClientPDF(Unknown Source)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet.access$1200(Unknown Source)
at com.momaf.tiermuis.applet.health.StatementGeneratorApplet$4.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
________________________________________________________________________________________
 
Do you know why?
 
THANKS
AnswerRe: FOP & AppletmemberMember 827588228 Sep '11 - 9:46 
I would say the XSL file is wrong or corrupt since it says "Failed to compile stylesheet". Double check that
QuestionHow to limit the number pages it createsmembervicky0000011 Jan '10 - 21:21 
Hi all,
 
I am new to XML FO technique, and my requirement is to limit the pdf page to 2 and the remaining contend can lose. Is it possible.
 
is there any alternative to fo:float. pls share to me if u have any idea
 
Thanks in advance
Vicky00000
Generalnot bad but...membercinamon27 Jun '09 - 2:50 
itext from Bruno Lowagie will blow this away and is easier to learn and use, and once you learn itext you can do way more then just creating pdfs from scratch...you can manipulate, fill, read forms, extract, combine pages..the list goes on and on
good effort, thanks
GeneralRe: not bad but...memberTefik Becirovic27 Jun '09 - 9:12 
You have it right, concerning the generating of PDFs,
but this is only one link in a chain.
 
This article hasn’t to do with generating of PDFs only.
 
Here is the accent on a massive, automatical, datadriven creation of stylized PDFs,
and therefore is using of XSL-FO already right.
 
The question is a difficulty and broad unacceptability of this technology.
There are also other solutions, but not that much.
 
One of these you can find on www.winstom.com.
 
They offer you a simple GUI for document templates design
instead of difficult XSL-FO programming.
 
Regards
Tefik Becirovic
GeneralRe: not bad but...memberMember 827588228 Sep '11 - 9:55 
I would not say iText is better than XSL-FO or the other way round, these are 2 different approaches for different scenarios. iText is for Java programmers and give you more flexibility however Apache FOP let's you create PDF files in a declarative manner (no programming required), and also give you programming language independance, since you could use the same XSL-FO file in different FOP processor. There are many commercial products like Oracle APEX that use XSF-FO for reporting.
 
And last but not least, if you do not want to learn XSL-FO, you can use a XSL-FO editor (WYSIWYG tool) like this one J4L FOP Designer
 
Using this tool it is much easier to create PDF files than using iText.

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

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