Apache FOP
FOP (Formatting Objects Processor) is from the apache group. It is said to be the print formatter driven by XSL formatting objects (XSL-FO). FOP can be downloaded from http://xmlgraphics.apache.org/fop/
Its ability to convert XSL files (*.fo) to PDF certainly makes it worth mention. Basically FOP is great for template based PDF generation. Simple programs can fetch data from any data source and populate such a template, and generate PDF’s on the fly !
XSL-FO (Extensible Stylesheet Language Formatting Objects)
XSL-FO is a language for formatting XML data for output to screen, paper or other media
Required Jars
- xml-apis.jar
- jaxb-api-2.2.3.jar
- fop-1.0.jar
- avalon-framework-4.1.3.jar
- commons-io-2.0.1.jar
- commons-logging-1.0.3.jar
- xmlgraphics-commons-1.4.jar
Steps to generate PDF
Step 1: Create the template(.xsl) for formatting the output in resource folder
Step 2: Convert the java object to XML
Step 3: call the transformer code to convert the xml source to pdf using the xsl template.
Example Code:
template.xsl
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:barcode="org.krysalis.barcode4j.xalan.BarcodeExt" xmlns:common="http://exslt.org/common"
xmlns:xalan="http://xml.apache.org" exclude-result-prefixes="barcode common xalan">
<xsl:template match=" EmployeeData">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simple"
page-height="20cm" page-width="10.5cm" margin-left="0.2cm"
margin-right="0.2cm">
<fo:region-body margin-top="0.5cm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple">
<fo:flow flow-name="xsl-region-body">
<fo:block font-family="Arial" font-size="7pt" font-weight="normal">
<fo:table>
<fo:table-column column-number="1" column-width="3cm" />
<fo:table-column column-number="2" column-width="2cm" />
<fo:table-column column-number="3" column-width="5cm" />
<fo:table-body height="10cm">
<xsl:for-each select="./employeeList/employee">
<fo:table-row border="solid 0.1mm black">
<fo:table-cell text-align="left">
<fo:block>
<xsl:value-of select="name" />
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="center">
<fo:block>
<xsl:value-of select="employeeId" />
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left">
<fo:block>
<xsl:value-of select="address" />
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
EmployeeData.java
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
*/
/**
* @author Debasmita.Sahoo
*
*/
@XmlRootElement(name="EmployeeData")
public class EmployeeData {
public EmployeeData () {
}
private List employeeList;
@XmlElementWrapper(name = "employeeList")
@XmlElement(name = "employee")
public List getEmployeeList() {
return employeeList;
}
public void setEemployeeList (List employeeList) {
this.employeeList = employeeList;
}
}
Employee.java
/**
* @author Debasmita.Sahoo
*
*/
public class Employee {
private String name;
private String employeeId;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmployeeId() {
return employeeId;
}
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
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 PDFHandler {
public static final String EXTENSION = ".pdf";
public String PRESCRIPTION_URL = "template.xsl";
public String createPDFFile(ByteArrayOutputStream xmlSource, String templateFilePath) throws IOException {
File file = File.createTempFile("" + System.currentTimeMillis(), EXTENSION);
URL url = new File(templateFilePath + PRESCRIPTION_URL).toURI().toURL();
// creation of transform source
StreamSource transformSource = new StreamSource(url.openStream());
// create an instance of fop factory
FopFactory fopFactory = FopFactory.newInstance();
// a user agent is needed for transformation
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// to store output
ByteArrayOutputStream pdfoutStream = new ByteArrayOutputStream();
StreamSource source = new StreamSource(new ByteArrayInputStream(xmlSource.toByteArray()));
Transformer xslfoTransformer;
try {
TransformerFactory transfact = TransformerFactory.newInstance();
xslfoTransformer = transfact.newTransformer(transformSource);
// Construct fop with desired output format
Fop fop;
try {
fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, pdfoutStream);
// Resulting SAX events (the generated FO)
// must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
try {
// everything will happen here..
xslfoTransformer.transform(source, res);
// if you want to save PDF file use the following code
OutputStream out = new java.io.FileOutputStream(file);
out = new java.io.BufferedOutputStream(out);
FileOutputStream str = new FileOutputStream(file);
str.write(pdfoutStream.toByteArray());
str.close();
out.close();
} catch (TransformerException e) {
e.printStackTrace();
}
} catch (FOPException e) {
e.printStackTrace();
}
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
e.printStackTrace();
}
return file.getPath();
}
public ByteArrayOutputStream getXMLSource(EmployeeData data) throws Exception {
JAXBContext context;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try {
context = JAXBContext.newInstance(EmployeeData.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(data, outStream);
} catch (JAXBException e) {
e.printStackTrace();
}
return outStream;
}
}
Call the PDF handler Code
TestPDF.java
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
/**
* @author Debasmita.Sahoo
*
*/
public class TestPDF {
public static void main(String args[]){
System.out.println("Hi Testing");
ArrayList employeeList = new ArrayList();
String templateFilePath ="D:/Projects/Sample/pdftest/src/";
Employee e1= new Employee();
e1.setName("Debasmita1 Sahoo");
e1.setEmployeeId("10001");
e1.setAddress("Pune");
employeeList.add(e1);
Employee e2= new Employee();
e2.setName("Debasmita2 Sahoo");
e2.setEmployeeId("10002");
e2.setAddress("Test");
employeeList.add(e2);
Employee e3= new Employee();
e3.setName("Debasmita3 Sahoo");
e3.setEmployeeId("10003");
e3.setAddress("Mumbai");
employeeList.add(e3);
EmployeeData data = new EmployeeData();
data.setEemployeeList(employeeList);
PDFHandler handler = new PDFHandler();
try {
ByteArrayOutputStream streamSource = handler.getXMLSource(data);
handler.createPDFFile(streamSource,templateFilePath);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}