Linuxのフォント

このテキストについて

このテキストは次の授業で使用されている標準化された教材です。

実際の授業では解説が加わる上に、生徒様に合わせて教材は制作・調整されるため、 このテキストよって得られる理解がMimir Yokohamaの授業のすべてではないことを予めご理解ください。

Linuxとフォント

Linuxでのフォントの扱いはX Window Systemによって提供されています。

古のUnix

かつてUnixでは主に点描であるビットマップフォントを扱っていました。

早い時期に線で書かれたアウトラインフォントが採用されましたが、 基本的にはビットマップを中心とした考え方はかわらず、 「フォントサイズごとにビットマップフォントを指定する」という仕組みを採用していました。

PCFビットマップフォントはX Serverの実装であるX11によってサポートされているビットマップフォントです。 これは次のような論理名を持っていました。

-adobe-courier-bold-o-normal--14-100-100-100-m-90-iso8859-1

これは

となっています。

アウトラインフォントではないため、どう考えてもピクセル数とポイント数が釣り合いませんが、 そもそもUnixではビットマップフォントをスケーリングすることはできなかったため、 「14ピクセルで表示するときは14ピクセルのフォント、16ピクセルなら16ピクセルのフォント」が必要でした。

なお、画面上の点を示す“Pixel”という言葉は“Picture Element”または“Picture Cell”に由来すると言われています。

Unixでのアウトラインフォントは.pfaという拡張子でしたが、これはPostScriptフォントであり、 Windowsで採用されていたpfbフォントと同様のものです。 ただし、アウトラインフォントを使用する場合でも、ピクセル数ごとに指定する必要がありました。

表示するに当たっては必ずなんらかのフォントが指定されている必要があったのですが、例えば長く日本語対応のターミナルエミュレータとして親しまれてきたktermでのフォント指定においては

kterm -fn '-*-fixed-*--16-*' -fk '-*-fixed-*--16-*'

のようにして指定していました。この場合、16ピクセルの任意のフォントのうち、Xが列挙するフォントで最初に出現する ものを使用します。

フォントを要求するプログラムはそれぞれデフォルトで、あるいは設定ファイルによって要求すべきフォントが定められており、プログラムの起動時に常に指定する必要はありません。

この機能は“XCore”というフォントシステムです。

ちょっと昔のLinux

フォント周りに関しては乱立していた時代があります。

優勢だったのはクライアント/サーバーシステムをとるXftというフォント管理システムを利用することです。 XftのフォントサーバーはXfsといい、各プログラムはXfsにフォントを要求し、そうするとXfsがXftを利用してフォントを得る仕組みです。得てきたフォントはFreeTypeフォントレンダリングエンジンによって描画されます。

ただし、実際はこんなに単純な話ではありません。極めて複雑な仕組みでした。 Linuxのフォント設定は

の3つがあり、高機能な分、非常に専門的な情報を扱う必要がありました。

このような仕組みは無駄だと考えられ、X向けのグラフィックスライブラリであるcairoはXftを使用せず、直接FreeTypeでのレンダリングを行うようになりました。

このことから、「Xfsを使うものと使わないものがあり、Xfsを使うものはXfsのフォント設定が反映されるが使わないものには反映されない」という状況がありました。

現在のLinux

Xfsは廃止に向かっています。

FreeTypeはFreeType2となりました。 今や、GtkあるいはQtで書かれたソフトウェアを扱う限りはFreeType2のことだけを考えていれば良いでしょう。 Cairo, Pango, QtのいずれもWaylandに対応しており、その上でFreeType2もWaylandに対応しているため、 もし仮にX window systemが使われなくなる日がきたとしても問題ありません。

むしろ、「X window systemに依存しない、FreeType直接の取り扱い」というのはWaylandにつながる道だとも言えます。

ではその内容を見ていきましょう。

まず、Cairoとはクロスプラットフォームでデバイスに依存しないベクトルベースの2D描画ライブラリです。 Cairoを使うことでプログラムは様々な事情を気にせずベクトルベースのグラフィックスを描画できます。

PangoはGtk用のテキスト組版ライブラリです。 ここでいう組版は画面上の話であり、行列を持って文字を並べる、という機能があります。 Pangoには縦書き機能もあります。1

QtはPangoを利用しておらず、独自にテキストレイアウトを行っています。

さらに、FLOSSにおけるテキスト標準化の取り組みとしてHarfBuzz2があり、HarfBuzzの成果はPangoとQtの共通ライブラリとして採用され、現在はPango, Qtの共通ライブラリとして使用されています。

現在、描画やフォントに関することでX window systemの存在を気にする必要はありません。 気にする必要があるのはこのようなライブラリであり、 もしこれらのライブラリが変更されることがあれば劇的な違いとなって現れるでしょう。

しかし、PangoやCairoをユーザーが意識することはありません。 FreeTypeですらあまり気にする必要はなく、FreeTypeのフォント設定ライブラリであるFontConfigだけを気にすることになるでしょう。

ここまで言葉のおさらいです。

名前 現用 機能
FreeType yes フォントレンダリングエンジン(大抵は2のこと)
FreeType2 yes フォントレンダリングエンジンの新しいバージョン
Xft/Xfs no FreeTypeを使ったフォントサーバー
XCore no X window system本来のフォント機能
Cairo yes グラフィックス描画用のライブラリ
Pango yes Gtkでテキストをレイアウトするためのライブラリ
Qt yes GUIツールキット。テキストレイアウト機能もある
HarfBuzz yes テキストレイアウトエンジン。PangoとQtで使われている
FontConfig yes フォント設定用ライブラリ

PangoとHarfBuzzの関係ですが、QtとPangoはHarfBuzzをベースにしつつ、 それぞれが従来もっていてHarfBuzzにない機能を追加したものになっており、 例えばPangoでは縦書きができますが、HarfBuzzではできません。 そのため、Pango, QtともにHarfBuzzを使っていますが、縦書きが可能なのはPangoを使用するGtkだけです。

他環境とLinuxのフォントの美しさについて

ここではフォントレンダリングエンジンの話をしますが、フォントレンダリングエンジンと、フォントレンダラ、そしてフォントラスタライザという言葉は全て同じものを指しています。

レンダリングとは描画することをいいます。 ラスタライズとはベクターグラフィックスをラスター(点描)に変換することをいいます。

Linuxのフォント表示は美しいと言われています。 これはFreeType採用によってアンチエイリアス機能が採用されたためです。

アンチエイリアスとはなんでしょうか。 ディスプレイの特性上、表示されているものは点描であり、ピクセルよりも小さな単位で表示することができません。 そのため、アウトラインフォントを使ってもどうしてもギザギザに見えることとなり、特に小さなフォントではそれが目立ちます。 そこで本来描画すべき点の周囲に淡い色で描画を行い、視覚上「にじませる」ことでギザギザを目立たなくするものです。

美しいフォントレンダリングといえば、Core Textを採用するMac OS Xがあります。 アンチエイリアスが美しいとされ、現在においても表示の最高峰となっています。 ごく一時期リリースされていたWindows版SafariにはCore Textが搭載され、話題となりました。 Linuxで使われているFreeTypeは、Core Textに倣ったものです。

線数の多い和文においては可読性が求められる一方で、醜さも目立ちます。 アンチエイリアスのにじみによってアルファベットが解読不能になるのはよほど小さな文字であるときだけなのに対し、日本語であれば簡単に読めなくなります。 ところが、アンチエイリアスを有効にし、求めてきたのは日本人です。

一方で実用主義者はこれに賛同しません。ビットマップフォントはそもそもが点描であり、サイズも固定です。ビットマップフォントが適切なサイズで表示される限りはそもそも変換する必要がなく、そのサイズで可読性が確保されるように制作されており、どれほど小さい文字であっても(美しさはさておき)判別可能となっています。

そのため、実用主義者はディスプレイでの日常的サイズではビットマップフォントを使えば良いし、大きな文字を表示させるときにはアウトラインフォントを使えば良いのだ、と考えています。現在は高解像ディスプレイが普及し、ピクセルサイズで固定されたビットマップフォントは物理的に小さく表示されて読めないという問題がありますが、実用主義者は「物理的にピクセルが小さいのであればアンチエイリアスなどいらない」と主張しています。3

GDIというフォントレンダラを使うWindowsのフォントレンダリングはかつてはひどいの一言でした。

ClearTypeはWindowsの新しいフォントレンダリングエンジンです。 WindowsはWindows 7でDirectWriteというテキストレンダリングAPIを用意しました。 これは、レンダリング自体はDirectXによって行うのですが、これがClearTypeが機能するための条件でした。

GDI++というライブラリがあり、これはフォントにこだわるWindowsユーザーの間で流行したものです。 GDI++はWindowsのフォントレンダリングエンジンを置き換え、割り込んでWindowsの標準レンダラーに変わって描画します。

GDI++やMacTypeのようなWindows向けのサードパーティのフォントレンダリングエンジンはWindows 10で排除されましたが、Windowsのフォントレンダリングは、Windows 7でClearType採用にも関わらずひどいままだったものの、これについては改善の方向にあります。

「Windowsのフォントレンダリングが汚い」ということはMicrosoftも認めており、 しかし一部のソフトウェアはWindowsに頼らずフォントレンダリングをしていることから(例えば一時期のSafariや、現在のSlaipnirブラウザなど)システムレベルでのフォントレンダリングの変更は古いアプリケーションで支障をきたす可能性があるために変更できない、ということです。

ただし、実際はMicrosoft内部にフォントの改善に否定的な勢力がいることが大きな理由であることは間違いありません。 実際のところ、ウェブブラウザなど利用頻度の高いソフトウェアが独自にフォントレンダリングを行っており、新しいプログラム、特に「Windowsアプリ」として提供されているプログラムは表示が改善していることから改善の見通しに乏しい状況でもあります。

そもそもWindowsのフォントが汚かったのは、記帳なコンピュータリソースをフォント表示に割いてしまわないためで、意図的なものでした。 現在は比較的新しい環境で作られたプログラムはClearTypeが美しく機能するようになっている。この修正は2016年に行われ、さらに現在はアンチエイリアスの調整もできるようになっている。

MacTypeはWindows向けに移植されたFreeTypeを利用するプログラムです。 MacTypeは開発凍結ののち、現在は再始動していますが、Windows 10があまりシステムに関わるプログラムを受け入れないため、難しいところです。

アンチエイリアスの問題は、そもそも「物理的に点が小さくなれば問題自体が縮小する」というものであり、高解像ディスプレイが普及しつつある現状においては目立たなくなりつつあるのも事実です。 しかし、300ppiを越える高解像度液晶においてさえ、依然としてアンチエイリアスは有効です。

Inifnality

InfinalityはFreeTypeにおけるレンダリングを改善するためのパッチです。 Mac OS Xのレンダリングをエミュレーションし、美しい見た目を得ることができます。

フォント表示を改善するテクニックとして「Infinalityパッチの当たったFreeTypeを使う」という手法は長く親しまれてきましたが、 FreeType 2.7においてInfinality相当の機能が取り込みとなりました。

これにより、Infinalityパッケージはobsolateとなり、 /etc/profile.d/freetype.shにおいて

truetype:interpreter-version=38

とすることでInfinality相当のサブピクセルレンダリングを行います。

FontConfig

FontConfigの役割

FontconfigはLinuxのフォント設定ライブラリです。

Fontconfigはフォントを使用するアプリケーションに対して「何のフォントを」「どのように表示すべきか」という情報を提供します。

この大きな意味としてはフォントの物理名のほかに論理名を与えることができるということです。 代表的なものとして、以下の論理フォント名が使われています

これらはフォントの形状の名前であり、フォントの名前ではありません。 例えばmonospaceは等幅フォントを意味しており、monospaceという名前のフォントが存在しているわけではないのです。 しかし、アプリケーションは予めどのフォントを選択すべきかという情報を与えられなくても適切に表示できるよう、このような論理フォント名によるアクセスを行います。

私の環境でmonospaceフォントは次のように選択されています。

$ fc-match monospace
VL-Gothic-Regular.ttf: "VL ゴシック" "regular"

つまり、アプリケーションがmonospaceを要求すると、FontConfigはVL-Gothic-Regular.ttfファイルに定義されているVL ゴシックを表示するように指示するわけです。

また、Yahoo Japanのページを見ると、表示フォントとして次のような指定がなされています。

MS PGothicはMicrosoft WindowsにバンドルされているMS Pゴシックフォント、 Hitagino Kaku Gothic ProN及びOsakaはMacにバンドルされているフォントです。

最終的にはsans-serifが指定されていますから、ウェブブラウザは当該論理名のフォントがなければ最終的にsans-serifを使用します。 では、sans-serifの指定がなかったらどうなるのでしょうか?4 この場合、ウェブブラウザはFirefoxでもChromeでもウェブブラウザが当該要素におけるデフォルトのフォントを要求します。 これは通常の段落内のフォントであるため、Firefoxはserifを、Chromeはsans-serifを要求します。

このフォント選択においては、ウェブブラウザは論理名があるかどうかだけを確認しており、その論理名のフォントを要求していません。 実際にFontConfigに対して要求するのはsans-serifあるいはserifということになります。 では、アプリケーションがMS PGothicを要求した場合はどうなるのでしょう。

その場合、FontConfigは「それっぽいフォントを適当に見繕って」返します。 明示的に指定されている場合は、sans-serif, serif, monospaceのいずれかのフォントを返すことがほとんどです。 ただし、Courier Newを要求したときに、当該フォントがなければCourierを返す、といった挙動を示す場合もあります。

もちろん、FontConfigの設定によって、特定の論理名に対して特定のフォントを返すようにすることもできます。

FontConfigがどのようなフォントを選択するかについては、fc-matchコマンドで確認することができます。

$ fc-match Courier
NimbusMonoPS-Regular.otf: "Nimbus Mono PS" "Regular"

また、fc-listコマンドによってFontConfigが認識しているフォントを列挙することもできます。

FontConfigの設定

FontConfigの設定は多くの場合、/etc/fonts/conf.availディレクトリにインストールされています。 有効な設定は/etc/fonts/conf.dです。

/etc/font/conf.d内に/etc/font/conf.availディレクトリ内のファイルへのシンボリックリンクを張ることでその設定を有効化することができます。 また、/etc/fonts/conf.dからリンクを削除することで設定を無効化できます。

日本語の場合は65-nonlatin.confを有効にするほうが良いでしょう。 また、ビットマップフォントを嫌うのなら70-yes-bitmaps.confを無効化し、70-no-bitmaps.confを有効化します。

ユーザーごとの設定ファイルは$XDG_CONFIG_HOME/fontconfig/fonts.confにあります。 設定ファイルはXML形式で、

という形式になっています。例えばMS PGothicUme P Gothic S5に置き換えるには次のようにします。

OsakaやMS PGothicを要求されるとぐちゃぐちゃなフォントが表示される場合があるため、置き換えておいたほうが良いかもしれません。 次の例は実際に私が使用しているfonts.confファイルです。

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match target="pattern">
    <edit name="dpi" mode="assign"><double>103</double></edit>
  </match>
  <match target="font">
    <edit name="hinting" mode="assign">
      <bool>true</bool>
    </edit>
  </match>
  <match target="font">
    <edit name="autohint" mode="assign">
      <bool>true</bool>
    </edit>
  </match>
  <match target="font">
    <edit name="hintstyle" mode="assign">
      <const>hintfull</const>
    </edit>
  </match>
  <match target="font">
    <edit name="rgba" mode="assign">
      <const>rgb</const>
    </edit>
  </match>
 <match target="font">
  <edit name="lcdfilter" mode="assign">
   <const>lcddefault</const>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>serif</string>
  </test>
  <edit binding="strong" name="family" mode="assign">
   <string>Source Han Serif JP</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>sans-serif</string>
  </test>
  <edit binding="strong" name="family" mode="assign">
   <string>VL PGothic</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>sans</string>
  </test>
  <edit binding="strong" name="family" mode="assign">
   <string>VL PGothic</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>monospace</string>
  </test>
  <edit binding="strong" name="family" mode="assign">
   <string>VL Gothic</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="all" compare="not_eq" name="family">
   <string>sans-serif</string>
  </test>
  <test qual="all" compare="not_eq" name="family">
   <string>serif</string>
  </test>
  <test qual="all" compare="not_eq" name="family">
   <string>monospace</string>
  </test>
  <edit name="family" mode="append_last">
   <string>VL Gothic</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>MS PGothic</string>
  </test>
  <edit name="family" mode="assign" binding="same">
   <string>Ume P Gothic S5</string>
  </edit>
 </match>

 <match target="pattern">
  <test qual="any" name="family">
   <string>MS Pゴシック</string>
  </test>
  <edit name="family" mode="assign" binding="same">
   <string>Ume P Gothic S5</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>Osaka</string>
  </test>
  <edit name="family" mode="assign" binding="same">
   <string>Ume P Gothic S5</string>
  </edit>
 </match>
 <match target="pattern">
  <test qual="any" name="family">
   <string>Helvetica</string>
  </test>
  <edit name="family" mode="assign" binding="same">
   <string>Ume Gothic O5</string>
  </edit>
 </match>
 <dir>~/.fonts</dir>
</fontconfig>

詳しい情報はArchWikiを見ると良いでしょう。

フォントのインストール

システムのフォントは/usr/share/fonts以下にインストールされます。 再帰的に検索されるため、ディレクトリを作っても構いません。

ユーザーのフォントディレクトリは$HOME/.fontsです。 システムにない独自のフォントを使用する場合はこのディレクトリに放り込むことになります。

Javaにおけるフォントレンダリング

Javaの標準ツールキットであるAwtが独自にフォントレンダリングを行うため、 アンチエイリアス設定が反映されません。

Awtでアンチエイリアスを有効にするにはJavaのオプションとして

-Dawt.useSystemAAFontSettings=lcd -Dawt.useSystemAAFontSet

tings=on -Dswing.aatext=true -Dsun.java2d.opengl=true

を渡します。 これを環境変数$_JAVA_OPTIONS_に設定するのが一般的です。

DPI

1inchあたり(平方インチではなく)にいくつのドットがあるか、ということを指してDPI(Dot Per Inch)あるいはPPI(Pixel Per Inch)といいます。

長年、Macでは72dpi, Windowsでは96dpiという暗黙の了解がありましたが、現在は実際はそのような値からはかけ離れています。

一般的なモニタのおおよそのDPIは次のようになっています。5

ディスプレイサイズ FWXGA (1366x768) FullHD (1920x1080) WQHD (2560x1440) 4k (3840x2160)
4.7 333 469
5 313 441 587 881
5.5 285 401 534 801
6 261 367 490 734
10.1 155 218 291 436
12.1 130 182 243 364
13.3 118 166 221 331
14.0 112 157 210 315
15.6 100 141 188 282
21.5 73 102 137 205
24 65 92 122 184
27 58 82 109 163
31.5 50 70 93 140

Linuxシステムでは設定されていなければ96DPIであるとみなします。 フォントサイズ「ポイント」は物理的な大きさであり、いくつ点があればその物理的な大きさに達するかはDPIを元に計算します。

1ポイントは0.0138889インチです。 10ポイントは0.138インチ、1インチで96の点を描くのですから、10ポイントの領域には13.248個の点、ピクセルは半端な数は書けないので13個のピクセルを描くことになります。 この計算によって、DPIが設定されていない限り、物理的にどのようなモニターを使用していたとしても13ピクセルを描画します。

さて、あなたがDell XPS13の4kモデルを使用していたとします。DPIは331です。 「10ポイント」と思って描いた13ピクセルは、実際は0.04インチまで縮小してしまいます。 0.04インチとは2.88ポイントです。 10ポイントの文字ならば小さいものの読める文字ですが、2.88ポイントは虫眼鏡が必要かもしれません。 これは、誤ったDPI設定の結果「10ポイント」と指定して描画した文字は2.88ポイントで描画されるということです。

これを修正するために、最近のデスクトップ環境ではフォントのDPIを設定することができるようになっています。 これは、FontConfigの設定に反映する形で行います。

しかし、実のところ問題は決して文字だけではありません。 実際のDPIが設定されたDPIの倍になれば、アイコンもボタンもその面積は1/4になるということです。 これはとても不便です。120DPI程度までであればそれほど苦労しませんが、140DPIにもなると「明らかに小さい」と感じます。

DPIの設定はX.Orgも保有しています。 X.OrgサーバーはDDC(VESA Display Data Channel)によって正しい画面サイズを自動検出しますが、できていない場合も多々あります。

$ xdpyinfo | grep -B2 resolution

ひとつの方法として、X.Orgの設定ファイルとしてディスプレイのサイズを記述するという方法があります。

あるいは、Xrandrツールを使用できる環境であれば、これを使う方法もあります。 (永続化するためにはXProfileとして記載します。)

$ xrandr --dpi 331

GtkやQtなどにおいて物理的な寸法で記述されているパーツはこのDPI変更によって自動的にスケールされます。 Qtにおいてはこれだけで問題を解決することができます。

Gtk3は$GDK_SCALE及び$GDK_DPI_SCALE環境変数で調整できますが、あまりうまくいきません。 しかも、UIコンポーネントを設定する$GDK_SCALEは2倍、3倍という単位でしか調節できません。

さらにデスクトップ環境によってはFontConfigで明示的にDPIを指定する必要がある場合もあります。

このようなHiDPIサポートはWindowsよりMacより先にLinuxが行っているにも関わらず、あまり快適に動作しません。 最も良い解決方法は(GTKアプリケーションを使う場合であっても)KDE Plasmaを使用するという方法です。

KDE Plasmaを使用する場合は次の方法でほとんどの環境で良い動作を得られます。

ただし、この場合でもlightdm greeterのサイズは別問題となります。 greeterの設定を変更してこの問題を改善できます。 gtk-greeterを使用している場合は、

/etc/lightdm/lightdm-gtk-greeter.conf

xft-dpi=331

のように記述を追加します。

未来

HarfBuzzはHarfBuzz-NGとして再構築を行っています。

X window systemを廃止してWaylandに移行する動きがあります。

Waylandはディスプレイへの描画を管理するものであり、ウィンドウの位置や重なりを管理し、適切に重って表示されるようにします。 WaylandはX window systemのようなものではなく、あくまでディスプレイ上での表示の管理しか行いません。 ウィンドウマネージャがグラフィックスハードウェアとのやりとりや、アプリケーションとのやりとりをする際にXを介する構造ではなく直接やりとりをするようになります。 キーボードなどの入力ハードウェアの管理もWaylandは行いません。

WaylandはX window systemのようなロスがなく、優れた応答性と低負荷が期待されています。 その仕組み自体はWindowsに近いものです。 しかし、大部分を外部に頼る仕組み上、入力処理やハードウェアリソースの管理においてライブラリが乱立してしまうと、 まともに機能せず、設定も散らばってしまう可能性があります。

また、X window systemの便利な機能は使えなくなります。


  1. Pangoという名前はギリシャ語で「全て」を表すPanに、日本語の「語(Go)」を組み合わせたものです。

  2. こちらはペルシャ語です。

  3. これはある程度正しい主張です。判別しやすさはアンチエイリアスがない方が良好であり、300ppiあるようなディスプレイではアンチエイリアスはないほうが読みやすく疲れにくいという主張があります。一方、300ppiあってもアンチエイリアスは有効であるほうが美しく感じるということも分かっています。

  4. 日本語のページであるにも関わらず、欧文フォントであるArialを指定しているのは、非常に「悪い」ことだと考えられます。

  5. “網膜の解像度は300DPIなので、300DPIに達するとドットを視認できなくなる”という説があります。しかし、実際はディスプレイと目の距離もありますし、そうはいきません。しかし300DPI前後に到達すると非常になめらかに見えるのは事実です。

Fullsized Image