🦧
続・続Vivliostyle, VFM で図が使いたい
Vivliostyle VFM でも github と同じように mermaid の図を埋め込みたかったので、ハックしてできるように試行錯誤している(前回と前々回)。さらに発展形。
今回は mermaid
だけでなく plantuml
も使えるようにした。もっと言うと、kroki
がサポートしている言語はどれも同じように使える。
dosvg.js
- KROKIHOST : kroki として使うホスト。https アクセスします。
- KROKILANGS : svg として埋め込みたいコードブロックの言語をカンマ区切りで。デフォルトは mermaid
- KROKIEMBED :
data:
スキームで画像を埋め込むか、KROKIHOST を使ったURLが出力されるかが切り替わる。デフォルトでは埋め込まない。
kroki で自己証明書使っている場合は、KROKIEMBED=1
を使ってください。nodejs に証明書を信用させるには NODE_EXTRA_CA_CERTS
を使います。
上で書いたオプションを全部指定したコマンドラインは、例えば次のようになります。
NODE_EXTRA_CA_CERTS=server.crt KROKIEMBED=1 KROKIHOST=kroki.example.jp KROKILANGS=plantuml,mermaid NODE_OPTIONS=--require=./dosvg.js vivliostyle build input.md -o output.pdf
dosvg.js
const hostname = process.env.KROKIHOST || "kroki.io";
const langs = process.env.KROKILANGS || "mermaid";
const embed = process.env.KROKIEMBED;
const https = require("node:https");
const deasync = require("deasync");
const pako = require("pako");
const p = require("prismjs/components/prism-core");
const r = require("refractor");
const klangs = langs.split(",");
p.hooks.add("wrap", function (env) {
if (env.type == "base64") {
env.tag = "img";
env.classes.push("kroki");
env.attributes = {
src: env.content.value
};
env.content.value = "";
}
});
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)) {
function use_kroki_direct_src() {
const data = Buffer.from(env.code, 'utf8')
const compressed = pako.deflate(data, { level: 9 })
const result = Buffer.from(compressed).toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
env.code = `https://${hostname}/${env.language}/svg/${result}`;
}
function use_kroki_data_src(resolve) {
const rb = {
diagram_source: env.code,
diagram_type: env.language,
output_format: "svg"
};
const req = https.request({
hostname: hostname,
port: 443,
path: "/",
method: "POST",
}, res => {
let resp = [];
res.on("data", (c) => { resp.push(c); });
res.on("end", () => {
const value = Buffer.concat(resp).toString("base64");
env.code = `data:image/svg+xml;base64,${value}`
resolve();
});
});
req.write(JSON.stringify(rb));
req.end();
};
if (embed) {
deasync(use_kroki_data_src)();
} else {
use_kroki_direct_src();
}
};
});
function func(Prism) {
klangs.forEach(e => {
Prism.languages[e] = {
raw: { pattern: /\A.*\z/m }
}
});
}
func.displayName = "kroki";
r.register(func);
Discussion