Create a PDF with Apache FOP in Java

There are several techniques to Create a PDF with Apache FOP in Java. With this method it is possible to separate the Java code from the visual structure that the PDF will have. You need to have at least three things:

  • Java code using Apache Fop 2.2 libraries.
  • XML file that provides the data to be displayed in the PDF.
  • An XSLT style sheet that defines the way the data will be displayed visually.

ยป See more examples

In this example we only show how to generate a very simple PDF. Note that you may need to learn more about FOP XSLT for more complex documents.

First we show you the XML file with sample data.

report.xml
<?xml version="1.0"?>
<company>
  <company_name>Test Company Corp.</company_name>
  <employee>
    <id>1</id>
    <name>Eva</name>
    <job>Manager</job>
  </employee>
  <employee>
    <id>2</id>
    <name>David</name>
    <job>Sales</job>
  </employee>
  <employee>
    <id>3</id>
    <name>Martha</name>
    <job>Executive</job>
  </employee>
  <employee>
    <id>4</id>
    <name>Jhon</name>
    <job>Executive</job>
  </employee>
  <employee>
    <id>5</id>
    <name>Sofia</name>
    <job>Executive</job>
  </employee>
</company>

The XML file is very simple, it is a hypothetical list of employees of a company.

Now the XSLT file with the definition to generate the PDF.

report.xsl
<?xml version="1.0" encoding="UTF-8"?>
<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="company">
        
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="simpleA4" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm">
                    <fo:region-body/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="simpleA4">
                <fo:flow flow-name="xsl-region-body">
                    <fo:block font-size="16pt" font-weight="bold" space-after="5mm">Company: <xsl:value-of select="company_name"/>
                    </fo:block>
                    <fo:block font-size="12pt">
                        <fo:table table-layout="fixed" width="100%" border-collapse="separate">    
                            <fo:table-column column-width="4cm"/>
                            <fo:table-column column-width="4cm"/>
                            <fo:table-column column-width="5cm"/>
                            <fo:table-body>
                                <xsl:apply-templates select="employee"/>
                            </fo:table-body>
                        </fo:table>
                    </fo:block>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>
    <xsl:template match="employee">
        <fo:table-row>   
            <xsl:if test="job = 'Manager'">
                <xsl:attribute name="font-weight">bold</xsl:attribute>
            </xsl:if>
            <fo:table-cell>
                <fo:block>
                    <xsl:value-of select="id"/>
                </fo:block>
            </fo:table-cell>
     
            <fo:table-cell>
                <fo:block>
                    <xsl:value-of select="name"/>
                </fo:block>
            </fo:table-cell>   
            <fo:table-cell>
                <fo:block>
                    <xsl:value-of select="job"/>
                </fo:block>
            </fo:table-cell>
        </fo:table-row>
    </xsl:template>
</xsl:stylesheet>

There are at least a couple of things that you should notice in the XSLT file, the first is that templates are defined in the file that will be used to generate headers or lists depending on the data that matches during the exploration of the data source file in XML.

If you can see we have a definition for handling header data in the node:

    <xsl:template match=”company”> 

And another one for the list of employees:

    <xsl:template match=”employee”>

Both with different rules. The employee defines that the employee’s data will be displayed in the cells of a table. This table is in turn contained in the company template.

    <fo:table-body>
        <xsl:apply-templates select=”employee”/>
    </fo:table-body>

In this way it is possible to separate and define more clearly, how the data that belongs to the header and perhaps a footer will be displayed, from the data that varies in quantity, such as the list of employees.

Now the Fop libraries in the Maven repository.

<dependency>
  <groupId>org.apache.xmlgraphics</groupId>
  <artifactId>fop</artifactId>
  <version>2.2</version>
</dependency>

And finally the Java code.

package com.geekole; // Name of your java package

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.xmlgraphics.util.MimeConstants;

/**
 *
 * @author geekole.com
 */
public class CreatePDF {

    public static void main(String args[]) {

        try {
            // File name FO
            File xsltFile = new File("/path/report.xsl");
            // XML file that will provide data
            StreamSource xmlSource = new StreamSource(new File("/path/report.xml"));

            FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
            FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
            OutputStream out;
            // PDF File
            out = new FileOutputStream("/path/report.pdf");
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(xmlSource, res);
            out.close();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The file with the style of the PDF

    File xsltFile = new File(“/path/report.xsl”);

The Java code, we define the source data file:

StreamSource xmlSource = new StreamSource(new File(“/path/report.xml”));

We create a Factory object.

    FopFactory fopFactory = FopFactory.newInstance(new File(“.”).toURI());

We define the name of the output PDF file.

    out = new FileOutputStream(“/path/report.pdf”);

And finally we do the transformation.

    transformer.transform(xmlSource, res);

You should note that FOP supports other formats. But the one that is of interest to us in this example is the following:

    MimeConstants.MIME_PDF

Below is the generated PDF document.

Create PDF with Apache FOP in Java

We hope this example of Create a PDF with Apache FOP in Java will be useful to you. More information about FOP on the provider page:

https://xmlgraphics.apache.org/fop/quickstartguide.html