- 澁川喜規
- 約 2,200 文字
- 1,900 View
前回の記事ではMermaid.jsをレンダリングしましたが、次はPlantUMLをレンダリングしてみます。こちらも、前回のMermaid.js同様、メンテナンスされているPlantUML対応コンポーネントはありません。
PlantUMLもMermaid.js同様、多様なダイアグラムをサポートしています。しかし、PlantUMLはJavaで書かれており、一部C言語製のGraphvizにレイアウト処理を任せているようです。そのままの仕組みでGitHub Pagesのような静的HTMLで気軽に動かすことはできません。Haxeで作り直してJavaScriptでも動かせるようにするPlantUML2というのもあるようですが、まだまだ安定版ではなさそうです。
しかし、GitHub Pagesでプレビューを実現しているサイトは確かにあって、どうやっているのかと思っていたら、その仕組みについては公式ページで紹介されていました。plantuml.comにリクエストを投げて画像を生成していました。ソースコードを圧縮し、base64にして、それを末尾に付与したURLを作り、imgタグのsrcに入れるという方法です。
ロジックを実装してみる
jQuery用のコードが上記のページで紹介されています。そのまま使えば動きはすると思いますが、いかんせんcjs形式でES6 modules形式でもなく、scriptタグで読み込む形式なので手を加えたいところ。そしてやっていることはdeflateの圧縮とbase64なので、より良いコードがnpmにあるので苦労はないだろう・・・と思ったらなかなかうまくいかず。
まずは高速というpakoを使ってdeflateしてbtoaしたらplantuml.comがエラーに。次にzlib.jsとかいろいろ試したがダメ。で、ダメもとでbase64の部分をPlantUMLのサイトのコードを使ったらOKでした。再度pakoを使ったらOK。base64難しい。まあatobもいろいろdeprecatedであったりするのですが。最終形は以下の2つのコードになりました。このブログをかいているときにplantuml-encoderというのも見つけました。これもみたら同じような構成でした。
export function encode64(data: string) { |
import { useMemo } from "react"; |
最終系は、useMemoを使ってソースが変更されたときだけ変換計算をするようにしてみたのと、imgタグのさまざまな属性をそのまま受け取れるようにした感じです。alt属性とかのアクセシビリティ用属性もそのまま使える。
以下のコードから次の図が生成できるようになりました。
@startuml |
日本語対応
このままだと日本語を使うとエラーになってしまいます。とはいえ、PlantUMLのライブエディターを使うと日本語通ります。エラー画面を見ると文字化けしているようです。
検索すると、テキストエンコーディングのページがありました。ここを見ると、UTF-8でエンコードしろ、とあります。あと興味深かったのはBrotli圧縮形式にも対応しているとのこと。まあBrotliはnpmで検索しても使いやすいPure JS版はなかったのでDeflateのままにしておきます。
JavaScriptのオリジナルのUTF-16をUTF-8にするのはTextEncodingを使います。次のように圧縮の前に変換するようにすれば日本語も通るようになります。
- const bin = pako.deflateRaw(unescape(encodeURIComponent(src))); |
ちなみに、btoaなどの既存のbase64アルゴリズムが使えない理由も上記のページに書かれていました。歴史的経緯。
plantuml.com以外のサーバー対応
これまでのコードは、plantuml.comの変換サーバーを使って表示していました。何度もリクエストを送ってしまうのは申し訳ないので、ローカルで建てたサーバーなど、別サーバーも使えるようにします。
まずはローカルサーバーを起動します。 Dockerを使うのが簡単ですね。
version: '3' |
以下のように起動します。
docker compose up |
次にファイルを変更します。この手の共通のパラメータをインジェクションするにはコンテキストを使うのが一番行儀が良いでしょう。まずContextを宣言しつつ、アプリ側に組み込むProviderをエクスポートします。
-import { useMemo } from "react"; |
コンポーネント内部ではuseContext()を使って設定値を取り出します。何も指定されていなかったら今まで通りのパスを使うようにします。
export function PlantUML(plops: ImageProps) { |
使う場合はルートの要素で変数を設定します。今回はNext.jsを使っているので、ここではNEXT_PUBLIC_PLANTUML_SERVER環境変数から情報を取得するようにします。
+import { PlantUMLProvider } from '../components/plantuml' |
次のように環境変数を設定して実行すればローカルに建てたPlantUMLサーバーを利用します。Windowsだったらsetとかで環境変数してからnpm run devすれば大丈夫です。
NEXT_PUBLIC_PLANTUML_SERVER=http://localhost:18080 npm run dev |
開発者ツールで見てみると、きちんとローカルホストのサーバーにアクセスしていることがわかります。
まとめ
今回も最小の依存(deflateのpakoのみ)でPlantUML表示のコンポーネントが作れました。また、標準でお手頃なplantuml.comへのアクセス以外に、plantuml-serverのパスは切り替えられるようにもしてみました。
先ほどのMermaid.jsはGitHubでレンダリングされたり、確かにお手軽なのですが、例えばエンティティに日本語を入れるとか、ノードの色を変えるとか、テーマとか、表現力では圧倒的にPlantUMLですね。