LoginSignup
5
1

はじめに

SpringBootを使った、管理サイトを構築予定です。
その中の要件としてPDFを出力したいというものがあったので調べてみました。
もちろん有償のライブラリを使えばできるのは知ってましたが、Google Maps Platformを
使ったりランニングコストがかさむので、無償にこだわって調査から開始

やってみる

環境

Spring Boot 3.3.0

PDFの作成には、以下を利用しています。

日本語フォントファイルを格納

無料で使えるフォントファイルを取得します。

ダウンロードしたファイルを解凍して、以下の通り格納
src/main/resources/fonts/NotoSansJP-Regular.ttf

Gradleの設定

PDF変換のために、以下の2行を追加してください。

    implementation 'com.openhtmltopdf:openhtmltopdf-core:1.0.10'
    implementation 'com.openhtmltopdf:openhtmltopdf-pdfbox:1.0.10'

Controllerの準備

コントローラを作っていきます。
generate-pdfをGetで呼び出すと、PDFを返却するように実装していきます。

package com.example.demo.controller;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;

@Controller
public class PdfController {

    @Autowired
    private SpringTemplateEngine templateEngine;

    @GetMapping("/generate-pdf")
    public ResponseEntity<InputStreamResource> generatePdf(Model model) throws IOException {
        // テンプレートエンジンにデータを渡す
        Context context = new Context();
        context.setVariable("message", "こんにちは、PDFBox!");

        // HTMLを生成
        String html = templateEngine.process("pdf-template", context);

        // HTMLをPDFに変換
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfRendererBuilder builder = new PdfRendererBuilder();

        builder.withHtmlContent(html, null);

        builder.useFont(() -> getClass().getResourceAsStream("/fonts/NotoSansJP-Regular.ttf"), "Noto Sans CJK JP");
        builder.toStream(outputStream);
        builder.run();

        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", "inline; filename=example.pdf");

        return ResponseEntity.ok()
                .headers(headers)
                .contentType(MediaType.APPLICATION_PDF)
                .body(new InputStreamResource(inputStream));
    }

}

CSSの準備

CSSファイルを作っていきます。
src/main/resources/static/css/style.css
に格納してください。


body {
    font-family: 'Noto Sans CJK JP', sans-serif;
}

.container {
    width: 80%;
    margin: 50px auto;
    padding: 20px;
    border: 1px solid #333;
    border-radius: 10px;
}

.header {
    text-align: center;
    margin-bottom: 20px;
    font-size: 24px;
}

.content {
    line-height: 1.6;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 20px;
}

table, th, td {
    border: 1px solid #333;
    padding: 10px;
    text-align: center;
}

th {
    background-color: #f2f2f2;
}

Imageの準備

サンプルなので画像はなんでもいいのですが、以下に格納します。
src/main/resources/static/images/example.jpg

Thymeleafの準備

以下のような内容で、ファイルを作成し
src/main/resources/templates/pdf-template.html
に、格納します。

ポイントは、CSSとか、画像のパスの指定の仕方ですね。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="UTF-8" />
	<title>PDF Example</title>

	<link rel="stylesheet" type="text/css" href="classpath:/static/css/style.css" />	
</head>

<body>
	<div class="container">
		<div class="header">
		</div>
		<div class="content">
			<p th:text="${message}"></p>
			<table>
				<thead>
					<tr>
						<th>列1</th>
						<th>列2</th>
						<th>列3</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td>データ1</td>
						<td>データ2</td>
						<td>データ3</td>
					</tr>
					<tr>
						<td>データ4</td>
						<td>データ5</td>
						<td>データ6</td>
					</tr>
				</tbody>
			</table>
			<div class="image-container">
				<img src="classpath:/static/images/example.jpg" alt="Sample Image" />
				<p>このように、画像をPDFに含めることができます。</p>
			</div>
		</div>
	</div>
</body>

</html>

実行結果

http://localhost:8080/generate-pdf
へアクセスするとこんな結果がPDFとして表示できました。

image.png

感想

CSSを使ってHTMLを成型できるので、ほぼどんなPDFでも作成できそうな感覚。
画像ファイルと、CSSをクラスパス上のものを設定するのに結構苦戦しましたが一度できてしまえば、たいしたことはなさそうです。あとは繰り返し項目とかの設定ができるとかなり実用的かなと思います。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1