LoginSignup
26
11

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-05-22

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

結論

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

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

なぜ日本語が描画できない?

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

参考 : Goでカラー絵文字を使って画像を合成する

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

文字化け画像
mojibake.png

文字化けソース全文

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) は何をやっているのかソースを覗きました。

gobold.TTFより抜粋

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を確認すると

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

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

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

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

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

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

日本語が描画できた画像

nihongook.png

ソース全文

: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())
}

26
11
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
26
11