GO言語 DrawStringで画像に日本語を描画する

GO言語で画像に日本語を描画する方法を調べました。
同様の記事は見つけられなかったので投稿しておきます。

## 結論

以下のように日本語キャラクタを含む外部フォントファイルを読み込むことで、日本語を描画できました。

```golang
	ftBinary, err := ioutil.ReadFile("Koruri-Bold.ttf")
	ft, err := truetype.Parse(ftBinary)
```

## なぜ日本語が描画できない？

日本語を描画したいと思い、下記記事を参考にさせていただき、画像に日本語を描画するソースを大雑把に作成しました。

[参考 : Goでカラー絵文字を使って画像を合成する](https://qiita.com/uobikiemukot/items/11dac0f1418492493226)

しかし、おなじみの文字化けが発生しました。

*文字化け画像*
![mojibake.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265837/07c926c6-ea93-357a-d890-ff1f947a116f.png)

*文字化けソース全文*

```golang:mojibake.go
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/png"
	"os"

	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gobold"
	"golang.org/x/image/math/fixed"
)

func main() {
	// フォントの読み込み
	ft, err := truetype.Parse(gobold.TTF)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	opt := truetype.Options{
		Size:              90,
		DPI:               0,
		Hinting:           0,
		GlyphCacheEntries: 0,
		SubPixelsX:        0,
		SubPixelsY:        0,
	}

	imageWidth := 100
	imageHeight := 100
	textTopMargin := 90
	text := "あ"

	img := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))

	face := truetype.NewFace(ft, &opt)

	dr := &font.Drawer{
		Dst:  img,
		Src:  image.Black,
		Face: face,
		Dot:  fixed.Point26_6{},
	}

	dr.Dot.X = (fixed.I(imageWidth) - dr.MeasureString(text)) / 2
	dr.Dot.Y = fixed.I(textTopMargin)

	dr.DrawString(text)

	buf := &bytes.Buffer{}
	err = png.Encode(buf, img)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	file, err := os.Create(`test.png`)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer file.Close()

	file.Write(buf.Bytes())
}
```

## `truetype.Parse(gobold.TTF)` はなにをやっているのか

フォントを読み込む `truetype.Parse(gobold.TTF)` は何をやっているのか[ソース](https://github.com/golang/image/blob/master/font/gofont/gobold/data.go)を覗きました。

*gobold.TTFより抜粋*

```golang:data.go
// generated by go run gen.go; DO NOT EDIT

// Package gobold provides the "Go Bold" TrueType font
// from the Go font family. It is a proportional-width, sans-serif font.
//
// See https://blog.golang.org/go-fonts for details.
package gobold

// TTF is the data for the "Go Bold" TrueType font.
var TTF = []byte{
    0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x80, 0x00, 0x03, 0x00, 0x60, 0x4f, 0x53, 0x2f, 0x32,
// 以下略
```

コメントより`Go Bold True Type フォント`の`バイナリデータ配列`が`gobold.TTF`に登録されていることがわかりました。

コメントに記載の[The Go Blog - Go fonts](https://blog.golang.org/go-fonts)を確認すると

`Go fonts`は`WGL4キャラクタセット`とのことで、日本語は含まれていません。

そのため対応する文字がないので文字化けしていると推測されます。

## 日本語キャラクタを含む外部フォントを読み込めば文字化けしない

冒頭に記載したように、外部フォントファイルを読み込めば文字化けせず日本語が描画できました。

日本語フォントには [Koruri](https://github.com/Koruri/Koruri) を利用させてもらいました。
(あらかじめダウンロードし、GOバイナリと同じ場所に配置しています。)

```golang
	ftBinary, err := ioutil.ReadFile("Koruri-Bold.ttf")
	ft, err := truetype.Parse(ftBinary)
```

*日本語が描画できた画像*

![nihongook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265837/3c33bc53-0b13-287f-5102-a6bc519848bb.png)

*ソース全文*

```golang
:nihongook.go
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/png"
	"os"

	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gobold"
	"golang.org/x/image/math/fixed"
)

func main() {
	// フォントファイルを読み込み
	ftBinary, err := ioutil.ReadFile("Koruri-Bold.ttf")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	ft, err := truetype.Parse(ftBinary)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	opt := truetype.Options{
		Size:              90,
		DPI:               0,
		Hinting:           0,
		GlyphCacheEntries: 0,
		SubPixelsX:        0,
		SubPixelsY:        0,
	}

	imageWidth := 100
	imageHeight := 100
	textTopMargin := 90
	text := "あ"

	img := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))

	face := truetype.NewFace(ft, &opt)

	dr := &font.Drawer{
		Dst:  img,
		Src:  image.Black,
		Face: face,
		Dot:  fixed.Point26_6{},
	}

	dr.Dot.X = (fixed.I(imageWidth) - dr.MeasureString(text)) / 2
	dr.Dot.Y = fixed.I(textTopMargin)

	dr.DrawString(text)

	buf := &bytes.Buffer{}
	err = png.Encode(buf, img)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	file, err := os.Create(`test.png`)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer file.Close()

	file.Write(buf.Bytes())
}

```