17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Goでソースコードを画像化するCLIを作った

Last updated at Posted at 2020-08-01

こんにちわ
ゴリラです

普段、Twitterでたまにこういうふうにソースコードの画像を貼り付けることがあります。

image.png

画像を作るのに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で画像をファイルではなくクリップボードにコピーします。そのままツイートに貼り付けたいときに使用すると便利です。

実装

処理の流れは大まかと次になります。

  1. コードをトークナイズして、トークンごとに色を情報をつける
  2. トークンを1文字ずつpngに書き込む

ソースコードをトークナイズしてトークンごとに色(RGBA)をつけますが、ここをスクラッチで多言語に対応するのは骨が折れるので、素直にgithub.com/alecthomas/chromaというライブラリを使用しました。

このライブラリを使えば、2の処理だけを書けば済みます。

2の処理は大まかに次になります。

  1. 文字を描画するfontを読み込み
  2. 座標を計算しつつ、1文字ずつ描画する

fontに関してはマルチバイト対応のCicagithub.com/jessevdk/go-assetsで埋め込んでいます。
そのためCLIのサイズが倍くらい増えましたが、致し方ない…

文字描画の処理は次になっています。このiterator.Tokens()がtoken情報を返してくるので、
tokenの種類からstyle.Get()で色情報を取得しています。その後、1文字ずつpngに書き込んで、座標を計算して…を繰り返します。

ここでポイントですが、マルチバイトの場合は座標は +2 しないと文字が重なって読めなくなります。なので1文字の長さを確認して必要あれば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というプラグインを作りました。

vim-code2img.gif

さいごに

依存なしでソースコードを画像に変換できるので、ぜひ試してみてください。エディタとも連携できて便利です。

17
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?