June 24, 2022

Generate PDF From XML in Java Using Apache FOP

This post shows how to generate PDF from XML in Java using Apache FOP.

Apache FOP

Apache™ FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. Output formats currently supported include PDF, PS, PCL, AFP, XML (area tree representation), Print, AWT and PNG, and to a lesser extent, RTF and TXT. The primary output target is PDF.

Maven dependency for Apache FOP

To get the Apache FOP related jars in your application’s classpath you need to add following dependency in the pom.xml file.

<dependency>
  <groupId>org.apache.xmlgraphics</groupId>
  <artifactId>fop</artifactId>
  <version>2.3</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Note that java.xml package in xml-apis was conflicting with the java.xml package in JDK 10 that’s why it is excluded.

Alternatively you can download Apache FOP from here- https://xmlgraphics.apache.org/fop/download.html and copy the required jars yourself.

Steps to create PDF from XML using Apache FOP

  1. First you need to transform your XML file to XSL-FO using XSLT.
  2. Then using FOP you transform the XSL-FO to PDF.
XML to PDF in Java using FOP

Creating PDF from XML in Java using Apache FOP – Hello World example

First let’s create a hello world PDF from XML using Apache FOP and Java which just shows the passed name in the PDF.

XML used for this example is a very simple one having just one element name.

name.xml
<?xml version="1.0" encoding="UTF-8"?>
<name>knpcode</name>

You also need a style sheet which is used to transform the XML to XSL-FO.

style.xsl
<?xml version="1.0" encoding="UTF-8"?>
<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>
      <fo:layout-master-set>
        <fo:simple-page-master master-name="simpleA4"
            page-height="29.7cm" page-width="21.0cm" margin="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-family="Helvetica" color="red" font-size="16pt" font-weight="bold">
            Hello, <xsl:value-of select="name"/>!
          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>
</xsl:stylesheet>

Few things to note in this XSL are-

  1. Namespace for fo is added in the XSL- xmlns:fo="http://www.w3.org/1999/XSL/Format"
  2. Here matching is done using “/” root itself.
  3. The value of the name element is extracted from the XML, there are also font and text color settings.

Java program that does the transformation from XML to XSL-FO and from XSL-FO to PDF.

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
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;

public class PDFFromFOP {
  public static void main(String[] args) {
    try {
      File xmlfile = new File("resources\\name.xml");
      File xsltfile = new File("resources\\style.xsl");
      File pdfDir = new File("./Test");
      pdfDir.mkdirs();
      File pdfFile = new File(pdfDir, "hello.pdf");
      System.out.println(pdfFile.getAbsolutePath());
      // configure fopFactory as desired
      final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());		
      FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
      // configure foUserAgent as desired
      // Setup output
      OutputStream out = new FileOutputStream(pdfFile);
      out = new java.io.BufferedOutputStream(out);
      try {
        // Construct fop with desired output format
        Fop fop;      
        fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
				
        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));
					
        // Setup input for XSLT transformation
        Source src = new StreamSource(xmlfile);
        
        // Resulting SAX events (the generated FO) must be piped through to FOP
        Result res = new SAXResult(fop.getDefaultHandler());
		    		
        // Start XSLT transformation and FOP processing
        transformer.transform(src, res);
      } catch (FOPException | TransformerException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } finally {
          out.close();
      }
    }catch(IOException exp){
      exp.printStackTrace();
    }
  }
}
Created PDF
XML to PDF using Java

Creating PDF from XML using Apache FOP – PDF table example

Here is another Java example where data from the XML is shown as a table in PDF. In the XSL you need to specify the matching element and the program loops through that element to create one row in the table for each repetition of the matched element.

organization.xml
<?xml version="1.0" encoding="UTF-8"?>
<organization>
  <orgname>XYZ Pvt. Ltd.</orgname>
  <branch>
    <name>XYZ software services</name>
    <city>Bangalore</city>
    <startdate>12/05/2002</startdate>
  </branch>
  <branch>
    <name>XYZ financial services</name>
    <city>New York City</city>
    <startdate>10/04/1975</startdate>
  </branch>
  <branch>
    <name>XYZ hardware services</name>
    <city>Taipei</city>
    <startdate>20/10/2004</startdate>
  </branch>
</organization>

In the PDF we need one row per branch element and show data for the child elements with in that row.

organization.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    
        xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <!-- Attribute used for table border -->
  <xsl:attribute-set name="tableBorder">
    <xsl:attribute name="border">solid 0.1mm black</xsl:attribute>
  </xsl:attribute-set>
  <xsl:template match="organization">
    <fo:root>
      <fo:layout-master-set>
        <fo:simple-page-master master-name="simpleA4"
              page-height="29.7cm" page-width="21.0cm" margin="1cm">
          <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-family="Helvetica" color="blue" font-weight="bold" space-after="5mm">
            Organization Name: <xsl:value-of select="orgname"/>
          </fo:block>
          <fo:block font-size="10pt">
            <fo:table table-layout="fixed" width="100%" border-collapse="separate">    
              <fo:table-column column-width="5cm"/>
              <fo:table-column column-width="5cm"/>
              <fo:table-column column-width="5cm"/>
              <fo:table-header>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">Name</fo:block>
                </fo:table-cell>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">City</fo:block>
                </fo:table-cell>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">Start Date</fo:block>
                </fo:table-cell>
              </fo:table-header>
              <fo:table-body>
                <xsl:apply-templates select="branch"/>
              </fo:table-body>
            </fo:table>
          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>
  <xsl:template match="branch">
    <fo:table-row>   
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
        <fo:block>
          <xsl:value-of select="name"/>
        </fo:block>
      </fo:table-cell>
     
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
        <fo:block>
          <xsl:value-of select="city"/>
        </fo:block>
      </fo:table-cell>   
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
      <fo:block>
        <xsl:value-of select="startdate"/>
      </fo:block>
      </fo:table-cell>
    </fo:table-row>
  </xsl:template>
</xsl:stylesheet>

In the XSL you can see initial matching is for the root element “organization” then again there is template matching for element "branch". For <branch> element value of the child elements are extracted and displayed with in the table cells in PDF.

Java program that do the transformation from XML to XSL-FO and from XSL-FO to PDF.

public class PDFFromFOP {
  public static void main(String[] args) {
    try {
      File xmlfile = new File("resources\\organization.xml");
      File xsltfile = new File("resources\\organization.xsl");
      File pdfDir = new File("./Test");
      pdfDir.mkdirs();
      File pdfFile = new File(pdfDir, "organization.pdf");
      System.out.println(pdfFile.getAbsolutePath());
      // configure fopFactory as desired
      final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());        
      FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
      // configure foUserAgent as desired        
      // Setup output
      OutputStream out = new FileOutputStream(pdfFile);
      out = new java.io.BufferedOutputStream(out);
      try {
        // Construct fop with desired output format
        Fop fop;                
        fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);    
        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));        
        // Setup input for XSLT transformation
        Source src = new StreamSource(xmlfile);                   
        // Resulting SAX events (the generated FO) must be piped through to FOP
        Result res = new SAXResult(fop.getDefaultHandler());        
        // Start XSLT transformation and FOP processing
        transformer.transform(src, res);
      } catch (FOPException | TransformerException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } finally {
        out.close();
      }
    }catch(IOException exp){
      exp.printStackTrace();
    }
  }
}
created PDF
XML to PDF using Java and Apache FOP

Creating PDF from XML using Apache FOP – In web application

If you want to render the generated PDF on the browser in your web application then you can use something similar to as given below-

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {	
  try{
    FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
    //Setup a buffer to obtain the content length
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    //Setup FOP
    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer(new StreamSource(PATH_TO_XSL));
    //Make sure the XSL transformation's result is piped through to FOP
    Result res = new SAXResult(fop.getDefaultHandler());

    //Setup input
    Source src = new StreamSource(new File("PATH_TO_XML"));

    //Start the transformation and rendering process
    transformer.transform(src, res);

    //Prepare response
    response.setContentType("application/pdf");
    response.setContentLength(out.size());

    //Send content to Browser
    response.getOutputStream().write(out.toByteArray());
    response.getOutputStream().flush();
  }catch(Exception e){
    e.printStackTrace();
  }
}

That's all for the topic Generate PDF From XML in Java Using Apache FOP. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment