JavaでHTMLファイルをpdfに変換する
これは何
HTMLファイルをPDFファイルに変換する方法のメモです。
前提
htmlファイルは以下の手順で作成したものをそのまま使う想定です。
ライブラリなど
iTextとflying-saucer-pdfが今回のpdf変換で使うライブラリです。
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/com.lowagie/itext --> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf --> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.1.16</version> </dependency>
ディレクトリ構成
outputディレクトリの下にhtmlファイルとpdfを出力する想定です。 html出力先にfontsディレクトリ及び、フォントファイルが無いとpdf変換時に日本語が出力されません。
src └ main ├ java │ └ sample │ └ Main.java └ resource └ sample.html output └ fonts └ ipag.ttf
Javaプログラム
package sample; import java.io.*; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Calendar; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.context.IContext; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; import org.xhtmlrenderer.pdf.ITextRenderer; public class Main { public void execute(String... args) throws Exception { // テンプレートエンジンを初期化する final TemplateEngine engine = initializeTemplateEngine(); // コンテキストを生成する final IContext ctx = makeContext(args); // 今回はWriter経由で結果を出力するのでWriterも初期化 final Writer writer = new FileWriter("output/sample.html"); // テンプレート名とコンテキストとWriterを引数としてprocessメソッドをコール engine.process("sample", ctx, writer); // Writerをクローズ writer.close(); // ここからがpdf出力用 File html = new File("output/sample.html"); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(html); renderer.layout(); String outputFile = "output/report.pdf"; try (OutputStream os = new FileOutputStream(outputFile)) { renderer.createPDF(os); } } private TemplateEngine initializeTemplateEngine() { // エンジンをインスタンス化 final TemplateEngine templateEngine = new TemplateEngine(); // テンプレート解決子をインスタンス化(今回はクラスパスからテンプレートをロードする) final ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); // テンプレートモードはXHTML resolver.setTemplateMode("XHTML"); // クラスパスのtemplatesディレクトリ配下にテンプレートファイルを置くことにする resolver.setPrefix("templates/"); // テンプレートの拡張子はhtml resolver.setSuffix(".html"); // テンプレート解決子をエンジンに設定 templateEngine.setTemplateResolver(resolver); return templateEngine; } private IContext makeContext(String... args) { final IContext ctx = new Context(); // 変数マップにテンプレート変数を設定 String[] values = {"aaa", "bbb"}; ((Context) ctx).setVariable("args", values); SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar cal = Calendar.getInstance(); ((Context) ctx).setVariable("today", dateFormat.format(cal.getTime())); return ctx; } public static void main(String... args) throws Exception { new Main().execute(args); } }
htmlファイル
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Sample</title> <!-- スタイルシートの中でフォントを明示的に指定しないとpdf出力時に日本語が出ない --> <style> @font-face{ font-family: "IPAGothic"; src: url("fonts/ipag.ttf"); -fs-pdf-font-embed: embed; -fs-pdf-font-encoding: Identity-H; } body{ font-family: "IPAGothic"; } </style> </head> <body> <h1>Arguments</h1> <p> The sample application received <span th:text="${#arrays.isEmpty(args)} ? 'no' : 'some'">...</span> arguments. </p> <p>今日 is: <span th:text="${today}">13 February 2011</span></p> <ul> <li th:each="a : ${args}"><span th:text="${a}">...</span></li> </ul> </body> </html>
出来上がったpdf
こんな感じのpdfがhtmlファイル経由で出力できます。
感想
JasperReportsでGUIで帳票のレイアウトを決めるのもいいけど、Thymeleafでhtmlを出力して、iTextでpdfに変換する方が可読性がいいかもしれない。