- 絵文字での合字の幅は、合字を構成する各文字の幅の合計ではなく、最も大きい1文字の幅とする
(ほとんどの場合、構成文字も全て端末画面の2セルであることが多いため、合字も2セルが多い) - 濁音記号があとについた半角カナのセットの幅は、それぞれの文字の幅の合計とする
(1セル分+1セル分=2セル分)
があるべき姿だが、go-runewidth の StringWidth では濁音記号を含む2文字は、どうも絵文字での合字と同じ扱いになるらしく、1セル分と算出されてしまう。
StringWidth を使っていたケースについては、これを排除するために RuneWidth を何回も呼べばよいだけの話が、Truncate などはそうもいかない。
修正パッチを送るにしても、一般の合字と濁音のケースを効率的に分けるアイデアを思いつかないため、手をこまねいている。さらに、グリフを分割するライブラリはメンテナが別であるため、対応が難しくなることが予想される。
なお、拙作のツールでは runewidth.Truncate 用の簡単なラッパー関数を作って回避した。
- Fix display overflow caused by halfwidth kana with voiced/semi-voiced sound marks by hymkor · Pull Request #48 · hymkor/csvi
- Internal: slightly improved handling of halfwidth voiced sound marks during truncation by hymkor · Pull Request #59 · hymkor/csvi
→最終的にはカナ文字と濁音記号の間に\x7F(DEL) を割り込ませることで、合字化させない方法をとった(\x7F以外の ZERO WIDTH 系の空白は存在を無視されたり、Windows のレガシーターミナルで表示されたりうまくゆかなかった)
package main
import (
"fmt"
"github.com/mattn/go-runewidth"
)
func main() {
tests := []string{
"\uFF76",
"\uFF76\uFF9E",
"\uFF9E",
"\U0001F9D1",
"\U0001F9D1\u200D\U0001F33E",
"\U0001F33E",
}
for i, s := range tests {
fmt.Printf("Case %d\n", i+1)
fmt.Printf("String: `%v` (%#v)\n", s, s)
fmt.Printf("StringWidth: %d\n", runewidth.StringWidth(s))
rw := 0
for _, c := range s {
rw += runewidth.RuneWidth(c)
}
fmt.Printf("Sum of RuneWidth: %d\n", rw)
fmt.Println()
}
}Case 1
String: `カ` ("カ")
StringWidth: 1
Sum of RuneWidth: 1
Case 2
String: `ガ` ("ガ")
StringWidth: 1
Sum of RuneWidth: 2
Case 3
String: `゙` ("゙")
StringWidth: 1
Sum of RuneWidth: 1
Case 4
String: `🧑` ("🧑")
StringWidth: 2
Sum of RuneWidth: 2
Case 5
String: `🧑🌾` ("🧑\u200d🌾")
StringWidth: 2
Sum of RuneWidth: 4
Case 6
String: `🌾` ("🌾")
StringWidth: 2
Sum of RuneWidth: 2
module github.com/hymkor/study-dakuon
go 1.20
require github.com/mattn/go-runewidth v0.0.19
require github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
