よくわかっていないツールと言語でよくわかっていないマークアップのCustom Writerをやっていく
この記事は
EWB アドベントカレンダー2019の21日目の記事です。
Pandocアドベントカレンダー2019の21日目の記事です。
https://adventar.org/calendars/4338
20日目は某ZR(ざんねん🙃) (@zr_tex8r) | Twitterさんで https://zrbabbler.hatenablog.com/entry/2019/12/04/234053でした。
最初に書いておくと完成してません。 明日夜だと思っていたタスクが朝締切だったりするなど、師走を実感しますね。
前置き
さて、EWBの情報を色々と集めてきたわけですが、
「reSTをEWBに!」「いいですとも!」
ということで、他の軽量マークアップ形式からEWBにして作成された書籍がありました。残念ながら変換処理については出版社内部で行われたようで詳細は分かりませんが、DocutilsはreST以外のReader、やHTML以外のWriterを独自に作ることもできるので、この辺りを活用するとちょっと楽そうですね。楽だとは言っていませんが。
そんな感じに記事のネタを探していたワケですが、うっかり21日にPandocアドベントカレンダーの登録をしていたことを思い出しました。
そういえば鹿野さんの記事でPandocとDocutilsの相似について言及されていました。
ヨシ!
EWBの記法おさらい
EWB文書本体のマークアップには 「組版トリガ」と「編集トリガ」 があります。このうち「組版トリガ」は書籍上の見た目に関わるものなので、今回Pandocで変換元とするMarkdownからは考えなくとも良さそうです。
編集トリガの基本文法は以下のようになります。実はブロックかインラインかは記法上は区別がなく、各トリガそれぞれで異なり、なんだったら書き換えられます。
//<文字列>{ ...//<文字列>}...
見出しなどは開始タグ閉じタグがつきません。
//i 見出し
リストの基本は以下のようになります。
///k1{ <記号> <文字列> <記号> <文字列> //k1}
テーブルはシンプルな構造であれば タブ文字区切りで似たことをやればよいので、基本は大体これでいける気がしてきました。
今回はこれらの基本的なところに絞り、 Custom Writerに挑戦してみます。
みりあやんないよ
- EWBでは改行が反映されますが、今回は正しく処理できるかは考慮しないものとします。一応
SoftBreak
設定してやれば動くハズ? - EWBで使える文字種はJIS全角範囲とされています。EWBで処理時にエラーを吐いてくれるはずなので今回はスルーします。
Pandoc Custom Writer
Pandocアドベントカレンダー2019にはReaderの記事がありますが、
Writerは既にPandocに渡っている構造の出力を弄るだけなのでより簡単、な筈、です。
カスタムライターを作るために、先ずはLuaにおける出力例の一覧を取得します*1。
これを書き換えていきます。
例えば
-- sample.lua function Emph(s) return "<em>" .. s .. "</em>" end
が
-- ewbfilter.lua function Emph(s) return "//g{" .. s .. "//g}" end
になる、といった感じですね。実際のEWBでは 文字エンコーディングはSJISかEUC-JPになりますが、そんなんは後で変換かければええ。
エスケープ
さて、出力毎に調整が必要な機能として エスケープがあります。
(Pandoc)Markdownでは問題の無かった表現がEWBではNG、というものですね。
EWBのトリガは先頭に//
があるので、ここから3種くらいの区切りまでがトリガとされます。//
がきたら特別な場合を除き
////
に直してやる必要があります。
/**hoge**
といった並びのとき、素朴な置換をすると///g{hoge//g}
となりますが、ここはEWB(ハンドブック)を信じてそのままに。
また、今回扱わないといった組版トリガも
Markdown文中に表われるのはけしからんのでエスケープします。@@
は@@@@
にします。
改行
function SoftBreak() return "\n" end function LineBreak() return "\n" end
箇条書き
BulletList
ある意味単純ですね。ネストも問題無いハズ。
項目先頭の記号はとりあえず・
でハードコーディング。
function BulletList(items) local buf = {} for _, items in pairs(items) do table.insert(buf, "・ //|" .. item .. "\n") end return "//k1{{" ..table.concat(buf, "\n") .. "\n//k1}" end
OrderedList
先程よりちょっと厄介なのは、やHTMLと違って自動インクリメントされるマークアップではないので、 数字をこちらで回すこと。まあそれだけのハズ。
function OrderedList(items) local buf = {} for i, item in pairs(items) do table.insert(buf, i .. ".//| " .. item .. "\n") end return "//k2{{" .. table.concat(buf, "\n") .. "\n//k2}}" end
DefinitionList
まあ箇条書きのグループとしてみれば、やることは大体同じでしょう。
function DefinitionList(items) local buffer = {} for _, item in pairs(items) do for k, v in pairs(item) do table.insert(buf, k .. " //| ".. v .."\n") end end return "//k3{" .. table.concat(buf, "\n") .. "//k3}" end
コードブロック
元のサンプルだとエスケープ処理挟んでたんですが、 アトリビュートはまあオプション引数に影響するにしても トリガ定義側でなんとかする奴なので今回は飛ばします。
ただ、コードブロック内部のコメントは特殊な処理をしてコメントであることを明示するようにしなければなりません。 えっアイデアが浮かばない。とりあえず空のローカル関数を定義して濁します。
function CodeBlock(s, attr) local cmt_parse = comment_perse(s) return "//list1{\n" .. cmt_perse .."\n//list1}" end local function comment_parse(s) return s end
水平線
function HorizontalRule() return "//kei\n" end
Caption付き図表
EWBでは、基本的には図表を文書中では扱いません。テーブル などは記述可能ですが、文書中には基本的にユニークな番号とキャプションの文字列だけです。実際に図表リストを別に作成するのは処理が複雑化するのでスルーします。 ユニークな番号に関しては数字以外でもいいはずですが、面倒なので数字ということにしましょう。実装も手を抜いて 使用直前にインクリメントでいきましょう。
local fignum = 0 function CaptionedImage(src, tit, caption, attr) fignum = fignum + 1 return '//f'.. fignum .. " " .. caption .. "\n" end
Table
EWBのテーブルはそれなりに複雑な図表も可能ですが、シンプルテーブルの入力を仮定します。 寄せの指定は簡単ですが、カラム幅の文字数指定って互換ありそうでないんですよね。今回はやりません。
function Table(caption, aligns, widths, headers, rows) local buf = {} local function add(s) table.insert(buf, s) end add("//table1") for _, align_raw in pairs(align) do local align = ewb_align(align_raw) add("[" .. align .. "]") end add("{\n") local header_row = {} local empty_header = true for i, h in pairs(headers) do empty_header = empty_header and h = "" end if empty_header then head = "" else add("//g{") for _, h in pairs(header_row) do add(h .. "\t") end add("//g}\n") for _, row in pairs(rows) do for i, c in pairs(row) do add(c .. "\t") end add("\n") end end add("//table1}") return table.concat(buf,'\n') end
未完
とりあえずlocal
でないsample.luaに出てくる関数を全部埋めれば完成するハズ。
Span
やDev
も、本格的に変換を作るなら編集トリガと対応を作るべきなんだろうなあ。
というワケで、Readerでの難しい話が嘘のようにWriterの出力を弄るのは楽かもしれないという結びです*2。
明日の枠も空いてるみたいですが、明日はSphinxをやっている予定なのでこの続きで埋めるとかはないです。
*1:(新・工事中)Pandocユーザーズガイド 日本語版 — 日本Pandocユーザ会 2019.02.21 ドキュメント
*2:ならなぜ完成してないんでしょうねぇ