書いた人
始めに
今日はLibreOffice を使ったPDF出力についてのお話です。
よく有りますよね、PDF出力。
Webでこの機能を実現しようとする場合に、その手法として以下のパターンをよく見かけます。
・文字も線も1つ1つコードで描画する (原理主義)
・HTMLからPDFに変換する(中道左派)
・ライブラリを使用し、独自の記法でPDFコンテンツを定義する(過激派)
・ExcelをPDFに変換する(急進派)
どのやり方も、案件によって採用出来たり出来なかったり、学習コストが高かったり低かったり様々です。
今回取り上げるのは急進派です。
どんな用途に向いていますか?
罫線とロゴ程度の体裁で、一覧・合計の表示が必要な帳票に向いています。
理屈上はPhpSpreadsheetを使用して表現できる範囲なら何でもできますが、
手間を考えると、テンプレートのExcelを用意して値・合計値・行の増減程度に収めるのが良いでしょう。
どんな用途に向いていませんか?
・動的に図形やレイアウトが多様な変化をするもの
・罫線じゃなくて丸角が良いなど、デザインがリッチなもの
Excelでやらんやろという見た目のものはまぁ向いていません。
解説
今回の処理の全体図はこちらです。
手順1.テンプレートExcelを用意する
手順2.PhpSpreadsheetを用いて、Excelにデータを埋め込む
手順3.LibreOfficeのライブラリを使用し、手順2で出来たExcelをPDFへ変換する
以上です!
手順1、手順2についてはweb上に記事がたくさん有ると思います。
今回は手順3を成立させるために必要な、LibreOfficeのインストールから詳しく見てみましょう。
LibreOfficeのインストール
yum install libreoffice libreoffice-langpack-ja
これで /usr/lib64/libreoffice/program/の下にsofficeというバイナリが置かれます。
/usr/bin/sofficeにシンボリックリンクがつくられるので、そちらを利用しましょう。
このバイナリに対し、下記のようなコマンドでExcelをPDFに変換する事が出来ます。
# /usr/bin/soffice --headless --convert-to pdf --outdir {出力先のディレクトリ} {変換する元のExcel}
# 例) /tmp/sample.xls -> /tmp/sample.pdfに変換する場合
/usr/bin/soffice --headless --convert-to pdf --outdir /tmp /tmp/sample.xls
PHPから利用する為の設定
phpからこのsofficeを利用する場合、少しコツが要ります。
単純にphpからexecで上記のコマンドを実行してもPDFは作成されません。
理由は、phpを実行するapacheユーザーがホームディレクトリに対し書き込み権限を持っていないこと、です。
対処1.ホームディレクトリへの書き込み権限をどうにかする
apacheのホームディレクトリは環境によってパターンが分かれると思います。
私の環境では/usr/share/httpdでした。
ここに対しchmodないしはchownすれば良いのですが、このディレクトリ全体にやるのは精神衛生上よろしくありません。
sofficeがホームディレクトリに対して何をするのかをもう少し詳しく調べましょう。
一時的に/usr/share/httpdへの書き込みを許可し、phpからpdf変換を実行します。
すると次の隠しディレクトリが作成されていました。
[root@xxxxxxxx httpd]# ls -all /usr/share/httpd/
total 32
drwxrwxrwx 1 root root 4096 2月 9 20:06 .
drwxr-xr-x 1 root root 4096 2月 9 19:34 ..
drwx------ 3 apache apache 4096 2月 9 20:06 .cache
drwx------ 3 apache apache 4096 2月 9 20:06 .config
drwxr-xr-x 3 root root 4096 2月 9 19:30 error
drwxr-xr-x 1 root root 4096 2月 9 19:31 icons
drwxr-xr-x 4 root root 4096 2月 9 19:30 noindex
.cacheと.config です。
この2つのディレクトリさえ事前に作成しておけば、phpから利用する事が出来ます。
cd /usr/share/httpd
mkdir {.cache,.config}
chown apache:apache .cache .config
対処2.Apacheユーザーのホームディレクトリをごまかす
もう一つ別の回避方法が有ります。
apacheがホームディレクトリに書き込み出来れば良いので、
sofficeを利用するときだけ書き込み権限のあるディレクトリをホームだと言い張ってしまいます。
exec("export HOME=/tmp && /usr/bin/soffice --headless --convert-to pdf --outdir /tmp /tmp/sample.xls");
export HOME=/tmp
この記述でexecを実行するapacheのホームディレクトリを一時的に/tmpに設定します。
どちらのやり方でも目的を満たせますが、個人的には対処1が良い気がします。
理由1.キャッシュが利用できる
理由2./tmpの方はディレクトリが残らない為、毎回ディレクトリ作成をしている
もちろん、
・キャッシュされると困る
・ゴミデータを残したくない
等の事情であれば対処2が適しているでしょう。
フォントを設定する
これまでの作業で、晴れてPHPからsofficeを利用する事が出来ました。
が、実際にPDFを出力してみると、なんだか残念な見た目になっています。
画像等のシェイプはまだ良いとして、文字がさすがにカクカクし過ぎです。
これを何とかしましょう。
フォントを変えるといいのですが、Excel側でフォントを弄っても直りません。
PDFを直接出力しているのはsofficeなので、sofficeの認識しているフォントを変更する必要が有ります。
フォントをインストールする
何でも良いですが、今回は商用利用可能なIPAのフォントを使用します。
yum install ipa-gothic-fonts ipa-pgothic-fonts
sofficeに分からせる
コマンドの前にLANGを設定しましょう。
exec("export LANG=ja_JP.UTF-8 && /usr/bin/soffice --headless --convert-to pdf --outdir /tmp /tmp/sample.xls");
の部分です。export LANG=ja_JP.UTF-8
これで再度実行しましょう!
だいぶくっきりしました。
これなら許されます。
Docker環境でやるなら
まとめるとDockerfileを以下のように設定すると良いでしょう。
# libreofficeをインストール
RUN yum install -y libreoffice libreoffice-langpack-ja
RUN mkdir {/usr/share/httpd.cache,/usr/share/httpd.config}
RUN chown apache:apache /usr/share/httpd.cache /usr/share/httpd.config
# 日本語フォントインストール(libreofficeが利用)
RUN yum install -y ipa-gothic-fonts ipa-pgothic-fonts
蛇足
原理主義の悪いところ
1.とにかくメンドクサイ!
2.文字の中央寄せやら右寄せとか自力でやんなきゃいけない
3.線引くのがどちゃくそ疲れる
4.x軸y軸の当たりを付けるための微調整が大変
中道左派の悪いところ
1.印刷用のCSSが必要(ブラウザ毎に)
2.何故見た目そのまま出力されないんだ。。
過激派の悪いところ
1.何が出来て何が出来ないのか分からない
2.PDFを作りたいだけなんだ変な勉強させないでくれ
急進派の悪いところ
1.かゆいところに手が届かない!
2.画像がなんだか粗くなる気がする
3.Excelで操作できないところは表現できない
ともかくPDF出力は大変だというお話でした。
それはそうと
私は「靴下をどちらの足から履くべきか論」の右翼過激派ですが、
ロジカルスタジオはどんな思想のエンジニアも受け入れる土台があります!
ぜひ、下記採用サイトからご応募くださいませ!