In this post we'll see how to create PDF from XML in Java using Apache FOP.
What is 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.
FOP uses the standard XSL-FO file format as input, lays the content out into pages, then renders it to the requested
output.
Read more about it here- https://xmlgraphics.apache.org/fop/
How to get Apache FOP
Get the FOP download from here.
https://xmlgraphics.apache.org/fop/download.html
I have used fop-2.0 for this example code.
Needed jars (found in the lib and build directory in the fop download)-
- Commons-io
- Commons-logging
- Xml-apis
- Xmlgraphics-commons
- Fop
- Batik-all
- Avalon-framework
<dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>fop</artifactId> <version>2.7</version> <exclusions> <exclusion> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </exclusion> </exclusions> </dependency>
Here we have an exclusion for xml-apis because of the javax.xml package conflict, this package is present in Java too and that's what will be used.
Steps for creating a PDF from XML in Java using Apache FOP
To produce a PDF file from a XML file, first step is that we need an XSLT stylesheet that converts the XML to XSL-FO.
Created XSL-FO file is also an XML file which contains formatted objects.
The second step will be done by FOP when it reads the generated XSL-FO document and formats it to a PDF document.
Creating PDF using FOP Java example
XML used for creating PDF is as follows.<?xml version="1.0"?> <employees> <companyname>ABC Inc.</companyname> <employee> <id>101</id> <name>Ram</name> <designation>Manager</designation> </employee> <employee> <id>102</id> <name>Prabhu</name> <designation>Executive</designation> </employee> <employee> <id>103</id> <name>John</name> <designation>Executive</designation> </employee> </employees>
Stylesheet Used
<?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="employees"> <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 Name: <xsl:value-of select="companyname"/> </fo:block> <fo:block font-size="10pt"> <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="designation = '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="designation"/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> </xsl:stylesheet>
If you see the XSL, first I am looking for the employees element to get the company Name and also there some formatting is done like how many columns are needed and what should be the width. Then I am looking for the employee element and printing the values, also some logic is there to print the field values in bold if the designation is manager.
Copying the output of PDF I got, that will make it easy to understand the XSL.
Java code
import java.io.File; import java.io.IOException; import java.io.OutputStream; import javax.xml.transform.Result; 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.StreamResult; 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 FOPPdfDemo { public static void main(String[] args) { FOPPdfDemo fOPPdfDemo = new FOPPdfDemo(); try { fOPPdfDemo.convertToFO(); } catch (FOPException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TransformerException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Method that will convert the given XML to PDF * @throws IOException * @throws FOPException * @throws TransformerException */ public void convertToPDF() throws IOException, FOPException, TransformerException { // the XSL FO file File xsltFile = new File("D:\\NETJS\\xml\\template.xsl"); // the XML file which provides the input StreamSource xmlSource = new StreamSource(new File("D:\\NETJS\\xml\\Employees.xml")); // create an instance of fop factory FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); // a user agent is needed for transformation FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); // Setup output OutputStream out; out = new java.io.FileOutputStream("D:\\NETJS\\xml\\employee.pdf"); try { // Construct fop with desired output format Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out); // Setup XSLT TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(xsltFile)); // Resulting SAX events (the generated FO) must be piped through to FOP Result res = new SAXResult(fop.getDefaultHandler()); // Start XSLT transformation and FOP processing // That's where the XML is first transformed to XSL-FO and then // PDF is created transformer.transform(xmlSource, res); } finally { out.close(); } } /** * This method will convert the given XML to XSL-FO * @throws IOException * @throws FOPException * @throws TransformerException */ public void convertToFO() throws IOException, FOPException, TransformerException { // the XSL FO file File xsltFile = new File("D:\\NETJS\\xml\\template.xsl"); /*TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource("F:\\Temp\\template.xsl"));*/ // the XML file which provides the input StreamSource xmlSource = new StreamSource(new File("D:\\NETJS\\xml\\Employees.xml")); // a user agent is needed for transformation /*FOUserAgent foUserAgent = fopFactory.newFOUserAgent();*/ // Setup output OutputStream out; out = new java.io.FileOutputStream("D:\\NETJS\\xml\\temp.fo"); try { // Setup XSLT TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(xsltFile)); // Resulting SAX events (the generated FO) must be piped through to FOP //Result res = new SAXResult(fop.getDefaultHandler()); Result res = new StreamResult(out); //Start XSLT transformation and FOP processing transformer.transform(xmlSource, res); // Start XSLT transformation and FOP processing // That's where the XML is first transformed to XSL-FO and then // PDF is created transformer.transform(xmlSource, res); } finally { out.close(); } } }
In the code there are two methods convertToPDF() and convertToFO(), convertToPDF() method is used to convert XML to PDF. convertToFO() method will create the XSL-FO from the XML using the XSLT. If you want to see the created FO which in turn is used to create PDF please call this method.
Create PDF in web application using Apache FOP
In case of web application if you want to provide PDF as a download, here is a Servlet method as reference-
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(); 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("./resources/Employees.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 this topic How to Create PDF From XML in Java Using Apache FOP. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Java Programs Page
Related Topics
You may also like-
Hi, can you specify the version of required jars. I am getting below exception while executing the example "Caused by: java.lang.ClassNotFoundException: org.apache.batik.bridge.FontFamilyResolver". This class is not available in batik-all.jar
ReplyDeletebatik-all-1.8.jar ..It should be in the lib folder
DeleteI am getting the same exception.Can you please provide the jar file download link.Actually org.apache.batik.gvt.font.FontFamilyResolver. I didnt get any jar file in google with this package class org.apache.batik.bridge.FontFamilyResolver
Deletehttp://svn.apache.org/viewvc/xmlgraphics/fop/tags/fop-2_1/lib/
Deletethere you can get batik-all-1.8.jar. Click on the link for batik-all-1.8.jar and you can download the latest revision.
The Issue got resolved,Thank you very much
Deletelog4j:WARN No appenders could be found for logger (org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry).
Deletelog4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Exception in thread "main" java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at org.apache.fop.image.loader.batik.BatikUtil.isBatikAvailable(BatikUtil.java:41)
at org.apache.fop.image.loader.batik.ImageLoaderFactorySVG.isAvailable(ImageLoaderFactorySVG.java:56)
at org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry.registerLoaderFactory(ImageImplRegistry.java:182)
at org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry.discoverClasspathImplementations(ImageImplRegistry.java:111)
at org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry.(ImageImplRegistry.java:79)
at org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry.(ImageImplRegistry.java:87)
at org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry.(ImageImplRegistry.java:71)
at org.apache.xmlgraphics.image.loader.ImageManager.(ImageManager.java:64)
at org.apache.fop.apps.FopFactoryBuilder$FopFactoryConfigImpl.(FopFactoryBuilder.java:373)
at org.apache.fop.apps.FopFactoryBuilder.(FopFactoryBuilder.java:89)
at org.apache.fop.apps.FopFactoryBuilder.(FopFactoryBuilder.java:80)
at org.apache.fop.apps.FopFactoryBuilder.(FopFactoryBuilder.java:70)
at org.apache.fop.apps.FopFactory.newInstance(FopFactory.java:143)
at com.db.ebpp.event.PDFGeneration.convertToPDF(PDFGeneration.java:51)
at com.db.ebpp.event.PDFGeneration.main(PDFGeneration.java:25)
Caused by: java.lang.ClassCastException: org.apache.batik.extension.svg.BatikDomExtension cannot be cast to org.apache.batik.dom.DomExtension
at org.apache.batik.dom.ExtensibleDOMImplementation.getDomExtensions(Unknown Source)
at org.apache.batik.dom.ExtensibleDOMImplementation.(Unknown Source)
at org.apache.batik.anim.dom.SVGDOMImplementation.(Unknown Source)
at org.apache.batik.anim.dom.SVGDOMImplementation.(Unknown Source)
... 17 more
Hi, How to parse the HTML text between XML tags like <description><h1> some description </h1></description>. I would like to render the 'description' node text with 'h1' styling on PDF. Please help me on this.
ReplyDeleteSuppose I have this XML
Delete<?xml version="1.0"?>
<employees>
<description><h1> some description </h1></description>
<employee>
<id>101</id>
<name>Ram</name>
<designation>Manager</designation>
</employee>
</employees>
Then if I change the XSL as provided in the example in the post to have description that line will look like this -
<fo:block role="H1" font-weight="bold" space-after="5mm">Description: <xsl:value-of select="description/h1"/>
Note in the XSL given in the example I have changed the line with company name to have description/h1 instead. For H1 styling "role" attribute has been used.
can i use Apache FOP in andorid?
ReplyDeleteHi,
ReplyDeleteHow can use the above code to generate AFP File?
What if the xml file is dynamic? As in, xml tags vary every time. Then we might need to dynamically generate xsl file. Is this the way? or is there any other approach?
ReplyDeleteXML will always be based on some fixed schema. If it has XSD you can see that ofcourse some elements are required some are optional. If you want to check for optional element while transforming you can use XSL if-else condition for that.
DeleteException in thread "main" java.lang.NoClassDefFoundError: org/apache/xmlgraphics/io/ResourceResolver
ReplyDeleteat org.apache.fop.apps.FopFactoryBuilder.(FopFactoryBuilder.java:70)
at org.apache.fop.apps.FopFactory.newInstance(FopFactory.java:143)
at src.main.java.Fop_Pdf.convertToPDF(Fop_Pdf.java:46)
at src.main.java.Fop_Pdf.main(Fop_Pdf.java:27)
Caused by: java.lang.ClassNotFoundException: org.apache.xmlgraphics.io.ResourceResolver
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 4 more
I get when i use d this code could u help to solve
Have you included all the JARs, ResourceResolver class should be inside Xmlgraphics-commons-2.0.1 jar.
Deletewhile triyng to run above code getting exception which is specified below.
Deletenote:already Xmlgraphics-commons-2.0.1 jar added to builpath
Exception in thread "main" java.lang.NoSuchMethodError: org.apache.xmlgraphics.xmp.Metadata.mergeInto(Lorg/apache/xmlgraphics/xmp/Metadata;)V
at org.apache.fop.render.pdf.PDFRenderingUtil.renderXMPMetadata(PDFRenderingUtil.java:356)
at org.apache.fop.render.pdf.PDFDocumentHandler.handleExtensionObject(PDFDocumentHandler.java:290)
at org.apache.fop.render.intermediate.util.IFDocumentHandlerProxy.handleExtensionObject(IFDocumentHandlerProxy.java:197)
at org.apache.fop.render.intermediate.IFRenderer.startPageSequence(IFRenderer.java:519)
at org.apache.fop.area.RenderPagesModel.startPageSequence(RenderPagesModel.java:97)
at org.apache.fop.layoutmgr.PageSequenceLayoutManager.activateLayout(PageSequenceLayoutManager.java:104)
at org.apache.fop.area.AreaTreeHandler.endPageSequence(AreaTreeHandler.java:267)
at org.apache.fop.fo.pagination.PageSequence.endOfNode(PageSequence.java:128)
at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:347)
at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:181)
at com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:265)
at com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.endElement(ToXMLSAXHandler.java:559)
at temp.template$dot$0()
at temp.applyTemplates()
at temp.applyTemplates()
at temp.transform()
at com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.transform(AbstractTranslet.java:617)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:748)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:359)
at FOPPdfDemo.convertToPDF(FOPPdfDemo.java:71)
at FOPPdfDemo.main(FOPPdfDemo.java:24)
Hi, can we download PDF after converting XML to PDF?? can you please provide the code?
ReplyDeleteIt's there with in the post. In case of web application if you want to provide PDF as a download you need to add few extra lines. Just read the post till end!
Deleteyeah, have seen that. my question here is where we can add few lines in convertToPDF() method? or we can add anywhere in convertToPDF() method?
DeleteI am getting the exception: Please help me
ReplyDeleteException in thread "main" java.lang.NoClassDefFoundError: org/apache/xmlgraphics/io/ResourceResolver
at org.apache.fop.apps.FopFactoryBuilder.(FopFactoryBuilder.java:70)
at org.apache.fop.apps.FopFactory.newInstance(FopFactory.java:143)
at com.FOPPdfDemo.convertToPDF(FOPPdfDemo.java:80)
at com.FOPPdfDemo.main(FOPPdfDemo.java:27)
You should download the xmlGraphic. Look which edition your fop.jar is and download the fitting xmlGraphic.jar.
DeleteThanks for putting this article up. Copying and pasting basically generated the output PDF file when I implemented on FOP-2.2.
ReplyDeleteHi All,
ReplyDeletePlease use the below URL
https://github.com/bzdgn/apache-fop-example
for generating the pdf by using the *.xsl and *.xml.
Extremely interesting! Much obliged additionally to share the blog. Extremely helpful to comprehend the impact of PDF 2 XML
ReplyDeletePDF2XML
There are many ways to manipulate pdf files, including convert pdf to image, extracting PDF text, images, merging multiple pages of pdf, from XML to pdf,etc.
ReplyDeleteHi . I could able to generate PDf through XSLT and FOP. But the problem is Is I am getting XSLT from URL and one XSLT has dependency of another XSLT ( using include ). When i am trying to access it dynamically through web . I could not able to access the child elements
ReplyDeletehi i am getting error
ReplyDeletefile:/F:/template.xsl; Line #3; Column #12; stylesheet requires attribute: version
(Location of error unknown)org.apache.fop.fo.ValidationException: First element must be the fo:root formatting object. Found (Namespace URI: "", Local Name: "employees") instead. Please
make sure you're producing a valid XSL-FO document.
please slove.
Seeing the error looks like you have copied XML at the location where you needed to have the xsl.
DeleteI want the PDF to be created in the server as byte stream and send it to front end as an api response. Basically dont want to save in the server machine. Is that possible with this.
ReplyDeleteHave you gone through this section in the post- Create PDF in web application using Apache FOP
ReplyDelete