Site icon SpringHow

Generate PDF files with Spring Boot using ITextPDF

In this post, we will discuss how to generate PDF files using Spring Boot, thymeleaf, and Itext library.

Understanding Itext PDF

The library helps generate PDF files by either creating each element manually or by converting HTML+CSS into PDF. The methods provided by this library are straightforward. Let’s see both of these methods in action.

Generating PDF from HTML

To start with, We need the following maven dependency.

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>7.1.12</version>
        </dependency>Code language: HTML, XML (xml)

The Itext library comes with a supporting library called html2pdf that can convert Html and CSS to visually pleasing PDF documents. Unlike using Java code, this method is clean to implement. So let us add that dependency as well to our java project.

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>html2pdf</artifactId>
            <version>3.0.1</version>
        </dependency>Code language: HTML, XML (xml)
<!doctype html>
<html lang="en">
<head>
    <title>SpringHow html to pdf</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
     <div>
        <p >Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. Lorum ipsum some text before image. </p>
        <img src="photo.jpg" alt="Orange">
        <p >Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image. Lorum ipsum some text after image.</p>
        <table>
            <tr><th>Product</th><th>Quantity</th><th>Price</th><th>Total</th></tr>
            <tr><td>Jeans</td><td>2</td><td>10.99</td><td>20.98</td></tr>
            <tr><td>Shirt</td><td>2</td><td>7.99</td><td>14.98</td></tr>
        </table>
     </div>
</body>
</html>Code language: HTML, XML (xml)

Call the above HTML as pdf-input.html.I placed appropriate styles in the styles.css file. You can use your own. With these in place, let’s call the HtmlConverter.convertToPdf method in our Java code.

import java.io.*;
import com.itextpdf.html2pdf.HtmlConverter;

public class GeneratePDFUsingHTML {

    public static void main(String[] args) throws IOException {

        HtmlConverter.convertToPdf(new File("./pdf-input.html"),new File("demo-html.pdf"));
    }
}Code language: JavaScript (javascript)

This helper method takes an input HTML file parses it applies CSS and converts it to a pdf output. This approach is simple, isn’t it? It is far better to update the HTML file rather than digging into a thousand lines of code. And here is the result.

HTML to PDF using Java and ITEXT PDF

We can use the above approach anywhere in java applications to generate rich and visually pleasing PDF files. Even though this seems like a great way to create PDF files, We are still lacking the ability to create dynamic PDF files.

Generating PDF from MVC views

Spring MVC with a template engine can provide dynamic HTML content. We can easily convert these into PDF responses with the following approach. For this example, I imported spring-boot-starter-web and spring-boot-starter-thymeleaf for MVC and thymeleaf support to my spring boot project. You may use your own choice of template engine. Take a look at this thymeleaf template below. This will generate us the order Details. Also, I have a helper method from OrderHelper to generate some dummy order content.

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
          name="viewport">
    <meta content="ie=edge" http-equiv="X-UA-Compatible">
    <title>Spring Boot - Thymeleaf</title>
    <link th:href="@{/main.css}" rel="stylesheet"/>
</head>
<body class="flex items-center justify-center h-screen">
<div class="rounded-lg border shadow-lg p-10 w-3/5">
    <div class="flex flex-row justify-between pb-4">
        <div>
            <h2 class="text-xl font-bold">Order #<span class="text-green-600" th:text="${orderEntry.orderId}"></span>
            </h2>
        </div>
        <div>
            <div class="text-xl font-bold" th:text="${orderEntry.date}"></div>
        </div>
    </div>
    <div class="flex flex-col pb-8">
        <div class="pb-2">
            <h2 class="text-xl font-bold">Delivery Address</h2>
        </div>
        <div th:text="${orderEntry.account.address.street}"></div>
        <div th:text="${orderEntry.account.address.city}"></div>
        <div th:text="${orderEntry.account.address.state}"></div>
        <div th:text="${orderEntry.account.address.zipCode}"></div>

    </div>
    <table class="table-fixed w-full text-right border rounded">
        <thead class="bg-gray-100">
        <tr>
            <th class="text-left pl-4">Product</th>
            <th>Qty</th>
            <th>Price</th>
            <th class="pr-4">Total</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="item : ${orderEntry.items}">
            <td class="pl-4 text-left" th:text="${item.name}"></td>
            <td th:text="${item.quantity}"></td>
            <td th:text="${item.price}"></td>
            <td class="pr-4" th:text="${item.price * item.quantity}"></td>
        </tr>
        </tbody>
    </table>
    <div class="flex flex-row-reverse p-5">
        <h2 class="font-medium  bg-gray-200 p-2 rounded">
            Grand Total: <span class="text-green-600" th:text="${orderEntry.payment.amount}"></span>
        </h2>
    </div>
    <h2 class="text-xl font-bold">Payment Details</h2>
    <table class="table-fixed text-left w-2/6 border">
        <tr>
            <th class="text-green-600">Card Number</th>
            <td th:text="${orderEntry.payment.cardNumber}"></td>
        </tr>
        <tr>
            <th class="text-green-600">CVV</th>
            <td th:text="${orderEntry.payment.cvv}"></td>
        </tr>
        <tr>
            <th class="text-green-600">Expires (MM/YYYY)</th>
            <td th:text="${orderEntry.payment.month +'/'+ orderEntry.payment.year}"></td>
        </tr>
    </table>
</div>
</body>
</html>Code language: HTML, XML (xml)
    @RequestMapping(path = "/")
    public String getOrderPage(Model model) throws IOException {
        Order order = OrderHelper.getOrder()
        model.addAttribute("orderEntry", order);
        return "order";
    }Code language: JavaScript (javascript)

With the above setup, Your response when hitting the controller API looks like below.

Sample HTML view to be converted to PDF

To convert this MVC response into PDF, you can simply take over the thymeleaf generated HTML content and convert it into PDF using HtmlConverter.

@RequestMapping(path = "/pdf")
public ResponseEntity<?> getPDF(HttpServletRequest request, HttpServletResponse response) throws IOException {

    /* Do Business Logic*/

    Order order = OrderHelper.getOrder();

    /* Create HTML using Thymeleaf template Engine */

    WebContext context = new WebContext(request, response, servletContext);
    context.setVariable("orderEntry", order);
    String orderHtml = templateEngine.process("order", context);

    /* Setup Source and target I/O streams */

    ByteArrayOutputStream target = new ByteArrayOutputStream();

    /*Setup converter properties. */
    ConverterProperties converterProperties = new ConverterProperties();
    converterProperties.setBaseUri("http://localhost:8080");

    /* Call convert method */
    HtmlConverter.convertToPdf(orderHtml, target, converterProperties);  

    /* extract output as bytes */
    byte[] bytes = target.toByteArray();


    /* Send the response as downloadable PDF */

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_PDF)
            .body(bytes);

}Code language: PHP (php)

Testing Spring Boot PDF Generation

I have written my controller method on /orders/pdf/ and here are the results when I hit the URL from the browser.

Spring Boot PDF generation using ITEXT PDF and Thymeleaf

Here you can see that the response is a PDF document. You can confirm the same by the look of Chrome’s PDF viewer.

Points to Note When using HTML to PDF in Spring Boot

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=order.pdf") 
                .contentType(MediaType.APPLICATION_PDF) 
                .body(bytes);             Code language: JavaScript (javascript)

That’s all I have on this topic for now. The sample is available in the Github repository.

Related

Exit mobile version