(追記 2019.12.14)
Pillowは少なくとも Ver6以降(+raqmの0.7以降)で日本語縦書きが可能になっています。
本記事の内容は古いものなのでご注意下さいませ。
前置き
ずいぶん前に以下の記事を投稿しました。
xmlで記述したテキストから、日本語の縦書き画像を生成するというものです。記事を書いた時点ではPillowが縦書きの文字描画に対応していなかったので、フォントファイルに手を加えて強制的に縦書き用のグリフを描画する事で縦書き描画をしていました。
その後、Pillowはバージョンがあがり、4.2.0で、テキストの描画方向を指定できるようになりました。ところが、いざ試してみると全く正しく動作しません。Pillowのコードを調べてみると、実際にはy_advanceの値を考慮しておらず縦書きに全く対応していませんでした。
修正してみる
というわけで、forkして、手元で修正をしてみました。
https://github.com/masushin/Pillow
from PIL import Image, ImageDraw, ImageFont
txt = Image.new('RGBA', (200,300), (215,215,220,255))
fnt = ImageFont.truetype("GenEiKoburiMin-R.otf", 24, layout_engine=ImageFont.LAYOUT_RAQM)
d = ImageDraw.Draw(txt)
d.multiline_text((120,20), "パパの動かす車なんて、\nあぶなくてのれますか。", spacing=24, font=fnt, fill=(0,0,0,255), direction="ttb")
txt.show()
このコードで、以下の画像が表示されます。ちゃんと縦書きのグリフが使われていますね。
修正の概要
ほぼ自分向けの備忘録になりますが、
freetypeからはbounding box(グリフを含む最小矩形)に相当するサイズのビットマップデータが得られます。これを1文字毎に、Pillowのイメージ上の適切な位置に転送していく必要があります。
PillowのAPIから指定された描画始点を上図でのorigin(黒の四角点)であるとすると、raqmライブラリから得られる水平オフセット位置(x_offset)から、bounding boxの最小x(xMin)だけ正方向に位置するのがグリフの左端位置になります。
また、originからascender値だけ下方向に水平のベースラインがあるのでここから、bouding boxの最大yだけ上方向に位置するのがグリフ上端としました。
グリフを上端からグリフの高さ(height)だけビットマップを転送します。
次の文字を描画するには、originをy_advanceだけ加算して、次の文字の描画基準となるoriginを求め、以降同様の処理を行ないます。