Excel方眼紙をHTMLライクに作ってみる

Slideshareに投稿したのですが、LTでやったので詳細が伝わらないと思ったので解説します。
http://www.slideshare.net/kawasima/ss-33220875

Excel方眼紙の話題は尽きることはありませんが、どうも殺意ばかりが前面にでて、Excel方眼紙と上手く付き合う方法がみられなかったので、作ってみた次第です。
Excel方眼紙をPDF等のただの帳票として考えると、これはなかなか面白いものですし、iTextを使ってPDFを作るより、Excel方眼紙を作ってからPDF化した方が簡単に帳票出力コードかける気がしています。帳票レイアウトもExcel方眼紙であればユーザに直接作ってもらったものが使えますし、ね :laughing:

## axbomber-cljとは

[axebomber-clj](https://github.com/kawasima/axebomber-clj)は、HTML-likeなコードでExcel方眼紙をアウトプットするためのツールです。

構文は[hiccup](https://github.com/weavejester/hiccup)と同じく`[タグ 属性 コンテンツ]`というClojureのvectorによるツリー構造になります。非常に直感的です。

renderタグは方眼紙に対する絶対座標での位置を示しますが、中のコンテンツは原則的に垂直フローティングされます。すなわち、行の中身が数行に及べば、その分だけ後ろのコンテンツは自動的に下にずれて配置されます。

## レンダリングの基本

### 指定した位置に書き込む

render関数を使って、任意の方眼紙のマス目に書き込めます。

```clj
(render sheet 1 1 "こんにちは、Excel方眼紙！")
(render sheet 1 2 "さようなら、UIとしてのExcel…")
```

![SnapCrab_NoName_2014-4-9_16-23-20_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/9fe5312c-04c0-1df4-be10-c3f01cdb8a53.png)


第1引数はPOIのシートオブジェクトで第2、第3引数が、x,y座標を指します。
axebomber-cljには、Excelシートを方眼紙にするユーティリティ関数の`to-grid`があるので、それを使うとよいでしょう。

```clj
(let [sheet (to-grid (.createSheet wb "Simple table"))]
  ;; renderの処理をここに書く
)
```

### 表組み

このように、HTMLのtable,tr,tdタグを使ってテーブルを表現すると、Excelシート上に

```clj
(render sheet 0 0
        [:table
         [:tr
          [:td "ID"]
          [:td "名前"]]
         [:tr
          [:td 1]
          [:td "りんご"]]
         [:tr
          [:td 2]
          [:td "ばなな"]]])
```

![SnapCrab_NoName_2014-4-9_15-53-53_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/4ec5a141-3f77-440e-c071-6efcdcc64d2d.png)

tdタグにはHTMLのスタイルシートよろしく、スタイルを定義できます。

```clj
(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 "ばなな"]]])
```

![SnapCrab_NoName_2014-4-9_16-26-19_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/8943bea5-3865-7daf-c297-671a984c4bad.png)

スタイルは、クラス定義して要素に適用することも可能です。

```clj
(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 "ばなな"]]])
```

便利ですね！

テーブルのネストも可能です。※ただし、適切にマージンを取らないとデザインが崩れます。

```clj
(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 "ばなな"]]])
```

![SnapCrab_NoName_2014-4-9_17-5-3_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/4ae9c89f-5865-3447-4a3e-304587c48eba.png)


### リスト

リストもHTMLと同じようにul, ol, liタグが使えます。

```clj
(render sheet 1 1
        [:ul
         [:li "りんご"]
         [:li "ばなな"]
         [:li "いちご"]])
```

![SnapCrab_NoName_2014-4-9_17-10-36_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/92642c37-cb2f-e432-480b-2e09cea3e3de.png)

```clj
(render sheet 1 1
        [:ol
         [:li "りんご"]
         [:li "ばなな"]
         [:li "いちご"]])
```

![SnapCrab_NoName_2014-4-9_17-10-43_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/564dacb6-6bbd-c160-5833-4fa83c55e897.png)

### 図

まだ、ハコしか書けませんが、方眼紙を利用して図を書くことができます。

```clj
(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"]])
```

![SnapCrab_NoName_2014-4-9_17-13-13_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/ed05ac83-572c-73bd-5e38-8e5fd4cd8d27.png)

スケジュールや体制図もExcel方眼紙に書くことができますね！

## 実用的な方眼紙を書く

さて、それではこれらを組み合わせて、かの有名な[「Excel方眼紙」の何が悪い？](http://itpro.nikkeibp.co.jp/article/Watcher/20140321/545102/)の記事に出ていた営業日報を作ってみます。

hiccupでHTMLレンダリングするときと同じように、データを定義します。

```clj
(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`を使った制御も可能です。


```clj:一部抜粋
(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方眼紙「営業日報」が出力できます。

```clj
(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)
```

![SnapCrab_NoName_2014-4-9_17-27-27_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/8475/aae7eee2-1818-3e4e-bd77-d6bb769d7cae.png)

詳しくは、axebomber-cljのテストコードを見てみてください。

## 今後の予定

まだ、開発中のSNAPSHOTバージョンしかないので、これからもインタフェース変えることもあると思いますが、データとスタイルを分離したテンプレートを元に、Excel方眼紙にキレイにレンダリングできることは、Excel方眼紙に対する評価を180度変える可能性があるので、LTの一発ネタだけではなく、開発継続したいと思います。