雑文発散

«前の日記(2015-07-14) 最新 次の日記(2015-07-16)» 編集
過去の日記

2015-07-15 [長年日記]

[Pebble] Pebble SDK のフォント用ツール「fontgen.py」でマルチバイトが詰まったフォントファイルを加工するとエラーが出るのは、手元の Python 環境の問題だった

Pebble アプリにカスタムフォントを追加するときに、正規表現によって「文字を限定」できる機能がある。

どういうときに「文字を限定」したいかというと、Pebble アプリの容量を小さく抑えたいとき。Pebble アプリの最大容量はキロバイト単位なので、余計なものはそぎ落としたい気持ち。アプリの内容にもよるけど、基本的にはアプリ内で利用している文字だけに限定できればサイズが落とせる。

具体的な方法は Pebble 公式のガイドの「Font Resources」にその方法が書かれている。characterRegex というキーの値に正規表現を記入できるってことらしい。

正規表現の部分とは直接関係ないけど、この(2015/07/15現在の)ドキュメントに書いてある「"type": "ttf"」というのは「"type": "font"」の間違いだと思う。そんな type は存在しないのでエラーが出る。ここでは正しいはずの「"type": "font"」として書いておく(Pebble 公式にはあとで報告するよ)。

"media": [
  {
    "characterRegex": "[:0-9]",
    "type": "font",
    "name": "EXAMPLE_FONT",
    "file": "fonts/example_font.ttf"
  }
]

この書き方を真似して、M+ OUTLINE FONTS の中から、[:0-9] だけを対象にしてみる。

"media": [
  {
    "characterRegex": "[:0-9]",
    "type": "font",
    "name": "MPLUS_FONT_14",
    "file": "fonts/mplus-1c-light.ttf"
  }
}

そして pebble build してみると、、、エラーが出る。

% pebble build
[snip]
[ 7/38] mplus-1c-light.ttf.MPLUS_FONT_14.pfo: resources/fonts/mplus-1c-light.ttf ../../../../../usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py -> build/resources/basalt/fonts/mplus-1c-light.ttf.MPLUS_FONT_14.pfo
[snip]
Build failed
 -> task in 'mplus-1c-light.ttf.MPLUS_FONT_14.pfo' failed (exit status 1):
    {task 4387118992: mplus-1c-light.ttf.MPLUS_FONT_14.pfo mplus-1c-light.ttf,fontgen.py -> mplus-1c-light.ttf.MPLUS_FONT_14.pfo}
' python \'/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py\' pfo  14  --filter "[:0-9]"   \'/Users/suzuki/work/pebble/resources/fonts/mplus-1c-light.ttf\' \'/Users/suzuki/work/pebble/build/resources/basalt/fonts/mplus-1c-light.ttf.MPLUS_FONT_14.pfo\' '

マルチバイト関係でなんかあるのかなーと思って、OS X に同梱されている Arial.ttf を使って試してみる。

"media": [
  {
    "characterRegex": "[:0-9]",
    "type": "font",
    "name": "ARIAL_FONT_14",
    "file": "fonts/Arial.ttf"
  }
}

これで build すると、何事もなく成功する。

% pebble build
[snip]
[ 7/38] Arial.ttf.ARIAL_FONT_14.pfo: resources/fonts/Arial.ttf ../../../../../usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py -> build/resources/basalt/fonts/Arial.ttf.ARIAL_FONT_14.pfo
[snip]
'build' finished successfully (0.962s)

問題を特定するために、pebble build ではなく、フォントの加工をしている Python スクリプト( fontgen.py )を直接叩いてみる。スクリプトの引数は先程のエラーメッセージに出ていたので基本的にはそれをそのまま使うけど、input / output 用のパスは長くなるので相対パスに変えた。

% python /usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py pfo  14 --filter "[:0-9]"  ./resources/fonts/mplus-1c-light.ttf ./build/resources/basalt/fonts/mplus-1c-light.ttf.MPLUS_FONT_14.pfo
Traceback (most recent call last):
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 367, in <module>
    main()
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 363, in main
    process_cmd_line_args()
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 355, in process_cmd_line_args
    args.func(args)
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 300, in cmd_pfo
    f.convert_to_pfo(args.output_pfo)
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 287, in convert_to_pfo
    self.build_tables()
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 252, in build_tables
    if (codepoint_is_in_subset(codepoint)):
  File "/usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py", line 222, in codepoint_is_in_subset
    if self.regex.match(unichr(codepoint)) is None:
ValueError: unichr() arg not in range(0x10000) (narrow Python build)

最後の行に unichr() arg not in range(0x10000) というメッセージがある。unichr() が何かというと、ドキュメントには次のように書かれている(強調はオレが勝手に入れている)。

Unicode におけるコードが整数 i になるような文字 1 文字からなる Unicode 文字列を返します。
[snip]
この関数は Unicode 文字列に対する ord() の逆です。引数の正当な範囲は Python がどのように構成されているかに依存しています — UCS2 ならば [0..0xFFFF] であり UCS4 ならば [0..0x10FFFF] であり、このどちらかです。それ以外の値に対しては ValueError が送出されます。

「Python がどのように構成されているかに依存」ってなんだー!?と調べ始めたら、どうやら Python の Unicode には UCS-2 と UCS-4 の違いがあるようだ。

そんな話、知らなかったぞ?と「How to find out if Python is compiled with UCS-2 or UCS-4?」での検証方法で「pebble build を実行している Python 環境」をチェックしてみた。

% python
Python 2.7.10 (default, Jun 24 2015, 17:00:45)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.maxunicode
65535

オレが使っているのは UCS-2 の python だったので、もしかすると UCS-4 の python なら成功するんじゃないか?と考えた。

UCS-4 の python をビルドするには、configure で次のように指定すれば良いとのこと。

% ./configure --enable-unicode=UCS4

手元の Python 環境は、pyenv でインストールしていたので、pyenv で --enable-unicode=UCS4 を指定する方法がないものか?と思って探してみると、pyenv の Issue にヒントがあった。

PYTHON_CONFIGURE_OPTS="--enable-unicode=ucs4" pyenv install 2.7.4

環境変数 PYTHON_CONFIGURE_OPTS に指定すれば良さげなのが分かったので、手元でもこれを使ってみた。UCS-2 の python 2.7.10 は比較用にそのまま残しておきたかったので、一番近いバージョンの 2.7.9 で試した。

% PYTHON_CONFIGURE_OPTS="--enable-unicode=ucs4" pyenv install 2.7.9

インストール終了後、手元の Python 環境を 2.7.9 に切り替えて、先ほどの sys.maxunicode の値をチェックしてみると、UCS-4 になっていることが確認できた。

% python
Python 2.7.9 (default, Jul 15 2015, 13:56:57)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.maxunicode
1114111

この 2.7.9 で pebble build を実行してみると、、、

% pebble build
[snip]
[ 7/38] mplus-1c-light.ttf.MPLUS_FONT_14.pfo: resources/fonts/mplus-1c-light.ttf ../../../../../usr/local/Cellar/pebble-sdk/3.1/Pebble/common/tools/font/fontgen.py -> build/resources/basalt/fonts/mplus-1c-light.ttf.MPLUS_FONT_14.pfo
[snip]
'build' finished successfully (1.053s)

ビルドが成功した!!

だいぶ長くなったので簡単にまとめる。

  • Pebble アプリにカスタムフォントを入れる時に正規表現で文字を限定できる
  • フォントにより、また、実行時の Python 環境により unichr() のエラーが発生する
  • unichr() のエラーが出て、実行時の Python 環境が UCS-2 なら UCS-4 にすれば解決する

はー、トラブルシュートをするのも、この日記を書くのにも時間がかかってしまった。。。じゃあ Pebble アプリ作りに戻ろうっと。