読者です 読者をやめる 読者になる 読者になる

rxvt-unicodeのフォントレンダリングを改善する

問題

最近はNoto Fontという高品質な日本語フォントが無料で手に入るいい時代になりました。 しかし、rxvt-unicodeでこのフォントを指定すると、とても残念な表示になってしまいます。

フォントの設定は以下のものです:

Rxvt.font: \
  xft:Consolas:pixelsize=13, \
  xft:Noto Sans CJK JP:pixelsize=13:style=DemiLight
Rxvt.lineSpace: 6

f:id:emonkak:20161209173120p:plain

サンプルテキストにはロレム・イプサムを利用させて頂きました。

これはrxvt-unicodeのバージョン9.22で表示した画像ですが、いつくかの表示上の問題があります。

  1. 欧文の文字間隔が異常に広い
  2. lineSpaceで指定した分の数値が行の高さとして単純に足されるので文字が上によってしまう
  3. アンダーラインの表示がベースライン上に引かれるのでベースラインの下にはみ出す 'g' や 'y' が読みづらい(これは好み)
  4. 日本語フォントが著しく小さい

解決

1の文字間隔については既にパッチが出まわっていて、gentooのパッケージでは alt-fontwidth のuseオプション有効にすることで改善します。

f:id:emonkak:20161209173115p:plain

他の問題については1つづつ自分でパッチを書いて改善していきます。

まずはlineSpaceの問題についてですが、これ単純に文字の描画位置を調整するだけでいいので簡単です:

diff --git a/src/rxvtfont.C b/src/rxvtfont.C
index 1914539..20a77af 100644
--- a/src/rxvtfont.C
+++ b/src/rxvtfont.C
@@ -1395,7 +1395,7 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,

           ep->glyph = glyph;
           ep->x = x_ + (cwidth - extents.xOff >> 1);
-          ep->y = y_ + ascent;
+          ep->y = y_ + ascent + (term->lineSpace >> 1);

           if (extents.xOff == 0)
             ep->x = x_ + cwidth;

次のアンダーラインの問題も同様です:

diff --git a/src/screen.C b/src/screen.C
index 9eb375a..7b3cdbf 100644
--- a/src/screen.C
+++ b/src/screen.C
@@ -2432,8 +2432,8 @@ rxvt_term::scr_refresh () NOTHROW
                 XSetForeground (dpy, gc, pix_colors[fore]);

               XDrawLine (dpy, vt, gc,
-                         xpixel, ypixel + font->ascent + 1,
-                         xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
+                         xpixel, ypixel + Height2Pixel (1) - 1,
+                         xpixel + Width2Pixel (count) - 1, ypixel + Height2Pixel (1) - 1);
             }
         }                     /* for (col....) */
     }                         /* for (row....) */

最後の日本語の文字が著しく小さくなる問題ですが、これは中々苦労しました。

条件としては組み合わせるフォントのアセンダ・ディセンダの値が大きく異なると発生します。 ですので、FontForgeなどでアセンダ・ディセンダを調整することで回避することもできます(やってました)。

しかし、これはあんまりなのでソース側で対応することにしました。 どうやら、欧文フォントに比べて日本語フォントのアセンダ・ディセンダが大きすぎるので、枠内に収まるように縮小しているようです。 これはもう余計なお世話という他ないので、この処理はばっさりと消してしまいます。

すると、文字が正しい大きさで描画されるようになりますが、欧文フォントと日本語フォントのベースラインが揃わずにガタガタになってしまいます。 これは以下のことをして調整しました:

  • フォントのアセンダ・ディセンダ、高さの値を xterm で使っている値と同じものを使うようにするパッチを適用
  • 描画する行の高さを取得し、アセンダ・ディセンダの比率から描画位置を決定する
  • ベースラインから描画位置を決定する

これで以下のように綺麗に描画されるようになりました:

f:id:emonkak:20161209173122p:plain

ここまでやったすべての修正を含むソースは以下にあります。

https://github.com/emonkak/rxvt-unicode/tree/9.22-patched

パッチは以下になります:

おわりに

rxvt-unicode はフォントレンダリングにはいくつか問題がありますが、他にいい代替がないので困りものでした。 今回これを綺麗に描画するように修正できて今はrxvt-unicodeに満足しています。

本当は本家が修正されるのがいいんでしょうけど、様々なディストリのパッケージが独自にパッチを適用しているのを見ると厳しいのかもしれません。

追記:描画位置の修正が不十分だったのでさらに修正しました