CakePHP 4 に mPDF 8 を導入してテンプレートから PDF を生成

はじめに
業務用システムなどを開発していると、様々な書類を PDF 形式で発行する場合があると思います。
今日は CakePHP 4 で mPDF 8 を使い、PDFを生成してダウンロードする方法をご紹介します。
- CakePHP
- 4.0.3
- mPDF
- 8.0.5
- PHP
- 7.3.11
- OS
- Win 10 Home
1. mPDF を Composer でインストール
下記公式ドキュメントにある通り composer でインストールします。
ドキュメントには v7.x などの表記がありますが、インストールされるバージョンは 8.0 です。
- Installation v7+ – Installation & Setup – mPDF Manual
- https://mpdf.github.io/installation-setup/installation-v7-x.html
> cd \path\to\cakephp4
> composer require mpdf/mpdf
2. PDFをダウンロードするサンプル
最初のサンプルとして PDF ファイルを生成し、ダウンロードさせるプログラムを作ります。
<?php
declare(strict_types=1);
namespace App\Controller;
use Mpdf\Mpdf;
class SampleController extends AppController
{
public function index()
{
$mpdf = new Mpdf();
$mpdf->WriteHTML('<h1>Hello world!</h1>');
$encodedName = rawurlencode('サンプル.pdf');
return $this->response->withType('pdf')
->withHeader('Content-Disposition', "attachment;filename*=UTF-8''{$encodedName}")
->withStringBody($mpdf->Output('', 'S'));
}
}
今回は withStringBody() と $mpdf->Ouput('', 'S') を組み合わせているのがポイントです。Output() のオプションは mPDF の公式ドキュメントで確認することが出来ます。
- Output() – mPDF functions – mPDF Manual
- https://mpdf.github.io/reference/mpdf-functions/output.html
下記サンプルコードのように $mpdf->Output('サンプル.pdf', 'D') を使えば簡単そうですが、そうしないのは、コントローラの中で何かを出力すること(echo や header() など)を避けるためです。
避けたほうがいい理由の詳細は割愛しますが、例えば $mpdf->Output('サンプル.pdf', 'D') を使うと logs/error.log に下記のようなメッセージが出ます。
public function index()
{
$mpdf = new Mpdf();
$mpdf->WriteHTML('<h1>Hello world!</h1>');
// ▼ このようにすると error.log に下記 warning が出る
$mpdf->Output('サンプル.pdf', 'D');
}
2020-02-15 12:34:56 Warning: Warning (512): Unable to emit headers. Headers sent in file=path\to\cakephp4\vendor\mpdf\mpdf\src\Mpdf.php line=9464 in [path\to\cakephp4\vendor\cakephp\cakephp\src\Http\ResponseEmitter.php, line 72]
Request URL: /sample
Client IP: 127.0.0.1
Trace:
Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 188
Cake\Http\ResponseEmitter::emit() - CORE\src\Http\ResponseEmitter.php, line 72
Cake\Http\Server::emit() - CORE\src\Http\Server.php, line 130
[main] - ROOT\webroot\index.php, line 40
3. 日本語を表示可能に
実はこのままだと日本語が表示されません。上記コードの「'<h1>Hello world!</h1>'」の箇所に日本語を入れると文字化けします。
日本語を表示するためには、フォントをダウンロードして、それを読み込む必要があります。下記サンプルコードを実行するには、IPA フォントの「IPA Pゴシック(Ver.003.03)」をダウンロードして /data/font/ipagp.ttf に置いてください。
フォントファイルはサイト閲覧者にアクセスさせたくないので /webroot には置かない方が良いです。
- IPAフォントのダウンロード
- https://ipafont.ipa.go.jp/old/ipafont/download.html
public function index()
{
// ▼ここを変更
$mpdf = new Mpdf([
'fontDir' => [ROOT . DS . 'data' . DS . 'font'],
'fontdata' => [
'ipa' => ['R' => 'ipagp.ttf'],
],
'default_font' => 'ipa',
]);
$mpdf->WriteHTML('<h1>こんにちは、世界!</h1>');
...
}
フォントファイルを vender/mpdf/mpdf/ttfonts に追加する例を見かけますが、これはあまりおススメしません。composer で管理する vendeor フォルダの中は変更を加えないほうが良いと思います。
mpdf に含まれるフォントも併せて使いたい場合は、公式ドキュメントにあるとおり array_merge() などを使って対応してください。
- Fonts in mPDF v7+ – Fonts & Languages – mPDF Manual
- https://mpdf.github.io/fonts-languages/fonts-in-mpdf-7-x.html
4. テンプレートを使って PDF 生成
CakePHP 4 のテンプレートを使って PDF を生成してみます。今回は下記のように実装しました。
<html>
<head>
<?= $this->Html->css(['sample/my_pdf.css']) ?>
</head>
<body>
<h1><?= $title ?></h1>
<p>サンプル用のPDFです</p>
</body>
</html>
h1 {
color: #f00;
}
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\View\View; // ← これを追加
use Mpdf\Mpdf;
class SampleController extends AppController
{
public function index()
{
$mpdf = new Mpdf([
'fontDir' => [ROOT . DS . 'data' . DS . 'font'],
'fontdata' => [
'ipa' => ['R' => 'ipagp.ttf'],
],
'default_font' => 'ipa',
]);
// ▼ここを変更
$view = new View();
$view->set('title', 'こんにちは、世界!');
$template = 'Sample/my_pdf';
$layout = false;
$content = $view->render($template, $layout);
$mpdf->WriteHTML($content);
$encodedName = rawurlencode('サンプル.pdf');
return $this->response->withType('pdf')
->withHeader('Content-Disposition', "attachment;filename*=UTF-8''{$encodedName}")
->withStringBody($mpdf->Output('', 'S'));
}
}
テンプレートの内容を View クラスの render() 関数を使ってテキストとして取得しています。$template、$layout 変数に入れてから引数に渡しているのは、コードを分かりやすくするためです。
mPDF で使用できる HTML タグと CSS は公式ドキュメントの下記ページに書いてあります。
- HTML Tags – HTML support – mPDF Manual
- https://mpdf.github.io/html-support/html-tags.html
- Supported CSS – CSS & Stylesheets – mPDF Manual
- https://mpdf.github.io/css-stylesheets/supported-css.html
5. おわりに
今回は mPDF の使い方をご説明しましたが、肝はレスポンスオブジェクトを生成して返すところかなと思います。CakePHP や mPDF に限らずですが、フレームワークと一緒にライブラリを使うときには、ただ説明書通りに使うのではなく、フレームワークに合わせて使うことが大切かなと思います。
そのためには、フレームワークの仕組みも把握しなければいけないので、最初はなかなか難しいんですけどね。
このブログの記事が、少しでも役に立てばいいなと思っています。