Goでソースコードを画像化するCLIを作った
初めに
こんにちは ゴリラです
普段、Twitterでたまにこういうふうにソースコードの画像を貼り付けることがあります。
画像を作るのにcarbonというサービスを使っています。 このサービスできれいな画像を生成できますが、インターネットとブラウザなしでは使えないためちょっと不便と感じています。 また、sliconというrust製のツールもありますが、これもcarbonと同様の制限があります。 そこで、ブラウザやネットを使用せずCLIでソースコードを画像化できたら良いなと思いCLIを作りました。
どんな感じ
こんな感じでpngファイルを出力できます。外部ツール依存なしでGoだけで動くのでインターネットもブラウザも必要ないです。
使い方
2通りあります。ソースコードを標準入力で渡すか、ファイルを渡すかです。
$ code2img
code2img - generate image of source code
Version: 1.1.0
Usage:
$ code2img -t monokai main.go main.png
$ echo 'fmt.Println("Hello World")' | code2img -ext go -t native -o sample.png
$ code2img -c main.go
-t color theme(default: solarized-dark)
-o output file name(default: out.png)
-c copy to clipboard
-ext file extension
ファイルの場合はオプションなくても使えます。カラースキーマを指定したい場合は-t
を使用します。
使用可能なカラースキーマはこちら、サポートしている言語一覧はこちらを参照ください。
-c
で画像をファイルではなくクリップボードにコピーします。そのままツイートに貼り付けたいときに使用すると便利です。
実装
処理の流れは大まかと次になります。
- コードをトークナイズして、トークンごとに色を情報をつける
- トークンを一文字ずつpngに書き込む
ソースコードをトークナイズしてトークンごとに色(RGBA)をつけますが、ここをスクラッチで多言語に対応するのは骨が折れるので、素直にgithub.com/alecthomas/chromaというライブラリを使用しました。
このライブラリを使えば、2の処理だけを書けば済みます。
2の処理は大まかに次になります。
- 文字を描画するfontを読み込み
- 座標を計算しつつ、一文字ずつ描画する
fontに関してはマルチバイト対応のCicaをgithub.com/jessevdk/go-assetsで埋め込んでいます。 そのためCLIのサイズが倍くらい増えましたが、致し方ない…
文字描画の処理は次になっています。このiterator.Tokens()
がtoken情報を返してくるので、
tokenの種類からstyle.Get()
で色情報を取得しています。その後、一文字ずつpngに書き込んで、座標を計算して…を繰り返します。
ここでポイントですが、マルチバイトの場合は座標は +2しないと文字が重なって読めなくなります。なので一文字の長さを確認して必要あればx座標を +2しています。これでマルチバイトでも問題なく描画されます。
for _, t := range iterator.Tokens() {
c := style.Get(t.Type).Colour
dr.Src = image.NewUniform(color.RGBA{R: c.Red(), G: c.Green(), B: c.Blue(), A: 255})
for _, c := range t.String() {
if c == '\n' {
x = fixed.Int26_6(padding)
y++
continue
} else if c == '\t' {
x += fixed.Int26_6(padding)
continue
}
dr.Dot.X = fixed.I(10) * x
dr.Dot.Y = fixed.I(20) * y
s := fmt.Sprintf("%c", c)
dr.DrawString(s)
// if mutibyte
if len(s) > 2 {
x = x + 2
} else {
x++
}
}
}
エディタと連携
標準入力に対応しているので、Vimなどのエディタと連携してサクッと画像化できます。
code2img.vimというプラグインを作りました。
さいごに
依存なしでソースコードを画像に変換できるので、ぜひ試してみてください。エディタとも連携できて便利です。