Generate PDF Using Apache FOP (Formatting Objects Processor)
What is FOP? An open-source print and page layout formatter primarily used to produce printed output in the form of PDFs from various XML documents. It’s part of the Apache XML Graphics project, and its primary purpose is to convert XSL-FO (Extensible Stylesheet Language Formatting Objects) documents into various output formats, with PDF being the most common.
Here’s a brief overview of its features and usage:
- XSL-FO Support: Apache FOP supports the XSL-FO standard, which defines a set of elements and properties that describe the visual formatting of an XML document for print or other visual formats.
- Output Formats: While PDF is the most commonly used output format, Apache FOP can also generate other formats like PostScript, PCL, SVG, AWT, and more, depending on the configuration and use case.
- Integration: Apache FOP can be integrated into various Java applications or used as a standalone command-line tool. This makes it versatile and adaptable to different software environments.
- Customisation: Users can customize the behavior and appearance of the output by configuring various parameters, stylesheets, and extensions. This allows for flexibility in meeting specific formatting requirements.
- Performance: While FOP provides powerful capabilities for document formatting, it’s essential to note that complex documents or large volumes of data might require optimization for performance, especially when generating PDFs.
To generate PDF using FOP, you need to setup the spring boot application in your local environment. If you want to setup both spring boot refer this article. Here, please adjust spring boot and java versions according current latest compatible versions.
Once you setup the spring boot application in your local environement, let’s start !
- Add following dependency to pom.xml. This is the dependency we use here to create pdfs.
<!-- Apache FOP dependencies -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.9</version> <!-- Use the latest version -->
</dependency>
</dependencies>
- Create input.xml file and add following code. This file is includes all the data we need to represent in the pdf.
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Spring Boot in Action</title>
<author>Craig Walls</author>
<year>2022</year>
</book>
<book>
<title>Java: The Complete Reference</title>
<author>Herbert Schildt</author>
<year>2023</year>
</book>
</books>ß
- Create stylesheet.xsl. This file includes the UI (style elements) of the pdf.
<?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">
<!-- Root template -->
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm">
<fo:region-body margin="1cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="24pt" font-weight="bold" margin-bottom="20pt">List of Books</fo:block>
<fo:block font-size="18pt" margin-bottom="12pt">Books:</fo:block>
<!-- Loop through each book and format its details -->
<xsl:for-each select="books/book">
<fo:block font-size="16pt" margin-bottom="8pt">
<xsl:value-of select="title"/> by <xsl:value-of select="author"/> (Year: <xsl:value-of select="year"/>)
</fo:block>
</xsl:for-each>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
- Create PdfGenerationController.java file and add following code
package com.example.fop;
import org.apache.fop.apps.FOPException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping("api/pdf")
public class PdfGenerationController {
@Autowired
PdfGenerationService pdfGenerationService;
@PostMapping("/create")
public ResponseEntity<String> createPdf() throws FOPException, IOException {
pdfGenerationService.createPdf();
return ResponseEntity.ok("PDF generated successfully!");
}
}
@RestController
: This annotation indicates that the class is a REST controller, meaning it's intended to handle web requests and produce web responses.
@RequestMapping("api/pdf")
: This annotation specifies that the controller will handle requests that start with /api/pdf
. So, for instance, if the application's base URL is http://localhost:8080
, the full URL to this endpoint would be http://localhost:8080/api/pdf
.
@Autowired
: This annotation is used for automatic dependency injection. It tells Spring to inject an instance of PdfGenerationService
into this controller.
@PostMapping("/create")
: This annotation specifies that the method will handle HTTP POST requests to the /create
endpoint relative to the base URL (/api/pdf/create
when combined with the class-level @RequestMapping
).
public ResponseEntity<String> createPdf() throws FOPException, IOException
: This method definition indicates that: The method returns a ResponseEntity<String>
, which is a Spring representation of an HTTP response. It might throw two exceptions: FOPException
(from Apache FOP) and IOException
(from Java's I/O operations).
— Inside the method:pdfGenerationService.createPdf();
: This line invokes the createPdf()
method of the pdfGenerationService
bean (an instance of PdfGenerationService
), which is presumably responsible for generating the PDF.return ResponseEntity.ok("PDF generated successfully!");
: After successfully generating the PDF, the method returns an HTTP 200 OK response with the message "PDF generated successfully!".
- Create PdfGenerationService.java file
package com.example.fop;
import org.springframework.stereotype.Service;
import org.apache.fop.apps.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
@Service
public class PdfGenerationService {
public void createPdf() throws IOException, FOPException {
File xmlFile = new File("src/main/resources/input.xml");
File xsltFile = new File("src/main/resources/stylesheet.xsl");
File pdfFile = new File("src/main/resources/output.pdf");
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
try (OutputStream out = new FileOutputStream(pdfFile)) {
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
Source src = new StreamSource(xmlFile);
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
}
@Service
: This annotation indicates that the class is a Spring service component, which typically contains business logic.
public void createPdf() throws IOException, FOPException
: This method definition indicates that:
The method does not return any value (void
).
It might throw two exceptions: IOException
(for input/output operations) and FOPException
(from Apache FOP).
File xmlFile = new File("src/main/resources/input.xml");
File xsltFile = new File("src/main/resources/stylesheet.xsl");
File pdfFile = new File("src/main/resources/output.pdf");
These lines specify the paths to the input XML file (input.xml
), the XSLT stylesheet file (stylesheet.xsl
), and the output PDF file (output.pdf
).
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
FopFactory
: Initializes a new FOP factory.
FOUserAgent
: Creates a new Formatting Objects (FO) user agent.
- Inside the
try
block: AnOutputStream
is created for the output PDF file. AnFop
instance is created to represent the PDF output.ATransformer
is set up with the XSLT stylesheet. The XML source (input.xml
) is transformed using the XSLT stylesheet to produce the PDF output (output.pdf
). If any transformation exceptions occur, they’re caught in thecatch
block, and a runtime exception is thrown. - Build and run the application
- Testing using postman
Check your output.pdf in “src/main/resources/output.pdf” path.
Folder Structure