Unicode の曖昧な文字幅問題 その2
9月30日の続き。
UTF-8 環境で w3m のメニュー表示が崩れる原因が分かった。俺は GNU screen を常用してるんだが、実はそちらが原因だった。(使っていることを忘れるくらいに使いまくってるため、screen 以外の環境でテストすることを思いつかなかった。間抜け過ぎる)
調べてみたところ、UTF-8 の East Asian Ambiguous Character Width に関してパッチ付きのバグ報告がされていた*1。このパッチを適用してみたところメニューの表示が崩れなくなり、とりあえず問題が解決したらしい。
ついでなので、Unicode の曖昧な文字幅問題に関して、各ソフトウェアでの対処法のまとめ。
w3m
w3m-dev 4049 に投稿されている ambwidth パッチを使い、オプションで "Use double width for some Unicode characters" を有効または無効にする。
Emacs
Emacs 21 + mule-ucs の環境では、勝手に2文字幅だと解釈するっぽい。明示的に1文字幅だと見なす方法は調べてない。
Emacs 22 の場合は utf-translate-cjk-mode というのがあるらしい。*2
GNU screen
bug #16666 に添付されているパッチを使う。文字幅は環境変数(LC_ALL, LC_CTYPE, LANG) に応じて適当に変更してくれる。明示的に指定したい場合は、screenrc に "cjkwidth on/off" を書く。
Vim
vimrc に "set ambiwidth=double" と書けば、2文字幅として扱ってくれる。1文字幅にしたい場合は "ambiwidth=single" にする。
xterm
-cjk_width というオプションを付けて起動すれば 2 文字幅として扱ってくれる。
mlterm
-ac 2 とオプションを付けて起動すれば 2 文字幅になる。もしくは、設定ファイルで col_size_of_width_a = 2 を指定する。
lv
対象となる文字セットのほぼ全てを1文字幅として扱っている。
内部では unicode_width_threshold (-T オプションで指定できる値。デフォルトでは 0x3000) を境に 1 文字幅 / 2 文字幅を切り替えているので、場当たり的な対応として、この値を変更するという方法もある。多言語対応を無視して日本語だけに特化するなら -T256 とかの無茶な設定でも何となく正しく見える気がする。
根本的な対処をするには、IcharWidth() の実装を変更する必要がある。具体的には、src/itable.c の以下のあたり。
public int IcharWidth( byte charset, ic_t c ) { if( charset < PSEUDO ){ switch( charset ){ case UNICODE: if( c < unicode_width_threshold ) return 1; else return 2; } } else {
オプションを追加して1文字幅/2文字幅を選べるようにする、とかがまっとうな対応だろうなあ。
GNU libc
glibc-2.3.2 で wcwidth() 関数の挙動を調べてみたところ、locale の設定に関わらず 1 文字幅だと見なしているようだ。
もっとも、このあたりの問題に対応した wcwidth() の実装が存在しており*3、前述の screen のパッチでもこのソースが参考に挙げられていた。
ただし、参照している Unicode のバージョンが若干古いのか、ambiguous width な文字の範囲が最新の規格とは異なっているので、流用する場合は注意が必要なようだ。
*1:https://savannah.gnu.org/bugs/?16666
*2:utf-translate-cjk-mode の参考: http://nijino.homelinux.net/emacs/utf-cjk.html