序
Linuxで割とよくある問題が、「日本語が表示はされるが、一部の漢字が中華漢字で表示される」という問題である。
この問題は特にウェブにおいて発生しやすく、日本語に特化していないLinuxディストリビューションでは非常によくある。
一見すると、「フォントをインストールすればいいんだろう」くらいに思うかもしれないが、実のところこの問題は非常に根深く、難しい。 そもそも、「フォント」という科目はMimir Yokohamaの授業、「Linux基礎初級」において最難関と言っても良いものである。
ちょっと調べてみたがこの問題についてちゃんと説明している記事が出てこなかった。 難しい問題であると同時に、わかっている人にとっては別に「特別なことは何もない」作業なので記事にしたりしないのだろう。
そこで、ここではその問題、修正方法、修正の意味を解説する。
前提知識
FontConfig
FontConfigはLinuxで使われている、フォントを選択する機構である。
FontConfigを用いることで、「論理的な」フォントファミリーが要求されたときに、具体的なフォントファイルとして何を使うべきかを決定できる。 また、その論理的なフォントファミリーで必要とされるグリフを満たすために、複数の具体的なフォントファイルを優先度順に回答したりすることもできる。
また、FontConfigを用いてレンダリングオプションを制御したりすることもできる。 ただし、レンダリングオプションはLinuxで標準的な1フォントレンダリングエンジンであるFreeType以外では基本的に効かない。
ブラウザとFontConfig
FirefoxおよびChromeは完全にFontConfigに頼ってはおらず、要求されたフォントファミリーについてFontConfigを使ってフォントファイルを特定することはするが、2番目以降の候補は無視し、グリフが埋まらなけれれば次のフォントファミリーを試す。
例えば和文において font-family: "Roboto", "VLGothic"; が要求されたとして、RobotoがRoboto > Source Han Sans JPという優先順位で定義されていたとする。
FreeTypeにおまかせするアプリケーションの場合は和文にはSource Han Sans JPが使用されるが、ウェブブラウザの場合はRobotoだけを適用して、VLGothicを適用しようとする。
FontConfigの設定
FontConfigの設定は/etc/fontsにあるfonts.confと、conf.d/以下の*.confである。
これらのファイルのincludeによってさらに他の設定ファイルが読まれる場合もある。
ちなみに、/etfc/fonts/conf.d/自体、/etc/fonts/fonts.confで読んでいるものなので、その記述がなければconf.dは読まれない。
ユーザーの設定はFontConfig標準の設定ファイルではuser.confによって読むようになっており、これにより${XDG_CONFIG_HOME:-$HOME/.config}/fontconfig/fonts.confと${XDG_CONFIG_HOME:-$HOME/.config}/fontconfig/conf.d/、およびdeprecatedになっている~/.fonts.confと~/.fonts.conf.d/を読むようになっている。
FontConfig設定の優先度
ファイル名の辞書順で読み、読んだ順に適用していく。
ディストリビューションとFontConfigの設定
ディストリビューションはFontConfigの設定に個性を出していることが多く、その設定の違いで挙動に違いが出たりする。
YouTube
YouTubeはDroid Sansを要求したりしなかったりするため、中華漢字になったりならなかったりする。
どういう基準なのかは不明。
FontConfigのコマンド
fc-listはFontConfigで認識されているすべてのフォントを一覧する。
オプションをつけない場合、フォントファイルが持っている名前が一覧される。
fc-matchは引数として与えた論理名に対してFontConfigの回答を受け取れる。
-aオプションをつけると、すべての候補を優先度順に返す。
fc-cacheはフォントキャッシュを更新する。
-fをつけるとキャッシュを削除して更新する。
設定の反映にも使える。
修正する
まず、日本語フォントを導入する。
ここでは例として、ZEN角ゴシック NEW (Zen Kaku Gothic New)を使うと仮定する。
基本的に~/.local/share/fonts/以下に置けば良い。
~/.config/fonts/20-general.confとして次のようにする。
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- Default sans-serif font -->
<match target="pattern">
<test qual="any" name="family"><string>sans-serif</string></test>
<edit name="family" mode="prepend" binding="strong">
<string>Zen Kaku Gothic New</string>
</edit>
</match>
<!-- Japanese Default -->
<match target="scan">
<test name="lang">
<string>ja-jp</string>
</test>
<test name="family">
<string>Zen Kaku Gothic New</string>
</test>
</match>
<default>
<family>Zen Kaku Gothic New</family>
</default>
</fontconfig>次に~/.config/fonts/90-droid-sans.confとして次のようにする。
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- Default sans-serif font -->
<match target="pattern">
<test qual="any" name="family"><string>Droid Sans</string></test>
<edit name="family" mode="prepend" binding="strong">
<string>Zen Kaku Gothic New</string>
</edit>
</match>
<match target="pattern">
<test qual="any" name="family"><string>Droid Sans Fallback</string></test>
<edit name="family" mode="prepend" binding="strong">
<string>Zen Kaku Gothic New</string>
</edit>
</match>
</fontconfig>利用しているデスクトップ環境がフォントの設定を持っている場合、標準フォント、あるいはサンセリフフォントとしてsans-serifを指定する。
最後にブラウザのフォント設定を確認し、Droid Sansが指定されているようであればsans-serifあるいはSansに変更しておく。
Firefoxの場合は言語ごとの設定があるので注意。
解説
まず、webコンテンツでDroid Sansが適用される状況は次の通り。
- webコンテンツでDroid Sansが指定されている
- ブラウザが標準フォント、あるいはサンセリフフォントとしてDroid Sansを使っている
- FontConfigが
sans-serifを要求されたときにDroid Sansを返す
もちろん、日本語フォントがインストールされていることは前提として必要となる。
次にsans-serif対策。
sans-serifの候補リストの先頭に、高い優先度で希望のフォントを追加する。
また、日本語のデフォルトフォントとしても同じフォントを使うように設定する。
sans-serifとデフォルトフォントが常に同じとは限らない。
続いてDroid SansとDroid Sans Fallbackの候補リストに高い優先度で希望のフォントを追加する。
実はfc-listで確認してもDroid Sans Fallbackという名前は出てこないのだが、だいたいDroid Sans Fallbackを構成するパッケージにdroid-sans.confが含まれていて、これがDroid Sans Fallbackを定義している。
この中で
<match target="scan">
<test name="fullname">
<string>Droid Sans Fallback</string>
</test>
<edit name="family">
<string>Droid Sans</string>
</edit>
<edit name="fullname">
<string>Droid Sans</string>
</edit>
<edit name="fontversion">
<int>2</int>
</edit>
</match>としていて、ここでDroid Sans Fallbackが生まれている。
で、ウェブでDroid Sansが要求される場合は当然ながらDroid Sansの結果を返すのだけど、なぜかウェブではDroid Sans Fallbackのほうが使われたりする。
おそらくはFontConfig任せにフォントを選んでいないからだろう。
このため、Droid SansとDroid Sans Fallbackの両方に対して追加しておく必要がある。
さらに、デスクトップ環境のフォント設定する機能が設定ファイルよりも優先される場合があり、明示的にsans-serifを使わせることで設定ファイルに従わせる。
おことわり
FontConfigの機能は非常に豊富なので、これが最適解ではない可能性がある。
ただ、一般的によく使う機能・記述を用いて実現したので、応用はしやすいと思う。
ひとこと
FontConfigはただでさえ機能がめちゃくちゃ多い上にわかりづらいというのに、XMLで書かされるせいで本当にきついし、理解しづらい。
LuaのDSLとかにしてくれないか。 それでもそこまで楽になる気はしないけど。
「標準的な」と言っているけれど、一部自前でフォント描画するもの以外は基本的にFreeTypeで描画する以外の選択肢はない。↩︎