OpenHTMLToPDF — это мощная библиотека Java, которая генерирует документы PDF из содержимого HTML/HTML5 и CSS. Она использует встроенный механизм рендеринга PDFBox для преобразования шаблонов HTML в высококачественные файлы PDF. Она хорошо подходит для генерации документов PDF, таких как счета-фактуры, отчеты или сертификаты.
1. Знаток
Перед использованием библиотеки OpenHTMLToPDF в приложении добавим ее зависимости в приложение:
<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>
Соответствующие зависимости Gradle:
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. Преобразовать HTML-шаблон в PDF-файл
В следующем примере PdfRendererBuilder используется для построения и рендеринга PDF-документов из HTML. Сначала мы считываем файл шаблона HTML в объекте java.io.File и используем объект builder для преобразования содержимого HTML в PDF.
Функция useFastMode() оптимизирует процесс рендеринга, включая быстрый режим(что в некоторых случаях может снизить точность рендеринга).
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();}}}
Ниже приведен HTML-контент, который необходимо распечатать в формате 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>
Сгенерированный PDF-файл:

2. Создайте PDF-файл из шаблона Thymeleaf
В веб-приложениях, таких как веб-приложения Spring Boot, у нас могут быть динамические данные, которые необходимо заполнить в статическом файле шаблона Thymeleaf перед конвертацией в PDF. В этом случае нам сначала нужно будет сгенерировать HTML-контент из шаблона, а затем преобразовать сгенерированный HTML в PDF.
Предположим, у нас есть следующий простой файл шаблона Thymeleaf:
<!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>
Затем мы можем заполнить данные и распечатать файл в формате PDF следующим образом:
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 HTMLPath 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 templateClassLoaderTemplateResolver 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 valuesContext context = new Context();context.setVariable("name", "John Doe");context.setVariable("amount", "$1,200");context.setVariable("date", "2024-12-05");// Step 3: Generate HTML contentString htmlContent = templateEngine.process("invoice", context);return htmlContent;}}
Сгенерированный PDF-файл выглядит следующим образом:

3. Создайте PDF-файл из шаблона Freemarker
Предположим, что приложение использует Freemarker в качестве шаблонизатора. В этом случае мы можем следовать аналогичному процессу: сгенерировать HTML-контент из шаблона FTL, заполнив его значениями, а затем преобразовать HTML-контент в 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>
Код Java для генерации PDF-файла из файла шаблона FTL выглядит следующим образом:
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 FreeMarkerConfiguration cfg = new Configuration(Configuration.VERSION_2_3_31);cfg.setClassForTemplateLoading(FtlToPdf.class, "/templates");cfg.setDefaultEncoding("UTF-8");// Step 2: Load the templateTemplate template = cfg.getTemplate("invoice.ftl");// Step 3: Prepare data modelMap<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 modelStringWriter stringWriter = new StringWriter();template.process(dataModel, stringWriter);String htmlContent = stringWriter.toString();// Step 5: Generate PDFPath 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();}}}
Это сгенерирует тот же PDF-файл, что и в шаблоне thymeleaf:

4. Часто задаваемые вопросы
4.1. Отказоустойчивый анализ HTML
OpenHTMLToPDF не обеспечивает большой поддержки для недействительных или неполных HTML-документов, однако он по своей сути пытается быть отказоустойчивым при разборе HTML настолько, насколько это возможно. Если вы сталкиваетесь с проблемами при разборе шаблона, содержащего неполный HTML(например, теги HTML правильно закрыты), вы можете использовать библиотеку, например jsoup, для очистки и проверки вашего HTML перед его передачей в OpenHTMLToPDF.
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.16.1</version></dependency>
Затем очистите и очистьте HTML:
import org.jsoup.Jsoup;import org.jsoup.nodes.Document;//...String htmlContent = templateEngine.process("invoice", context);//Rebuilds a well-structured HTMLDocument document = Jsoup.parse(htmlContent);String sanitizedHtml = document.html();//...builder.withHtmlContent(sanitizedHtml, null);builder.toStream(os);builder.run();//...
4.2. Настройка ведения журнала по умолчанию
По умолчанию сообщения журнала отправляются в java.util.logging.Logger. Большинство фреймворков, таких как Spring Boot, теперь включают фасад ведения журнала SLF4J. Если вы используете один из этих фреймворков, рекомендуется использовать адаптер SLF4J.
<dependency><groupId>com.openhtmltopdf</groupId><artifactId>openhtmltopdf-slf4j</artifactId><version>${openhtml.version}</version></dependency>
//...XRLog.setLoggerImpl(new Slf4jLogger());//...
5. Заключение
В этом кратком руководстве по Java рассматриваются различные примеры создания PDF-файлов из HTML-шаблонов, такие как:
- Простые HTML-файлы
- HTML-контент как строка
- Шаблоны Freemarker
- Шаблоны тимелифа
мы также обсудили, как обрабатывать неполный/недействительный HTML-контент и настроить ведение журнала по умолчанию в SLF4j.