Linux のフォントはどのように表示されるのか

前回の記事では、日本語フォントそのものをどう選ぶかを整理した。そこでは VL ゴシック、IPAex、Noto Sans CJK、Source Han Sans などの候補を比較し、GNU/Linux では欧文用と和文用を分けて考えた方が理解しやすい、という結論までを扱った。今回の続編で扱うのは、その次の段階である。つまり、「選んだフォント設定が Linux のどこに効き、どこには効かないのか」を、fontconfigFreeType を軸に整理する。Linux のフォント問題が分かりにくく見えるのは、候補選定の話、フォント名の指定方法の話、実際のマッチングの話、テキスト整形の話、そして最終描画の話が、日常会話では全部まとめて「フォント設定」と呼ばれがちだからである [1][2][3]


1. Linux のフォント問題はなぜ分かりにくいのか

Linux でフォントを変えようとすると、しばしば奇妙な現象に遭遇する。たとえば、フォントを入れたのに見た目が変わらない、fc-match では期待した名前が返らないのに GUI ではそれなりに読める、逆に GUI では綺麗なのに PDF 生成や画像生成では別の結果になる、といった現象である。これは「設定したフォント名」と「実際に選ばれたフォント」と「最終的に描かれたグリフ」が同じ場所で決まっていないために起きる。Linux のテキスト処理では、まずアプリケーションが文字列とフォント条件を渡し、その後に shaping と layout が行われ、fontconfig が候補フォントを解決し、最後に FreeType がグリフを描画する、という複数段階の流れになっている [4][5][6][7]。したがって、問題を切り分けるためには、まず「フォントを選ぶ層」と「フォントを描く層」を分けて考える必要がある。

見えている問題 実際に疑うべき層 典型的な誤解
指定したフォント名と違うものが使われる fontconfig のマッチング インストール済みなら必ずそのフォントだけが使われると思いやすい
一部の文字だけ別の見た目になる layout と fallback 全部 fontconfig の失敗だと思いやすい
小さい文字だけ印象が悪い FreeType の描画 フォント名だけ変えれば全部解決すると考えやすい
GUI と生成物で結果が違う アプリケーション側の利用経路 Linux 全体で 1 つの設定だけが効いていると思いやすい

つまり、Linux におけるフォント問題は「単一の設定を探す話」ではない。むしろ、「どの層の話をしているのか」を明示しないまま議論しがちなこと自体が混乱の原因である。候補比較だけで終わると、なぜ fc-match と実表示が食い違うのか、なぜ CUI ではほぼ無関係なのに GUI ターミナルでは効くのか、といった疑問に答えられない。今回の目的は、この混同をほどいて構造を見えるようにすることにある。


2. Linux のフォント描画スタック

Linux のフォント処理は、単一のソフトウェアで完結しているわけではない。アプリケーションは文字列を描こうとし、その途中で shaping engine や text layout ライブラリを経由し、fontconfig が候補フォントを決め、FreeType が実際のグリフをピクセルへ落とす。HarfBuzz の文書では、shaping engine は Unicode codepoint 列に対して、言語や書記体系に応じたグリフ選択と位置決めを行うものと説明されている [4][5]。Pango の文書では、属性付きテキストが itemization や shaping や rendering の複数段階に影響すると説明されており、PangoLayout は段落単位の高水準なレイアウトドライバとして行分割、揃え、折り返しなどを担う [6][7]。この途中で fontconfig が実フォント候補を返し、その結果を用いて FreeType がグリフを描画する。

項番 主な役割 ここで起きる代表的なこと
1 アプリケーション フォント要求や文字列描画要求を出す アプリごとにフォント指定方法が違う
2 shaping / layout グリフ選択、配置、改行、フォールバックを扱う 欠字時の別フォント選択や行分割が起きる
3 fontconfig 要求パターンから候補フォントを選ぶ sans や monospace が実フォントへ解決される
4 FreeType 輪郭をピクセルへ落とす ヒンティングやアンチエイリアスで印象が変わる
5 フォントファイル 字形データそのものを持つ 存在していても優先されるとは限らない

この責務分離を理解すると、どの設定がどこまで効くかを切り分けやすくなる。fontconfig の開発者向け文書でも、fontconfig は XML 設定から内部設定を組み立てる構成モジュールと、パターンを受け取って最も近いフォントを返すマッチングモジュールから成ると説明されている [2][3]。一方、FreeType はフォントファイルを解釈し、グリフをレンダリングするライブラリである [9][10]。この二層分離を一度理解すれば、「fontconfig を触っているのに見た目の印象が変わらない」「描画だけ変えたいのに generic family まで崩してしまった」といった失敗を減らせる。


3. fontconfig は何をしているのか

fontconfig は、システム全体のフォント設定、カスタマイズ、アプリケーションからのアクセスを提供するライブラリである [1][2]。ここで重要なのは、fontconfig が「フォント名そのものを信じて固定的に返す仕組み」ではないという点である。fontconfig は、アプリケーションが渡したパターンを見て、システム上に存在するフォント群との距離を測り、最も近いものを返す。fonts-conf のマニュアルでも、font matching は提供されたパターンと利用可能な全フォントとの距離を測り、最も近いフォントを選ぶと説明されている [3]

このとき、アプリケーションが必ずしも具体的なフォント名を渡すとは限らない。よく使われるのは、sans、serif、monospace といった generic family である。これらは実フォント名ではなく、「どういう系統のフォントを欲しいか」を表す抽象名である。fontconfig はこの抽象名を、設定ファイル、優先順位、言語条件、収録グリフなどを見ながら実フォントへ解決する [1][2]。したがって、アプリケーションが「Sans 10」と要求したからといって、画面上の全グリフが単一ファミリーから来るとは限らない。欧文は 1 つの候補から、日本語は別の候補から、さらに一部の記号は別の候補から、という混成は普通に起こり得る。

問い合わせ 意味 注意点
sans 汎用サンセリフ系の候補 具体的なフォント名ではない
serif 汎用セリフ系の候補 設定や言語条件で実体が変わる
monospace 汎用等幅系の候補 和文が混ざると見た目は 1 つに見えないことがある

このため、fontconfig を理解するうえでは「フォントファミリー名を見る」のでは足りない。むしろ、「アプリが何を問い合わせたのか」「現在の設定がどういう優先順位を作っているのか」「言語条件やフォールバックがどう働くのか」を見る必要がある。Linux で欧文用と和文用を分けて考えるべきだという前回記事の結論は、この generic family と fallback の仕組みから見ても妥当である。


4. fallback と layout はどこで効いてくるのか

Linux の実表示でさらに重要なのが、font fallback と text layout である。Pango の文書では、属性付きテキストは itemization の入力となり、フォントやサイズに関する属性はフォント選択に、font features や letter spacing は shaping に、色や下線は rendering に影響すると説明されている [6]。また、Pango の fallback 属性では、fallback を無効化した場合、文字は最も近いフォントからしか取られず、他のフォントへの代替は行われないと明記されている [8]。これは裏返せば、通常は fallback が前提であり、欠字があれば別フォントへ落ちるのが標準動作だという意味である。

この層を無視すると、fontconfig の返り値だけを見て安心してしまう。たとえば fc-match が期待どおりでも、実際の表示では一部の文字が fallback されるため、本文中のある箇所だけ雰囲気が変わることがある。逆に、fc-match では見慣れない候補名が返っていても、layout 側でのフォールバックや shaping の結果として、読みにくさが表面化しないこともある。つまり、フォント問題を考えるときには、fontconfig の直後にある layout 層を飛ばしてはいけない。

現象 背景にある処理 実際の見え方
一部の文字だけ別の雰囲気になる fallback 本文中の一部だけ字面が変わる
同じフォント名でも言語で印象が違う itemization と shaping スクリプトごとにグリフ選択と配置が変わる
fc-match と実表示が一致しない感覚がある fontconfig の後段で layout が動いている 最終見た目は複数レイヤの合成になる

表層だけを見ると、「フォント名を 1 つ決めれば、そのフォントですべて描かれる」と思いがちである。しかし Linux の一般的な GUI や描画系ライブラリでは、実際には文字ごと、言語ごと、場合によっては属性ごとに経路が変わる。このことを先に理解しておくと、あとで fc-match や local.conf の結果を読むときにも、過剰な期待を抱かずに済む。


5. FreeType は何をしているのか

fontconfig がフォントを選ぶ一方で、FreeType はそのフォントをどう描くかを担当する。つまり、fontconfig が「誰を使うか」を決め、FreeType が「どう見せるか」を決める。文字の輪郭が硬く見える、あるいは柔らかく見える、小さいサイズで読みにくい、細い線が潰れる、といった印象差は、フォント名だけではなく描画時の処理に依存する [9][10]。FreeType のチュートリアルでも、render mode によって通常のアンチエイリアス付きビットマップか、1-bit のモノクロビットマップかが変わることが説明されている [11]。つまり、「同じフォントを使っているのに何となく印象が違う」というときは、フォント名だけでなく描画モードを見る必要がある。

さらに、FreeType の LCD サブピクセルレンダリング文書では、LCD の RGB ストライプ構造を利用して有効解像度を高められる一方、未加工のサブピクセルカバレッジは細い線に色フリンジを生みやすいと説明されている [12]。また、driver properties の文書では、ヒンティング関連の動作がドライバプロパティで制御されることが示されている [13]。要するに、「色にじみが嫌だからグレースケール」「高 DPI だからヒンティングなしでもよい」「小さい文字だから slight が読みやすい」といった判断は、fontconfig の話ではなく、FreeType 側の描画品質の話である。

描画要素 何を変えるか 体感しやすい違い
ヒンティング ピクセル格子への補正量 小さい文字の輪郭の安定感
アンチエイリアス 輪郭の滑らかさ ざらつき感の減少
サブピクセル LCD の色ストライプ利用 解像感と色にじみのトレードオフ

Linux のフォント調整で混乱しやすいのは、fontconfig の設定を触っているのに、実際に気になっている差は FreeType 側のヒンティングやアンチエイリアスによって生じている、というケースが多いからである。フォント名と描画品質は分けて考える、という原則はこの章が核心になる。


6. 設定ファイルはどこにあり、何が上書きされるのか

fontconfig の設定は XML で記述され、システム全体の設定は /etc/fonts 以下に配置される [1][2][3]。実務上よく見るのは、/etc/fonts/conf.d と /etc/fonts/local.conf である。conf.d は有効設定の集合であり、local.conf は管理者が追加で上書きするための場所と考えると理解しやすい。ただし、local.conf は「何でも 1 つで決める万能ファイル」ではない。generic family の prefer 順を変えれば、その系統全体に影響する。局所調整のつもりで書いた設定が、sans や monospace の全体挙動を大きく変えてしまうことがある。

場所 役割 実務上の注意
/etc/fonts/conf.d/ 有効設定の集合 読まれているかは一覧で確認する必要がある
/etc/fonts/local.conf 管理者の追加上書き 触りすぎると generic family 全体に効く
フォントパッケージ付属設定 フォント導入時の補助設定 インストール済みでも最優先になるとは限らない

ここでつまずきやすいのは、「設定ファイルがある」と「その設定が今の挙動を決めている」を同一視してしまう点である。実際には、有効になっている設定だけが読まれるし、複数の設定が重なれば後勝ちや優先順位の組み合わせで結果が決まる。したがって、local.conf を書いた後に fc-cache を実行しても、期待どおりに見えないことは十分あり得る。それはキャッシュの失敗ではなく、設定内容か優先順位の問題かもしれない。


7. fc-* コマンドは何を見ているのか

Linux のフォント環境を確認するとき、fc-* コマンド群は非常に有用である。ただし、それぞれが見ている対象は違う。fc-match はあるパターンに対して最終的に誰が選ばれるかを見るコマンドであり、通常の fontconfig matching rules を使って best font を求める [14]。fc-list はシステム上で fontconfig から見えているフォントとスタイルを一覧表示する [15]。fc-pattern は pattern を解析して表示し、必要なら config substitution や default substitution を適用した後のパターンを確認できる [16]。fc-conflist は fontconfig が処理した設定ファイル一覧を注釈付きで表示し、どの設定が無視され、どの設定が処理されたかを確認できる [17]。fc-cache はフォントディレクトリを走査し、FreeType が読めるフォントファイルの情報から cache files を作る [18]

コマンド 確認対象 主な用途
fc-list 見えているフォント一覧 そのフォントが存在するか確認する
fc-match 最終的に選ばれるフォント 実マッチ結果を確認する
fc-pattern 問い合わせパターン 何を問い合わせているか確認する
fc-conflist 有効設定 どの設定が読まれているか確認する
fc-cache -fv キャッシュ再生成 追加・変更後に再構築する
1
2
3
4
5
6
7
fc-list :lang=ja family
fc-match sans
fc-match monospace
fc-match "sans:lang=ja"
fc-pattern sans
fc-conflist
fc-cache -fv

この一覧を見れば分かるとおり、fc-match の 1 行だけで「Linux 全体の見た目」を断定するのは危険である。fc-match は「そのパターンに対する最終候補」を返す道具であり、実際のアプリケーションではフォールバックや描画条件がさらに加わるからである。逆に言えば、この 5 つを役割分担どおりに読めるようになると、Linux のフォント問題はかなり切り分けやすくなる。


8. 日本語環境ではなぜフォント混在が起きるのか

日本語環境でよく起きるのが、「設定したフォントと実際に見えるフォントが一致しない」という感覚である。しかし、これは異常ではなく通常動作であることが多い。たとえば、欧文用のフォントが日本語グリフを十分に持っていなければ、日本語部分だけ別のフォントへ落ちる。fontconfig は言語条件やグリフ収録状況を見て最適な候補を返すため、lang=ja を付けた問い合わせと付けない問い合わせで結果が変わることは普通に起きる [1][2][3]。そしてその後段では、layout 側の fallback がさらに効くため、実表示では複数のファミリーが混在し得る。

見かけ上の設定 実際に起きること 結果
欧文フォントだけを指定する 和文は別フォントへフォールバックする 一見 1 つに見えて実際は混成になる
monospace を指定する 英数字は等幅でも和文は別系統になることがある 列幅や印象が揃わない場合がある
ja 文脈で問い合わせる 日本語を持つ候補が強く優先される 通常問い合わせと返り値が変わることがある

したがって、Linux で欧文用と和文用を分けて考えるべきだという前回記事の結論は、好みの話ではなく、fontconfig と layout の通常動作からも裏づけられる。1 つのフォント名で全部を支配できると考えるより、最初から「混在は起きる」と理解して設計した方が、見た目も運用も安定する。


9. CUI ではどこまで関係し、どこから関係しないのか

ここは実務上とても重要である。純粋な Linux コンソールや SSH の文字表示では、サーバー側の fontconfig は本質ではないことが多い。setfont のマニュアルでは、setfont はフォントファイルを読み込み、EGA/VGA character generator にロードすると説明されている [19]。また console_codes のマニュアルでは、Linux console が UTF-8 モードなら入力バイト列を Unicode code に組み立て、それ以外では現在の mapping table で Unicode に変換すると説明されている [20]。つまり、Linux コンソールは fontconfig ではなく console font と console driver の世界で動いている。

環境 fontconfig の影響 補足
Linux コンソール ほぼ使わない console font の世界で動く
SSH サーバー側は本質でない 表示はクライアント側端末が担う
tmux / screen 本質ではない 端末エミュレータ側の描画に従う
GUI ターミナル 影響する 端末アプリ自体が GUI だからである
PDF / 画像生成 影響する 生成側で実際にフォント選択と描画が起きる

この違いを理解していないと、CUI のみのサーバーで local.conf を細かくいじったり、逆に PDF 生成サーバーなのに「どうせ CUI だから関係ない」と判断したりしてしまう。重要なのは、「この処理で本当に fontconfig と FreeType が呼ばれているか」を見極めることである。


10. ヒンティングは「なし」でもよいのか

Linux のフォント設定では、昔はヒンティングを強めにする感覚が広く共有されていた。しかし現在は、常に強が正解というわけではない。FreeType の driver properties 文書を踏まえると、ヒンティングはアウトラインをピクセル格子へどれだけ強く合わせるかの問題であり、none、slight、medium、full の違いは、字形忠実性と視認性のトレードオフとして理解するのが適切である [13]。高 DPI 環境では hintnone でも十分自然に見えることがあり、逆に低 DPI や小さい文字を多用する環境では hintslight の方が安定することがある。

設定 向いている場面 備考
none 高 DPI、字形忠実性重視 小サイズでは甘く見えることがある
slight 一般的なデスクトップ 現在もっとも無難
medium 小サイズ重視 やや硬く見えることがある
full 低 DPI や昔の環境 現在は常用前提ではない

したがって、「ヒンティングなしは間違いか」という問いに対する答えは、そうではない、である。重要なのは、fontconfig の alias や prefer をいじる話と、FreeType の描画調整をいじる話を混同しないことだ。前者は誰を使うかであり、後者はどう見せるかである。この区別がついていれば、「ヒンティングを none にしたのに fc-match が変わらない」といった現象も自然に説明できる。


11. 実務上の落としどころ

ここまでを踏まえると、Linux のフォント設定で最も安定する考え方は、デフォルトを大きく崩しすぎず、必要なところだけを明示する、という方針である。通常用途では、使いたいフォントをアプリケーション側または最小限の設定で指定し、描画品質はまずデフォルトを基準に観察した方がよい。generic family の全面上書きを local.conf で行うと、思わぬ範囲へ影響が広がることがある。逆に、生成サーバー、CI、PDF 出力、画像生成のように再現性が重要な用途では、fontconfig 設定とフォントパッケージを明示的に固定する価値がある [1][2][18]

やりたいこと 主に触る場所 触りすぎない方がよい場所
使うフォントを決めたい アプリ設定、必要最小限の fontconfig FreeType の細部を最初から全部触らない
見た目を少し整えたい ヒンティング、アンチエイリアス generic family の全面上書き
日本語優先を確認したい fc-match の ja 条件つき確認 fc-match の 1 行だけで全体を断定しない
CUI の崩れを直したい 端末側設定や文字コード側の確認 サーバー側 fontconfig のみを過信しない

最も重要なのは、「全部を一度に触らない」ことである。fontconfig の設定、layout の fallback、FreeType の描画品質は、それぞれ独立した変数である。1 つずつ動かして結果を見る方が、最終的には最短距離になる。前回が「何を選ぶか」の記事だったとすれば、今回は「その選択がどこに効くのか」を整理する記事である。この 2 本を合わせることで、候補比較と構造理解がようやくつながる。


参考文献

  1. Fontconfig User Guide. https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
  2. Fontconfig Developers Reference. https://fontconfig.pages.freedesktop.org/fontconfig/fontconfig-devel/
  3. Fontconfig Functional Overview. https://www.freedesktop.org/software/fontconfig/fontconfig-devel/x19.html
  4. What is HarfBuzz? https://harfbuzz.github.io/what-is-harfbuzz.html
  5. hb-shape: HarfBuzz Manual. https://harfbuzz.github.io/harfbuzz-hb-shape.html
  6. Pango: Text Attributes and Markup. https://docs.gtk.org/Pango/pango_markup.html
  7. Pango.Layout. https://docs.gtk.org/Pango/class.Layout.html
  8. Pango.attr_fallback_new. https://docs.gtk.org/Pango/func.attr_fallback_new.html
  9. FreeType Project. https://freetype.org/
  10. FreeType Glyph Conventions. https://freetype.org/freetype2/docs/glyphs/index.html
  11. FreeType Tutorial / I. https://freetype.org/freetype2/docs/tutorial/step1.html
  12. FreeType API Reference: LCD Rendering. https://freetype.org/freetype2/docs/reference/ft2-lcd_rendering.html
  13. FreeType API Reference: Driver Properties. https://freetype.org/freetype2/docs/reference/ft2-properties.html
  14. fc-match(1). https://manpages.debian.org/experimental/fontconfig/fc-match.1.en.html
  15. fc-list(1). https://manpages.debian.org/testing/fontconfig/fc-list.1.en.html
  16. fc-pattern(1). https://manpages.debian.org/testing/fontconfig/fc-pattern.1.en.html
  17. fc-conflist(1). https://manpages.debian.org/testing/fontconfig/fc-conflist.1.en.html
  18. fc-cache(1). https://manpages.debian.org/unstable/fontconfig/fc-cache.1.en.html
  19. setfont(8). https://man7.org/linux/man-pages/man8/setfont.8.html
  20. console_codes(4). https://man7.org/linux/man-pages/man4/console_codes.4.html