laravel-dompdfを使用してPDF化を実装してみた【日本語対応】

# はじめに
webページをPDFでダウンロードできるようにしたいと、実務で要望があったため、今回laravel-dompdfを用いてPDF化を実装してみました。

# laravel-dompdfを採用した理由
PDF化を実装できるライブラリは幾つかあり、laravel-dompdfとlaravel-snappyの２つで迷いましたが、以下のメリット・デメリットを比較して、今回はlaravel-dompdfで実装することとしました。

### laravel-dompdf
<dl>
    <dt>メリット</dt>
    <dd>・導入が簡単である。</dd>
    <dd>・比較的新しいCSSが使用できない。
⇒flexBoxなど</dd>
</dl>
<dl>
    <dt>デメリット</dt>
    <dd>・外部バイナリに依存するため、環境設定が少し複雑である。</dd>
</dl>


### laravel-snappy
<dl>
    <dt>メリット</dt>
    <dd>・高い互換性と精確なレンダリング</dd>
    <dd>・複雑なデザインやスクリプトが含まれるページでも正確にPDFを生成。</dd>
</dl>
<dl>
    <dt>デメリット</dt>
    <dd>・外部バイナリに依存するため、環境設定が少し複雑である。</dd>
</dl>



今回はPDF化したいページのデザインが複雑でもなく、スピード感を求められていたため、laravel-dompdfを採用しました。

# 実装手順

## 1. パッケージのインストールをする
まず、laravel-dompdfのパッケージをインストールします。
```
composer require barryvdh/laravel-dompdf
```

## 2. 日本語に対応するため、日本語対応フォントを用意する
laravel-dompdfは、初期設定では日本語は文字化けしてしまいます。そのため日本語対応のフォントを用意する必要があります。

今回は、「Noto Sans Japanese」を使用しました。

https://fonts.google.com/noto/specimen/Noto+Sans+JP

ダウンロードした後、/storage/fontsにttfファイルを保存してください。

## 3. ControllerにPDFダウンロードの処理を書く
```php:PdfController
use Barryvdh\DomPDF\Facade\Pdf;

public function downloadPdf(){
        $pdf = PDF::loadView('/pdf')
                    ->set_option('compress', 1)
                    ->setPaper('a4', 'portrait'); // 縦A4サイズに指定

        $fileName = '領収書.pdf';
        
        return $pdf->download($fileName);
}
```

## 4. PDF化するビューを用意する
ここでは例として、領収書のテンプレートを用意しています。
```html:pdf.blade.php
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>領収書</title>
    <style>
        /* 基本の文字 */
        @font-face {
            font-family: 'NotoSansJP';
            font-style: normal;
            font-weight: normal;
            src: url('{{ storage_path('fonts/NotoSansJP-Regular.ttf') }}');
        }
        /* 全てのHTML要素に適用 */
        html, body, textarea, table {
            font-family: 'NotoSansJP', sans-serif;
        }
        body {
            padding-top: 5mm;
            width: 190mm;
            height: 297mm;
            margin-left: auto;
            margin-right: auto;
            font-size: 12px;
        }

        .header,
        .footer {
            width: 100%;
            overflow: hidden;
        }

        .left,
        .right {
            width: 48%;
            box-sizing: border-box;
        }

        .left {
            float: left;
        }

        .right {
            float: right;
            text-align: right;
        }

        .clearfix:after {
            content: "";
            display: table;
            clear: both;
        }

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

        table,
        th,
        td {
            border: 1px solid #000;
        }

        th,
        td {
            padding: 8px;
            text-align: left;
        }

        th {
            background-color: #f2f2f2;
        }

        .total {
            text-align: right;
            margin-top: 10px;
        }
    </style>
</head>

<body>
    <p style="font-size:22px; text-align:center; margin-top: 5px; margin-bottom: 5px;">領収書</p>
    <div class="header clearfix">
        <div class="left">
            <p>
                〇〇　〇〇様
            </p>
        </div>
        <div class="right">
            @php($date = new \Carbon\Carbon($order->created_at))
            <p>注文日：〇月〇日</p><br>
            <p>
                株式会社〇〇<br>
                〒〇〇〇-〇〇〇〇<br>
                東京都〇〇〇〇<br>
                登録番号：〇〇〇〇<br>
            </p>
        </div>
    </div>
    <div style="border-top: 1px solid #000; border-bottom: 1px solid #000; text-align:center;">
        <div>
            <p style="font-size: 16px;">合計金額　￥〇〇円</p>
        </div>
    </div>
    <div>
        <div>
            <p>上記、正に領収いたしました。</p>
        </div>
    </div>
    <div>
            <p style="margin: 0;">【サービス料金内訳】</p>
    </div>
    <TABLE class="price" style="margin-top: 0;">
        <TR>
            <Th style="font-weight:normal;">サービス項目</Th>
            <Th style="font-weight:normal;">単価</Th>
            <Th style="font-weight:normal;">時間</Th>
            <Th style="font-weight:normal;">数量</Th>
            <Th style="font-weight:normal;">金額（税抜）</Th>
            <Th style="font-weight:normal;">金額（税込）</Th>
            <Th style="font-weight:normal;">消費税</Th>
        </TR>
        <TR>
            <TD>〇〇</TD>
            <TD>〇〇</TD>
            <TD>〇〇</TD>
            <TD>〇〇</TD>
            <TD>〇〇</TD>
            <TD>〇〇</TD>
            <TD>10%</TD>
        </TR>
    </TABLE>
    <div class="header clearfix" style="border-top: 1px solid #000; border-bottom: 1px solid #000;">
        <div class="left">
            <p style="font-size: 14px; margin:0;">合計金額（税抜）</p>
            <p style="font-size: 14px; margin:0;">合計金額（消費税額）</p>
            <p style="font-size: 14px; margin:0;">合計金額（税込）</p>
        </div>
        <div class="right">
            <p style="font-size: 14px; margin:0;">〇〇円</p>
            <p style="font-size: 14px; margin:0;">〇〇円</p>
            <p style="font-size: 14px; margin:0;">〇〇円</p>
        </div>
    </div>
</body>
</html>
```

# 5. ファイルサイズを小さくする処理を追加する。
これで日本語でPDFとしてダウンロードできるようになりましたが、ファイルサイズが3.5MBほどと大きくなってしまっています。

これは追加した日本語フォントがすべて組み込まれてしまっているため、ファイルサイズを圧迫しています。

そのため、dompdfの設定ファイルに設定を変更しましょう。

設定ファイルを以下のコマンドで作成します。
```
php artisan vendor=publish --provider="Barryvdh"
```
これでconfigフォルダに「dompdf.php」が作成されたと思います。

dompdf.php内の以下の値を「true」に変更してください。
```
"enable_font_subsetting" => true
```

# さいごに
パッケージを使用すると、簡単にPDF化できるので、今後も使っていきたいと思います。
また今回は採用しなかったlaravel-snappyを使用したPDF化も時間がある際に実装してみたいと思います。