Spring Boot Web Download PDF File from HTML Template

Tags: Flying Saucer PDF pdf download pdf export pdf TemplateEngine ITextRenderer Thymeleaf IOUtils ByteArrayInputStream ByteArrayOutputStream Thymeleaf

In this Spring Boot tutorial we learn how to implement a Spring Boot Web application which allow user to download the .pdf files. To make it flexible to export PDF file we also implement the PDF export feature which allow dynamically generate PDF file from a HTML template file in Thymeleaf format.

Table of contents

  1. Create New Spring Boot Project
  2. Add Flying Saucer PDF and Apache Commons IO libraries to the Spring Boot project
  3. Implement Export PDF Service Interface and Class
  4. Add HTML Template file to generate PDF File
  5. Implement Entity classes
  6. Implement Controller class and HTML view
  7. Complete Source Code and Run the Web Application
  8. Download The Source Code

Create New Spring Boot Project

Open IntelliJ IDEA, select the menu File > New > Project.

On the New Project dialog, select Spring Initializr and click Next button.

Spring Boot Web Download PDF File from HTML Template

On the Spring initializr Project Settings dialog input the new project information as below and click Next button.

  • Group: dev.simplesolution
  • Artifact: spring-boot-download-pdf
  • Version:1.0.0
  • Name: spring-boot-download-pdf
  • Description: Spring Boot Download PDF file from HTML Template
  • Package: dev.simplesolution.downloadpdf

Spring Boot Web Download PDF File from HTML Template

On the Dependencies dialog, select below dependencies and click Next button.

  • Spring Web
  • Thymeleaf

Spring Boot Web Download PDF File from HTML Template

Select the location for your project and click Finish button to create new Spring Boot project.

Spring Boot Web Download PDF File from HTML Template

You can also create new Spring Boot project using Spring Initializr online tool at start.spring.io as below screenshot.

Spring Boot Web Download PDF File from HTML Template

Add Flying Saucer PDF and Apache Commons IO libraries to the Spring Boot project

To use the Flying Saucer PDF and Apache Commons IO libraries in the Maven project, add the following dependency to the pom.xml file.

<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.22</version>
</dependency>

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

If you are using Gradle, add the following dependency to the build.gradle file.

implementation group: 'org.xhtmlrenderer', name: 'flying-saucer-pdf', version: '9.1.22'

implementation group: 'commons-io', name: 'commons-io', version: '2.11.0'

Implement Export PDF Service Interface and Class

In this step we implement the PDF export service which export PDF file content to an array of bytes from a given template file name and input data to dynamically populate with Thymeleaf template.

Adding a new package named dev.simplesolution.downloadpdf.service and add a new interface named ExportPdfService as below.

src/main/java/dev/simplesolution/downloadpdf/service/ExportPdfService.java

package dev.simplesolution.downloadpdf.service;

import java.io.ByteArrayInputStream;
import java.util.Map;

public interface ExportPdfService {
    ByteArrayInputStream exportReceiptPdf(String templateName, Map<String, Object> data);
}

Adding new package named dev.simplesolution.downloadpdf.service.impl and implement a new service class named ExportPdfServiceImpl as below.

src/main/java/dev/simplesolution/downloadpdf/service/impl/ExportPdfServiceImpl.java

package dev.simplesolution.downloadpdf.service.impl;

import com.lowagie.text.DocumentException;
import dev.simplesolution.downloadpdf.service.ExportPdfService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Map;

@Service
public class ExportPdfServiceImpl implements ExportPdfService {
    private Logger logger = LoggerFactory.getLogger(ExportPdfServiceImpl.class);

    @Autowired
    private TemplateEngine templateEngine;

    public ByteArrayInputStream exportReceiptPdf(String templateName, Map<String, Object> data) {
        Context context = new Context();
        context.setVariables(data);
        String htmlContent = templateEngine.process(templateName, context);

        ByteArrayInputStream byteArrayInputStream = null;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocumentFromString(htmlContent);
            renderer.layout();
            renderer.createPDF(byteArrayOutputStream, false);
            renderer.finishPDF();
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        } catch (DocumentException e) {
            logger.error(e.getMessage(), e);
        }

        return byteArrayInputStream;
    }
}

Add HTML Template file to generate PDF File

In this step we add a HTML template file which be used as a template file to generate the PDF. For example in this project we want to generate the receipt file in PDF format. In your templates directory under project’s resources directory add a new receipt.html template file as below.

src/main/resources/templates/receipt.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <style>
        h1 {
            color: #25a7e7;
            text-align: center;
        }
        .receipt-header {
            width: 100%;
        }
        .receipt {
            width: 100%;
        }
        .receipt, .receipt th, .receipt td {
            border: 1px solid #25a7e7;
            border-collapse: collapse;
        }
        .receipt th {
            background-color: #25a7e7;
            color: white;
        }
        .total {
            text-align: right;
        }
    </style>
</head>
<body>
<h1>Receipt</h1>
<div>
    <table class="receipt-header">
        <tr>
            <td>
                <table>
                    <tr>
                        <th>Bill To:</th>
                    </tr>
                    <tr>
                        <td th:text="'Company Name: ' + ${customer.companyName}"></td>
                    </tr>
                    <tr>
                        <td th:text="'Address: ' + ${customer.address}"></td>
                    </tr>
                    <tr>
                        <td th:text="'Email: ' + ${customer.email}"></td>
                    </tr>
                    <tr>
                        <td th:text="'Phone: ' + ${customer.phone}"></td>
                    </tr>
                </table>
            </td>
            <td align="right">
                <img width="140" src="https://simplesolution.dev/images/Logo_S_v1.png" />
                <br />
                Simple Solution
            </td>
        </tr>
    </table>
</div>

<br />
<table class="receipt">
    <tr>
        <th>Item #</th>
        <th>Description</th>
        <th>Quantity</th>
        <th>Unit Price</th>
        <th>Total</th>
    </tr>
    <tr th:each="item, iterStat: ${receiptItems}">
        <td th:text="${iterStat.index + 1}"></td>
        <td th:text="${item.description}"></td>
        <td th:text="${item.quantity}"></td>
        <td th:text="${item.unitPrice}"></td>
        <td th:text="${item.total}"></td>
    </tr>
    <tr>
        <td class="total" colspan="4"><b>Total</b></td>
        <td><b th:text="${#aggregates.sum(receiptItems.![total])}"></b></td>
    </tr>
</table>
</body>
</html>

Implement Entity classes

In the template file at above step we can see that it need to pass variables of customer information and a list of receipt items. In this step we implement entity classes to pass data to the receipt template in later steps.

Add new Java package named dev.simplesolution.downloadpdf.entity and implement below classes.

src/main/java/dev/simplesolution/downloadpdf/entity/Customer.java

package dev.simplesolution.downloadpdf.entity;

public class Customer {
    private String companyName;
    private String contactName;
    private String address;
    private String email;
    private String phone;

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public String getContactName() {
        return contactName;
    }

    public void setContactName(String contactName) {
        this.contactName = contactName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

src/main/java/dev/simplesolution/downloadpdf/entity/ReceiptItem.java

package dev.simplesolution.downloadpdf.entity;

public class ReceiptItem {
    private String description;
    private Integer quantity;
    private Double unitPrice;
    private Double total;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public Double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(Double unitPrice) {
        this.unitPrice = unitPrice;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }
}

Implement Controller class and HTML view

At this step we implement the controller class and html view to show the web page allow user to download file via their browser.

Add a new Java package named dev.simplesolution.downloadpdf.controller and implement new controller class named DownloadPdfController.

src/main/java/dev/simplesolution/downloadpdf/controller/DownloadPdfController.java

package dev.simplesolution.downloadpdf.controller;

import dev.simplesolution.downloadpdf.entity.Customer;
import dev.simplesolution.downloadpdf.entity.ReceiptItem;
import dev.simplesolution.downloadpdf.service.ExportPdfService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class DownloadPdfController {

    @Autowired
    private ExportPdfService exportPdfService;

    @RequestMapping("/")
    public String index() {
        return "index";
    }

    @GetMapping("/downloadReceipt")
    public void downloadReceipt(HttpServletResponse response) throws IOException {
        Map<String, Object> data = createTestData();
        ByteArrayInputStream exportedData = exportPdfService.exportReceiptPdf("receipt", data);
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=receipt.pdf");
        IOUtils.copy(exportedData, response.getOutputStream());
    }

    private Map<String, Object> createTestData() {
        Map<String, Object> data = new HashMap<>();
        Customer customer = new Customer();
        customer.setCompanyName("Simple Solution");
        customer.setContactName("John Doe");
        customer.setAddress("123, Simple Street");
        customer.setEmail("contact@simplesolution.dev");
        customer.setPhone("123 456 789");
        data.put("customer", customer);

        List<ReceiptItem> receiptItems = new ArrayList<>();
        ReceiptItem receiptItem1 = new ReceiptItem();
        receiptItem1.setDescription("Test Item 1");
        receiptItem1.setQuantity(1);
        receiptItem1.setUnitPrice(100.0);
        receiptItem1.setTotal(100.0);
        receiptItems.add(receiptItem1);

        ReceiptItem receiptItem2 = new ReceiptItem();
        receiptItem2.setDescription("Test Item 2");
        receiptItem2.setQuantity(4);
        receiptItem2.setUnitPrice(500.0);
        receiptItem2.setTotal(2000.0);
        receiptItems.add(receiptItem2);

        ReceiptItem receiptItem3 = new ReceiptItem();
        receiptItem3.setDescription("Test Item 3");
        receiptItem3.setQuantity(2);
        receiptItem3.setUnitPrice(200.0);
        receiptItem3.setTotal(400.0);
        receiptItems.add(receiptItem3);

        data.put("receiptItems", receiptItems);
        return data;
    }
}

To add the HTML view page add index.html to the templates directory as below.

src/main/resources/templates/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Download PDF File - simplesolution.dev</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
</head>
<body class="container">
    <h2>Spring Boot Web Application Download PDF File</h2>
    <a href="http://localhost:8080/downloadReceipt" class="btn btn-primary">Click Here to Download Your Receipt</a>
</body>
</html>

Complete Source Code and Run the Web Application

At this step we have finished implementing of Spring Boot web application that allow user to download .pdf files.

Your source code should be structured as below screenshot.

Spring Boot Web Download PDF File from HTML Template

Run the web application and open http://localhost:8080/ on your browser to access the web page below.

Spring Boot Web Download PDF File from HTML Template

In the above web page user can click on the button to download the PDF file. After download user can open the file and see the content as below.

Spring Boot Web Download PDF File from HTML Template

Download The Source Code

The source code in this article can be found at: github.com/simplesolutiondev/spring-boot-download-pdf

or clone at:

git clone https://github.com/simplesolutiondev/spring-boot-download-pdf.git

or download at:

Download Source Code

Happy Coding 😊

Spring Boot Generate PDF File from HTML Template

Java Export PDF File from HTML Template

Java Convert HTML to PDF

Spring Boot Web Convert HTML String to PDF File

Java Convert HTML to Image