Slideshareに投稿したのですが、LTでやったので詳細が伝わらないと思ったので解説します。
http://www.slideshare.net/kawasima/ss-33220875
Excel方眼紙の話題は尽きることはありませんが、どうも殺意ばかりが前面にでて、Excel方眼紙と上手く付き合う方法がみられなかったので、作ってみた次第です。
Excel方眼紙をPDF等のただの帳票として考えると、これはなかなか面白いものですし、iTextを使ってPDFを作るより、Excel方眼紙を作ってからPDF化した方が簡単に帳票出力コードかける気がしています。帳票レイアウトもExcel方眼紙であればユーザに直接作ってもらったものが使えますし、ね
axbomber-cljとは
axebomber-cljは、HTML-likeなコードでExcel方眼紙をアウトプットするためのツールです。
構文はhiccupと同じく[タグ 属性 コンテンツ]
というClojureのvectorによるツリー構造になります。非常に直感的です。
renderタグは方眼紙に対する絶対座標での位置を示しますが、中のコンテンツは原則的に垂直フローティングされます。すなわち、行の中身が数行に及べば、その分だけ後ろのコンテンツは自動的に下にずれて配置されます。
レンダリングの基本
指定した位置に書き込む
render関数を使って、任意の方眼紙のマス目に書き込めます。
(render sheet 1 1 "こんにちは、Excel方眼紙!")
(render sheet 1 2 "さようなら、UIとしてのExcel…")
第1引数はPOIのシートオブジェクトで第2、第3引数が、x,y座標を指します。
axebomber-cljには、Excelシートを方眼紙にするユーティリティ関数のto-grid
があるので、それを使うとよいでしょう。
(let [sheet (to-grid (.createSheet wb "Simple table"))]
;; renderの処理をここに書く
)
表組み
このように、HTMLのtable,tr,tdタグを使ってテーブルを表現すると、Excelシート上に
(render sheet 0 0
[:table
[:tr
[:td "ID"]
[:td "名前"]]
[:tr
[:td 1]
[:td "りんご"]]
[:tr
[:td 2]
[:td "ばなな"]]])
tdタグにはHTMLのスタイルシートよろしく、スタイルを定義できます。
(render sheet 0 0
[:table
[:tr
[:td {:data-width 3 :style "background-color: lightblue"} "ID"]
[:td {:data-width 8 :style "background-color: lightgreen"} "名前"]]
[:tr
[:td 1]
[:td "りんご"]]
[:tr
[:td 2]
[:td "ばなな"]]])
スタイルは、クラス定義して要素に適用することも可能です。
(create-style ".title1" :background-color "lightblue")
(create-style ".title2" :background-color "lightgreen")
(render sheet 1 1
[:table
[:tr
[:td.title1 {:data-width 3} "ID"]
[:td.title2 {:data-width 8} "名前"]]
[:tr
[:td 1]
[:td "りんご"]]
[:tr
[:td 2]
[:td "ばなな"]]])
便利ですね!
テーブルのネストも可能です。※ただし、適切にマージンを取らないとデザインが崩れます。
(render sheet 1 1
[:table
[:tr
[:td.title1 {:data-width 3} "ID"]
[:td.title2 {:data-width 8} "名前"]]
[:tr
[:td 1]
[:td [:table {:data-margin-top 1 :data-margin-left 1 :data-margin-bottom 1}
[:tr
[:td {:data-width 3} ""]
[:td {:data-width 3} "産地"]]
[:tr
[:td "ふじ"]
[:td "青森"]]
[:tr
[:td "あずさ"]
[:td "長野"]]]]]
[:tr
[:td 2]
[:td "ばなな"]]])
リスト
リストもHTMLと同じようにul, ol, liタグが使えます。
(render sheet 1 1
[:ul
[:li "りんご"]
[:li "ばなな"]
[:li "いちご"]])
(render sheet 1 1
[:ol
[:li "りんご"]
[:li "ばなな"]
[:li "いちご"]])
図
まだ、ハコしか書けませんが、方眼紙を利用して図を書くことができます。
(render sheet 1 1
[:graphics {:data-width 30 :data-height 30}
[:box {:x 1 :y 1 :w 3 :h 3} "ハコ1"]
[:box {:x 5 :y 2 :w 4 :h 2} "ハコ2"]])
スケジュールや体制図もExcel方眼紙に書くことができますね!
実用的な方眼紙を書く
さて、それではこれらを組み合わせて、かの有名な「Excel方眼紙」の何が悪い?の記事に出ていた営業日報を作ってみます。
hiccupでHTMLレンダリングするときと同じように、データを定義します。
(def model
{:店舗 "西新宿店"
:担当 "川島義隆"
:日付 (Date.)
:計画
{:来客数 150
:売上高 150000
:販売点数 1000}
:実績
{:来客数 2
:売上高 1800
:販売点数 30}
:備考 "さっぱりでした…"
:取引記録
[{:取引先名 "A社"
:連絡先 "03-XXXX-XXXX"
:内容 "担当者の笑顔が素敵です。"}
{:取引先名 "B社"
:連絡先 "03-XXXX-XXXX"
:内容 "担当者怖いです。"}
{:取引先名 "C社"
:連絡先 "03-XXXX-XXXX"
:内容 "電話にでんわ。"}]
:特記事項 "特になし\n\n\n\nですが、改行の分だけ行数が伸びます。"})
それから、これらの値をテンプレートに埋めていきます。if
やfor
を使った制御も可能です。
(defn template [sheet model]
(render sheet 1 1
[:table
[:tr
[:td.header {:data-width 28} "営業日報"]]
[:tr
[:td.title {:data-width 3} "店舗"]
[:td {:data-width 8} (:店舗 model)]
[:td.title {:data-width 3} "担当"]
[:td {:data-width 7} (:担当 model)]
[:td.title {:data-width 2} "日付"]
[:td {:data-width 5} (:日付 model)]]
そして定義したモデルをテンプレートでレンダリングしてやると、Excel方眼紙「営業日報」が出力できます。
(create-style ".title"
:border-style "solid"
:background-color "lightgreen")
(create-style ".header"
:border-style "solid"
:color "white"
:background-color "seagreen")
(create-style ".number"
:text-align "right")
(create-style ".vertical"
:writing-mode "vertical-rl"
:vertical-align "top"
:text-align "center")
(create-style ".center"
:text-align "center")
(template sheet model)
詳しくは、axebomber-cljのテストコードを見てみてください。
今後の予定
まだ、開発中のSNAPSHOTバージョンしかないので、これからもインタフェース変えることもあると思いますが、データとスタイルを分離したテンプレートを元に、Excel方眼紙にキレイにレンダリングできることは、Excel方眼紙に対する評価を180度変える可能性があるので、LTの一発ネタだけではなく、開発継続したいと思います。