ちょっと前にimagemagickで絵文字を含むテキストを合成する時に結構ハマったのでなんとなく備忘録を。
imagemagickはフォントが1つしか指定できないので、大抵の場合絵文字があると文字化けしてしまいます。
そこで、pangoを使って複数フォントに対応します。
(以下、dockerでrailsを動かす前提で進めます。)
フォントを置く
まずは使いたいフォントをDLしてコンテナのfontsに置きます。
(apt-getで手に入るならそっちのほうがいいと思います)
今回はNoto Sans CJK JPとNoto Color EmojiをDLして/assets/fontsに置きました。
# Dockerfile
COPY /assets/fonts /usr/share/fonts
memo: Noto Color Emojiについて
Noto Color Emojiは定番のフォントだと思いますが、入手先が3つあってそれぞれ対応しているUnicodeのバージョンが違います。
- 公式サイト → Unicode10
- apt-get → Unicode11
- Github → Unicode12(最新)
Github以外は更新が止まってるようなので、Unicodeが更新される度にGithubからDLして上書きする必要があります。
フォントの設定を変える
Noto Color Emojiはビットマップフォントですが、設定によってはこれがデフォルトで無効になってることがあるので書き換えます。
# Dockerfile
RUN rm /etc/fonts/conf.d/70-no-bitmaps.conf
RUN ln -s /etc/fonts/conf.avail/70-yes-bitmaps.conf /etc/fonts/conf.d/
ビットマップフォントを無効にする設定ファイルを消して、conf.availから有効にする設定をコピーしてます。
次にNoto Color Emojiの優先度を上げるため、設定ファイルを作って/etc/fonts/に置きます。(/usr/share/fontsではないので注意)
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
<alias>
<family>sans-serif</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family>serif</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
</fontconfig>
# Dockerfile
COPY /config/local.conf /etc/fonts/
RUN fc-cache -f
最後に念の為fc-cache -fでキャッシュを消して設定を読み込ませます。
これで、フォントの設定は完了です。
pangoで画像を生成する
MiniMagick::Tool::Convert.new do |convert|
convert.size "600x200"
convert.pango("<span font='Noto Sans CJK JP' size='36864'>#{title}</span>")
convert << "image.png"
end
2行目で指定したサイズに合わせてよしなに改行してくれます。
pangoはフォールバックフォントに対応しているので、Noto Sans CJK JPで表示できない文字が来た場合は先程設定したfontconfigに基づいてNoto Color Emojiで表示されるという仕組みです。
sizeはフォントサイズに1024掛けた数値を指定します。
pangoは他にも色々リッチなテキストを生成できるので、興味ある方は公式のdocを御覧ください。
https://www.imagemagick.org/Usage/text/#pango
絵文字がモノクロになる
出力された画像を見たら絵文字がモノクロだった…なんて時はOSのバージョンが古いかもです。
linuxの場合はUbuntu 18.04以降でないとカラーになりません。
自分はdebianのdockerコンテナ使ってたので、バージョンをbusterに変えて無事カラーになりました。
# Dockerfile
FROM ruby:2.6.5-buster
おわりに
こうしてまとめると大したことやってないですが、これに辿り着くまでにえらい時間かかりました…
あと例えば画像サイズに収まるように文字数をカウントしてトリミングしたい時は絵文字の扱いに注意が必要です。
サロゲートペアとか、肌の色を表す文字とか、複数の絵文字を合成してたりとか、ややこしい仕様が色々あります…
その辺の仕組みは↓の記事に詳しく書かれてます。
https://qiita.com/_sobataro/items/47989ee4b573e0c2adfc
https://tech.drecom.co.jp/count-length-of-string-including-pictogram/
絵文字、知れば知るほど難しい……