2014/11/12

PythonのCairoでSVGをつくる

最近、Android用のキーボードの開発をやってます。

0からつくるわけにはいかんので、Mozc for Androidってのを使ってるんですが、
こことかにも書かれてるみたいに、Mozcにはキートップの画像が含まれていません。
(正確に言うと、画像はありますが無刻印キーボード的な画像が含まれています)


なので、ビルドするとこんなかんじになります。


キーボードの配列を覚える練習にはなるかもしれませんが、実用的ではないですね。


そんなMozcを↑のようにしたいわけですが、
ここのページで公開されてるキートップ画像では[t][y][u]の上下が微妙にずれていて、若干気持ちが悪いです。そこで、キートップ画像の自作をすることにしました。


ただ、社会人が片手間にやるには、InkScapeでチマチマと作る時間がありません。
そんなわけで、Pythonで一気に作っちゃいました、ってのが今回のお話。


さて、前置きが長くなりました。まずは
https://gist.github.com/yi01/1fe1bffabdc583c09de6
にコードは公開しちゃいます。間違いを発見した方は指摘ください。(笑)


で、あまり世の中には書かれていない?話をちょっとだけ書いておくと

・Mozcのキートップ画像の要件
キートップ用のSVGなんですが、textタグは使えません。pathタグで書く必要があります。
なので、Pythonだと、svgwriteとかは使えなくて、唯一の選択肢がcairoです。

<svg>に、id="style-keyicon-main"が指定されている必要があります。これがないと、スキンを暗い色にした時に、キートップの文字が白抜きにならない不具合が発生します。

QWERTYのキー画像サイズは48x74ピクセルです。cairoはptを単位系として使っているようなので、座標系を変換しなければなりません。
でも、変換がめんどくさすぎたので、今回は成果物のxmlからptの単位がついてるものを表示しなくしてみました、それにより、意図したサイズの画像が得られます。


・Cairoの文字描画
座標系がちょっと特殊(?)です。
xBearing, yBearing, width, height, xAdvance, yAdvance = context.text_extents(text)
で文字の描画位置やサイズが取得できるわけですが、座標系が下向きY軸正方向のものなので、直感とは逆になります。

で、もうひとつ注意点としては、そのへんのサンプルにある(xBearing+width/2, yBearing+height/2)という座標は、文字領域のど真ん中(赤色の四角の重心)であって、全体的な文章のラインの中心とは異なります。これが、冒頭に書いた「ずれていて若干気持ち悪い」ということです。

↑で、赤枠ベースでの配置だと上下して見えますよね。

じゃあ、どうすればいいのか、っていうのが青枠ベースの配置。

どうやら、フォントサイズと文字のラインの高さには3/4の関係があるみたいなので
↑にあるように、64ptのフォント指定の場合は、24px(=フォントサイズの3/8)だけ下に移動してやることで、行の中心をとらえた描画となるのです。




おまけ
E/MessageQueue-JNI(31310): java.lang.IllegalArgumentException: Duplicataed sourceId is found: Binary XML file line #1710
E/MessageQueue-JNI(31310):    at org.mozc.android.inputmethod.japanese.keyboard.KeyboardParser.parseKeyEntity(KeyboardParser.java:772)

みたいなエラーになった時のチェック用スクリプト
grep -r ":sourceId" kbd_*.xml | awk '{a[$2]+=1};END{for(k in a){if(a[k]>1){print k,a[k]}}}'