| 「文字化け対策」(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を比較するようにした。
とりあえずこれで文字化けはなくなった。
「文字化け対策」へのコメント コメントを書く
「文字化け対策」へのトラックバック