Fontconfig (7日目) - DPI
========================
.. post:: Dec 07, 2022
:tags: fontconfig
一週間経って未だに設定らしい設定をしていないという事実……!!
さて、今回も普段であれば設定なんかしないDPIの話です。
DPIはもちろんDots Per Inchで、Dotsの代わりにPixelsでPPIと呼ばれることもあります。
1インチが何ドットに対応するかという比率ですね。
ディスプレイのDPI
-----------------
ディスプレイ毎の値は ``xdpyinfo`` で確認したり ``xrandr`` や *~/.Xresources* で設定できたりします。
(複数のディスプレイを接続した環境でディスプレイ毎に異なるDPIを設定するのは難しいらしいです。
やったことはないです。)
高解像度ディスプレイ等ではない普通のディスプレイのDPIは96です。
もちろん物理的なDPIはばらつきますが、大概のディスプレイは問い合わせに対して誤差を無視して96と返してきますのでだいたい96です。 ::
$ xdpyinfo | grep resolution
resolution: 96x96 dots per inch
``xdpyinfo`` では物理サイズも取得できますので本当の物理的なDPIを計算して設定してやることもできますが、96を仮定しているアプリケーションも多いでしょうから誤差程度であれば96のままにしておいたほうが良いです。
高解像度ディスプレイですとスケーリング設定にもよりますがもっと大きい値になるはずです。
フォントサイズ計算時のDPI
-------------------------
アプリケーションにもよりますがフォントのサイズ設定ではピクセルサイズ(単位px)で直接設定するよりもポイント(単位pt)という単位で設定することが多いです。
.. compound::
ポイントとピクセルサイズの関係は
.. math:: ピクセルサイズ_{px} = \frac{DPIの値_{px/inch} \times ポイントサイズ_{pt}}{72_{pt/inch}}
となっています。
この72という定数は昔のMacではDPIが72のディスプレイが標準だったことに由来するそうです。
今ではMacはRetinaで300を超えるのが当たり前ですし、そうでなくても普通のディスプレイのDPIは96です。
それでもMacではフォントサイズを計算するときは今でもDPIは72です。
72ですとポイントサイズとピクセルサイズが一致して何かと楽です。
WindowsやLinux(といいますかX Window)ですと特に設定等しなければフォントサイズを計算するDPIも96です。
DPIが96ですとポイント単位でフォントサイズを設定するとき困るんですよね。
9ptは12px、12ptは16pxになりますが、実際に使われているフォントサイズはその間の10ptや11ptが多いです。
そして切り上げ切り捨てまたは小数のままで計算してくれる等の動作がアプリケーションごとにまちまちだったりして同じサイズを設定しても揃わなかったり。
全角/半角のある日本語ではフォントサイズはなるべく偶数ピクセルにしたいところですが14pxは10.5ptになりますし。
そもそも小数を指定できないアプリケーションもありますから……。
同じフォントを指定しているのに高さが違って見えるぞという時に疑うべき原因のひとつです。
(ここでは述べませんが悲しいことに疑うべき原因は他にもあり簡単には特定できません。)
で、昨日KDEのフォント設定画面で72に設定したDPIは(ディスプレイのDPIではなく)フォントのサイズ計算だけの仮想的なDPIです。
ディスプレイ自体のDPIは96のままです。
この値は *~/.config/kcmfonts* に記録されていますのでKDEライブラリを使っていないアプリケーションには反映されない……と思ったら、VLC(QtアプリだがKDEアプリではない)やFirefox(GTK3)にもちゃんと反映されてるっぽいですね……?
ま、まあ、どうにかこうにかして上手く値を伝えてくれているのでしょう。
Fontconfigの把握しているDPI
---------------------------
Fontconfigも ``Ubuntu-12`` または ``Ubuntu:size=12`` みたいにフォントパターンにサイズを含めることができます。
これによってサイズによってヒンティングの方法を変えたりもできるわけですが、この時の ``size`` 属性の単位はポイントです。
直接ピクセルサイズで指定したい時は ``pixelsize`` 属性を指定します。
ですのでFontconfigにもDPIの値を把握してもらわないといけません。
把握してくれてるのでしょうか? ::
$ fc-match -v ':size=12' | grep 'size\|dpi'
Pattern has 41 elts (size 48)
size: 12(f)(s)
pixelsize: 14(i)(w)
dpi: 75(f)(s)
75……!?
しかも 14/12×72=84 ですから計算もおかしいです。
.. container:: inserted
計算がおかしかったのはフォントファミリー名を指定しなかったため ``LANG`` からNoto Sans CJK JPが選ばれ、かつ */etc/fonts/conf.d/56-neon-noto.conf* でCJKの最小 ``pixelsize`` が14とされていたからのようです。
他のフォントファミリー名を指定しますと75で計算された結果がそのまま出てきます。 ::
$ fc-match -v 'Noto Sans CJK JP:size=12' | grep size
Pattern has 41 elts (size 48)
size: 12(f)(s)
pixelsize: 14(i)(w)
$ fc-match -v 'Ubuntu:size=12' | grep size
Pattern has 41 elts (size 48)
size: 12(f)(s)
pixelsize: 12.5(f)(s)
``size`` ではなく ``pixelsize`` を指定した場合はどうでしょうか? ::
$ fc-match -v ':pixelsize=14' | grep 'size\|dpi'
Pattern has 39 elts (size 48)
size: 13.44(f)(s)
pixelsize: 14(i)(w)
今度は ``dpi`` が ``grep`` の結果に出てこなくなりました。
14/13.44×72=75 ですので75で計算されてはいるようです。
ソースコードをgrepしたところ *src/fcdefault.c* にそれらしい75を見つけました。
この75はFontconfigのデフォルトのようです。
というわけでディスプレイのDPIの96も、フォントサイズ計算時のDPIの72も、Fontconfigには全く伝わっていないのでした。
では困るか……というと、実際困りません。
環境変数 ``FC_DEBUG=1`` を設定して適当なアプリケーションを起動しますとFontconfigに要求されたパターン(変形後)とそれに一致したフォントをトレースできます。
で、大抵のアプリケーションでは ``size`` に中途半端な小数の値、 ``pixelsize`` の方に整数値が来ているのが確認できます。
つまり大抵のアプリケーションはフォントパターンとして ``pixelsize`` を直接指定しているわけです。
Firefoxなんかは丁寧に ``dpi`` も指定してきてるようです。
ですのでFontconfigの設定ファイルを書くときも ``pixelsize`` だけを相手にしていれば良さそうです。
というわけでDPIで何かすることもなく今日もFontconfigの設定は行わないのでした。
sizeだけ渡された場合に対応する
++++++++++++++++++++++++++++++
偏執狂っぽく、それでも ``size`` だけ渡してくるアプリケーションに対応したいんだ!という方(私のような人間)のために一応書いてみました。
.. code-block:: xml
:caption: ~/.config/fontconfig/conf.d/10-dpi.conf
72
false
0
true
0
false
size
dpi
72
まず ``dpi`` 属性を、ここでは72にします。
そして ``size`` が0以上、かつ ``pixelsize`` が0以上ではない(指定されていない)場合に ``pixelsize`` を計算してやります。
(属性が存在していない場合は ```` は必ず失敗するようですので ``PIXELSIZE_EXISTS`` を導入しています。)
上手く動いているようには見えますが、警告も出てますね。 ::
$ fc-match -v 'ubuntu-30' | grep 'size\|dpi\|PIXELSIZE_EXISTS'
Fontconfig warning: "~/.config/fontconfig/conf.d/10-dpi.conf", line 37: saw range, expected number
Pattern has 42 elts (size 48)
size: 30(f)(s)
pixelsize: 30(i)(w)
dpi: 72(i)(w)
PIXELSIZE_EXISTS: False(w)
この警告は ``size`` の型は本当はDoubleではなくDoubleの範囲で、Doubleが必要なところでは変換(*src/fcrange.c* の ``FcRangePromote``)されているからのようです。
マニュアルには勿論 ``size`` はDoubleとしか書いてません。
このやろう。
まあ、どの道こんなことする必要はありませんので……。