Apache XSL-FO’ sho v1.0

Transforming XML into PDFs.. and stuff

If you’ve ever been tasked with providing PDF documents via xsl, you’ve surely done some homework and shopped around for viable third party libraries. Some are good, some are great and rightly so charge a price, and some are just flat out incomplete or shanty in their documentation. It’s not a knock on anyone, its just a fact well known to open source developers. Historically what has been missing is an open standard for pdf generation, and possibly other output formats.

Enter XSL-FO: XSL Formatting Objects is an open standard for formatting documents in order to produce media artifacts such as PDF, postscript (PS), rich text format (RTF), and png files. Because it’s XML centric, you can marry your XML data to an XSL-FO stylesheet and perform a translation that will output a file in any of these format or others. XSL-FO is simply the XSL dialect used to lay out the document, and Apache FOP is the open source java based software you can use to process those transformations.

Apache FOP has been slowly making its complete debut over the past 3 years. Version 1.0 was finally released around the 12th of july, so its essentially a fresh release. Before that, .95 was the closest thing to production ready, but now that 1.0 is out, a more complete implementation awaits. There are still a few loose ends to tie up though, a complete rundown of FO compliance can be found on on apache’s XSL-FO compliance page

On with the examples:

The XML data

<block>
	<date>july 27th, 2010</date>
</block>

This is a very simple xml document, which we will be reading from in order to stamp the date onto a pdf document.

The XSL-FO layout

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet 
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:fo="http://www.w3.org/1999/XSL/Format">
	
	<xsl:template match="/">
			
	<fo:root font-family="Verdana" font-size="12pt" text-align="center"
	    xmlns:fo="http://www.w3.org/1999/XSL/Format"
	    xmlns:fox="http://xml.apache.org/fop/extensions">
	
	<fo:layout-master-set>
	  <fo:simple-page-master master-name="master">
		<fo:region-body margin="0in"
	  		background-image="http://my.images.com/banner.jpg"
			background-repeat="no-repeat"
			background-position="center"  />
	  </fo:simple-page-master>
	</fo:layout-master-set>
	
	<fo:page-sequence master-reference="master">

	  <fo:flow flow-name="xsl-region-body">
		  
		<fo:block 
          	margin-top="50px"
          	margin-left="200px">
			Today's XML date is: <xsl:value-of select="/block/date"/>
		</fo:block>
		  
	  </fo:flow>
	</fo:page-sequence>
	
	</fo:root>
	  
  </xsl:template>

</xsl:stylesheet>

This is the XSL-FO layout we’ll be using to stamp on the pdf. It’s marked up using regular XSL-FO. Covering the syntax of XSL-FO is beyond the scope of this article, but there are plenty of resources and tutorials online such as the W3Schools.com XSL-FO and Renderx.com tutorials.

On with the java

Finally, we come to the java code and apache’s fop usage:

	protected void export() throws  IOException {
	
	    //Setup a buffer to obtain the content length
		FileOutputStream out = new FileOutputStream("C:/image/layout.pdf");
		
		try {
		    
			// generic files to String XML and XSL
			String xml = FileUtils.readFile("C:/image/banner.text.xml");
			String xsl = FileUtils.readFile("C:/image/banner.layout.fo"); 
			
	        // configure fopFactory as desired
	        FopFactory fopFactory = FopFactory.newInstance();
	        TransformerFactory factory = TransformerFactory.newInstance();
	        
		    //Setup FOP
		    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
	
		    //Setup Transformer
		    Source xsltSrc = new StreamSource(new StringReader(xsl));
		    Transformer transformer = factory.newTransformer(xsltSrc);
	
		    //Setup input
		    Source src = new StreamSource(new StringReader(xml));
	
		    //Make sure the XSL transformation's result is piped through to FOP
		    Result res = new SAXResult(fop.getDefaultHandler());	    
		    
		    //Start the transformation and rendering process
		    transformer.transform(src, res);
		    
		} catch (Exception e) {	
			e.printStackTrace();
		} finally {
			out.close();
		}
	}

Pretty straight forward xslt looking code. But what if we want to override the FOP PDFgeneration defaults? What if we want to produce a document not regular PDF page size, like a banner or if we want to produce a png image? Luckily, FOP offers a factory configuration mechanism we can use to customize the outputs.

Rendering the output as a PNG file

The java code is pretty much the same thing, with some small differences. First you’ll want to invoke the fopFactory.setUserConfig(String pathToConfig) method on the FoPFactory object. This will flag apache FOP to load a custom configuration from the specified file. Secondly you’ll need to set the exporting mime type to MimeConstants.MIME_PNG, as show in the java code snippet below.

// configure fopFactory as desired
FopFactory fopFactory = FopFactory.newInstance();
fopFactory.setUserConfig(new File(rootPath + "export.conf.xml"));
TransformerFactory factory = TransformerFactory.newInstance();

//Setup FOP
Fop fop = fopFactory.newFop(MimeConstants.MIME_PNG, out);

Lastly, you’ll want to define your export.conf.xml file. The only thing that you’d be changing that strays from the defaults would be the exported object’s dimensions (set in the example below to 150px length by 900px wide) and adding the renderer element that defines an “image/png” type. This renderer block flags the processor to export as PNG. At the moment the only other image export format is TIFF, but between these two, most purposes are likely met. It’s worth mentioning that FOP supports export into Postscript, PCL, AFP, RTF, XML, and TXT to name a few. More details can be found on Apache FOP’s Output Target page. Here’s the source:

<?xml version="1.0"?>

<fop version="1.0">

	<!-- Base URL for resolving relative URLs -->
	<base>.</base>

	<!--
		Source resolution in dpi (dots/pixels per inch) for determining the
		size of pixels in SVG and bitmap images, default: 72dpi
	-->
	<source-resolution>72</source-resolution>
	<!--
		Target resolution in dpi (dots/pixels per inch) for specifying the
		target resolution for generated bitmaps, default: 72dpi
	-->
	<target-resolution>72</target-resolution>

	<!--
		Default page-height and page-width, in case value is specified as auto
	-->
	<default-page-settings height="150px" width="900px" />

	<!-- Uses renderer mime type for renderers -->
	<renderers>

		<renderer mime="image/png">
		  <transparent-page-background>false</transparent-page-background>
		  <fonts><!-- described elsewhere --></fonts>
		</renderer>

	</renderers>

</fop>

So if you want to export to a different format, all you’d need to do is use a custom configuration and set the renderer formats to match the one you’d like to use, as well as override any default document properties you wish.

By leveraging an open standard like XSL-FO you can use different vendors for your pdf generation code, and while Apache’s FOP implementation isn’t 100% complete in its support for XSL-FO, it does do a good job of supporting what most folks will need on a daily basis. It’s nice to see a complete version release after a long wait.

Resources:
Apache FOP website. v1.0 Finally released on 7/12/2010?, yay!
Apache FOP compliance guide
XSL-FO Object Model documentation
Renderx.com“>Renderx.com tutorial on XSL-FO

There’s also the ultimate XSL-FO list of resources:
Whoishostingthis.com xsl-fo Resources

Comments (0)

› No comments yet.

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Pingbacks (0)

› No pingbacks yet.