2004年05月09日

「文字化け対策」MT導入と改造

エントリーの編集画面でエントリーのリストのタイトルが文字化けしてしまう。 このバグを修正した。

このバグはtitle_short作成時にUTF8の文字列を文字境界を無視して切ってしまうところに原因がある。 問題の箇所はlib\MT\App\Cms.pmのlist_entries関数の次のtitle_shortを作成している部分である。

 1564          unless ($row->{title_short}) {
 1565              my $title = remove_html($obj->text);
 1566              $row->{title_short} = substr(($title, 0, 22) . '...';
 1567          }
 1568          $row->{title_short} = substr(($row->{title_short}, 0, 22) . '...'
 1569              if length($row->{title_short}) > 25;
 1570          $row->{title_short} = encode_html($row->{title_short}, 1);

このsubstr関数が悪者。

これを解決するのはそんなに難しくなく、utf8を理解するsubstr関数を用意すればいい。 ちょっと引用が長いが次のような関数を用意した。

 1432  my $utf8charpatternx = q {
 1433      [\x00-\x7F]                            # UCS-2 U+0000..U+007F
 1434      |[\xC2-\xDF][\x80-\xBF]                # UCS-2 U+0080..U+07FF
 1435      |\xE0[\xA0-\xBF][\x80-\xBF]            # UCS-2 U+0800..U+7FFF
 1436      |[\xE1-\xEF][\x80-\xBF][\x80-\xBF]     # UCS-2 U+1000..U+D7FF, U+E000..U+FFFF
 1437  #    |\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]
 1438  #    |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]
 1439  #    |\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]
 1440  #    |[\xF5-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF]
 1441  #    |[\xF8-\xFD][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF]
 1442  };
 1443  sub substrutf8 {
 1444      my ($str, $pos, $len) = @_;
 1445      my @chars = $str =~ /$utf8charpatternx/gox;
 1446      return "" if $#chars < $pos;
 1447      my $endpos = $pos + $len - 1;
 1448      $endpos = $#chars if $endpos > $#chars;
 1449      join "", @chars[$pos .. $endpos];
 1450  }
 1451  sub lengthutf8 {
 1452      my ($str) = @_;
 1453      my @chars = $str =~ /$utf8charpatternx/gox;
 1454      return $#chars + 1;
 1455  }
 1456  sub charwidthutf8 {
 1457      my ($str) = @_;
 1458      my @chars = $str =~ /$utf8charpatternx/gox;
 1459      my $w = 0;
 1460      foreach my $ch (@chars) {
 1461          $w += (length($ch) < 2 ? 1 : 2); # assume U+0800..U+FFFF as wide!!!
 1462      }
 1463      return $w;
 1464  }

substrutf8関数は、まず、与えられた文字列を$utf8charpatternx正規表現で分解する。 その後に必要な範囲の配列を要素を一つの文字列につなげて返す。

lengthutf8は同様のことをしてその配列の長さを返す。

charwidthutf8はlengthutf8と似ているが全角半角をおおざっぱに見分けて文字列の幅を返す。 UnicodeのU+0800以降は全角であると強引に決めうちしてしまっている。 ただし、UTF-8では原理的に全角半角を単純なレンジで判断できないので、 この方法はあまり良い方法とはいえないだろうが、とりあえず簡単に実装してしまった。

上記のバグの該当箇所のsubstrとlengthをそれぞれsubstrutf8とcharwidthutf8で置き換えた。

 1564          my $collimit = 15;
 1565          unless ($row->{title_short}) {
 1566              my $title = remove_html($obj->text);
 1567              $row->{title_short} = substrutf8($title, 0, $collimit - 3) . '...';
 1568          }
 1569          $row->{title_short} = substrutf8($row->{title_short}, 0, $collimit - 3) . '...'
 1570              if charwidthutf8($row->{title_short}) > $collimit;
 1571          $row->{title_short} = encode_html($row->{title_short}, 1);

それでもまだ25文字で区切っている部分で、タイトルを省略しても表示が折り返してしまう。 25の半分である12~13とlengthutf8を比較するようにしても良かったのだが、 実際には試行錯誤の末にcharwidthutf8と15を比較するようにした。

とりあえずこれで文字化けはなくなった。

この記事のトラックバック用 Ping URL: http://www.mediaware.jp/blog/mt-tb.cgi/16
「文字化け対策」へのコメント  コメントを書く
「文字化け対策」へのトラックバック
コメントを書き込む









メールアドレスを記憶する?


この記事の評価
悪い あんまり 普通 まあまあ 良い





@@@@