図を「文字」で管理する — AsciiDoc に Kroki を組み込む
前回で、.adoc を1枚書けば A4 の PDF が1本出るところまで来ました。ただし今の状態で PlantUML を書いても、そのソースがコードブロックとしてそのまま印字されるだけです。今回は asciidoctor-kroki を組み込んで、テキストで書いた図が画像として PDF に乗るところまで進めます。
画像ファイルを置かない理由
第1回で挙げた3つの原因のうち、いちばん壊れやすいのが「図が本文と別管理」でした。作図ツールで描いた画像を貼る運用では、編集可能な原本が版管理の外に置き去りになります。原本が見つからなければ図は直せず、本文だけが更新されて図は古いまま残ります。図をテキストで書けば原本は本文と同じリポジトリのファイルになり、矢印を1本足した変更が「1行の追加」としてレビューできます。
asciidoctor-kroki を組み込む
Kroki は図のソース(テキスト)を受け取って画像を返す変換サーバーで、asciidoctor-kroki 拡張は AsciiDoc 中の図ブロックを Kroki への画像 URL に置き換えます。
検証した構成は第2回と同じ asciidoctor@4.0.7 / asciidoctor-kroki@1.0.1 / @vivliostyle/cli@11.1.0 です。HTML を生成するコマンドは次のように変わります。
npx asciidoctor -b html5 --extension asciidoctor-kroki -o dest/index.html index.adoc
第2回で保留した話がここに来ます。この構成で拡張を登録するオプションは --extension であり、-r(--require)ではありません。 -r asciidoctor-kroki と書いても拡張は登録されません。
asciidoctor コマンドの実体は、バージョンによって別のパッケージから来ています。3.x 系は @asciidoctor/cli に任せ、4.x 系は自分で CLI を持つようになりました。実装が違うので、同じ -r を書いても拡張が有効になったりならなかったりします。
| 構成 | 拡張の登録に使うオプション |
|---|---|
asciidoctor@4.0.7(本連載の構成) | --extension asciidoctor-kroki |
asciidoctor@3.x 系(@asciidoctor/cli 同梱) | -r asciidoctor-kroki |
厄介なのは、取り違えても何のエラーも警告も出ないことです。手順の検証を行っている際、コマンドは正しく終了したように見えて、PlantUML のソースが <pre> のコードブロックとして出力されました。存在しないオプションを渡した場合も黙って無視されます。図が変換されないときは、まず npx asciidoctor --help を見てください。--extension があるかどうかで、自分がどちらの CLI を使っているかが分かります。
PlantUML でシーケンス図を書く
拡張が入れば、AsciiDoc 側はブロックマクロを書くだけです。
[plantuml, login-sequence, svg]
----
@startuml
User -> App: ログイン要求
App -> Auth: 資格情報を検証
Auth --> App: アクセストークン
App --> User: ログイン完了
@enduml
----
角括弧の中の3つの引数には、それぞれ役割があります。
- 第1引数
plantuml:図の記法です。構成図を Graphviz で描きたければgraphvizに変えるだけで、ローカルに処理系を入れる必要はありません。 - 第2引数
login-sequence:図の名前で、生成される画像の識別子になります。 - 第3引数
svg:出力フォーマットです。
3つ目は png も選べますが、仕様書では svg を勧めます。 ベクター形式なので、PDF に埋め込んだあとで拡大しても線や文字が粗くなりません。PNG は解像度が固定されるため、大きく配置した構成図や、印刷して細部を追う場面で不利になります。
図を別ファイルに切り出す
図が増えてくると、本文が図のソースで埋まって読めなくなります。.puml を別ファイルに分け、include:: で取り込む形にしておくと本文の見通しが保てます。
project/
├── index.adoc
└── diagram/
└── system-sequence.puml
本文側はこうなります。
[plantuml, system-sequence, svg]
----
include::diagram/system-sequence.puml[]
----
ここで押さえておくべきなのがパス解決の基準です。include:: の相対パスは、実行時のカレントディレクトリではなく、その記述がある .adoc ファイルの場所を基準に解決されます。 ビルドスクリプトをリポジトリのルートに置いても文書フォルダの中で実行しても、.adoc から見た相対パスさえ正しければパスは壊れません。
公開サーバーと自前ホストの使い分け
ここまで何も指定していませんが、既定の変換先は公開サーバーの kroki.io です。生成される HTML を覗くと <img src="https://kroki.io/plantuml/svg/..."> という形になっていて、URL のパス部分に図のソースそのものがエンコードされて埋め込まれています。
つまり、図のソースが外部のサーバーへ送信されるということです。しかも URL は HTML に残るので、その HTML を受け取った人が開けば、その人の環境からも同じリクエストが飛びます。
社内システムの構成図には、ホスト名や内部のサブシステム名、外部連携先といった情報が普通に入ります。顧客の内部構成を含む図であれば、公開サーバーをそのまま使ってよいかを事前に判断すべきです。Kroki は Docker イメージが公開されているので、自前で立てて変換先をそちらへ向ける選択肢があります。ネットワークの内側で完結し、外部サービスの停止にビルドが左右されなくなる利点もあります。
ビルドが遅い・失敗するとき
図を入れ始めると、第2回までとは違う壊れ方をします。検証で見えた範囲で、確認の順番を挙げます。
まず、図がコードブロックのまま PDF に出ているときは、ほぼ拡張の登録漏れです。無言で失敗するため、コマンドのオプションを疑うのが最短経路です。
次にビルド時間です。図は HTML の中では外部画像への参照でしかないので、PDF に焼き込む段階で Vivliostyle が1つずつ取得しに行きます。図の数だけ HTTP リクエストが増える構造で、第2回で触れたテーマの取得も加わるため、ビルド時間はネットワークの往復に引っ張られます。
裏を返せば、ネットワークが切れていれば図は出ません。 オフラインでのビルドを前提にするなら、自前ホストの検討がここでも効いてきます。
これで、本文も図もテキストで書き、同じコミットに乗せられる状態になりました。残っているのは体裁です。表紙と目次を付け、章ごとに改ページし、複数の文書をまとめてビルドするところまでを次回で扱います。
この回で使ったツール
- AsciiDoc — 技術文書向けの軽量マークアップ言語
- Kroki — テキストの図式記法を画像に変換するサーバー
- asciidoctor-kroki — AsciiDoc の図ブロックを Kroki 経由で画像に変換する Asciidoctor 拡張
- PlantUML — テキストから UML 図などを生成する記法
- Graphviz — グラフ構造を記述して図を生成するツール
- Vivliostyle — CSS で組版して PDF を生成するツール