📗

Vivliostyle (v8.16.0+) で mermaid.js の図を表示する

2024/11/13に公開

Vivliostyle で組版するときに、ドキュメント内に Mermaid.js による図のDSLを記述して、ビルド時にレンダリングして掲載したい、という需要は多々あると思う。これまでも、Vivliostyle 内で head タグで JavaScript を指定して defer で動かす、という方法はインターネット上でいくつか方法が紹介されていた。

https://qiita.com/AyumuTakai/items/6267f93afebf726378a0
https://qiita.com/msquare33/items/f484486437bb6f732159

ただし、これはローカルで Chromium を立ち上げるプレビュー時には上手く描画されても、PDF にビルドする際に上手くいかないことがある[1]。Mermaid.js のレンダリングが終わる前に vivliostyle-cli の内部で動く Playwright が PDF 出力を走らせてしまうためだ。

そこで使えるのが、v8.16.0 から使えるようになった、VFM 以外の unified プロセッサーを動作させられるという機能を使うと、意外と簡単に Mermaid.js の展開を実現できる。

https://github.com/vivliostyle/vivliostyle-cli/pull/526

vivliostyle-cli では、GitHub Flavored Markdown を Vivliostyle コミュニティが独自に拡張した VFM (Vivliostyle Flavored Markdown) という記法を採用しており、内部ではこの VFM ベースでのマークダウンから HTML への変換を行い、ブラウザでレンダリングしたウェブページから PDF を生成している。

ところで、markdown を AST[2] に変換して各種操作を行い HTML に変換するには、unified というエコシステムが便利である。便利である、というか、VFM も中身をたどれば実態は unified のプロセッサーなのだ。

上述のプルリクエストで追加した機能は、vivliostyle-cli の設定ファイルに unified プロセッサーの生成ロジックを記載すれば、VFM ではなく自らが指定したプロセッサーに差し替えて利用することができる、という拡張ポイントの導入だ。

unified エコシステム上で HTML の AST を操作するフレームワークは rehype という。rehype のプラグインとして rehype-mermaid というものがある。VFM の生成ロジックの中にこれを組み合わせたものをつくり、前述のプロセッサ差し替えの拡張ポイントからねじ込めば、やりたいことが実現できる。

vivliostyle.config.jsの設定例
import { VFM } from "@vivliostyle/vfm";
import rehypeMermaid from "rehype-mermaid";

export default {
  // ... 中略 
  documentProcessor: (config, metadata) => {
    return VFM(config, metadata)
        .use(rehypeMermaid, {strategy: 'img-png'});
    },
  // ...後略
}

package.json"type": "module" を指定するのをお忘れなく。rehype のプラグインもバージョンによっては ESM でしか提供されていないものも多いが、Vivliostyle の create-book コマンドで生成されるコードベースは、現時点では ESM 指定がされていない。

VFM も unified のプロセッサなので、.use メソッドが生えている。なので、これを使って最後の処理に rehype-mermaid をくっつけている。

あとは、原稿ファイルの中で mermaid 言語のコードブロックを書く。以下の記述は Mermaid.js のドキュメントから拝借してきたものを一部加工したものだ。

原稿内の mermaid 記述例
```mermaid
classDiagram
    note "From Duck till Zebra"
    Animal <|-- Duck
    Animal <|-- Fish
    Animal <|-- Zebra
    Animal : +int age
    Animal : +String gender
    Animal: +isMammal()
    Animal: +mate()
    class Duck{
        +String beakColor
        +swim()
        +quack()
    }
    class Fish{
        -int sizeInFeet
        -canEat()
    }
    class Zebra{
        +bool is_wild
        +run()
    }
```

これを PDF 出力すると、以下のように展開されている事がわかる。padding や margin などは、各自調節されたし。

ここまでのコード例とビルド成果物のPDFは、以下の Sample Repository で公開している。

https://github.com/Mura-Mi/vivliostyle-mermaidjs-2024

脚注
  1. 自分はどうやっても上手くいかなかったのですが、成功する方法があったらすみません。。。 ↩︎

  2. Abstract Syntax Tree ↩︎

Discussion