ここ最近絵文字で遊んでいて、Somemoji というライブラリをつくったので知見を共有します。
さまざまな絵文字セット
様々なプラットフォームのために、様々な組織が、様々な絵文字セット (絵文字画像の集合) を提供している。
大抵の絵文字セットはUnicodeのEmojiの仕様に則って実装されていて、このコードポイントに対応する絵文字画像はこれ、というように互換性がある。Unicode 6.0, Unicode 7.0, Unicode 9.0, ... とバージョンが増えるに従って定義されるEmojiの数も増えていっているので、それぞれの絵文字セットごとに対応具合はまちまちという状況ではあるものの、よく使う主要なものについては大体カバーされている。
オープンソースの絵文字セット
最近は寛容なライセンスで提供される絵文字セットも増えてきていて、それぞれのライセンスに従って自分のサービスで絵文字画像を利用できるようになってきている。
汎用絵文字ライブラリ
https://github.com/r7kamura/somemoji
さまざまな絵文字セットがあり、それぞれの絵文字セットごとにさまざまなライブラリがあるが、ライブラリで出来ることもまちまちで、サービスの都合で使う絵文字セットを切り替えたりしようと思うと簡単にはいかない。なので、ライブラリも含めた絵文字セットの選定の段階が非常に大事で、そこでミスると面倒で辛い状態になってしまう。そこで、絵文字セットに関わらず利用できる汎用絵文字ライブラリ Somemoji を開発することにした。
Somemojiには、以下のファイルが同梱されている。
使い方
絵文字画像を一括ダウンロードする
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
ちなみに1個ずつ画像ダウンロードしてて遅いので、次のマイナーバージョンで改善する予定。
文中に含まれる絵文字コードを画像に置換する
"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内の絵文字を置換する
RubyでMarkdownの変換処理を構造的にやる場合、恐らく 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 開発の背景や使い方を説明しました。