🐒
Vivliostyle, VFM で図が使いたい
Vivliostyle で VFM から PDF を生成できて大変便利!……なのだけど、githubみたいにmermaidの図もmarkdownでサクサク書いて、図と文章とセットで(バラバラにならないように)管理したい。この前初めてお邪魔させていただいた開発者会議でそんなことを漏らしたら、「最近追加された JavaScript を走らせる機能でできるんじゃない?」「やった人の記事があるから、参考にするといいかも」という話をいただいた。
そうは言うものの、よくよく考えてみたら VFM レベルで処理されるべきなので、掘り進めて実装してみたらできた気がする。
How-to
一言でいうと「NODE の引数で関数をプリロードさせて、ライブラリに潜り込ませる!」です。本体への改変なしでいけました。
NODE_OPTIONS=--require=./dosvg.js vfm example.md
NODE_OPTIONS=--require=./dosvg.js vivliostyle build example.md
dosvg.js
本体はちょっと長いですが、こんな感じです。kroki を使って svg を得ています。デフォルトでは kroki.io
を使いますが、自分で kroki を用意した場合は、環境変数 KROKIHOST
で切り替えてください。
デフォルトでは mermaid
のみを対象としていますが、他のエンジンで svg を出力したいときは、環境変数 KROKILANGS
にカンマ区切りで指定してください。refractor 3.x と kroki の両方でサポートされているのは mermaid,dot
です。
dosvg.js
const hostname = process.env.KROKIHOST || "kroki.io";
const langs = process.env.KROKILANGS || "mermaid";
const https = require("node:https");
const deasync = require("deasync");
const p = require("prismjs/components/prism-core");
const klangs = langs.split(",");
p.hooks.add("wrap", function (env) {
if (env.type == "base64") {
env.content = {
type: "raw",
value: Buffer.from(env.content.value, "base64").toString()
}
}
});
p.hooks.add("after-tokenize", function (env) {
if (klangs.includes(env.language)) {
env.tokens = [new p.Token("base64", env.code, null, null)];
}
});
p.hooks.add("before-tokenize", function (env) {
if (klangs.includes(env.language)) {
env.grammar = { raw: { pattern: /\A.*\z/m } };
const rb = {
diagram_source: env.code,
diagram_type: env.language,
output_format: "svg"
};
const p = (resolve) => {
const req = https.request({
hostname: hostname,
port: 443,
path: "/",
method: "POST",
}, res => {
let resp = [];
res.on("data", (c) => { resp.push(c); });
res.on("end", () => {
env.code = Buffer.concat(resp).toString("base64");
resolve();
});
});
req.write(JSON.stringify(rb));
req.end();
};
deasync(p)();
};
});
Discussion