先日、SATySFiで絵文字の結合を実装したので、そこで得た知見をまとめたいと思います。
絵文字結合とは
世の中に絵文字はたくさんありますが、それだけでは飽き足らなかった人類は「絵文字を合字みたいに結合させて新しいものを作ってしまえばよくない?」と思いつきます。Unicodeポイントを浪費してはいけないという理性が無駄に働いてしまったのでしょうね。
具体的には国旗・肌の色・性別・職業などです。
大きく分けて、2つの特定の絵文字が並んだときに結合させる種類と、特定の文字でもって結合させる種類の2つが存在し、それぞれ対応しなければいけません。
実装の方針
前提:
- 入力は複数の文字列の形で与えられる
- 文字列を分解・結合させてIDを作り、事前に用意してあったグラフィックのリストの中からIDを使って適切なグラフィックを探す
- 出力はグラフィック列
- 適切に解析できれば意図している絵文字に必ずなる文字列が与えられている。つまり、結合できないのに結合できると勘違いして文字列があたえられることはないということ
特定の絵文字2つで結合させる
特定の絵文字2つで結合する規則を持っているのは
- 国旗
- 肌の色
の2つです。
国旗結合
国名のアルファベット2文字が並ぶと国旗になります。 例えば日本の国旗であれば、Jの絵文字(U+1F1EF)とPの絵文字(U+1F1F5)が並んだ時に成立します。 間に何らかの文字が入れば成立しません。
この機能の実装は
- 1文字目がアルファベットの絵文字かを判定します
- 1でそうだとわかれば2文字目もアルファベットの絵文字かを判定します
- 1文字目も2文字目もアルファベットの絵文字でなければ分割したまま終える
- 両方ともアルファベットの絵文字であれば、国旗の組み合わせとなる2つの絵文字であるかを判定し、そうであればペアとして扱い、そうでなければ分割したまま終える
という方法で行います。国旗の個数は200弱と多いですが、法則も何もないのであらかじめペアとなる2つの文字の組を用意しておかないといけません。
肌色の変更
肌の色の変更は"人に関連する絵文字"+"特定の色の絵文字"によって実現します。 なので、グラフィックが用意されている「人に関連する絵文字」のリストを用意しておき、1個目の絵文字が人に関連するもので、2個目がU+1F3FBからU+1F3FFの間に入っているものであれば、その2つを結合させるだけで終わります。
地域の旗の結合(おまけ)
実は国旗以外にも、結合する旗の絵文字があります。 "黒旗(U+1F3F5)" + "0-9-a-zのいずれか5文字" + "Cancel TagU+E007F" の列でウェールズなどの旗にすることができます。よくわからないですね。 現在のところ
の7つが登録されているので、黒旗を見つけたらとりあえず後ろの文字を読んでパターンマッチで該当すれば結合、していなければ分割で実装します。
ZWJを使った結合
ZWJ(U+200D)という文字を間に入れることで絵文字同士を結合させます。たとえば黒旗(U+1F3F4)とZWJ(U+200D)と髑髏(U+2620)の3つが並ぶと海賊旗になります。
結合の組み合わせも莫大な上、規則性も存在しないので「ZWJがあったらその前後の絵文字は結合させることができる」と決め打ちして実装します。
実装そのものは単純で、1文字目・2文字目・3文字目を常に見て、2文字目がZWJであれば1文字目と3文字目を結合させるだけです。
結合の組み合わせ
「特定の2文字で結合」と「ZWJを使って結合」は同時に起こります。 例えば"U+1F469 U+1F3FD U+200D U+1F91D U+200D U1F468 U+1F3FF"という文字列は「medium skin toneの肌色の女の人とdark skin toneの肌色の男の人」が手をつないでいる絵文字になります。(U+1F91Dは🤝という絵文字)
よって、結合の順序は
- まず特定の2文字で結合する絵文字について結合処理を行い、結合できる絵文字についてはリストにしてまとめる
- 次にZWJを間に持つ2つの文字のリストを探し、あればそれらをまとめる
という順序になります。最初にZWJで結合させようとすると、肌色や国旗の結合に対応できません。
U+FE0Fの存在
絵文字であることを明確にするためにU+FE0Fという文字を末尾に付けることがあります。気を付けましょう。
グラフィックが無かったら
グラフィックが見つからなかった場合、結合してある絵文字をもう一度分解し、それぞれに対してグラフィックを割り当てて表示しましょう。対応するグラフィックが無い場合の処理についてはここに書いてあります。
おわりに
絵文字の仕様よくわからん