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

Thinreports 0.9.0 で .tlf ファイルの差分が取りやすくなった

2016.6.1 Thinreports 0.9.0 をリリースしました。

Thinreports 0.9.0 is out! | Thinreports - オープンソース PDF 帳票ツール for Ruby, Rails

0.9.0 以降の Editor では、.tlf ファイルが新しい形式で保存されるようになります。これによって、いわゆる「Diff 辛い問題」が解消されます。どういうことか簡単に紹介します。

古い形式

例えば、 test.tlf を Editor で編集して、一つのテキストブロックの文字色を #000000 から #ff0000 へ変更し上書き保存したとします。 0.8.0 以前の古い保存形式の場合、普通に diff を取ると次のような結果となります。

--- a/test.tlf
+++ b/test.tlf
@@ -1 +1 @@
-{"version":"0.8.2","config":{"title":"Test","option":{},"page":{"paper-type":"A4","orientation":"portrait","margin-top":"20","margin-bottom":"20","margin-left":"20","margin-right":"20"}},"svg":"<svg width=\"595.2\" height=\"841.8\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"none\" viewBox=\"0 0 595.2 841.8\"><g class=\"canvas\"><g class=\"s-text\" stroke-width=\"0\" fill=\"#000000\" fill-opacity=\"1\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-id=\"\" id=\"goog_45091349\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"72\" x-height=\"20.5\" x-left=\"20\" x-top=\"20\"><rect class=\"s-text-box\" stroke=\"none\" fill=\"#000000\" fill-opacity=\"0.001\" width=\"72\" height=\"20.5\" x=\"20\" y=\"20\"/><text class=\"s-text-l0\" xml:space=\"preserve\" stroke=\"none\" fill=\"inherit\" fill-opacity=\"1\" text-decoration=\"none\" x=\"20\" y=\"36\">\u30bf\u30a4\u30c8\u30eb</text></g><!--SHAPE{\"type\":\"s-tblock\",\"id\":\"text\",\"display\":\"true\",\"desc\":null,\"multiple\":\"false\",\"valign\":\"\",\"line-height\":\"\",\"line-height-ratio\":\"\",\"box\":{\"x\":20,\"y\":56,\"width\":164.1,\"height\":20.5},\"format\":{\"base\":\"\",\"type\":\"\"},\"value\":\"\",\"ref-id\":\"\",\"overflow\":\"\",\"word-wrap\":\"break-word\",\"svg\":{\"tag\":\"text\",\"attrs\":{\"x\":20,\"y\":72,\"xml:space\":\"preserve\",\"kerning\":\"auto\",\"letter-spacing\":\"normal\",\"id\":\"goog_45091350\",\"fill\":\"#000000\",\"fill-opacity\":\"1\",\"font-size\":\"18\",\"font-family\":\"IPAMincho\",\"font-weight\":\"normal\",\"font-style\":\"normal\",\"text-anchor\":\"start\",\"text-decoration\":\"none\"}}}SHAPE--><!--LAYOUT<g xmlns=\"http://www.w3.org/2000/svg\" class=\"s-tblock\" x-format-type=\"\" x-value=\"\" x-format-base=\"\" x-ref-id=\"\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-multiple=\"false\" id=\"goog_45091350\" x-id=\"text\" fill=\"#000000\" fill-opacity=\"1\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"164.1\" x-height=\"20.5\" x-left=\"20\" x-top=\"56\"><rect class=\"s-tblock-box\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"0.2\" width=\"164.1\" height=\"20.5\" x=\"20\" y=\"56\"/><text class=\"s-tblock-id\" font-size=\"10.5\" font-family=\"Helvetica\" font-weight=\"normal\" font-style=\"normal\" text-decoration=\"none\" text-anchor=\"start\" kerning=\"auto\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"1\" x=\"24\" y=\"67\">text</text></g>LAYOUT--></g></svg>","state":{"layout-guide":[]}}
\ No newline at end of file
+{"version":"0.8.2","config":{"title":"Test","option":{},"page":{"paper-type":"A4","orientation":"portrait","margin-top":"20","margin-bottom":"20","margin-left":"20","margin-right":"20"}},"svg":"<svg width=\"595.2\" height=\"841.8\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"none\" viewBox=\"0 0 595.2 841.8\"><g class=\"canvas\"><g class=\"s-text\" stroke-width=\"0\" fill=\"#000000\" fill-opacity=\"1\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-id=\"\" id=\"goog_45091349\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"72\" x-height=\"20.5\" x-left=\"20\" x-top=\"20\"><rect class=\"s-text-box\" stroke=\"none\" fill=\"#000000\" fill-opacity=\"0.001\" width=\"72\" height=\"20.5\" x=\"20\" y=\"20\"/><text class=\"s-text-l0\" xml:space=\"preserve\" stroke=\"none\" fill=\"inherit\" fill-opacity=\"1\" text-decoration=\"none\" x=\"20\" y=\"36\">\u30bf\u30a4\u30c8\u30eb</text></g><!--SHAPE{\"type\":\"s-tblock\",\"id\":\"text\",\"display\":\"true\",\"desc\":null,\"multiple\":\"false\",\"valign\":\"\",\"line-height\":\"\",\"line-height-ratio\":\"\",\"box\":{\"x\":20,\"y\":56,\"width\":164.1,\"height\":20.5},\"format\":{\"base\":\"\",\"type\":\"\"},\"value\":\"\",\"ref-id\":\"\",\"overflow\":\"\",\"word-wrap\":\"break-word\",\"svg\":{\"tag\":\"text\",\"attrs\":{\"x\":20,\"y\":72,\"xml:space\":\"preserve\",\"kerning\":\"auto\",\"letter-spacing\":\"normal\",\"id\":\"goog_45091350\",\"fill\":\"#ff0000\",\"fill-opacity\":\"1\",\"font-size\":\"18\",\"font-family\":\"IPAMincho\",\"font-weight\":\"normal\",\"font-style\":\"normal\",\"text-anchor\":\"start\",\"text-decoration\":\"none\"}}}SHAPE--><!--LAYOUT<g xmlns=\"http://www.w3.org/2000/svg\" class=\"s-tblock\" x-format-type=\"\" x-value=\"\" x-format-base=\"\" x-ref-id=\"\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-multiple=\"false\" id=\"goog_45091350\" x-id=\"text\" fill=\"#ff0000\" fill-opacity=\"1\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"164.1\" x-height=\"20.5\" x-left=\"20\" x-top=\"56\"><rect class=\"s-tblock-box\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"0.2\" width=\"164.1\" height=\"20.5\" x=\"20\" y=\"56\"/><text class=\"s-tblock-id\" font-size=\"10.5\" font-family=\"Helvetica\" font-weight=\"normal\" font-style=\"normal\" text-decoration=\"none\" text-anchor=\"start\" kerning=\"auto\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"1\" x=\"24\" y=\"67\">text</text></g>LAYOUT--></g></svg>","state":{"layout-guide":[]}}
\ No newline at end of file

これは辛い。主な原因は以下の通りです。

  1. 改行が取り除かれ、一行になっている
  2. レイアウトデザインは SVG がそのまま保存されている

(2) について少し説明が必要だと思います。.tlf の中身は JSON ではありますが、図形の位置やスタイルなどのデザインの状態は、SVG 形式で文字列として保存されています。下記は古い形式の .tlf の中身をわかりやすく整形したものですが、"svg" キーの値がそれです。

{
  "version": "0.8.2",
  "config": {
    "title": "Report Title",
    "page": {
      "paper-type": "A4",
      "orientation": "landscape",
      "margin-top": 0.0,
      "margin-bottom": 0.0,
      "margin-left": 0.0,
      "margin-right": 0.0,
    }
  },
  "svg": "<svg><g class=\"canvas\"><!--LAYOUT<rect x-id=\"rect_id\" x=\"100.0\" y=\"100.0\" width=\"200\" height=\"200\"/>--><!--SHAPE{ "type": "s-text", "id": "rect_id", "display": "true", "svg": { "attrs": {...} }}SHAPE--></g></svg>"
} 

当然、文字色の情報も "svg" キーの中のどこかに記録されていますが、単純な diff でその変更箇所を探すことは現実的ではありませんでした。

新しい形式

一方、新しい形式で diff を取ると以下のようになります。

--- a/test.tlf
+++ b/test.tlf
@@ -41,7 +41,7 @@
           "IPAMincho"
         ],
         "font-size": 18,
-        "color": "#000000",
+        "color": "#ff0000",
         "text-align": "left",
         "vertical-align": "top",
         "line-height": "",

一目瞭然。

新しい形式の詳細は下記で詳しく説明しています。興味がある方はどうぞ。

Diff が取れて何が嬉しいのか

いろいろあると思いますが、

  • git などの VCS で tlf ファイルの変更履歴が管理しやすくなる
  • Pull Request 等、コードレビューが捗る

などでしょうか。コードレビューが捗る、というか可能になったことは個人的にも本当に助かります。

まとめ

新しい形式に変更した理由は他にもありますが、その一つとして「Diff 辛い問題の解消」について説明しました。 今回の変更によって、Thinreports を使った開発が少しでもやりやすくなれば幸いです。