まずはdocxにする際に細工をする。
さて、前回の考察の結果として、docx生成時にCodeBlockの言語情報が失われているので再生しようがないため、docx生成時になんとか情報を残す方法を考えねばならぬことがわかりました。
やろうと思えば色々とデータをdocxファイルに埋め込むことは出来ると思いますが(例えば隠し文字とか)、管理が煩雑になるのは避けたいところです。
そこで、スタイルを言語ごとに変えるという方法で考えてみます。例えばR言語であれば"Source Code R"といった具合です。
そのためにはまずdocxのテンプレートファイル中で対応するスタイルを作っておかねばなりません。これもプログラムを書いて自動化できるはずですが、面倒くさいので今回は手でやります。
テンプレートとして使用するdocxファイルを開き、Source Codeのスタイルの段落にカーソルを置いた状態で新規にスタイルを作成すれば、Source Codeを元にしたスタイルを作れるはずです。 このスタイルの名前を冒頭のように"Source Code R"のように必要な言語の数だけ作っておけばオッケーです。まぁコピペするだけなので4-5言語くらいなら手でやった方が早いでしょう…。
Lua filterの準備
さて、Lua filterで処理を考えます。と言ってもどうやって作るか…?ですが、今回はCodeBlock
を囲う形でDiv
を作ることとしました。CodeBlock
にclassがあれば、それを"Source Code "の文字列の後につなげてあげれば、上で作ったカスタムスタイルに対応しますね。
ここでは対応する言語名に対してtext.upper()
で大文字に変更しています。
ここで注意しないといけないのがCodeBlock
をそのまま残すとスタイルが反映されないということです。おそらくですが、CodeBlock
が生成するSource Code
に阻まれる形で、外から囲おうとしているSource Code R
を邪魔しているのではないかと…。
そのため、Code
として再定義することとしました。ただし、Code
はインライン要素なのでDiv
にそのまま与えることが出来ません。一旦Para
などのブロック要素に入れてからDiv
にしまってあげる必要があります。
さて、これらを反映したLua filterのコードは下記のようになります。
local text = require('text') function CodeBlock(e) for k, v in pairs(e.attr.classes) do if v == "r" then local val = "Source Code " .. text.upper(v) local code = pandoc.Code(e.text, e.attr) local div = pandoc.Div({pandoc.Para(code)}) div.attributes["custom-style"] = val return(div) end end end
これを使うと、"Source Code R"というカスタムスタイルに囲われたCode
を持つPara
を生成できます。
Code
は不要ではとも見えるのですが、これはシンタックスハイライトを効かせるために必要です。フィルタが作用するASTの段階ではまだハイライトは実行されていないので、WriterにはCode
あるいはCodeBlock
をわたす必要があります(もちろん、先の理由でCodeBlock
ではうまくいかないのでCode
一択です)
これを反映させてdocxを生成後、Word上で"Source Code R"のスタイルでスタイルが適用されている範囲を選択すれば、意図した箇所に対して適用されていることが分かると思います。
これで、テンプレートファイルの改造は必要なものの、入力ファイルや出力の見た目に影響なく変換できました。
あとはこれに対応する逆変換用のコードを書くだけです。次回に続きます。