読者です 読者をやめる 読者になる 読者になる

FIELD NOTES: 書を持って街へ出よう

合同会社フィールドワークス プログラマ兼代表のブログ

Field Reportsの特徴 (5) テンプレートエンジンとの対比

製品情報

Webシステム開発において,Ruby on Rails や Django などのWebアプリケーションフレームワークSmarty, Cheetah, Mako, Jinja, Tenjin などのテンプレートエンジンを使用することが一般的になりました。CGIの時代と比べれば,ずいぶん生産性が向上したのではないでしょうか?

これらWebアプリケーションフレームワークやテンプレートエンジンは,成果ドキュメントとしてHTMLを出力しますので,通常ユーザは処理結果をWebブラウザで表示させて画面で確認します。これで完結すれば話は簡単なのですが,まだまだ「帳票」が必要とされるケースも残っています。

  • 出力結果を紙に印刷して顧客へ郵送する必要がある。
  • OCR振込用紙など,特殊な定形フォームにピタリと文字をはめ込む必要がある。
  • ブラウザに依存して微妙にレイアウトが異なるのを避けたい。
  • PDFで配布したい。
  • とにかく紙に印刷しないと気が済まない。

ところが,現行の帳票出力ツールを最新のテンプレートエンジンと比較すると,若干見劣りする感がありました。Field Reports を開発した動機として,最新のWeb開発ツールなみに使いやすい帳票開発ツールを作りたいという目標がありましたので,どこまで達成できたのかレビューをしてみたいと思います。

そこで今回は,テンプレートエンジンと対比させて Field Reprts の特徴を見ていきます。

テンプレートエンジンの構成要素

Wikipediaによれば,テンプレートエンジンは以下の要素から成るとされています。

  • ソーステンプレート
  • データモデル
  • テンプレートエンジン
  • 成果ドキュメント

ソーステンプレート

Web用テンプレートエンジンでは,“<% 変数名 %>”“{% 変数名 %}”“{$ 変数名 }”といった書式のプレースホルダをHTMLに埋め込んでソーステンプレートとするのが一般的です。

PDFを出力するテンプレートエンジンとしては,XMLをソーステンプレートとする形態が考えられますが,そのXMLを作成する事自体が煩雑な作業となります。そこで,専用のフォームエディタを用意するのが正攻法だと思いますが,それでもいくつか問題が出てきます。

  • 通常,運用中の帳票,Excelで作成したデザイン案を元に改めて入力することになるので,作業が二度手間になる(開発工数の増加)。
  • デザイン案と実際の出力に微妙な差異が発生することがある(手戻り発生の可能性)。

そこで,Field Reports では,PDFをそのままソーステンプレートとして使用する方式としました。プレースホルダとしてはPDFのフィールド(フォーム)を利用しますので,以下の2ステップでソーステンプレートの準備は終わります。

  1. 運用中の帳票,Excelで作成したデザイン案等をPDF化する。
  2. Adobe Acrobat 等でPDF上にフィールドを配置する(安価なサードパーディ製ツールの使用も可能)。

ただし,HTMLベースのテンプレートと違って,PDFテンプレートに「ロジック」を埋め込む事は困難です。そこでロジックを埋め込む代わりに,フィールドの表示属性をプログラム側から細かく制御できるようにしました。

以下のような属性を制御することができます。

  • 表示/非表示
  • テキスト表示色
  • フォントサイズ
  • 枠線のスタイル
  • フィールドの座標
  • フィールドの新規追加

これで,動的に変化する要素もかなりの所まで表現できるはずです。極端な話,テキスト・画像・罫線で表現できる帳票であれば,すべてプログラムで記述することができるからです。

ただし,ロジックとビューができるだけ分離できるように,スタイルシートを使って表示属性を指定できる仕組みも導入しています。

データモデル

Web用テンプレートエンジンと同様に,Rubyであればハッシュ/配列,Pythonでは辞書/リストといったLL言語ネイティブのデータ構造により,データを与えます。ファイル渡しの場合は,JSONも利用可能です。

例えばPythonであれば,以下のように記述します。

    {
        "date": "平成23年1月22日",
        "number": "10R0001",
        "to": "△△△惣菜株式会社",
        "title": "肉じゃがの材料",
        "delivery_date": "平成23年1月22日",
        "delivery_place": "貴社指定場所",
        "payment_terms": "銀行振込",
        "expiration_date": "発行から3ヶ月以内",
        "stamp1": {"icon": "./stamp.png"},
        "table": [
            ["1", "N001", "牛肉(切り落とし)", "200g", "250円", "500円"],
            ["2", "Y001", "じゃがいも(乱切り)", "3個", "30円", "90円"],
            ["3", "Y002", "にんじん(乱切り)", "1本", "40円", "40円"],
            ["4", "Y003", "たまねぎ(くし切り)", "1個", "50円", "50円"],
            ["5", "Y004", "しらたき", "1袋", "80円", "80円"],
            ["6", "Y005", "いんげん", "1袋", "40円", "40円"]
        ],
        "sub_total": "800円",
        "tax": "40円",
        "total": "840円"
    }

PDFのフィールド名と一致するように辞書のキー名を与えています。テーブル形式の部分では,リスト形式によりデータを与えることで,記述を簡略化しているとともに行数が可変のテーブルが表現できるようにしています。

テンプレートエンジン

HTMLに値を埋め込む処理と比較して,PDF出力では格段に複雑な処理が必要となります。

主な処理は以下のとおりです。

  • テンプレートPDFを読み込みパース(解析)する。
  • 各フィールドにデータモデルで与えられたテキストや画像をセットする。
  • 各フィールドの値と表示属性に対応したPDFの描画命令列を生成する(同時に禁則処理・ハイフネーション処理などの組版処理も行う)。
  • すべての要素を再構成して,1本のPDFとして出力する。

特に「PDFの描画命令列の生成」は,Webブラウザが画面表示時に行うレンダリング処理を事前に実施してPDFに埋め込んでおく形になりますので,この分だけでも確実に処理時間が掛かります。ただそうは言っても,裏方のツールとしては,すこしでも処理時間を早くしたいところです。

この,

  • 複雑なロジックを記述する必要がある。
  • 処理速度が要求される。

という要件を満たすためにOCamlを用いたということは,以前にも書いたとおりです。

成果ドキュメント

現在では,帳票出力の成果ドキュメントとして,PDFがデファクトスタンダードになっているのではないかと考えています。

その理由として,以下のようなメリットが考えられます。

  • 特殊なプラグインなどのインストールが不要
  • 画面で見たままのイメージで印刷できる。
  • ファイルとして配布することもできる。
  • 素のままでもWordやExcelと比べれば改ざんが困難。
  • セキュリティ(暗号化)や電子署名の仕組みを導入すれば,改ざんはほぼ不可能になる。

まとめ

以上見てきたとおり,テンプレートエンジンの要件を満たしていると思いますので,少なくとも Field Reprts をPDF出力専用のテンプレートエンジンとみなして良いのではないかと考えています。

Field Reprts は,以下の特徴を持つPDF出力テンプレートエンジンです。

  • Officeソフトで作成したPDFをベースとして,簡単にソーステンプレートが準備できます。
  • データモデルは,ハッシュ・辞書のようなLL言語ネイティブのデータ構造で表現します。
  • OCamlで開発した,高速かつ高度なテンプレートエンジンを装備しています。
  • 成果ドキュメントとして,デファクトスタンダードのPDFを出力します。