The OpenHTMLToPDF is a powerful Java library that generates PDF documents from HTML/HTML5 and CSS content. It uses the PDFBox rendering engine under the hood to convert the HTML templates into high-quality PDF files. It is well-suited to generate PDF documents such as invoices, reports, or certificates.
1. Maven
Before using OpenHTMLToPDF library in the application, we will add its dependencies to the application:
<properties>
<openhtml.version>1.0.10</openhtml.version>
</properties>
<dependency>
<!-- ALWAYS required, usually included transitively. -->
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-core</artifactId>
<version>${openhtml.version}</version>
</dependency>
<dependency>
<!-- Required for PDF output. -->
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>${openhtml.version}</version>
</dependency>
<dependency>
<!-- Required for image output only. -->
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-java2d</artifactId>
<version>${openhtml.version}</version>
</dependency>
The corresponding Gradle dependencies are:
ext {
openhtmlVersion = '1.0.10'
}
dependencies {
implementation "com.openhtmltopdf:openhtmltopdf-core:$openhtmlVersion"
implementation "com.openhtmltopdf:openhtmltopdf-pdfbox:$openhtmlVersion"
implementation "com.openhtmltopdf:openhtmltopdf-java2d:$openhtmlVersion"
}
2. Convert HTML Template to PDF File
In the following example, PdfRendererBuilder is used to build and render PDF documents from HTML. First, we read the HTML template file in the java.io.File object and use the builder object to convert the HTML content to PDF.
The useFastMode() optimizes the rendering process by enabling fast mode (which may reduce rendering precision in some cases).
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class HtmlToPdf {
public static void main(String[] args) throws Exception {
File template = new File(
HtmlToPdf.class.getClassLoader().getResource("templates/report_in.html").toURI());
try (OutputStream os = new FileOutputStream("C:\\temp\\htmltopdf\\report_out.pdf")) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.useFastMode();
builder.withFile(template);
builder.toStream(os);
builder.run();
}
}
}
The following is the HTML content that needs to be printed as PDF:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Invoice</title>
<style>
body {
font-family: Arial, sans-serif;
}
.invoice-container {
max-width: 100%;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.invoice-header {
display: flex;
justify-content: space-between;
align-items: top;
border-bottom: 2px solid #007BFF;
padding-bottom: 10px;
margin-bottom: 20px;
}
.invoice-header h1 {
color: #007BFF;
margin: 0;
}
.invoice-details {
margin-bottom: 20px;
}
.invoice-details p {
margin: 5px 0;
}
.table-container {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
th {
background-color: #007BFF;
color: #fff;
}
tfoot td {
font-weight: bold;
}
.footer {
text-align: center;
margin-top: 20px;
color: #666;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="invoice-container">
<div class="invoice-header">
<h1>Invoice</h1>
</div>
<div class="invoice-details">
<p><strong>Invoice #: </strong>INV-12345</p>
<p><strong>Date: </strong>2024-12-05</p>
<p><strong>Due Date: </strong>2024-12-12</p>
<p><strong>Bill To: </strong>John Doe</p>
<p><strong>Address: </strong>123 Main Street, Springfield</p>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Web Development Services</td>
<td>10</td>
<td>$50.00</td>
<td>$500.00</td>
</tr>
<tr>
<td>SEO Optimization</td>
<td>5</td>
<td>$100.00</td>
<td>$500.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Subtotal</td>
<td>$1,000.00</td>
</tr>
<tr>
<td colspan="3">Tax (10%)</td>
<td>$100.00</td>
</tr>
<tr>
<td colspan="3">Total</td>
<td>$1,100.00</td>
</tr>
</tfoot>
</table>
</div>
<p><strong>Notes: </strong>Payment is due within 7 days. Late payments may incur additional charges.</p>
<div class="footer">
<p>Thank you for your business!</p>
</div>
</div>
</body>
</html>
The generated PDF file is:

2. Generate PDF from Thymeleaf Template
In web applications, such as Spring Boot web applications, we may have dynamic data that needs to be populated in a static Thymeleaf template file before converting to PDF. In this case, we will first need to generate the HTML content from the template and then convert the generated HTML to PDF.
Suppose we have the following simple Thymeleaf template file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Invoice</title>
<style>
body {
font-family: Arial, sans-serif;
}
.invoice {
max-width: 600px;
margin: auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
}
.invoice h1 {
text-align: center;
color: #007BFF;
}
.details {
margin-top: 20px;
}
.details p {
margin: 5px 0;
}
</style>
</head>
<body>
<div class="invoice">
<h1>Invoice</h1>
<div class="details">
<p><strong>Name:</strong> ${name}</p>
<p><strong>Amount:</strong> ${amount}</p>
<p><strong>Date:</strong> ${date}</p>
</div>
</div>
</body>
</html>
Then, we can populate the data and print the file to PDF as follows:
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
public class ThymeleafToPdf {
public static void main(String[] args) {
try {
String htmlContent = getHtmlContent();
// Step 4: Generate PDF from HTML
Path pdfOutput = Path.of("C:\\temp\\htmltopdf\\invoice-thymeleaf.pdf");
try (OutputStream os = Files.newOutputStream(pdfOutput)) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.useFastMode();
builder.withHtmlContent(htmlContent, null);
builder.toStream(os);
builder.run();
}
System.out.println("PDF generated successfully at: " + pdfOutput.toAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getHtmlContent() {
// Step 1: Resolve the template
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
// Step 2: Create Thymeleaf Context and add values
Context context = new Context();
context.setVariable("name", "John Doe");
context.setVariable("amount", "$1,200");
context.setVariable("date", "2024-12-05");
// Step 3: Generate HTML content
String htmlContent = templateEngine.process("invoice", context);
return htmlContent;
}
}
The generated PDF looks like this:

3. Generate PDF from Freemarker Template
Suppose the application uses Freemarker as a template engine. In that case, we can follow a similar process: generate the HTML content from the FTL template by populating the values into it and then convert the HTML content to PDF.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Invoice</title>
<style>
body {
font-family: Arial, sans-serif;
}
.invoice {
max-width: 600px;
margin: auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
}
.invoice h1 {
text-align: center;
color: #007BFF;
}
.details {
margin-top: 20px;
}
.details p {
margin: 5px 0;
}
</style>
</head>
<body>
<div class="invoice">
<h1>Invoice</h1>
<div class="details">
<p><strong>Name:</strong> ${name}</p>
<p><strong>Amount:</strong> ${amount}</p>
<p><strong>Date:</strong> ${date}</p>
</div>
</div>
</body>
</html>
The Java code to generate the PDF file from FTL template file is:
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class FtlToPdf {
public static void main(String[] args) {
try {
// Step 1: Configure FreeMarker
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(FtlToPdf.class, "/templates");
cfg.setDefaultEncoding("UTF-8");
// Step 2: Load the template
Template template = cfg.getTemplate("invoice.ftl");
// Step 3: Prepare data model
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("name", "John Doe");
dataModel.put("amount", "$1,200");
dataModel.put("date", "2024-12-05");
// Step 4: Merge template with data model
StringWriter stringWriter = new StringWriter();
template.process(dataModel, stringWriter);
String htmlContent = stringWriter.toString();
// Step 5: Generate PDF
Path pdfOutput = Path.of("C:\\temp\\htmltopdf\\invoice.pdf");
try (OutputStream os = Files.newOutputStream(pdfOutput)) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.useFastMode();
builder.withHtmlContent(htmlContent, null);
builder.toStream(os);
builder.run();
}
System.out.println("PDF generated successfully at: " + pdfOutput.toAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
}
This will generate the same PDF output as in the thymeleaf template:

4. FAQs
4.1. Fault-Tolerant HTML Parsing
OpenHTMLToPDF does not provide a great support for invalid or incomplete HTML documents, however it inherently tries to be fault-tolerant with HTML parsing as much as possible. If you face issues in parsing a template containing incomplete HTML (such as HTML tags are properly closed) the you can use a library like jsoup to clean and validate your HTML before passing it to OpenHTMLToPDF.
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.16.1</version>
</dependency>
Then sanitize and clean the HTML:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
//...
String htmlContent = templateEngine.process("invoice", context);
//Rebuilds a well-structured HTML
Document document = Jsoup.parse(htmlContent);
String sanitizedHtml = document.html();
//...
builder.withHtmlContent(sanitizedHtml, null);
builder.toStream(os);
builder.run();
//...
4.2. Customize Default Logging
By default, the log messages are sent to a java.util.logging.Logger. Most frameworks, such as Spring Boot, now include the logging facade SLF4J. If you are using one of these frameworks it is recommended to use the SLF4J adaptor.
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-slf4j</artifactId>
<version>${openhtml.version}</version>
</dependency>
//...
XRLog.setLoggerImpl(new Slf4jLogger());
//...
5. Conclusion
This short Java tutorial discussed the different examples of generating PDF files from HTML templates, such as:
- Plain HTML files
- HTML content as String
- Freemarker templates
- Thymeleaf templates
we also discussed how to handle the incomplete/invalid HTML content and configure the default logging to SLF4j.
Happy Learning !!
Comments