Java Render XML Document as PDF

21,235

Solution 1

This is the solution using itext . Your html content is in the request. And itext is not free. Check out its licensing requirement as it has changed in recent years although it is not very expensive.

public class MyPDFGeneratorService {

    public byte[] generatePdf(final XhtmlPDFGenerationRequest request) {
        try {

            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(this.getDocument(request.getContent()), null);
            renderer.layout();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            renderer.createPDF(baos);
            return this.toByteArray(baos);

        }
        catch (Exception e) {
            throw new PDFGenerationException(
                    "Unable to generate  PDF.", e);
        }
    }

    private Document getDocument(final String content) {
        InputSource is = new InputSource(new BufferedReader(new StringReader(
                content)));
        return XMLResource.load(is).getDocument();
    }


    private byte[] toByteArray(final ByteArrayOutputStream baos)
        throws IOException {
    byte[] bytes = baos.toByteArray();
    baos.close();
    return bytes;

 }

}

Solution 2

Just for the sake of giving an example using fop - here you have it. For everyone to be able to follow this I'm using the fop command line tool.

The same can easily be performed within Java code and then you don't need to have the xml as a file at any time.

XSLT that produce a PDF

<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="content"
        page-width="210mm" page-height="297mm" margin="20mm 20mm 20mm 20mm">
      <fo:region-body/>
    </fo:simple-page-master>
  </fo:layout-master-set>
  <fo:page-sequence master-reference="content">
    <fo:flow flow-name="xsl-region-body">
      <fo:block>
        <xsl:apply-templates />
    </fo:block>
    </fo:flow>
  </fo:page-sequence>
</fo:root>
</xsl:template>

<xsl:template match="@*">
  <xsl:text> </xsl:text>
  <xsl:value-of select="name()" />
  <xsl:text>="</xsl:text>
    <xsl:value-of select="." />
  <xsl:text>"</xsl:text>
</xsl:template>

<xsl:template match="*">
  <xsl:param name="indent">0</xsl:param>
  <fo:block margin-left="{$indent}">
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="name()" />
    <xsl:apply-templates select="@*" />
    <xsl:text>&gt;</xsl:text>
    <xsl:apply-templates>
      <xsl:with-param name="indent" select="$indent+10" />
    </xsl:apply-templates>
    <xsl:text>&lt;/</xsl:text>
    <xsl:value-of select="name()" />
    <xsl:text>&gt;</xsl:text>
  </fo:block>
</xsl:template>

</xsl:stylesheet>

We call this file xml2pdf.xsl

Short explanation of the code

  • The template match="/" mainly builds the pdf except for the row which calls the other template match methods or more precise the template match="*".

  • The template match="" writes the element start and end and calls which in turn calls the template match="@" for each attribute in the element (if any). Finally it calls the

  • The indent parameter gets increased by 10 for each level the template reaches with the select="$indent+10" attribute in the with-param statement.

Using the code

# fop -xsl xml2pdf.xsl -xml sample.xml -pdf result.pdf

Solution 3

Try Googling, there are a number of code snippets. For example: http://www.vogella.com/articles/JavaPDF/article.html

I recommend iText rather than FOP, it's faster, less memory-intensive and you have more control over the result.

Share:
21,235
Javaddict
Author by

Javaddict

Updated on July 15, 2022

Comments

  • Javaddict
    Javaddict almost 2 years

    I have an XML document currently stored as an in-memory string & want to render it as a PDF. In other words, the PDF content will be an XML document. The XML being rendered by the method is generic -- multiple types of XML documents might be sent in.

    I'm having a bit difficulty figuring out how to accomplish using using various Java-based frameworks.

    Apache FOP

    It appears as if this framework require specific transformation for XML elements in the document to FOP entities. Since the method in questions must accept generic XML, I don't think this framework fits my requirement.

    iText

    I've tried rendering a document using a combination of iText/Flying Saucer (org.xhtmlrenderer) and while it does render a PDF, the content only contains space-separated data values and no xml elements or attributes. Using the code & test data below below:

    File

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
      <elem1>value1</elem1>
      <elem2>value2</elem2>
    </root>
    

    Code

    File inputFile = new File(PdfGenerator.class.getResource("test.xml").getFile());
    OutputStream os = new FileOutputStream("c:\\temp\\Sample.pdf");
    ITextRenderer renderer = new ITextRenderer();
    renderer.setDocument(inputFile);
    renderer.layout();
    renderer.createPDF(os);
    os.close();  
    

    Results in a PDF that contains the content values value1 value2, but no tags.

    My question is can someone provide a code snippet for rending a PDF containing XML content using one of the frameworks above, or is there another framework better suited to my needs?

    Edit: I realize the same question was asked here, but it seems the solution presented requires intimate knowledge of the structure of the incoming XML doc in the css file.