注意

必要があって自分で調べた事を書き散らしたのみで、あまり真剣に裏は取っていない。

Emacsとフォント設定

昔々のEmacsは、フォント設定をフォントをXFLDで書かないといけなかったりして面倒だったが、最近のEmacsはわりと楽にフォント設定ができる。

Emacsが受けつけるフォント名のパターンは下記の3つがある。

fontconfig pattern
FONTNAME[-FONTSIZE][:NAME1=VALUES1][:NAME2=VALUES2]…
Monospace
Monospace-12
Monospace-12:bold
DejaVu Sans Mono:bold:italic
Monospace-12:weight=bold:slant=italic

Infoによれば、WindowsだとFONTNAME[-FONTSIZE]しか食わないと書いてあるが、とりあえずboldとitalicは食ってくれるようだ。

GTK font pattern
FONTNAME [PROPERTIES] [FONTSIZE]
Monospace 12
Monospace Bold Italic 12

InfoによればやはりWindowsはフル記述を受けつけないと書いてあるが、これもBoldとItalicは食ってくれるようだ。

XFLD pattern

古くからのX Windows System使いならば馴染み(?)のあるXLFDも受けつける。まあ現代においては手で書くものじゃない気もする……。

-MAKER-FAMILY-WEIGHT-SLANT-WIDTHTYPE-STYLE...
...-PIXELS-HEIGHT-HORIZ-VERT-SPACING-WIDTH-REGISTRY-ENCODING

EmacsのInfoにはWindowsでは未対応と書いてあるが、少なくともEmacs-29以降ではこのフォント指定方法も食ってくれない事はないような挙動をしている。

fontとfontset

ところでEmacsは文字コード毎に別のフォントを与えることができるので、実際にEmacsが内部で取り扱うの は文字毎のフォント設定を持つfontsetである。

フォントを指定するところで、前述のフォント指定パターンでフォント名を指定した時、 実際にそれが表示に使われるタイミングで内部的にfontsetが自動的に生成される。

例を挙げる。例えば、

(set-frame-font "UDEV Gothic JPDOC 13")

とした後に、M-x list-fontsetを実行すると、こんなのがfontset一覧に現れる。

Fontset: -TWR-UDEV Gothic JPDOC-regular-normal-normal-*-18-*-*-*-d-0-fontset-auto1

この末端の"fontset-auto1"が自動生成されたfontsetの名前である。 このfontset名はフォント名と同じように使える。

(set-frame-font "fontset-auto1")

自動生成だと名前を好きにつけられないので、fontsetを指定して後からいろいろ設定したいならば、 自分で明示的にfontsetを生成したくなるだろう。

create-fontset-from-ascii-fontの沼

明示的なfontsetの作り方でよく見るのはcreate-fontset-from-ascii-fontを使う方法だ。 create-fontset-from-ascii-fontにASCII範囲を含むフォント名を与えると、そこからfontsetが作ってくれる。

例えば、

(create-fontset-from-ascii-font "UDEV Gothic JPDOC 13" nil "udevgothic")

とすると、だいたいUDEV Gothic JPDOCで埋められたfontsetのfontset-udevgothicが作られる。

(ちなみにこの関数はXLFD形式でfontsetを返す。XLFDはWindowsでは使えないんじゃなかったの?と思いそうだが、Windows版のEmacsでもXLFDを容赦なく返してきて、ここで返ってくるXLFDはWindowsでも使えているように見える)

ところが、italicやboldの持つフォントの場合、これで作られるfontsetやXFLDの結果はあまり幸せにならないものである。フォント指定はGTK font patternで、BoldもItalicも明示的に指定していないのに、boldとitalicがいっしょについたフォントが返ってくるのである。

;; あれえ?
"-twr-udev gothic jpdoc-bold-italic-normal-*-*-130-*-*-d-0-fontset-udevgothic"

いにしえのバグBug#2476の根本原因がおそらくこれなのだが、このBug自体はframeへのfontset指定時に細工する対策でcloseされた1。よって、GTK font patternから明示的にfontsetを生成時する時にはこの不具合を容赦なく踏む。結果として表示される文字が全部boldかつitalicになる。 今日も世界は太くてななめ……

対策としては、明示的にboldやitalic指定を「なし」に指定できるfontconfig patternを使うとよい。 例えば下記設定で無事boldでもgothicでもないフォントが使われる。

(create-fontset-from-ascii-font "UDEV Gothic JPDOC-13:weight=normal:slant=normal" nil "udevgothic")"
;; よしよし。
"-twr-udev gothic jpdoc-regular-normal-normal-*-*-130-*-*-d-0-fontset-udevgothic"

フォントのサイズ -ptとpixel-

ところでここまでに出てきたフォントサイズは全部point sizeである。Emacsの中で、point sizeで与えられたフォントが実際にどんなpixel sizeになるか。Xの場合はxdpyinfoで得られる情報を取っていそうだが、pure gtkだとどうだろう? windowsは? Macは?

……調べるのも面倒臭いし、そもそもwslgやVMの場合、GUI側にWindows側のディスプレイの正しい情報が渡るかどうかも怪しい。あと、日本語用のプログラムフォントはフォント高さが2の倍数になってないと、半角と全角の幅の比が1:2にならないという問題もあるので、俺は古くさいとは思いつつpixelで指定したい。

ところが、Infoを見る限り、fontconfig patternもGTK font patternも指定できるのはpoint sizeだけで、pixel sizeで指定できるのはXLFDだけに見える。えーXLFDを手で書くの?とつらい気分になるが、幸いfont-specからXLFDを作ってくれるfont-xlfd-nameの助けを借りるとわりと楽に書ける。

  (create-fontset-from-fontset-spec
   (font-xlfd-name
    (font-spec :family "UDEV Gothic JPDOC" :size 18 :weight 'normal :slant 'normal
      	     :registry "fontset-udevgothic")))

これで好きなpixelサイズのfontsetを作れる。

ちなみに:sizeのproperty、浮動小数を与えるとptに、整数を与えるとpixelになるので注意。

文字毎の指定

fontsetは文字毎にフォントを指定できると書いた。たとえば、基本的にはフォントはUDEV Gothic JPDOCを使いたいけど、記号はHackgenにしたくなったなら、下記のようにすればfontset-udevgothicのsymbol範囲をhackgenで上書きできる。ここで'prependは候補リストの先頭に入れる指定で、もしHackGenがフォントを持ってない文字だったら、UDEV Gothic JPDOCが表示に使われる。

(set-fontset-font "fontset-udevgothic" 'symbol (font-spec :family "HackGen") nil 'prepend)

ただし、use-default-font-for-symbolsがtだと、この指定よりもEmacs自身が選んだデフォルトフォントが優先されて、いくら設定しても表示が変わらなくて首をかしげる羽目になるので注意が必要である。(しかもこの値初期値がtなのでわりと罠である)

'symbolとか'asciiとかの文字セット単位ではなく、文字単位でも指定ができる。UDEV Gothic JPDOCだとが半角幅になって、org-modernの表示と合わさると具合が悪いときなんかはこう書ける。

;; org-modernで使う"☑"
(set-fontset-font "fontset-udevgothic" '(#x2611 . #x2611) (font-spec :family "HackGen") nil 'prepend)

こうやって、自分で作ったfontsetに文字毎にフォント設定を足していける。フォント設定が複雑なかわりにこの柔軟性を得ているので、

たいていの人は「ここまでの柔軟性は必要ないなあ……」と思うだろうが、まあ、はるか昔にMULEがマージされたEmacsならではの機能ではあるんじゃないかな。


1

frame.elにコメントつきで記載がある