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 な文字の範囲が最新の規格とは異なっているので、流用する場合は注意が必要なようだ。