#Abstract
GoでGUIアプリを作れるライブラリFyneで日本語が文字化けしないかつ可搬性あるアプリを作成する方法を見つけたので報告します。
#Introduction
FyneはGoでGUIアプリを作れるライブラリです。Fyneの概要や使用法は以下のリンクをご参照ください。
#Problem
でも、Fyneで日本語を使用するアプリを作ると文字化けします。原因はバンドルされたフォントが英語圏用のNoto Sansだからです。Githubには環境変数FYNE_FONTを用いた回避策が提示されています。でもこの方法はいまいちです。なぜならこのソースファイルを読むと分かるように、環境変数FYNE_FONTにあるフォントを読み込む方式になっているので実行環境での環境変数設定が必要になるからです。
#Solution
こんな可搬性のないアプリが使えるかっ!というわけでもっとましな回避策を探します。要するにバンドルされたフォント(正確に言うとフォントを[]byteで直書きしたソースファイル)に日本語文字が含まれていないのが問題なのだから、そのファイルを差し替えればいいわけです。たぶんこれ、バイナリファイルをGoのコードで直書きしているだけだろうと思って変換プログラムを作成しました。ページの一番下のAppendixを見てください。使用法は
var (
filenameRegular = "mplus-1c-Regular.ttf" //Regular Font TTF; Place it in the same folder
filenameBold = "mplus-1c-Bold.ttf" //Bold
filenameItalic = "NotoSans-Italic.ttf" //Italic
filenameBoldItalic = "NotoSans-BoldItalic.ttf" //BoldItalic
filenameMono = "NotoMono-Regular.ttf" //Mono
)
のfilenameに使用したいTTFファイルを代入してソースと同じフォルダにおいて走らせるだけです。ここでは例としてRegularとBoldにM+ FONTSを使用させていただいています。TTCしかないフォントの場合、uniteTTCでTTFを作成すれば使用できます。
吐き出されたbundled-fonts.goで***\src\fyne-io\fyne\theme\bundled-fonts.goを置き換えて、go install -i fyne.io/fyneで再インストールします。(ファイルの場所と再インストールのコマンドは環境それぞれです。)この状態でコンパイルして作成された実行ファイルは他のPCでも文字化けしませんでした。なお、正しく新しいbundled-fonts.goがインストールされたかどうかは生成された実行ファイルの容量が増加することで分かります。
#Appendix
最後に、今回作成した変換プログラムを置いておきます。TTFによってはTTFが不正だよとかなんとかエラーが出ます。エラーが出ないTTFをしよう使用(適当)。
//Go source to generate custom bundled-fonts.go
//Replace...)\fyne-io\fyne\theme\bundled-fonts.go by the generated file
package main
import (
"fmt"
"io/ioutil"
"strconv"
"strings"
"time"
"unsafe"
)
// func ttf_to_data(filename string) string {
// x, errx := ioutil.ReadFile(filename)
// if errx != nil {
// panic("Read Error in " + filename)
// }
// fmt.Println("Reading " + filename)
// str := "[]byte{"
// for _, byte1 := range x {
// str = str + strconv.Itoa(int(byte1)) + ","
// }
// return strings.TrimRight(str, ",") + "}"
// }
func ttf_to_data(filename string) string {
x, errx := ioutil.ReadFile(filename)
if errx != nil {
panic("Read Error in " + filename)
}
fmt.Println("Reading " + filename + ": length was " + strconv.Itoa(len(x)))
vec := make([]string, len(x))
for ix, byte1 := range x {
vec[ix] = strconv.Itoa(int(byte1))
}
//fmt.Println(strings.Join(vec, ","))
return "[]byte{" + strings.Join(vec, ",") + "}"
}
var (
filenameRegular = "mplus-1c-Regular.ttf" //Regular Font TTF; Place it in the same folder
filenameBold = "mplus-1c-Bold.ttf" //Bold
filenameItalic = "NotoSans-Italic.ttf" //Italic
filenameBoldItalic = "NotoSans-BoldItalic.ttf" //BoldItalic
filenameMono = "NotoMono-Regular.ttf" //Mono
)
func main() {
t0 := time.Now()
str := strings.Join([]string{
"// **** THIS FILE IS AUTO-GENERATED Custom bundled-fonts.go//\n//Replace (gopath)\\src\\github.com\\fyne-io\\fyne\\theme\\bundled-fonts.go by the generated file//\n\npackage theme\n\nimport \"fyne.io/fyne\"\n\nvar regular = &fyne.StaticResource{\n StaticName: \"Custom-Regular.ttf\",\n StaticContent: ",
ttf_to_data(filenameRegular),
"}\nvar bold = &fyne.StaticResource{\n StaticName: \"Custom-Bold.ttf\",\n StaticContent: ",
ttf_to_data(filenameBold),
"}\nvar italic = &fyne.StaticResource{\n StaticName: \"Custom-Italic.ttf\",\n StaticContent: ",
ttf_to_data(filenameItalic),
"}\nvar bolditalic = &fyne.StaticResource{\n StaticName: \"Custom-BoldItalic.ttf\",\n StaticContent: ",
ttf_to_data(filenameBoldItalic),
"}\nvar monospace = &fyne.StaticResource{\n StaticName: \"Custom-Mono-Regular.ttf\",\n StaticContent: ",
ttf_to_data(filenameMono),
"}",
}, "")
data := *(*[]byte)(unsafe.Pointer(&str))
errx := ioutil.WriteFile("bundled-fonts.go", data, 0777)
if errx != nil {
panic("Write error")
}
fmt.Print("Total time was ")
fmt.Print(time.Since(t0).Minutes())
fmt.Print(" minutes. ")
}
#追記(2019/12/29)
- M+ FONTSとIPA Font(TTF)での動作を確認しています。
- 投稿直後にFyneが1.2にVer UPされました。同様の作業が再度必要になりはしますが、問題なく動作しています。なんかAndroidに対応したとか...