Apache FOP (Formatting Objects Processor) は、XSL formatting objects (XSL-FO) 文書をPDF, PNGなどに変換して出力するツールです。
Ruby on RailsアプリケーションでPDF出力するために、内部で使えないか試してみました。
OSはFedora 7 Linuxです。
XSL FOは、印刷などでのレイアウトを指定するためのXML文書型で、直感的にはHTMLとCSSを混ぜたようなものです。XSL FO仕様の最新版は、2006.12.5付W3C勧告のバージョン1.1です。
普通のXML文書はレイアウト(見栄え)とは関係ないので、XSLTでXSL FOに変換することになります。今回は、直接XSL FO文書を用意してみます。
2007.10現在、Apache FOPの最新版はバージョン0.94です。バイナリ版をダウンロードしてきて展開します。fopコマンドへのシンボリックリンクを適当なところに張るだけでOKです。
Javaは、Fedora 7のgij版ではなく、SunのJava VMでないといけません。gij版だとFOPを走らせたときにエラーが発生したりしました。また、速度もSun版のほうが速いです。
最小の(と思う)XSL FO文書を作ってみます。ファイル名をfop_1.foとして保存します。
XSL FOの名前空間は、http://www.w3.org/1999/XSL/Format です。ルート要素はroot。
layout-master-setで余白などを指定します。流し込むテキストなどのデータはpage-sequenceで与えます。
テキストはblock要素の内容とし、font-sizeなどが指定できます。見栄えに関する属性は、CSS2とほぼ同じです。
これをPDFに変換します。
$ fop fop_1.fo -pdf fop_1.pdf
特にエラーもなく出力されました。表示も問題ありません。簡単。
日本語を表示するために、TrueTypeフォントを使えるようにします。
手順としては、
FOP 0.9.4では、フオントメトリクスファイルは不要です。FOP設定ファイルでTrueTypeフォントファイル (.ttf) を指定すれば、自動的にフォントデータが参照されます。
さざなみフォントで試してみます。まず、フォントメトリクスの作成は、
$ java -cp build/fop.jar:lib/commons-logging-1.0.4.jar:lib/commons-io-1.3.1.jar \ org.apache.fop.fonts.apps.TTFReader \ /usr/share/fonts/japanese/TrueType/sazanami-mincho.ttf \ ~/sazanami-mincho.xml
設定ファイルは conf/fop.xconfファイルをひな形にします。このファイルはfopコマンドで明示的に指定 (-cオプション) しないと参照されません。最初これに気付かず、なぜ修正しても反映されないのか悩みました。example configuration fileと書いてますね。
次のようにします。
fontタグのmetrics-url属性に相対パスを書くと、実行時のカレントディレクトリが基準になります。ちょっと扱いにくい。
font-tripletタグは複数書けますが、別名の指定になります。weight="bold"としたからといって、自動的に太字で表示できるわけではありません。
foファイルを更新します。
fopコマンドを走らせると、bold フォントがないのでフォント幅normalにfallbackする旨のメッセージが出力されます。文字化けしたりはしません。
(2007.11.9加筆)
実践的なFOファイルとして請求書を作ってみました。外部画像の参照、フォント選択、表、ヘッダ・フッタ、背景色の指定などをおこなっています。
FOファイル: fop-invoice.fo; 出力されたPDFファイル: fop-invoice.pdf
Adobe Readerでは問題なく表示できますが、Linux の Evince だと日本語部分が真っ白になります。なぜだろう?
(2007.11.1)
XSL FOの表オブジェクトは、HTMLの表と構造が似ています。表は行の集まりであり、行はセルの集まりです。
HTML | XSL FO |
---|---|
table | table |
tr | table-row |
th, td | table-cell |
TODO:
Apache FOPは、XML文書であるFOデータを与えればいいので、呼び出すほうのプログラミング言語やフレームワークを選びません。表現力も豊かです。
手書きでFOファイルを書こうとすると、タグ名(要素名)が長いのが難儀です。また、プレビュー & 試行錯誤がHTML + Webブラウザほど手軽ではありません。ラフにHTMLで書いて、XSLTでFOに変換して細部を調整するのがいいかもしれません。
Railsと組み合わせる場合、フォーム内のデータなどはデータベースから取得 (ActiveRecord) し、ERBでFOファイルを生成し、それからFOPに掛けることになります。