はじめに
pandoc
単体では、Markdownに埋め込まれたPlantUMLを自動的に処理することはできず、先人たちはmarkdown
のパース結果をjson
フォーマットで取り出し、python
等で苦心して変換していた(記憶がある)
幸いにも、バージョン2.0
以降、組み込まれたLua
言語によるフィルタ処理が使えるようになった。
実際、pandoc
公式のレポジトリ(pandoc/lua-filters)には、多くのLua
フィルタが用意されており、その中にあるlua-filters/diagram-generatorはPlantUML
のソースを画像に置き換えるフィルタがある。
ただしこのフィルタ、ローカルにインストールして動かすタイプのため、準備が何かとメンドイ。
今日日、docker
コンテナ立ち上げるくらいで済ませて欲しいものである。
ところで、PlantUML
は、テキスト エンコード 方式でエンコードして投げつけると、画像を返してくれるPlantUML Server
という機能(Web App)を持っている。 (see: https://plantuml.com/ja/text-encoding)
また、docker hub
にPlantUML Server
イメージも用意してくれている(plantuml/plantuml-server)。
これらを悪魔合体させてLua
フィルタに組み込んでやれば、幾分楽になるのではという思いでチャレンジしてみた。
ざっと調べてみたところ、Lua
フィルタ+PlantUML Server
は見つからなかったので新規性あるかも
レポジトリ
簡易的な内容のmarkdown
について置換できたので、github
にレポジトリぶち上げときました(ritalin/pandoc-plantuml-luafilter)。詳細はそちらを参照してください、
処理内容の詳細
PlantUML Server
にリクエストを送るための、テキスト エンコード 方式をサイトから引用すると、
- テキストをUTF-8でエンコードします。
- DeflateまたはBrotliアルゴリズムで圧縮します。
- Base64に類似したアルゴリズムを使って、ASCIIに再エンコーディングします。
とされている。
1については、pandoc
は、ソースをUTF-8
で記述しなければならないため、多分解決していると思われる。
2については、純Lua製のSafeteeWoW/LibDeflateが見つかった。1
問題は、Lua
フィルタで外部ライブラリを使うことができるかどうかということ。
pandoc
のドキュメントには、
Initialization of pandoc’s Lua interpreter can be controlled by placing a file init.lua in pandoc’s data directory.
とあり、一応組み込むことは可能っぽい。しかしグローバルに依存ライブラリを入れるのはちょっといただけない。
ということで、代案はないか探した。いっぱい探した。
結果、パッケージのソース検索パスに入れてあげれば、グローバルに組み込まなくても使えることが判明。
検索パスはpackage.path
に保持されているため、
local search_paths = {
package.path,
paths.join({ root_dir, "modules", "LibDeflate", "?.lua" })
}
package.path = table.concat(search_paths, ";")
としておくことで、
local libDeflate = require("LibDeflate")
のような一般的な形式で外部ライブラリをロードできた。
また、副効果としてフィルタパスに親ディレクトリ(..
)を含んでいても解決してくれるようになった。
3については、PlantUML
公式のサンプルをもとに頑張って実装した。
pandoc
のLua
実装はバージョン5.3
なので、ビット演算子が標準に組み込まれてくれてたおかげ(Bitwise Operators)でずいぶん楽できた。
次に、PlantUML Server
にリクエストを送ること。
これは、pandoc
サイドに用意されたpandoc.mediabag
モジュールにfetchメソッドがあり、それを使うことで解決。
ファイル保存は、標準ライブラリのio
モジュールを使用。
最後に、コードブロックを画像タグに置換して完了。
local img_el = pandoc.Image({}, image_src, "")
return pandoc.Para { img_el }
制限事項
- 日本語を含む
PlantUML
のソースで試していないためおかしくなるかも - 内蔵の
Lua
フィルタでは、フォルダ作ったり、ファイルの存在チェックしたり、URL
が正しいかチェックしたりできないので2、 設定間違えると謎のエラーが出るかもしれません。
あとがき
Lua
初めて触ったけど、右往左往してなんとか形にはできた。
-
ちなみに
Brotli
のライブラリも存在していたが、ネイティブライブラリに依存していて手軽さに欠けるため断念。 ↩ -
正確にはLuaFileSystemを使えば可能ではあるが、ネイティブライブラリ依存(要はソースからのビルドが必要) ↩