OpenPDFでPDFを作成して、ダウンロードまたはビューを表示させる

今日も備忘録です。

## このドキュメントについて
下記2点について記載していきます。

1. PDFをダウンロード、ビュー表示させる方法
2. OpenPDFを利用したPDF作成方法

※以下File操作系のクラスやメソッドについては、事前知識として必要
①：FileInputStream/FileOutputStreamクラス
②：Filesクラス
③：Pathクラスなど

## 実行環境

・Java17
・springboot 3.0.2

## 実装

### ①PDFをダウンロード、ビュー表示させる
PDFをダウンロードさせたり、ビュー表示させる方法は大きく分けて2つある。

#### 方法① : HttpServletResponseを利用する
```java
var response = new HttpServletResponse();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "inline");

try(var inputStream = new BufferedInputStream(new FileInputStream(Fileクラス));
    var res = response.getOutputStream()) {
    res.write(inputStream.readAllBytes());
} catch (IOException e) {
    throw new RuntimeException("ファイル操作で問題発生", e);
}
```

説明：
1. HttpServletResponseにContentTypeをpdfで設定する
1. 1に合わせてContent-Dispositionに"inline"を設定する
inlineは、ダウンロードではなくビューを表示する設定

1. HttpServletResponseの出力ストリームを取得して、PDFのバイトデータを書き込む


※レスポンスの返却とかは不要

#### 方法② : ResponseEntityを利用する
```java
public ResponseEntity<byte[]> otherPdfDl(HttpServletResponse response) {
    var input = Paths.get("pdfを置いているパス");
    var respHeader = new HttpHeaders();
    respHeader.setContentType(MediaType.APPLICATION_PDF);
    respHeader.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.pdf");

    try (var inputStream = new BufferedInputStream(new FileInputStream(Fileクラス))) {
        return new ResponseEntity<byte[]>(inputStream.readAllBytes(), respHeader, HttpStatus.OK);
    } catch (IOException e) {
            throw new RuntimeException("ファイル操作で問題発生", e);
    }
}
```

説明：
1. HttpHeadersクラスをインスタンス化する
1. ContentTypeをpdfを設定する
1. 2に合わせてContent-Dispositionに"attachment"を設定する
attachmentは、ダウンロードする設定

1. ResponseEntityをインスタンス化し、引数に設定したヘッダー情報、PDFのバイトデータ、ステータスを設定する


出来上がったPDFを表示、ダウンロードさせるだけなら上記でOK！

### ②OpenPDFを利用してPDFを作成する
```gradle
// gradle
implementation group: 'org.xhtmlrenderer', name: 'flying-saucer-pdf-openpdf', version: '9.1.11'
```

```java
Path pdfTempFile = null;
try {
    pdfTempFile = Files.createTempFile("temp", ".pdf");
} catch (IOException e) {
    throw new RuntimeException("一時ファイル作成でエラーが発生しました", e);
}

var document = new Document();
PdfWriter pdfWriter = null;
try {
    pdfWriter = PdfWriter.getInstance(document, new BufferedOutputStream(new FileOutputStream(pdfTempFile.toFile())));
    var baseFont = BaseFont.createFont("HeiseiMin-W3", "UniJIS-UCS2-H", BaseFont.NOT_EMBEDDED);
    // タイトル用
    var titleFont = new Font(baseFont, 20f, Font.ITALIC, Color.RED);
    // 本文用
    var font = new Font(baseFont, 12f, java.awt.Font.PLAIN, Color.BLACK);
    document.open();
    document.add(new Paragraph("タイトル", titleFont));
    document.add(new Paragraph("内容", font));
    document.close();
    pdfWriter.close();
} catch (FileNotFoundException e) {
     throw new RuntimeException("ファイルが存在しません", e);
} catch (DocumentException e) {
      throw new RuntimeException("ドキュメント操作でエラーが発生しました", e);
} catch (IOException e) {
      throw new RuntimeException("ファイル操作でエラーが発生しました", e);
}

```
説明：
1. 作成したPDFを一時的に置いておける一時ファイルを作成する
Files.createTempFile(prefix, suffix)

1. pdfWriterとdocumentのインスタンスを取得する
・pdfWriter：PDFドキュメントに対して直接書き込むためのAPIを提供
・document：PDFドキュメントを作成するためのベースクラス

3. PDFへの書き込みで日本語が利用できないので、利用できるように設定
・UniJIS-UCS2-Hこれを設定することで、PDFに日本語が表示できる

4. documentに内容をaddしていく、この時に第二引数にfontを設定
4. pdfWriterとdocumentをcloseする
・closeする順番が違うと、PDFファイルに必要なすべての情報が書き込まれていないため、エラーが起こる。documentからcloseする

6. 1で作成した一時ファイルにpdfが作成されているので、そのパスを使って後の処理をすれば、ダウンロードでもビュー表示でも可能

### PDF作成の注意点
1. 基本的にそのままでは日本語が利用できない。
例えば、ファイル名でも日本語が利用できないので、URLEncoder.encode()を利用して、エンコードしてあげる必要がある。

2. htmlを読み込んでPDFに落とし込む時は、ITextRendererを利用する必要がある？
今回紹介したやり方ではhtmlをPDFにはめ込むことができなかった。
ITextRendererを利用すると簡単にhtmlをはめこめた。
```java
var render = new ITextRenderer();
render.setDocumentFromString(stringHtml);
render.layout();
render.createPDF(res);
```
## まとめ
バイトデータをレスポンスに設定して返却する方法とHttpServletResponseに直接書き込む方法があることを学びました。
また、ビュー表示やダウンロードの方法やファイル名などに日本語を利用する時は、URLエンコードしないといけないことも学べました。

PDF作成やダウンロードの処理について、もっと簡単なやり方もあれば教えてほしいです。