はじめに
LaravelでPDFを出力するものでググると、まずTCPDFを使用する方法が出てきます。
私も試しました。すごく簡単にPDF出力ができることに驚きました。
しかし、CSSの制約が厳しすぎる!!
CSSが効いているかを試すのによく使うであろう background: red;
が使用できないんですよ。まじか。
TCPDFは以下のCSSしかサポートしていません。えぇ……
- font-family
- font-size
- font-weight
- font-style
- color
- background-color
- text-decoration
- width
- height
- text-align
ソースは以下
http://stackoverflow.com/questions/11395171/why-does-tcpdf-ignore-my-inline-css
そこで、普段フロント画面作成に使用するような
普通のCSSをサポートしているPDF出力ライブラリを探して見つけたのがコレ、
Laravel-Snappyです。
商用利用可能か?
可能です。MITライセンス。やったぜ。
ソースは以下、Licenseの項目を参照
https://github.com/barryvdh/laravel-snappy
実装
必要ライブラリの準備
これはPDF出力機能の根幹となる wkhtmltopdf が必要とするライブラリをインストールしています。
実際のところ、ためしに出力しようとしたら error while loading shared libraries: libXrender.so.1
みたいなのが出てきたんで、
そのエラーがなくなるまで yum install libXrender.so.1
みたいなことをした次第です。
多分この部分はもっと良い方法があると思う。
TCPDFならサクッと一発でいけたんですけどねえ…
yum install libXext libXext.i686 libXrender libXrender.so.1 fontconfig libfontconfig.so.1 zlib.i686 libstdc++.i686
20171010 追記
64bit版の場合はこれ一つでいけます。
なお、こちらでする場合は以下の操作で記述されている「i386」の部分は「amd64」で読み替えて下さい。
yum install wkhtmltopdf
インストール
composer require h4cc/wkhtmltopdf-i386 0.12.x
composer require h4cc/wkhtmltoimage-i386 0.12.x
composer require barryvdh/laravel-snappy
エイリアス登録
'providers' => [
Barryvdh\Snappy\ServiceProvider::class,
];
'aliases' => [
'PDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
'SnappyImage' => Barryvdh\Snappy\Facades\SnappyImage::class,
];
次にconfigディレクトリにsnappy.phpというのを用意します。
php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"
/vendor/barryvdh/laravel-snappy/config/snappy.php
というところに
snappyの設定ファイルがあり、wkhtmltopdfというものを /usr/local/bin/wkhtmltopdf
から読み込んでいるのですが、
その読み込み先をcomposerでインストールしたディレクトリからにするようオーバーライドしています。
<?php
return array(
'pdf' => array(
'enabled' => true,
'binary' => base_path('vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386'), // ここを変更しています
'timeout' => false,
'options' => array(),
'env' => array(),
),
'image' => array(
'enabled' => true,
'binary' => base_path('vendor/h4cc/wkhtmltoimage-i386/bin/wkhtmltoimage-i386'), // ここを変更しています
'timeout' => false,
'options' => array(),
'env' => array(),
),
);
公式ドキュメントでは wkhtmltopdf-amd64 みたいに書いていると思いますが、
それは64bit版をインストール( composer require h4cc/wkhtmltopdf-amd64 0.12.x
)した場合の話です。
私の場合は32bit版なので、i386になります。単純ですが少し詰まった。
Hello World
さて、ここまでできればいよいよ出力ができます。
まずはViewファイルにHello Worldと書いたものを用意し、Controllerで呼び出して印刷してみます。
必要ファイルの準備
php artisan make:controller TestController
touch resources/views/print.blade.php
View
<p style="background: red">hello world</p>
Controller
public function getPrint()
{
$pdf = \PDF::loadView('print');
return $pdf->download('print.pdf');
}
ルーティング
5.2だったら app/Http/routes.php
Route::get('/print', 'TestController@getPrint')->name('getprint');
実行
ここまでできたら、 example.com/print にアクセス。PDFがダウンロードされましたでしょうか?
されたら次は、フォームの値を変数に格納し、それを表示してみます。
POSTの値を印刷
必要ファイルの準備
touch resources/views/index.blade.php
View
<div>
<p>PDF出力検証</p>
<form action="print" method="post">
{{ csrf_field() }}
<input type="text" name="test" value="テスト" />
<input type="submit" value="送信" />
</form>
</div>
ちゃんとHTML全部書かないと日本語が表示されないみたいです。
まあHTMLの部分はincludeできると思うけど
<!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" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta name="Keywords" content="" />
<meta name="Description" content="" />
<title></title>
</head>
<body>
<p style="background: red">{{ $data['test'] }}</p>
</body>
</html>
Controller
public function index()
{
return view('index');
}
public function getPrint()
{
return redirect('/');
}
public function postPrint()
{
$data = \Request::all();
$pdf = \PDF::loadView('print', ['data' => $data]);
return $pdf->download('print.pdf');
}
ルーティング
Route::get('/', 'TestController@index')->name('index');
Route::get('/print', 'TestController@getPrint')->name('getprint');
Route::post('/print', 'TestController@postPrint')->name('postprint');
実行
ここまでできたら example.com/ にアクセス。
フォームに既に「テスト」とか値が入ってると思うので、送信ボタンをクリック。
PDFがダウンロードされ、「テスト」と表示されていることがわかると思います。
意外にも、日本語が普通に表示されるんですね。TCPDFの時はイチイチipaフォントのインストールとか必要だったのに。
以上
おわり。
追記
なお、いきなりダウンロードじゃなくて、PDFのプレビュー画面を表示するには次のようにします。驚くほど簡単ですね。TCPDFだと苦労したのに。
public function postPrint()
{
$data = \Request::all();
$pdf = \PDF::loadView('print', ['data' => $data]);
//return $pdf->download('print.pdf');
return $pdf->inline('print.pdf');
}
さらに追記
PDFをプレビューさせるのではなく、指定のディレクトリにアップロードして、
そのファイルにアクセスできるようにするみたいな。
また、その際のファイル名は「test_20161116140000.pdf」みたいにする。
今回はpublicディレクトリに置くことにします。
ディレクトリ作成
mkdir public/pdf
権限付与
chown -R apache:apache public/pdf
必要ファイルの準備
touch resources/views/complete.blade.php
Controller
postPrintとかはいらないので削除
class TestController extends Controller
{
public function index()
{
return view('index');
}
public function complete()
{
$data = \Request::all();
$location = public_path() . '/pdf/'; // example.com/pdf/test_20161116140000.pdf とかに置く
$filename = 'test_' . date('YmdHis') . '.pdf';
$pdf = \PDF::loadView('print', ['data' => $data]);
$pdf -> save($location . $filename);
return view('complete') -> with(['filename' => $filename]);
}
}
View
<div>
<p>PDF出力検証</p>
<form action="complete" method="post">
{{ csrf_field() }}
<input type="text" name="test" value="テスト" />
<input type="submit" value="送信" />
</form>
</div>
resources/views/print.blade.php は特に変更なし
<div>
<p>印刷完了</p>
<p><a href="/pdf/{{ $filename }}">ダウンロードはこちら</a></p>
</div>
ルーティング
Route::get('/', 'TestController@index')->name('index');
Route::get('/complete', function () {
return redirect('/');
});
Route::post('/complete', 'TestController@complete')->name('complete');
以上
その他
20171010 追記
もし文字化けする場合、日本語フォントを入れると解決するかもしれません。
yum install ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts