WebページをPDFに出力する

Symfony2でWebページをPDF出力するバンドルを紹介します。

KnpSnappyBundle

今回試した環境は以下のとおりです。

  • 開発環境:Mac OSX Mervericks
  • ステージング環境:Ubuntu 12.04

インストール

composerでインストールして、設定を追加します。

composer

php composer.phar require "knplabs/knp-snappy-bundle:1.0.*@dev"

AppKernel.php

<?php
public function registerBundles()
{
    $bundles = array(
        //...
        new Knp\Bundle\SnappyBundle\KnpSnappyBundle(),
        //...

config.yml

knp_snappy:
    pdf:
        enabled:    true
        binary:     /usr/local/bin/wkhtmltopdf
        options:    []
    image:
        enabled:    true
        binary:     /usr/local/bin/wkhtmltoimage
        options:    []

依存コマンド wkhtmltopdf のインストール

http://wkhtmltopdf.org/からダウンロードしてインストールします。

※ homebrewでもインストールできますが、バージョンが古いためKnpSnappyBundleでは動作しません。

インストールしたらバージョンを確認しましょう。

wkhtmltopdf -V
wkhtmltopdf 0.12.1 (with patched qt)

PDFを出力してみましょう

1ページ

<?php
$container->get('knp_snappy.pdf')->generate(
    'http://xxx.com',
    '/path/to/the/file.pdf'
);

複数ページ

<?php
$container->get('knp_snappy.pdf')->generate(
    array('http://xxx.com', 'http://www.zzz.com'),
    '/path/to/the/file.pdf'
);

これでひとまず完了ですね。

KnpSnappyBundleの仕組み

1) knp_snappy.pdf –> wkhtmltopdf

knp_snappy.pdfサービスは渡されたURLをwkhtmltopdfコマンドに渡します。

wkhtmltopdf --lowquality 'http://xxx.com' '~/output.pdf'

2) wkhtmltopdf –> Qt

Qt(キュート)のQtWebKitモジュールを使ってWebページがレンダリングされます。

3) ステータスコードが返される

EditCode Explanation
0 All OK
1 PDF generated OK, but some request(s) did not return HTTP 200
2 Could not something something
X Could not write PDF: File in use
Y Could not write PDF: No write permission
Z PDF generated OK, but some JavaScript requests(s) timeouted
A Invalid arguments provided
B Could not find input file(s)
C Process timeout

※ githubのコメントを引用しています

オプションの変更

config.ymlでオプションを設定することができます。 スピード優先のおすすめ設定はこれです。

knp_snappy:
    pdf:
        enabled:    true
        binary:     /usr/local/bin/wkhtmltopdf
        options:
          - { name: page-size, value: A4 }
          - { name: encoding, value: UTF-8 }
          - { name: disable-javascript, value: true } # javascriptを実行しない
          - { name: no-background, value: true } # バックグラウンド実行
          - { name: dpi, value: 80 } # 解像度を下げる
          - { name: allow, value: %kernel.root_dir%/../web } # 自プロジェクトのページを印刷する場合に、アセットをローカルから検索

一時的にオプションを変更して出力する場合はgenerate()の第3引数を使用します。

<?php
$container->get('knp_snappy.pdf')->generate(
    'http://xxx.com',
    '/path/to/the/file.pdf',
    array('page-size' => 'B4')
);

ステージング環境で実行

デスクトップ環境のないステージング環境とローカルの開発環境の両方で動作させるには、コマンドをparameters.ymlで切替えられるようにします。

config.yml

knp_snappy:
    pdf:
        enabled:    true
        binary:     %command_pdf%
        options:    []
    image:
        enabled:    true
        binary:     %command_image%
        options:    []

parameters.yml 開発環境

parameters:
    command_pdf: wkhtmltopdf

parameters.yml ステージング環境

parameters:
    command_pdf: xvfb-run --auto-servernum --server-num=1 wkhtmltopdf

Xvfb (X virtual framebuffer)

サーバーに別途インストールしてください。 仮想環境でXの入出力をシュミレートするものです。最大99個の仮想環境が作成できます。 今回は仮想環境の細かいオプションは指定しないので空いている番号を検索して使います。

--server-num=1 : 仮想環境は1番目以降を使う --auto-servernum=1 : 自動的に番号を振る

まとめ

Symfony2でPDF出力をサポートしてくれるバンドルはいくつか試しましたが、日本語がきちんと表示されないものが多かったです。 KnpSnappyBundleはレンダリングを外部コマンドに任せているため設定やインストールが少し面倒に感じますが、ブラウザのPDF出力とほぼ同じ結果が得られるのがいいですね。