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

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

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

CSVから帳票を生成するツールを作ってみました

製品情報

弊社のLL言語用PDF帳票ツールField Reportsを使って,CSVファイルを元にPDF帳票を生成するツールを試作してみました。

同様の処理はMS WordとExcelの「差し込み印刷」機能を使ってもできますが,このツールには以下の特長があります。

  • 雛形の作成手段がWordに限定されない(PDF化できる文書ならなんでも使える)。
  • Linux, Mac OS X, Windows環境で動作する(逆に,現在のところWindows環境では動作しません)(2011.12.28修正)。
  • 他のツールと組み合わせて処理を自動化するのが比較的簡単(例えば,どこかのWebサイトから取得したHTMLからデータを抜き出して,レポートを自動生成するなど)。
  • サーバサイドでの運用も問題ない(マイクロソフトでは,Officeをサーバサイドで使用することはお勧めしていないようです:Office のサーバーサイド オートメーションについて)。

テンプレートの準備

テンプレート(雛形)となるPDFファイルを用意します。

今回は,OpenOffice.orgで請求書のフォーマットを作成し,「PDFとしてエクスポート...」でPDF化しました。
次に,作成したPDFをAdobe Acrobatで開き,テキストを流し込みたい位置にフォーム・フィールドを配置していきます。

f:id:fet:20110629112150p:image:w360

CSVファイルの準備

PDFテンプレートに流し込むデータを作成し,CSV形式のファイルとして保存します。

CSVファイルの1行目はヘッダ行として使用し,データは2行目以降に置きます。
ヘッダ行の見出しは,PDFテンプレートに配置したフィールド名と一致させます。
テキストの文字コードSJISとします。

"date","number","to","title","delivery_date","delivery_place","payment_terms","expiration_date","table.0.0","table.0.1","table.0.2","table.0.3","table.0.4","table.0.5","table.1.0","table.1.1","table.1.2","table.1.3","table.1.4","table.1.5","table.2.0","table.2.1","table.2.2","table.2.3","table.2.4","table.2.5","table.3.0","table.3.1","table.3.2","table.3.3","table.3.4","table.3.5","table.4.0","table.4.1","table.4.2","table.4.3","table.4.4","table.4.5","table.5.0","table.5.1","table.5.2","table.5.3","table.5.4","table.5.5","sub_total","tax","total"
"平成23年1月22日","10R0001","△△△惣菜株式会社","肉じゃがの材料","平成23年1月22日","貴社指定場所","銀行振込","発行から3ヶ月以内",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円","800円","40円","840円"

CSV→PDF変換プログラムの作成

CSV→PDF変換プログラムをPythonで作成しました。実験的な位置づけのツールなので,エラー処理などはほどんど考慮していません。

基本的にはヘッダ行の見出しとデータ行の値を単純に組み合わせて辞書の要素を作成しているだけですが,2次元テーブルの要素名を“table.0.0”の形式で表現していて,
Field Reportsで利用するためには,

{'table': {'0': {'0': ''}}}

のような形式に変換する必要があり,dset()関数でその処理を行っています。

#!/usr/bin/env python
# coding: utf-8

import sys
import csv
from field import reports

param = {
    "template": [
        {"*": {"src": "./mitumori.pdf", "rows": 10}}
    ],
    "style": [
        {"*.stamp1": {"icon": "./stamp.png"}},
    ]
}

def dset(d, keys, value):
    if len(keys) == 0:
        return value
    else:
        if d.has_key(keys[0]):
            dset(d[keys[0]], keys[1:], value)
        else:
            d[keys[0]] = dset({}, keys[1:], value)
        return d

def read_csv(fname):
    result = []
    reader = csv.reader(file(fname, "rb"))
    header = reader.next()
    for row in reader:
        item = {}
        for key, value in zip(header, row):
            if key and value:
                keys = unicode(key, 'sjis').split('.')
                dset(item, keys, unicode(value, 'sjis'))
        result.append(item)
    return result

if __name__ == "__main__":
    if len(sys.argv) == 3:
        context = read_csv(sys.argv[1])
        param['context'] = context
        reports.set_log_level(3)
        reports.render(param, sys.argv[2])
    else:
        print("usage: %s <csvfile> <outfile>" % (sys.argv[0], ))

処理結果

使い方は,コマンドラインから以下のコマンドを実行します。

$ python csv2pdf.py data.csv out.pdf

作成されたPDFを以下に示します。

f:id:fet:20110629112151p:image:w360

今回の例では1ページの帳票を作成しましたが,データが複数行あれば複数ページのPDFが作成されます。