Home Page XSL-FO grouping techniques for Apache FOP
Home
XSL-FO Server
Getting started
Versions
How to
XSL-FO Editor
XSL-FO resources
XSL-FO in REALbasic
XSL-FO in Php
XSL-FO in VB 6
XSL-FO in .NET
XSL-FO in Ruby
XSL-FO in Delphi
Usage with APEX
Prof. license
XSL-FO grouping

 

This articles describes how to create grouping logic in XSL-FO using Apache FOP. A common requirement while creating reports is to group data by a given field. While XSLT 2.0 provides a way to do this, XSLT 1.0 does not provide any functionality in this regard. 

If you use Apache FOP for PDF generation you might need to group your XML data using XSLT 1.0 since this is what Apache Xalan supports today.

This document describes a simple example for grouping data. The input file will be a "flat" XML file like the following one:


<?xml version="1.0"?>
<cars>
<car>
<licenseid>1254-TH</licenseid>
<color>red</color>
</car>
<car>
<licenseid>2254-FH</licenseid>
<color>blue</color>
</car>
<car>
<licenseid>2264-FT</licenseid>
<color>blue</color>
</car>
<car>
<licenseid>4464-FT</licenseid>
<color>green</color>
</car>
<car>
<licenseid>2211-GT</licenseid>
<color>red</color>
</car>
</cars>


this file which is a flat list of cars will be converted to a PDF file that shows the list of cars grouped by color. The result will look like this:




this can be done with the XSL-FO file listed at the bottom of this page,  which is now explained, it involves 3 steps:

  • we create a key that assigns each car a key value that is the color of the car

    <xsl:key name="bykey1317757767468" match="cars/car" use="color" />

    once this key has been defined we can access all cars of a give color, for example:

    key('bykey1317757767468', 'red') would return all cars that have red color.

  • now we need to get the list of different car's colors in the list, this is done with

    <xsl:for-each select="cars/car[count(. | key('bykey1317757767468', color)[1]) = 1]">

    what is expression is doing is, check every car and select it only if it is the first one in the list returned by key('bykey1317757767468', color), that is the first car in the list of cars that have the same color. In this way each color is selected only once.

  • and finally for each color we need to get the cars that have such color

    <xsl:for-each select="key('bykey1317757767468', color)">

If you want to test this yourself you will need to download Apache FOP. After that you copy the XML data to a file called cars.xml and the xsl file to groupcars.xsl. Once you have these 2 files you run Apache FOP like this:

fop.bat -xml cars.xml -xsl groupcars.xsl -pdf groups.pdf

 

Do you want to know more?

If you want to learn more about this technique there is a good article here.

If on the other hand you do not want to get involved with these details, our J4L FO Designer will create the XSL-FO file for you.


XSL-FO file for grouping:


<?xml version="1.0" encoding="UTF-8"?><xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:key name="bykey1317757767468" match="cars/car" use="color" />
<xsl:template match="/"><fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="master0" page-width="21.0cm" page-height="29.7cm" margin-top="1.5cm" margin-left="1.5cm" > 
<fo:region-body region-name="body0" /> 
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="master0">


<fo:flow flow-name="body0"> 

<!-- group start, get list of different colors-->
<xsl:for-each select="cars/car[count(. | key('bykey1317757767468', color)[1]) = 1]">

<fo:block font-size="14pt" font-family="SansSerif" color="#0000FF" text-align="left">
Group for color <xsl:value-of select="color"></xsl:value-of>
</fo:block>

    <!-- for all cars in current color--> 
    <xsl:for-each select="key('bykey1317757767468', color)">
    <fo:block font-size="10pt" font-family="SansSerif" color="#000000" text-align="left"> <xsl:value-of  select="licenseid"></xsl:value-of></fo:block>
    </xsl:for-each>

</xsl:for-each>



</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:transform>