Noto Emojiにコントリビュートし縦書きできるようにした話

こんにちは、株式会社ミクシィのグループ会社の株式会社ノハナでエンジニアをしている @tacke_jp です。この記事はmixiグループ Advent Calendar 2017の24日目の投稿です。今日は少しばかりマニアックな話題になりますが、縦書きの絵文字の話をしたいと思います。

絵文字を含むテキストの縦組みレンダリングオープンソースで実現する手段はこれまで充実していませんでした。今回、オープンソースで絵文字の縦組みを実現するための技術調査を行いました。またその過程でNoto Emojiで縦組みが正常に行えなかったため、それを解消しnoto-emojicontributeを行いました

どのフォーマットを選択すべきか

絵文字フォントの規格には様々なものがあります。代表的なものはOpenTypeフォント内に独自のテーブルを定義し、そこにカラーのビットマップやSVGを入れる方式となっています。各社推しているフォーマットがばらばらで統一されておらず、足並みが揃っていないのが現状です。

オープンソースのライブラリFreeTypeは様々なフォントのフォーマットに対応しており、グリフのアウトライン情報(絵文字の場合は画像データ)やメトリクスを取得するための抽象レイヤを提供してくれます。そのためオープンソースで絵文字のテキストレンダリングをする場合はFreeTypeが対応しているフォーマットを選択することになるかと思います。今回の検証ではFreeTypeが対応しており、公開されているフリーの絵文字フォントの多くで採用されているCBDT/CBLCを利用しました。

オープンソースでの日本語テキストレイアウトの技術スタック

オープンソースで公開されており、GTK+などで採用されているテキストレイアウトエンジンにPangoがあります。

PangoはグラフィックバックエンドとしてCairoを利用することができます。CairoはGTK+などで用いられており、過去ChromeFirefoxなどでも採用されていたグラフィックエンジンです。Cairoが最近のアップデートv1.15.8でカラー絵文字に対応したため、Cairo+FreeTypeでのカラーグリフを含めたテキストレンダリングが実現可能となりました。

オープンソースのテキストレイアウトの技術スタックについては、State of Text Renderingに詳しくまとまっています。

NotoColorEmoji.ttfで絵文字の縦組みがズレる原因とその対応

Pango+Cairoで縦組みを試したところ、以下のように絵文字のみ描画位置がずれてしまっていました。

f:id:tacke_jp:20171224182900p:plain

Pangoでは_pango_fc_shapeで、cairoにグリフをレンダリングレンダリングする位置を指示しています。デバッガを噛ませて調べてみると、絵文字グリフのみFreeTypeから取得したメトリクスが縦組みの場合に限りすべて0になっており、そのためにグリフが全て同じ位置に重なるようにして描画されていることがわかりました。

さらにFreeTypeの中を追っていくと、OpenTypeフォントファイルのdecoderに行き着き、絵文字グリフの場合のメトリクスの取得にはNotoColorEmoji.ttfの場合はsmallGlyphMetricsという形式でメトリクスが含まれており、この場合に縦組みのメトリクスはすべて0初期化されるということが分かりました。

bigGlyphMetricsを使えば縦組み用のメトリクスをPangoまで渡すことができるため、noto-emojiのフォントファイル作成スクリプトに手を入れ、smallGlyphMetricsの代わりに、適切な縦組み用のメトリクスを含めたbigGlyphMetricsを使用するようにしました

f:id:tacke_jp:20171224182820p:plain

ご覧のように正しい位置にレイアウトされるようになりました。(画像生成には https://gist.github.com/yuki-takeichi/f9c1b7bf9154597518fbd9289b527004 を用いました。)

素敵な絵文字縦組みライフを

このcontributeにより、FreeTypeで特別な対応を行わなくても絵文字の縦組み用のメトリクスが取得できるようになり、絵文字縦組みの難易度が下がりました。例えばGTK+環境でNoto Emojiを用いた縦組みテキストのビューアやエディタが作りやすくなったのではないかと思います。また、NotoColorEmoji.ttfの生成に必要なファイルは全部noto-emojiにあるので、PNG画像があればnoto-emojiのemoji_builder.pyを利用して縦組みに対応したカスタム絵文字フォントを作成することができます。時間が許せば別のCBDT/CBLCを使用した絵文字フォントにもbigGlyphMetrics化のcontributeができればと思っています。

明日、ラストを飾るのは @isaoshimizu さんの「2017年の活動報告 - SREとビールの話」です。

それでは皆さんよい年末を〜

(追記 2019.2.9)

DroidKaigi2019で @ttuusskk さんに教えていただいたところによると、Androidでは絵文字のメトリックはHarfBuzzまで聞きに行っているらしく、そのHarfBuzzがsmallGlyphMetricsのテーブル(format17)しか読めないため、結局noto-emojiではsmallGlyphMetricsがデフォルトになるように再度スクリプトが変更されたとのことでした。 Some tooling updates for android u11. · googlei18n/noto-emoji@e571d53 · GitHub

ということなので、AndroidのHarfBuzzがupstreamになったら再チャレンジしようかな...