in Coding

Generating PDFs with Java, Flying Saucer and Thymeleaf (Part 1)

Generating PDFs can by a tough job. If you ever worked with templating engines like Jasper you probably know what I mean. On the other side, HTML makes it extremely simple to describe documents. So why not use HTML to generate PDFs?

The following post shows a simple approach to generate PDFs with Thymeleaf templates and Flying Saucer in plain Java.

Example on GitHub: https://github.com/tuhrig/Flying_Saucer_PDF_Generation

PDF rendering pipeline

Our PDF rendering pipeline consists of two basic steps:

  1. Using Thymeleaf, we populate XHTML templates with data in order to receive plain XHTML document.
  2. We save this XHTML document as a PDF using Flying Saucer.

Note that both steps are independent. You could easily use any other templating engine (just as Apache FreeMarker) or even plain XHTML. However, I would prefer Thymeleaf as it is a mature templating engine especially if you work in a Spring environment.

Project Setup

To start rendering, we need three different dependencies which we can find in Maven:

The project structure should look like this:

Thymeleaf Template

The Thymeleaf template which we are using is pretty simple. It just contains a sentence and variable to fill with data. Note that this is a very simple example. Thymeleaf is much more powerful and provides concepts like for-loops, if-statements and Java method calls out of templates. You will find a lot of tutorials on the internet.

To render this template with Thymeleaf, we can use the following code. It will substitute the variable ${name} with actual data. In the end, we will receive plain HTML which we could open in any browser.

PDF Generation

Generating the actual PDF is quite easy now. We basically put the HTML string to the PDF renderer which returns the byte stream of the generated PDF. The basic layout fits for our example, but we could also include some CSS in order to style the PDF.

Advanced Topics

A lot of important topics are not covered in the very basic example above. However, I want to give some external links and hints on a couple of points:

  • You can use CSS in order to generate page numbers with Flying Saucer as described here.
  • Thymeleaf templates can be divided into fragments. Those fragments can be included into other templates and provided reusable building blocks for documents (such as a header or footer). Read more here.

More

You find a running example on GitHub:

https://github.com/tuhrig/Flying_Saucer_PDF_Generation

Part 2

Go to part 2 for a deeper dive into styling with CSS and images.

Best regards,
Thomas

Write a Comment

Comment

24 Comments

    • Hey Manel, sorry for the late answer. An image is actually pretty easy. You can just add a normal HTML image tag to your template:

      However, it’s a little bit tricky to find the right place where the image must be located. My project setup looks like this:

      So the image logo.jpg is located directly in the root folder of the project.

      Best regards,
      Thomas

      • Hi Thomas,

        I tried to put the logo and css file into the /resources folder instead of the root folder. However, the logo and css file were not picked up.
        Can you please advise?

        Thanks

        • Hey Jimmy! The problem is the working directory of FlyingSaucer. By default, FlyingSaucer will look for the files in your root folder. However, you can tell FlyingSaucer to use your /resources folder as the base folder:

    • Hey Ketan. You can set the page size via CSS. To do so, you need to refer to a CSS file in your HTML. In the example below, a CSS file named style.css is used.

      In that CSS file, you can set the page properties. You can also change the font and so on.

      Here’s a link to a Stackoverflow question about the page properties:

      https://stackoverflow.com/questions/17529658/how-to-generate-a-pdf-in-land-scape-orientation-from-a-html-string-using-itext-2

      Hope that helps.

      Best regards,
      Thomas

    • Hey Surja, both ways should work. You can do this in your IDE (just as I did in my example) or in a Spring Boot app. In case of a Spring Boot app you just need to figure out where you want to place the templates. Should they be part of the JAR file (so they cannot be changed after deployment) or should they be outside of the JAR file. You can do it as you like.

    • Without knowing which technology you are using, this is hard to answer. But let’s assume you are using Spring, so you would need to add a new controller for the download. The controller would take the OutputStream to which the PDF is written (currently it’s a FileOutputStream) and return it:

      Note that I have not tested the code, so just take it as some pseudo-code.

      • This works only had to add the produces value for pdf:
        @GetMapping(value = “/pdf”, produces = {“application/json”, “application/x-pdf”})

  1. Hi, what’s the advantage of use this approach over JasperReports? Or when should we use one or the other.

    I try to implement this but instead of using Gradle I used Maven with the same dependencies(exactly same group, artifact and version). I have to add another dependency:

    com.itextpdf
    itextpdf
    5.0.6

    Use the same template and make this change:
    templateResolver.setTemplateMode(“HTML5”);
    because I was getting “Template mode “HTML” has not been configured”

    String html = templateEngine.process(“template”, context);
    //Here I’m printing the html variable but it’s not resolving the variable name.
    What am I doing wrong? Thanks

    • What’s the advantage of use this approach over JasperReports?

      So first of all, Jasper reports are great. I’ve seen applications which used them very successfully in production to generate letters, invoices or order confirmations. However, Jasper reports are designed for reports. They have a report oriented view of data sets with rows and columns. This doesn’t fit well for creating a single document. Jasper’s XML format (*. jrxml) is also a big pain for me personally. I definitely prefer HTML + CSS.

      I have to add another dependency: com.itextpdf:itextpdf:5.0.6

      Yes, you need this library. Gradle will pull it automatically. Maybe that’s different with Maven. I don’t know. I actually have com.itextpdf:itextpdf:5.5.11 on my classpath.

      Use the same template and make this change: templateResolver.setTemplateMode(“HTML5”);

      I would stick to org.thymeleaf.templatemode.TemplateMode.HTML. You definitely don’t need HTML5 feature. Maybe your problem starts somewhere else?

      Here I’m printing the html variable but it’s not resolving the variable name.

      So you how does your HTML look like? For example is <p>Dear [[${data.getFirstname()}]] [[${data.getLastname()}]],</p> printed as <p>Dear ,</p>?

      • Hi Thomas, sorry for the late answer.
        At the end it was a problem with maven. I create a blank project only with the download endpoint and everything works fine, and when return to my old project also works.
        Thank you for your help.
        I saw your pom and I’m using the same dependencies but also this:

        com.itextpdf
        itextpdf
        5.0.6

  2. How to display Unicode (Hex) characters in generated PDF ???

    I am trying to Display Unicode characters in The PDF file but it display ‘??’ signs

    How can I set UTF-8 encoding here ?

  3. Please can you tell me how to render Unicode characters in Generated PDF file, I am trying for that but it shows mw ‘???’ sign

    • Hey Ketan, it seems like your font doesn’t support Unicode characters. You must use another font which supports those characters. Try this:

      1. Add a new font to your CSS: * { font-family: 'Arial Unicode MS'; }
      2. Add this font to your Java code too: renderer.getFontResolver().addFont("arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
      3. And don’t forget to download it and place it in your /resources folder.

      I’ve used this font: https://www.wfonts.com/font/arial-unicode-ms. It worked very well.

      Good luck.

      • Thank you so much for your reply,
        I have added everything that you told me but sill I am not getting Currency symbol

        I want to display currency symbol in my PDF file from Unicode (Hex) numbers,
        Here is my whole code

        JAVA CODE
        context.setVariable(“currencySymbol”,”₹”);

        THYMELEAF CODE

        JAVA CODE (TO CREATE PDF)
        OutputStream outputStream = new FileOutputStream(quoteNumber+”.pdf”);
        BufferedOutputStream bs= new BufferedOutputStream(outputStream);
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(html);
        renderer.getFontResolver().addFont(“arialuni.ttf”, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        renderer.layout();
        renderer.createPDF(bs,true);
        outputStream.close();

        Please Can you tell me what Am I doing wrong. I want to display currency symbol

        I take Unicode value from this website https://www.ip2currency.com/currency-symbol

  4. I am getting a error when I try to run this :

    ERROR: ‘The markup in the document preceding the root element must be well-formed.’
    org.xhtmlrenderer.util.XRRuntimeException: Can’t load the XML resource (using TrAX transformer). org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 3; The markup in the document preceding the root element must be well-formed.

    The start of the HTML for example looks like :

    Assessment

  5. Hi Thomas,

    thank you for this tutorial. I’m currently coding a web-app which is not open-source.

    Itext says that you may use itext library in your project only if you open-source it. Otherwise you have to buy a license.

    When I use this method in your tutorial, since itext is used under the hood, will I get in legal trouble ?

  6. Hi, Thomas Help me

    I am using your code to generate PDF file from Thymeleaf HTML template.
    I am getting very nice PDF but facing one minor issue. I am showing price of products in PDF for multiple currency and using this reference website “https://www.ip2currency.com/currency-symbol” to get Unicode (Hex) value for different currency.

    .Java file Code
    context.setVariable(“currencySymbol”,”₹”); // this is Unicode for Indian rupee sysmbol

    Thymeleaf Template Code

    it is working fine and showing currency symbols for all, except Indian Rupee and United Arab Emirates Dirham.

    Please suggest me how can I display Indian Rupee and United Arab Emirates Dirham currency Symbols in my Generated PDF.

    Thanks in Advance.

    • I have written UNICODE value in this function but it is translated in symbol so I am writing this function again in comment
      context.setVariable(“currencySymbol”,”& # x 2 0 b 9 ;”);

Webmentions

  • Generating PDFs with Java, Flying Saucer and Thymeleaf (Part 2) – Thomas Uhrig October 11, 2018

    I have written UNICODE value in this function but it is translated in symbol so I am writing this function again in comment
    context.setVariable(“currencySymbol”,”& # x 2 0 b 9 ;”);

  • Get your documents written perfectly October 11, 2018

    I have written UNICODE value in this function but it is translated in symbol so I am writing this function again in comment
    context.setVariable(“currencySymbol”,”& # x 2 0 b 9 ;”);