ambiguous width character の幅を 2 にした

環境を UTF-8 にしてから表示が乱れる。ambiguous width character の幅の定義がターミナルエミュレータとコンソールで動くアプリで一致していないせいだろう。ambiguous width character の幅の定義は,PuTTY は[UTF-8]にすると 1,[UTF-8 (CJK)]にすると 2 のようだ。アプリは w3m 0.5.3 が 1 か 2(east_asian_width で切り替え),Mutt 1.5.21 が 1,Vim 7.3.401 が 1 か 2(ambiwidth で切り替え)。

すべて 1 に合わせてみたら,表示は乱れなくなったけど,やはり星や丸や四角がいわゆる半角で表示されるのは見づらい。やっぱり 2 がいい。すべて 2 に合わせるのがたいへんなのであれば,「表示が乱れる 2」と「表示が乱れない 1」のどちらがマシなのかはむずかしいが。

とりあえず Mutt は tt.wcwidth パッチを当てて --enable-cjk-ambiguous-width で configure して set cjk_width=yes で動かすと 2 になった。で,表示が乱れなくなった。

でもそもそもアプリのひとつひとつに手当てするんじゃなくて,システムが ambiguous width character の幅を 2 として取り扱ってくれるべきだろう。

Mutt がしているように wcwidth() のラッパを書いて,それを共有ライブラリにして LD_PRELOAD でみんなに使わせるか。と思って調べると,locale データをいじればいいようだ。それだな。

ambiguous width character をリストアップするのはどうしよう。とりあえず EUC-JP ファイルの WIDTH(2 行だけしかない)を単純に UTF-8 ファイルの末尾に追加してみた。EUC-JP ファイルも文字コードの頭に U がついているので,EUC-JP じゃなくて Unicode だろう。ところが,localedef がよくわからない理由で失敗する:
UTF-8-CJK:38301: number of bytes for byte sequence of beginning and end of range not the same: 2 vs 3
no output file produced because warnings were issued
38301 行はこれ:
<U02D8>...<U9FA5> 2
"3 vs 4" ならわかるんだけどなあ。定義が重複する文字があるのがいけないのかもと思って,UTF-8 ファイルにすでにあった行を削って EUC-JP ファイルの行だけにしてもおなじ。

ちゃんとやるしかないのか。ということで EastAsianWidth.txt を入手して,ambiguous width character をリストアップする Ruby スクリプト aw.rb を書いた。範囲指定 "<Uxxx>...<Uyyy>" が使えるところは使う。どうせ localedef でコンパイルするんだから,記述量を減らす必要はないんだろうけど。これはこう使う:
$ ruby aw.rb < EastAsianWidth.txt > w
で,出力 w の内容を UTF-8 ファイルに足した UTF-8-CJK をつくる。足す位置は WIDTH の最後にした。重複する文字もあるだろうけど,うしろのほうで上書きされないだろうかと。まあ幅が既定値の 1 である文字は記述が省かれているので,きっと重複していないか,していても幅の定義がおなじ(2)だろう。で:
# gzip -9nc UTF-8-CJK > /usr/share/i18n/charmaps/UTF-8-CJK.gz
# localedef -i ja_JP -f UTF-8-CJK ja_JP.UTF-8
これはうまく行った。locale データはアプリを起動するときに読みこまれるようで,localedef のあとアプリを起動すればもう locale データの変更が反映されている。

で,Mutt を --without-wc-funcs も何もつけずにビルドしたら表示が乱れなくなった。すげえ。たまに乱れるが,何かまだターミナルエミュレータとコンソールアプリで幅が一致しない文字があるのかもしれない。まあ ja_JP.eucJP でもたまに乱れることはあったので,しかたがないことなのかもしれない。原因が気にはなるが,乱れはまったくの許容範囲。

::2012-03-14 {unix}

update : 2012/03/14 (Wed) 23:06:14