Bulknews::Subtech RSSフィード

2007/11/12 (月)

KDDI/AUでutf-8のHTMLフォームから送られてくる絵文字コード 20:00  KDDI/AUでutf-8のHTMLフォームから送られてくる絵文字コード - Bulknews::Subtech を含むブックマーク はてなブックマーク -  KDDI/AUでutf-8のHTMLフォームから送られてくる絵文字コード - Bulknews::Subtech

長くなったので先に結論。KDDIのUnicode絵文字マッピングには2種類あり、KDDI仕様書にあるマッピング(A)と端末がSJISコードから変換して送信する機械的なマッピング(B)がある(仕様書には一切記述されてない)。Unicode なDBに保存する際は、(B)を利用するといろいろと効率がよくなるかもしれない。

以下、考察。

tomi-ruさんのつくった表(書いてる時点では消えてるけど キャッシュ から)と KDDI/AU にある絵文字表 を見比べて、コードを書きながら比較してみた。

Unicodeの番号のほうは相変わらずいい加減だけど、SJISコードと生成されるUnicode番号に関連性があることに気づいた。

#!/usr/bin/perl
use strict;
use warnings;
use Encode::JP::Mobile;
use Encode;

while (<>) {
    chomp;
    my($hex, $bytes) = /^U(\w{4}) => (.{12})$/;
    $bytes = eval '"' . $bytes . '"';
    my $unicode = ord decode_utf8 $bytes;
    my $au_unicode = hex $hex;
    my $sjis_hex = sjis_hex($au_unicode);
    my $sjis_num = hex $sjis_hex;
    printf "U$hex (Display) %s (SJIS) U%04X (Form) Diff: %d\n", sjis_hex($au_unicode), $unicode, ($unicode-$sjis_num);
}

sub sjis_hex {
    my $cp = shift;
    my $str = chr $cp; # Unicode
    my $bytes = encode "shift_jis-kddi", $str;
    uc unpack "H*", $bytes;
}

さっきの charset.txt を食わせる。

UE469 (Display) F641 (SJIS) UEF41 (Form) Diff: -1792
UE46A (Display) F642 (SJIS) UEF42 (Form) Diff: -1792
UE46B (Display) F643 (SJIS) UEF43 (Form) Diff: -1792
UE46C (Display) F644 (SJIS) UEF44 (Form) Diff: -1792
UE471 (Display) F649 (SJIS) UEF49 (Form) Diff: -1792
UE472 (Display) F64A (SJIS) UEF4A (Form) Diff: -1792
UE473 (Display) F64B (SJIS) UEF4B (Form) Diff: -1792
...
UEB7F (Display) F484 (SJIS) UED84 (Form) Diff: -1792
UEB80 (Display) F485 (SJIS) UED85 (Form) Diff: -1792
UEB82 (Display) F487 (SJIS) UED87 (Form) Diff: -1792
UEB83 (Display) F488 (SJIS) UED88 (Form) Diff: -1792
UEB84 (Display) F489 (SJIS) UED89 (Form) Diff: -1792
UEB87 (Display) F48C (SJIS) UED8C (Form) Diff: -1792
UEB88 (Display) F48D (SJIS) UED8D (Form) Diff: -1792

キタコレ。というわけで SJIS のコード(&#NNNNN; の数字)に 1792 (0x0700) を引いて utf-8 にする、という変換でOK。

おそらく Unicode の番号自体が後付けで、端末に乗ってる絵文字はSJISの外字領域コードがベースになってて、外字からUTF-8の私的領域(PRIVATE AREA) に変換するときに簡単なビット操作で変換するようにコードが書かれてるんじゃないかと推測。

→追記: この推測は一部間違いで、「基本的には」CP932 の外字<->Unicode 私的領域の変換表にもとづいて Unicodeにマッピングしているようです。CP932 に含まれていない文字領域もかなりありますが。

そもそも表示のほうのSJIS <-> Unicode の関連付けがめちゃくちゃすぎる上に softbank の絵文字領域 とかぶってるので、KDDI はこっちだけ使うように変更しろ!

ということでこれって実際に表示はできるのか、日本で実機もってる tokuhirom と実験してみた。

たとえば台風マークの絵文字は、SJIS/Unicode では以下のような値をとる。

  • SJIS コード \xF6\x41
  • Unicode U+E469 (KDDI 仕様書: Unicode (A))
  • UTF-8 \xEE\xBD\x81 (UTF-8なHTMLフォームから送信される値)
  • Unicode U+EF41 (↑のUTF-8のコードポイント: Unicode (B))

http://blog.bulknews.net/mt/au.html のようなUTF-8のページをつくって実機で表示してもらってみた。li タグの値と表示結果は上から、

  • \xEE\x91\xa9 (NG: ? と表示) ← Unicode (A) の UTF-8エンコーディング
  • &#xE469; (OK: 台風の絵文字) ← Unicode (A) の &#xXXXX; 表示
  • \xEE\xBD\x81 (OK: 台風の絵文字) ← Unicode (B) のUTF-8エンコーディング
  • &#xEF41; (NG: (火) の外字) ← Unicode (B) の &#xXXXX; 表示

のようになった。つまり、上記の仮説は実証され、端末上では絵文字のフォントは SJISのコードに対応しており、

  • &#xXXXX; で記述した場合、KDDI仕様書にある表どおり(CP932の外字変換がベース) にいったんSJISコードに一度変換してから絵文字表示している。よって仕様書にある Unicode は &#xXXXX; で表示可能であるがそれを UTF-8 にエンコードしても表示できない
  • 一方、UTF-8 な HTML form から送られる値は、内部的な SJIS <-> UTF-8 相互変換によって作られている。よってこれに対応する Unicode コードポイントを &#xXXXX; で送っても表示はされないが、これをUTF-8でエンコードした場合、UTF-8→SJISデコードでうまく絵文字にマップされるので、表示できる。

これは革命的な発見かもしれない。AU/Softbank の絵文字コンフリクトも、AUの仕様書にあるUnicode は無視して、SJISとの機械的マッピングで得られる方の私的領域をつかえばほかともコンフリクトせず、UTF-8で絵文字表示もできるようになる(ただし &#xXXXX; 形式ではできないので HTML フィルタなどで変換されないように注意が必要)。