1/25 更新
新しい形式のGoogle Charts(=loader.js
を読み込むヤツ)がうまくいかないのは、
どうやらwkhtmltopdf
がES6に対応していないことが原因っぽい。
google.charts.load()
はpromise
を使っているようなので、Polyfillでなんとかならないか実験中...(ダメっぽい)
はじめに
Laravel-Snappyでpdfを書き出すまででちょっとつまずいたりしたので、最終的にどうやってできるようになったかの記録。
やること
- pdfで集計データとかをダウンロードしたい!
- グラフ入れて!グラフはGoogleChartで書くよ!
- ってことはJSが動かなきゃだめ!もちろんCSSも!
環境
- PHP 7.1
- Laravel 5.5 (Laradock使用)
基本的なやり方はreadmeに書いてありますが、一応順を追って。
とりあえず動かしてみる
Laravel-Snappyのインストール
$ composer require barryvdh/laravel-snappy
Laravelの設定に追加
...
'providers' => [
...
+ Barryvdh\Snappy\ServiceProvider::class, //追加
...
],
'aliases' => [
...
+ 'PDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
+ 'SnappyImage' => Barryvdh\Snappy\Facades\SnappyImage::class,
...
],
Laravel-Snappyのconfigファイルを召喚
$ php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"
SnappyはwkhtmltopdfというGoogle謹製のツールをPHPでイイカンジに使えるようにしたアレです。
なので動作にはwkhtmltopdf(と、wkhtmltoimage)そのものであるバイナリが必要なわけです。
wkhtmltopdfをインストールします
$ composer require h4cc/wkhtmltopdf-amd64
$ composer require h4cc/wkhtmltoimage-amd64
./vender/bin
以下にシンボリックリンクを貼ってくれるので、これを使うようにLaravel-Snappy側を設定してあげます。
デフォルトでは/usr/local/bin/wkhtmltopdf
を使おうとしていますね。
'pdf' => array(
'enabled' => true,
- 'binary' => '/usr/local/bin/wkhtmltopdf', //これを消して
+ 'binary' => base_path('vendor/bin/wkhtmltopdf-amd64'), //これを追加
'timeout' => false,
'options' => array(),
'env' => array(),
),
'image' => array(
'enabled' => true,
- 'binary' => '/usr/local/bin/wkhtmltoimage', //消して
+ 'binary' => base_path('vendor/bin/wkhtmltoimage-amd64'), //追加
'timeout' => false,
'options' => array(),
'env' => array(),
),
pdf出力するページを用意
...
public function index()
{
$pdf = \PDF::loadView('pdf_tamplate');
return $pdf->inline('thisis.pdf'); //ブラウザ上で開ける
// return $pdf->download('thisis.pdf'); //こっちにすると直接ダウンロード
}
...
<h1>ウソみたいだろ。PDFなんだぜ。これで。</h1>
アクセスしてみます。
stderr: "/var/www/vendor/bin/wkhtmltopdf-amd64: error while loading shared libraries: libXrender.so.1: cannot open shared object file: No such file or directory
どうやら世の中はそう甘くないようです。
必要なモジュールとかインストール
libXrender.so.1
とかいうのがなくて怒られていますね。
なお、それを入れたらまた他のものが足りないと怒られました。
で、以下。
$ apt-get install -y libxrender1 libfontconfig1 libxext6 fonts-ipafont
なお、これらは__PHPが動作するコンテナ上にインストールする必要があります。__
laradockならphp-fpm
コンテナですね。よく考えればわかるのですが、ここでしばらくつまづいてしまいました。
fonts-ipafont
ですが、デフォルトのままでは日本語フォントが存在しないので日本語が表示できません。
ついでに入れときましょう
アクセスしてみましょう。
あああああああああああああああ
pdfのエンコードを指定
...
public function index()
{
$pdf = \PDF::loadView('pdf_tamplate')
+ ->setOption('encoding', 'utf-8');
return $pdf->inline('thisis.pdf'); //ブラウザ上で開ける
// return $pdf->download('thisis.pdf'); //こっちにすると直接ダウンロード
}
...
一件落着。
Google Chartでグラフを表示してみる
というわけで、ビューテンプレートをpdfに出力できました。
この上でJSを動かしてカッチョイイグラフをぶちこんでやりましょう。
グラフ表示用にテンプレートを作成してみます。
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
function init() {
google.load('visualization', '1.1', {'packages': ['corechart'], callback: drawCharts});
}
function drawCharts() {
drawSampleChart('piechart');
}
function drawSampleChart(containerId) {
var data = google.visualization.arrayToDataTable([
['Taste', 'Range per Pizza'],
['BBQChicken', 25],
['Charcoal-grilled Beef Rib', 25],
['Kokuuma Just Meat', 25],
['Shrimp Mayo Bacon', 25],
]);
var options = {
title: 'Would you like to eat a pizza?'
};
var chart = new google.visualization.PieChart(document.getElementById(containerId));
chart.draw(data, options);
}
</script>
</head>
<body onload="init()">
<div id="piechart" style="width: 900px; height: 500px;"></div>
</body>
</html>
現時点で、Google Chartを使う方法は2パターンあるようです。
このどちらを読み込むかですね。
後者の方が新しい方法のようで、Google Chart公式でもこれを使って説明されています。
ただ、この方法だとwkhtmltopdf上でAPIをロードした際のイベントが発火せず、
google.charts.setOnLoadCallback()
関数で登録したコールバックが呼ばれない
↓
グラフが出ない
ので、注意。
とりあえず上の方法でうまくいっていますが、Googleは古いやり方をいきなりスパッと捨てたりするので、なんとかしたいとは思っています。
とりあえずアクセス。
Yeah.
JavaScriptを別ファイルから読み出す
Google Chartのライブラリは外部から取得しているので当然ですが、JavaScript部分を別ファイルに切り出してもちゃんと動作します。
ここで注意したいのが、Laravel-SnappyはPHPが動作してるバックエンドのコンテナ上でpdfを書き出していることです。
つまり、.jsファイルのパスはサーバ内で考えます。
どういうことかというと、
- ダメな例(ブラウザからの見た目でjsファイルを取得している)
<script type="text/javascript" src="{{ asset('/js/pizza.js')}}"></script>
- イケる例(PHPコンテナ内のパスでjsファイルを取得している)
<script type="text/javascript" src="{{ base_path() . '/public/js/pizza.js' }}"></script>
先ほどの例のような、JavaScriptがインラインに書いてあるテンプレートを使っている場合、PHP側の書き換えで
-
return $pdf->inline('thisis.pdf');
-> ブラウザでpdfを表示 -
return view('pdf_template');
-> htmlとして表示
とどちらも対応できたのですが、ここにきて違いが出てくる点に気をつけましょう。
もっともらしく書いているということは、私もここで盛大にハマったということです。
おもむろに{{ base_path()...
とか使っていますが、もちろん__テンプレートエンジンもきちんと動作します。__
すばらしき。
CSSの反映
これもJavaScriptと同じで、__PHPが動作しているサーバ内でのパスでCSSを呼び出す__必要があります。
とはいえ、CSSはPHP側で当ててあげることが可能なので
- テンプレートはこれで
...
<link rel="stylesheet" href="{{ asset('/css/app.css') }}">
...
- PHPではこう書く
...
$pdf = \PDF::loadView('pdf_tamplate')
->setOption('encoding', 'utf-8');
+ ->setOption('user-style-sheet', base_path() . '/public/css/app.css')
;
return $pdf->inline('cool-na-pdf.pdf');
これを両方書いてもイイわけです。
なんならpdf用に調整したcssを個別で当ててもいいですね。
まとめ
pdf出力、コワクナイ
「このデータpdfで出してくれたら使いやすいんだけどなぁ(チラッチラッ」と言われてもドヤ顔でやったりましょう。