汎用絵文字ライブラリ Somemoji

image

ここ最近絵文字で遊んでいて、Somemoji というライブラリをつくったので知見を共有します。

さまざまな絵文字セット

様々なプラットフォームのために、様々な組織が、様々な絵文字セット (絵文字画像の集合) を提供しています。

大抵の絵文字セットはUnicodeのEmojiの仕様に則って実装されていて、このコードポイントに対応する絵文字画像はこれ、というように互換性があります。Unicode 6.0, Unicode 7.0, Unicode 8.0, ... とバージョンが増えるに従って定義されるEmojiの数も増えていっているので、それぞれの絵文字セットごとに対応具合はまちまちという状況ではあるものの、よく使う主要なものについては大体カバーされています。

オープンソースの絵文字セット

最近は寛容なライセンスで提供される絵文字セットも増えてきていて、それぞれのライセンスに従って自分のサービスで絵文字画像を利用できるようになってきています。

汎用絵文字ライブラリ

https://github.com/r7kamura/somemoji

さまざまな絵文字セットがあり、それぞれの絵文字セットごとにさまざまなライブラリがあるものの、ライブラリで出来ることもまちまちで、サービスの都合で使う絵文字セットを切り替えたりしようと思うと簡単にはいきません。なので、ライブラリも含めた絵文字セットの選定の段階が非常に大事で、そこでミスると面倒で辛い状態になってしまいます。そこで、絵文字セットに関わらず利用できる汎用絵文字ライブラリ Somemoji を開発することにしました。

Somemojiには、以下のファイルが同梱されています。

  • それぞれの絵文字の属性情報を定義したJSON
  • それぞれの絵文字セットで対応している絵文字を定義したJSON
  • 絵文字画像を手元に持ってくるためのコマンドラインツール
  • Ruby用のライブラリ

使い方

絵文字画像を一括ダウンロードする

somemoji というコマンドラインツールが同梱されていて、これを利用して絵文字画像を手元にダウンロードしてくることができます。例えば、デプロイ前にAmazon S3に絵文字画像を配置するときに利用したりします。なお、絵文字セットによってはMaxCDNなどから配信されている絵文字画像が利用できたりするので、Webサービスで利用する場合は必ずしもダウンロードしてくる必要はありません。

$ somemoji --help
Usage: somemoji extract [options]
    -p, --provider     (required) apple, emoji_one, noto, or twemoji
    -d, --destination  (required) directory path to locate extracted image files
    -f, --format       png or svg (default: png)
    -s, --size         Some providers have different size image files
    -h, --help         Display this help message

Twemojiの画像データを ./images/emoji 以下に持ってくるにはこう。

somemoji extract --provider=twemoji --destination=./images/emoji

文中に含まれる絵文字コードを画像に置換する

"I :heart: Emoji"heart の部分の名前のことを絵文字コードと言ったりするんですが (主にSlackなどはそう呼称している)、文中に含まれる絵文字コードをHTMLのimg要素に置き換えてみるというコードの例がこれです。EmojiOneで利用可能な絵文字について、置換を試みています。

Somemoji.emoji_one_emoji_collection.replace_code("I :heart: Emoji") do |emoji|
  %(<img alt="#{emoji.character}" class="emoji" src="/assets/emoji/#{emoji.base_path}.png">)
end
#=> 'I <img alt="❤" class="emoji" src="/assets/emoji/unicode/2764.png"> Emoji'

文中に含まれる絵文字を画像に置換する

文中に含まれる絵文字を、先述の例と同様にimg要素に置き換えてみるというコードの例がこれです。例えば、サービス上で表示する絵文字は全てTwitterの絵文字画像に統一したいなあ、みたいなときに使えます。

Somemoji.twemoji_emoji_collection.replace_character("I 💗 Emoji") do |emoji|
  %(<img alt="#{emoji.character}" class="emoji" src="/assets/emoji/#{emoji.base_path}.png">)
end
#=> 'I <img alt="💗" class="emoji" src="/assets/emoji/unicode/1f497.png"> Emoji'

独自の絵文字を追加する

サービスによっては、自社のアイコンなど独自の絵文字を追加したい場合もあると思います。そういう場合のために、ランタイムで絵文字を追加できるような機能を持っています。

custom_emoji_collection = Somemoji.emoji_one_emoji_collection + Somemoji::EmojiCollection.new(
  [
    Somemoji::Emoji.new(code: "foo"),
    Somemoji::Emoji.new(code: "bar"),
  ]
)
custom_emoji_collection.find_by_code("foo").class #=> Somemoji::Emoji
custom_emoji_collection.find_by_code("bar").class #=> Somemoji::Emoji
custom_emoji_collection.find_by_code("100").class #=> Somemoji::Emoji
custom_emoji_collection.replace_code("I :bar: Emoji") do |emoji|
  %(<img alt="#{emoji.character || emoji.code}" class="emoji" src="/images/emoji/#{emoji.base_path}.png">)
end #=> 'I <img alt="bar" class="emoji" src="/images/emoji/bar.png"> Emoji'

Markdown内の絵文字を置換する

RubyMarkdownの変換処理を構造的にやる場合、恐らく HTML::Pipeline を使うことになると思います。絵文字コードを置換するようなHTML::Pipelineのフィルターを書くのも、以下のように実現できます。HTML::Pipelineについては Markdownを拡張して独自記法をつくる という記事を前に書いたので良ければご覧ください。

class EmojiFilter < ::HTML::Pipeline::Filter
  IGNORED_ANCESTOR_ELEMENT_NAMES = %w(
    code
    pre
    tt
  )

  # @note Implementation for HTML::Pipeline::Filter
  def call
    doc.search(".//text()").each do |node|
      unless has_ancestor?(node, IGNORED_ANCESTOR_ELEMENT_NAMES)
        node.replace(
          ::Somemoji.twemoji_emoji_collection.replace_code(node.to_html) do |emoji|
            %W(
              <img
                alt="#{emoji.code}"
                class="emoji"
                height="20"
                src="/images/emoji/#{emoji.base_path}.png"
                title=":#{emoji.code}:"
                width="20">
            ).join(" ")
          end
        )
      end
    end
    doc
  end
end

おわり

Somemoji 開発の背景や使い方を説明しました。今後の展開として、JavaScript環境で使えるようにする対応と、絵文字画像ダウンロードの高速化、サポートする絵文字セットの増加、絵文字セットごとの利用できる文字種とライセンスの明示、絵文字定義ファイルの追加を考えています。

追記