おそらくはそれさえも平凡な日々

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 で良しとした。

参考: Lamination - Wikipedia

余談3: ファイル名サニタイズ github.com/spf13/pathologize

laminate のキャッシュファイルは cache/{lang}/{hash(input)}.{ext} に格納される。このとき {lang}{ext} は一応ユーザー入力になるため、トラバースや不正な文字列 ("CON"等) などを避けるために無害化処理を施している。

そのためのGoの定番ライブラリが案外定まってなさそうだったが、github.com/spf13/pathologize を見つけ、これを使うことにした。

これは、100行程度と小粒ながら良いライブラリで、一般的なトラバース処理だけではなく、クロスプラットフォームで問題になりそうなファイル名のサニタイズもやってくれる。例えば、Windows上の不正なファイル名などもケアしてくれる。Windowsは未だにに "CON" 等のファイルを作れないし、将来的にも変わらない可能性が高い。マルチプラットフォームなツールを作る上では意識をしておきたい所だ。

作者のspf13さんも cobraviper , Hugo 等を作られた方なので信頼性も高い。pathologize はスター数も多くなく、まだバージョンタグも打たれていな状況ではあるが、既に使える品質にはなっていると思うので、ローカルにユーザー入力に基づいた名前のファイルを保存する際には利用を検討すると良いと思う。もちろん、そもそもそんなことをやるべきではない、という話でもありますが。

created at
last modified at

2025-08-12T00:43:31+0900

comments powered by Disqus