スポンサーリンク

2016年7月25日月曜日

Emacs のフォント設定を克服する

Emacs入門者が最初につまずくのがフォント設定ではないでしょうか。ネット上には古い情報、新しい情報が入り乱れ、玉石混淆としています。色々な解説ページを参考にして、とりあえず設定していましたが、どうも全体像が捉みきれないモヤモヤ感を抱えていました。それでも長年に渡り少しずつ理解が進んいくと、呆れるくらい奥深い Emacs のフォントシステムが見えてきます。ここで一度知識を整理しておくべく、ちょっと気合を入れて解説を書いてみたいと思います。


まず始めにいくつか押さえておくべきポイントがあります。
  • キャラクターセット
  • フォントの指定方法
  • フォントセット

キャラクターセット

文字集合、charset、character set と色々な呼び方があります。
Emacs は予め様々な文字集合規格のデータベースを持っています。この文字集合規格を Emacs Lisp で表現したものが charset です。実際どのような charset を持っているか確認するには M-x list-character-sets を実行します。
更に特定の文字集合の文字コード表(区点コード表)を表示するには M-x list-charset-chars を実行し、charset 名を入力します。
これらの二つのコマンド、名前が紛らわしいのですが、上の list-character-sets で表示されたリスト上で charset を選択しリターンを押しても文字コード表が表示されるので、こちらだけでも事足ります。また下の list-charaset-chars は、helm がインストールされていれば helm インタフェースで絞り込み選択ができるので、これはこれで便利です。
さて実際に M-x list-character-sets を実行してみると、日本語の規格では以下のものが見てとれます。

  • japanese-jisx0208
  • japanese-jisx0208-1978
  • japanese-jisx0212
  • japanese-jisx0213-1
  • japanese-jisx0213-2
  • japanese-jisx0213-a
  • japanese-jisx0213.2004-1
  • jisx0201
  • katakana-jisx0201
  • latin-jisx0201

例えば JIS X 0213 系の charset が4つもあります。この辺が Emacs の訳の分からないところ。まあ 2004 とあるのは 2004年改訂版だということは分かりますが、他の -1, -2, -a などとサフィックスが付いているものは何なのでしょう。

答えを言ってしまうと、これは ISO 2022 の Final Byte の違いを示しています。ISO 2022 は複数の文字コードを混在させる規格で、Final Byte は文字コードの終了を示すエスケープシーケンスに何を使うかの違いを示しています。文字コードを混在させない限りどれを使っても違いはありません。

フォントの指定方法

Emacsでフォントを指定するのにいくつかの方法があります。まずは font-spec 関数を使う方法。書式は以下の通りです。
(font-spec ARGS...)
ARGS の部分には key value ペアの形でフォントのプロパティを指定します。実例を見た方が分かり易いでしょう。
(font-spec :family "MeiryoKe_Console" :size 14)
(font-spec :family "ヒラギノ角ゴ ProN W3" :size 14 :weight 'bold :slant 'italic)
指定できるプロパティは多数ありますが、よく使うのは :family, :size, :weight, :slant くらいでしょう。

フォントを扱う関数の多くが font-spec オブジェクトを引数に取りますが、更にテキスト形式でフォントを指定するものもあります。テキスト形式といと、昔はあの悪名高きXLFD形式でした。こんな感じのやつです。
-outline-MeiryoKe_Console-normal-normal-normal-mono-*-*-*-*-c-*-jisx0208-sjis

とても人間が扱うような代物ではありません。Emacs23 以降 Fontconfig 形式で指定できるようになりました。Fontconfig 形式とは、簡単に説明すると、フォント名、サイズ、プロパティを以下のような形で指定します。
<family>-<size>:<propety1>=<value1>:<property2>=<value2>:…:…
これも例を見た方が早いでしょう。こんな感じです。
"MeiryoKe_Console-14"
"ヒラギノ角ゴ ProN W3-14:weight=bold:slant=italic"

font-spec にしろテキスト形式にしろ、これらは直接特定のフォントを指定するものではなく、フォントシステムに対しフォントを要求するときのリクエスト仕様として働きます。なのでリクエストした仕様が必ず100%満たされるとは限りません。例えば bold を指定しても、bold 体を持たないフォントであればノーマル体が返さるかもしれません。フォントシステムは要求された仕様に最も近いものを返してくれます。

フォントセット

文字集合(charset)とフォントの指定方法が分かったところで、次に出てくるのがフォントセットです。フォントセットは charset とフォントの対応表です。どの文字コードに対してどのフォントを使うかを指示します。Emacs のフォント設定の肝と言えるかもしれません。
フォントセットはいくつでも持つことができ、自分で作成することもできます。存在するフォントセットを表示するには M-x list-fontsets を実行します。Emacs のデフォルトの状態で実行すると以下の様になります。

Fontset: -*-*-*-*-*-*-*-*-*-*-*-*-fontset-default
Fontset: -*-courier new-normal-r-*-*-13-*-*-*-c-*-fontset-standard
Fontset: -outline-Courier New-normal-normal-normal-mono-20-*-*-*-c-*-fontset-startup

まだ XLFD 的な表現が残っています。末尾の fontset-**** の部分がフォントセット名です。なので以下の三つのフォントセットが存在することが分かります。
  •  fontset-default
  •  fontset-standard
  •  fontset-startup

これらは Emacs がデフォルトで用意しているフォントセットです。この中で重要なのは fontset-default です。これはデフォルトフォントセットと呼ばれ、他のフォントセットで指定の無かったものがここにフォールバックしてきます。例えば fontset-standard は中身は空っぽで、そのまま fontset-default にスルーしています。

フォントセットの中身がどうなっているかを見るには M-x describe-fontset を実行し、フォントセット名を入力します。これも helm インタフェースが使えます。

フォント設定

さて、ここまでのことが理解できるとようやくフォント設定に取りかかることができます。フォント設定にも様々な方法があります。例えば上で説明したデフォルトフォントセット(fontset-default)をカスタマイズしていく方法や、フェイスをカスタマイズしていく方法等があります。

ここでは新たなフォントセットを作成し、それをデフォルトフレームに適用する方法を説明します。これは比較的よく使われる方法です。手順は以下の通りです。
  1. ASCIIコードをベースにフォントセットを作成
  2. 作成したフォントセットの日本語部分に日本語フォントを指定する
  3. フォントセットをフレームに適用する

ASCIIコードをベースにフォントセットを作る

まず新規にフォントセットを作るには create-fontset-from-ascii-font 関数を使います。この関数は ASCII コードの範囲で使用するフォントを指定します。

書式は以下の通りです。
(create-fontset-from-ascii-font FONT &optional RESOLVED-FONT FONTSET-NAME)

第一引数で、英語フォントをテキスト表現で指定します。何故かここは font-spec オブジェクトは使えません。
オプションの第二引数 RESOLVED-FONT はワイルドカード等の曖昧さを含まないフォント名を指定します。省略するか nil を指定すると第一引数から適切に変換してくれます。なのでここは nil 以外使われているのを見たことがありません。

最後の引数はフォントセット名です。ここで指定した名前に "fontset-" プレフィックスを付けたものが正式なフォントセット名になります。省略すると自動的に名前を割り当ててくれます。

実際の設定例は以下の通りです。ここでは MeiryoKe_Console の10ポイント、フォントセット名が fontset-MeiryoKe_Console になるよう指定しています。
(create-fontset-from-ascii-font
 "MeiryoKe_Console-10"
 nil
 "MeiryoKe_Console")

この状態で M-x describe-fontset を実行して確認してみましょう。
Fontset: -outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-c-*-fontset-meiryoke_console
CHAR RANGE (CODE RANGE)
FONT NAME (REQUESTED and [OPENED])
C-@ .. DEL
-outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-*-*-*-*
---<fallback to the default fontset>---
C-@ .. DEL
-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1
-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-2
(以下略)

これを見ると C-@〜DEL (0x00〜0x7F) の範囲の文字コード、つまりASCIIコードが MeiryoKe_Console にリダイレクトしているのが分かります。それ以外の文字コードはデフォルトフォントセットにフォールバックします。

ASCII以外のフォントを指定する

ASCII コード部分のみ指定するフォントセットができたところで、次は日本語文字にもフォントを指定していきます。これには set-fontset-font 関数を使います。書式は以下の通りです。

(set-fontset-font NAME TARGET FONT-SPEC &optional FRAME ADD)
これも実際の使い方を見た方が分かり易いでしょう。

(set-fontset-font
 "fontset-MeiryoKe_Console"
 'unicode
 "MeiryoKe_Console-10"
 nil
 'append)

第一引数はフォントセット名を指定します。nil の場合、第4パラメータのフレームに設定されているフォントセットが対象になり、t の場合デフォルトフォントセットが対象になります。ここでは上で作成した "fontset-MeiryoKe_Console" を指定しています。

第二引数は charset や文字コード範囲を指定します。ここでは 'unicode を指定しています。

第三引数はフォントを指定します。font-spec オブジェクトやテキスト形式で指定します。

第四引数はフレーム指定。第1引数で nil を指定した場合にフレームを指定します。

最後の引数は重要です。ここを省略するとASCIIで指定した文字コード領域を上書きしてしまいます。'append を指定することにより、既存のテーブルの後ろに追加する形でフォントが追加されます。また 'prepend を指定すると既存テーブルの前に追加されます。ここで使っている例では、ASCII フォントも日本語フォントも同じものを使っているので、どれにしても違いはありませんが、英語と日本語で別々のフォントを使う場合はここを適切に設定しないと期待通りになりません。

ではこの状態で M-x describe-fontset を実行して確認してみましょう。

Fontset: -outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-c-*-fontset-meiryoke_console
CHAR RANGE (CODE RANGE)
    FONT NAME (REQUESTED and [OPENED])
C-@ .. DEL
    -outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-*-*-*-*
€ .. 􏿿 (#x80 .. #x10FFFF)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
 [-outline-MeiryoKe_Console-normal-normal-normal-mono-13-*-*-*-c-*-iso8859-1]
  ---<fallback to the default fontset>---
C-@ .. DEL
    -*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1
    -*-*-*-*-*-*-*-*-*-*-*-*-iso8859-2
    -*-*-*-*-*-*-*-*-*-*-*-*-iso8859-3
    -*-*-*-*-*-*-*-*-*-*-*-*-iso8859-4
(以下略)

表示できない文字があるので分かりにくいかもしれませんが、ASCII の C-@ .. DEL の後ろに、0x80 .. 0x10FFFF の文字コード領域が追加され、これが MyiryoKe_Console を使うよう指定されています。0x10FFFF は Emacs が表現できる Unicode(UTF-8) 文字コードの上限値です。
上の例では unicode という charset を使ってフォント設定をしていますが、文字コードの範囲を明示的に指定してフォントを指定することもできます。例えば以下の様に特定の文字コード範囲だけ別フォントを指定することができます。

(set-fontset-font
 "fontset-MeiryoKe_Console"
 '(#x3042 . #3046)
 "Ricty-12")

ここで指定する文字コードは Emacs の内部コードである UTF-8 を整数で表現したものです。まあこの例の場合、以下の様に文字リテラルで表現した方が分かり易いと思います。

(set-fontset-font
 "fontset-MeiryoKe_Console"
 '(?あ . ?う)
 "Ricty-12")

更に特定の一文字だけ別フォントを使いたい場合は以下の様に指定することもできます。

(set-fontset-font
 "fontset-MeiryoKe_Console"
 ?あ
 "MS Gothic-10")

どこまで柔軟にできているんだよと呆れてしまいます。

フォントセットをフレームに適用する

フォントセットが出来たら後はこれをフレームに適用するだけです。フレームの font プロパティにフォントセットを設定します。デフォルトフレームに設定すると、特にフレーム指定していない全てフレームに反映されます。

(add-to-list 'default-frame-alist '(font . "fontset-MeiryoKe_Console"))


日本語文字集合を指定すべきか?

上の例で 'unicode を使っているの疑問に思った人がいるかもしれません。MeiryoKe_Console はメイリオ由来のフォントなので、カバーしているのは JIS X 0213 (2004年改訂版)です。メイリオに限らず、最近の日本語フォントであればほぼ JIS X 0213 をカバーしている筈です。なので以下の様にするのが正しいのではないでしょうか?

(set-fontset-font
 "fontset-MeiryoKe_Console"
 'japanese-jisx0213.2004-1
 "MeiryoKe_Console-10"
 nil
 'append)

また少し古いフォントだと JIS X 0208 + JIS X 0201(半角カナ)などというのもよくありました。その場合以下のようになります。

(set-fontset-font
 "fontset-MyFontset"
 'japanese-jisx0208
 "MS Gothic-10"
 nil
 'append)

(set-fontset-font
 "fontset-MyFontset"
 'japanese-jisx0201
 "MS Gothic-10"
 nil
 'append)

これはこれでまったく正しい設定です。実際このように解説しているページもかなりあります。では Unicode で指定した場合と何が違うのでしょう?

Unicode は CJK 統合漢字という考え方を取り入れたため、JIS X 0213 や JIS X 0208 に含まれる文字は Unicode 文字空間に何の規則性もなく、バラバラに配置されています。Emacs の内部コードは Unicode なので、JIS X **** で指定された場合、Emacs は内部でそれを Unicode にマップしてやらなくてはなりません。

JIS X 0213 で set-fontset-font した場合のフォントセットを M-x describe-fontset で見てみましょう。

Fontset: -outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-c-*-fontset-meiryoke_console
CHAR RANGE (CODE RANGE)
    FONT NAME (REQUESTED and [OPENED])
C-@ .. DEL
    -outline-meiryoke_console-normal-normal-normal-mono-*-100-*-*-*-*-*-*
  .. ¤ (#xA0 .. #xA4)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
¦ .. ´ (#xA6 .. #xB4)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
¶ .. ĉ (#xB6 .. #x109)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
Č .. ď (#x10C .. #x10F)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
đ .. ē (#x111 .. #x113)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
Ę .. ĝ (#x118 .. #x11D)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
Ĥ .. ĥ (#x124 .. #x125)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
ħ (#x127)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*
Ī .. ī (#x12A .. #x12B)
    -*-MeiryoKe_Console-*-*-*-*-*-100-*-*-*-*-*-*

とても全部は載せられないので、冒頭の部分だけを載せてあります。冒頭に C-@ .. DEL の ASCII があるのは変わりませんが、それ以降は JIS X 0213 の1万字以上の文字を、Unicode上でフォントにマップするテーブルが延々と続きます。ある程度連続した文字はまとめてマップしていますが、一文字だけのエントリもあります。
先に見た Unicode で指定した場合の単純なテーブルと比べると大違いです。これだけ大きなテーブルになるとそれなりにメモリーも食うし、当然ルックアップにも時間がかかります。Emacs は一文字表示する毎に必ずこのテーブルを参照するので、トータルのオーバーヘッドは相当なものになります。

なので、日本語と英語しか使わないと割り切ってしまうのであれば、JIS X 0213 や JIS X 0208 を使わず、Unicode ベースで日本語フォントを指定してしまう方が絶対に効率が良くなります。ここはフォント設定する上で重要なポイントだと思うのですが、何故かこの点について言及している解説を見たことがありません。

英語フォントと日本語フォント

よく英語と日本語に別々のフォントを指定する例を見かけますが、これはあまりお勧めできません。確かに英語フォントにはプログラミング用に優れたフォントが沢山あり、英語の部分だけでもそのフォントを使ってみたくなる気持は分かります。

しかし英語と日本語を混在させた場合、妙にバランスが悪くなることがあります。例えば英語と日本語の幅が 1:2 になっていないと、プログラミング用フォントとしては致命的です。まあこれは調整可能であり、そうしている解説記事もよく見かけます。

しかしフォントには幅以外にも、ベースラインやアセンダ、ディセンダといった縦方向のパラメータもあります。これらが揃わないと高さがガタガタになったりします。ここまで調整している解説はまず見かけません。調整するにはフォントに関する相当詳しい知識が必要になります。

まあサイズ的なバンランスを調整(または無視)したとしても、そもそもデザイン的に統一感がありません。そこまで気にする人は少ないかもしれませんが、フォント環境をつきつめていくと、こういった所まで気になるものです。

なので最初からバランスのとれた日本語フォントで統一し、その分別の所で労力を使った方ががよっぽど生産的です。

まとめ

Emacs のフォント設定は、ここまでやるかというくらい柔軟性が高くできています。1文字ごとにフォント指定できるなんて他に聞いたことがありません。これは Emacs のマルチ言語化をいち早く実現したMuleプロジェクトの成果が大きく取り入れられています。

しかし柔軟性が高いが故に、Emacs のフォント設定には、文字コードの知識、フォントの知識、Emacs Lisp の知識、更に(フェイスとかフレームとか)Emacsそのものの知識を総動員しなくてはなりません。Emacs を始めたばかりの人が気安く手を出せるような代物ではありません。フォント設定など、見よう見まねでも一応動いてしまうと、後はあまり気にしないものです。自分の長いことあまり理解せずに漫然と使っていましたが、ここまで理解するのに随分時間がかかりました。この記事を少しでも Emacs のフォントシステムの理解に役立てていただければと思います。

0 件のコメント :

コメントを投稿