laminate - 文字列からの画像生成を一元管理するコマンドラインツール
https://github.com/Songmu/laminate
文字列から画像を生成するコマンドラインツールがいくつか存在する。例えば以下のようなものだ。
- silicon: コード文字列からシンタックスハイライトされた画像を生成する
- qrencode: URL等からQRコードを生成する
- mmdc (mermaid-cli): Mermaid画像を生成する
- プロンプト文字列から生成AIに画像を生成させる
laminate
はそれらを一元管理する。laminate
コマンドを実行すれば、設定ファイル上の適切なコマンドに処理が渡され、画像が出力される。例えば以下のように使う。
% cat main.go | laminate -lang go > main.go.png
k1LoW/deck との連携
最近、k1LoW/deckというMarkdownからGoogleスライドを作成するツールを愛用し、開発にも携わっているのだが、laminate
はこの deck
のために作ったツールだ。
deck
にはMarkdown内のコードブロックを画像化してスライドに貼り付ける素晴らしい機能がある。画像化のための外部コマンドを指定することで実現するのだが、そこで以下のように laminate
を使うことを想定している。
% deck apply -c 'laminate' deck.md
これで、Markdown内のコードブロックが laminate
によって画像化され、Googleスライドに貼り付けられる。
利用方法
laminate
自体は画像生成機能を備えておらず、設定ファイル記述内の適切なコマンドを実行する。設定ファイルは以下のような具合だ。
cache: 1h
commands:
- lang: qr
run: 'qrencode -o "{{output}}" -t png "{{input}}"'
- lang: mermaid
run: 'mmdc --scale 8 -i - -o "{{output}}" --quiet'
ext: png
- lang: '{go,perl,rust,python,java,javascript,typescript}'
run: 'silicon -l "{{lang}}" -o "{{output}}"'
- lang: '*'
run: ['convert', '-background', 'white', '-fill', 'black', 'label:{{input}}', '{{output}}']
laminate
自体は標準入力から文字列を受取り、標準出力に画像データを出力するが、各画像生成コマンドの様々な入出力インターフェースに対応するために、{{input}}
と {{output}}
というテンプレート変数を利用できるようにしている。
lang指定には、globパターンを利用でき、最初に一致したコマンドが実行される。このように、langに応じた適切なコマンド実行が laminate
の役割だ。langと言いつつ、上記でもqrがそうであるように言語名に限らない様々な文字列に応じた画像生成コマンドを設定できる。
画像フォーマットはデフォルトでPNGを期待しているが、コマンドが別フォーマットを出力する場合は、extキーを指定することで、別の画像フォーマットに対応できる。(ただ、deck
というかGoogleスライドは、PNG, JPEG, GIFしかサポートしていないので注意)
deck
の --code-block-to-image-command (-c)
オプションで同様のことを実現しようとすると、少し複雑なシェルスクリプトを書く必要があるため、それを分かりよく書けるようにしたという次第である。
キャッシュ機構
上記の設定ファイル例から気付いたかもしれないが、laminate
にはキャッシュ機構がある。cache: 1h
だと、同じ入力に対しては1時間はコマンドは再度実行されず、キャッシュ画像が出力される。これにより、deck
のスライド生成の高速化が図れる。このキーを指定しない場合、キャッシュは無効になる。
その他細かい利用方法については、リポジトリのREADME.mdを参照して下さい。
ご利用下さい
ということで、 deck
を使う上では必須ツールではないかと思うので、是非ご利用下さい。
余談1: Siliconの設定
コード文字列のシンタックスハイライト画像生成には、Silicon を利用しているが、私は以下のような設定にしている。
% cat ~/.config/silicon/config
--font 'HackGen'
--theme 'Monokai Extended Origin'
--no-line-number
--pad-horiz 0
--pad-vert 0
--no-window-controls
HackGen (白源) フォントはSIL Open Font Licenseなので、画像化したものを頒布しやすいのも嬉しいポイント。
余談2: 命名
deck
との親和性も意識しつつ名付けた。結果として同様に検索性の低いツール名となった。ラミネート加工のように、このツール自体は図版の生成を行わなず、あくまで最後の処理を実施するツールであることも表現できており気に入っている。物理のカードやスライドにラミネート加工を施すこともあることも deck
との親和性を感じられて良い。
しかし、実は、我々日本人がイメージする「ラミネート加工」は英語では "pouch lamination" という "laminate" の限定的な意味であり、英語の "laminate" は薄いレイヤーを貼り合わせるようなことを指すようだ。例えば「積層板」は "laminated plate" となるらしい。
"laminator" だと我々が認識しているラミネート加工のイメージに近くなるようなので、もしかしたらそっちの方が名前としては良かったのかも知れないとも考えたが、コマンド名は動詞の方が良いという話もあるし、原義の"laminate" の意味合いであっても、このツールが果たす役割とそんなにずれていないとも思い、laminate
で良しとした。
余談3: ファイル名サニタイズ github.com/spf13/pathologize
laminate
のキャッシュファイルは cache/{lang}/{hash(input)}.{ext}
に格納される。このとき {lang}
や {ext}
は一応ユーザー入力になるため、トラバースや不正な文字列 ("CON"等) などを避けるために無害化処理を施している。
そのためのGoの定番ライブラリが案外定まってなさそうだったが、github.com/spf13/pathologize を見つけ、これを使うことにした。
これは、100行程度と小粒ながら良いライブラリで、一般的なトラバース処理だけではなく、クロスプラットフォームで問題になりそうなファイル名のサニタイズもやってくれる。例えば、Windows上の不正なファイル名などもケアしてくれる。Windowsは未だにに "CON" 等のファイルを作れないし、将来的にも変わらない可能性が高い。マルチプラットフォームなツールを作る上では意識をしておきたい所だ。
作者のspf13さんも cobra
や viper
, Hugo
等を作られた方なので信頼性も高い。pathologize
はスター数も多くなく、まだバージョンタグも打たれていな状況ではあるが、既に使える品質にはなっていると思うので、ローカルにユーザー入力に基づいた名前のファイルを保存する際には利用を検討すると良いと思う。もちろん、そもそもそんなことをやるべきではない、という話でもありますが。