idiocy.org

Emacs, fonts and fontsets


One problem I've had with Emacs is understanding how the font system works. It's always been clear that fontsets are the answer to a lot of my problems, but I really struggled to work out how to use it. Here I plan to note what I've learned about how to set fonts in Emacs.

Setting the default

There appear to be as many ways to set the default font as there are users of Emacs, but I've gone with this option:

(set-face-attribute 'default nil :font "Droid Sans Mono")

This modifies the default fontset, therefore setting the font in all frames.

Setting fall-back fonts

But what if you use the same configuration on multiple boxes, or the font you choose doesn't provide all the glyphs you need? By default Emacs will search through the existing fonts until it finds one that contains the glyph, but this can be a fairly haphazard affair and can also be really slow1.

Emacs allows you to specify fall-backs yourself. To do this for the default fontset you could do something like this:

(set-fontset-font t nil "Courier New" nil 'append)

Using a first argument of t means it updates the default fontset. It is possible to create other fontsets and use them, but I've never had much luck with that and prefer to just modify the default. The second argument is the glyph range, which we will come back to later. The last argument, append, tells Emacs to add this font to the end of the fontset, so it will be searched after any other fonts in the set. You can also use prepend, which puts the font first in the list, although after the default set with set-face-attribute.

Setting fonts for specific glyphs

Back to that second argument, the glyph range one. You can specify a single glyph, a range of glyphs, a character set name, or a language.

Say you want to set 😊 to use a specific font.

(set-fontset-font t ?😊 "Segoe UI Emoji")

Or you can set ranges.

(set-fontset-font t '(?😊 . ?😎) "Segoe UI Emoji")

You can't set ASCII characters this way, Emacs won't let you.

Setting fonts for different character sets or languages

Lets say you deal with Thai writing a lot but your default font doesn't support it, or you just really like the look of another font for Thai writing.

Have a look at script-representative-chars and list-charset-chars to see if what you're looking for is listed, then use that name. Alternatively, use describe-char on one of the characters in question and look at the charset or script entries.

(set-fontset-font t 'thai "Noto Sans Thai")

This can result in enormous speed-ups as Emacs no longer has to run through hundreds of fonts looking for a compatible one.

If you need to set a fall-back font for Thai writing then you just do it the same as above.

(set-fontset-font t 'thai "Leelawadee UI" nil 'append)

The downside of this is that if you're using your configuration on machines that don't have these fonts then it won't even bother searching for a compatible font and will just give you a row of boxes. But don't worry, we can force Emacs to search by using font-spec.

(set-fontset-font t 'thai (font-spec :script 'thai) nil 'append)

You can put whatever you want in that call to font-spec and Emacs will search through the fonts to find something suitable. There's no reason why you couldn't use font-spec to set a specific font.

So our completed configuration for Thai writing now looks like this:

(set-fontset-font t 'thai "Noto Sans Thai")
(set-fontset-font t 'thai "Leelawadee UI" nil 'append)
(set-fontset-font t 'thai (font-spec :script 'thai) nil 'append)

Note that you can only append or prepend a font when there is already something set for that glyph or range, which makes sense, but I originally thought I was appending to one big list in the fontset rather than to one of a set of lists and couldn't understand why it wasn't working.

How to check if a font is installed

Rather than relying on the fall-back behaviour, you can check whether a font is installed before trying to use it. This is pretty straight forward as all available font families are available from font-family-list2, so you can just check the list:

(member "Noto Sans" (font-family-list))

Appendix

I set up some basic fall-backs to Noto fonts for various languages to try speeding up navigating Emacs's Hello file (C-h h). Because there are no fall-backs set for these fonts, if I used this configuration on a computer without the fonts then I'd just see lots of empty boxes everywhere, but it might give you a head-start in setting up your own configuration.

(set-face-attribute 'default nil :font "Droid Sans Mono")

;; Latin
(set-fontset-font t 'latin "Noto Sans")

;; East Asia: 你好, 早晨, こんにちは, 안녕하세요
;;
;; Make sure you use the right font. See
;; https://www.google.com/get/noto/help/cjk/.
;;
;; This font requires "Regular". Other Noto fonts dont.
;; ¯\_(ツ)_/¯
(set-fontset-font t 'han "Noto Sans CJK SC Regular")
(set-fontset-font t 'kana "Noto Sans CJK JP Regular")
(set-fontset-font t 'hangul "Noto Sans CJK KR Regular")
(set-fontset-font t 'cjk-misc "Noto Sans CJK KR Regular")

;; South East Asia: ជំរាបសួរ, ສະບາຍດີ, မင်္ဂလာပါ, สวัสดีครับ
(set-fontset-font t 'khmer "Noto Sans Khmer")
(set-fontset-font t 'lao "Noto Sans Lao")
(set-fontset-font t 'burmese "Noto Sans Myanmar")
(set-fontset-font t 'thai "Noto Sans Thai")

;; Africa: ሠላም
(set-fontset-font t 'ethiopic "Noto Sans Ethiopic")

;; Middle/Near East: שלום, السّلام عليكم
(set-fontset-font t 'hebrew "Noto Sans Hebrew")
(set-fontset-font t 'arabic "Noto Sans Arabic")

;;  South Asia: નમસ્તે, नमस्ते, ನಮಸ್ಕಾರ, നമസ്കാരം, ଶୁଣିବେ,
;;              ආයුබෝවන්, வணக்கம், నమస్కారం, བཀྲ་ཤིས་བདེ་ལེགས༎
(set-fontset-font t 'gujarati "Noto Sans Gujarati")
(set-fontset-font t 'devanagari "Noto Sans Devanagari")
(set-fontset-font t 'kannada "Noto Sans Kannada")
(set-fontset-font t 'malayalam "Noto Sans Malayalam")
(set-fontset-font t 'oriya "Noto Sans Oriya")
(set-fontset-font t 'sinhala "Noto Sans Sinhala")
(set-fontset-font t 'tamil "Noto Sans Tamil")
(set-fontset-font t 'telugu "Noto Sans Telugu")
(set-fontset-font t 'tibetan "Noto Sans Tibetan")

Updates

Updated 31/3/2019. Thanks to Yuan Fu for his suggestions.

Updated 1/4/2019. Added link to Chinese translation.

Updated 31/12/2019. Sebastian Urban has sent me a more complete Noto font set‐up. He says:

I found your post "Emacs, fonts and fontsets" helpful, but I thought why stop there. Because there are other Noto Sans fonts, I decided to make as complete list as possible. It has form of "(set-fontset-font "default-fontset" 'SCRIPTNAME "Noto Sans FONT")". I didn't include "Noto Sans Symbols" and "Noto Sans Symbols2" and I'm using "Noto Sans CJK SC Regular" for 'cjk-misc.

Also because it takes a lot of space, I keep them in separate file and load with "(load …)" from my init.

Footnotes:

1
Emacs has a default fall-back to Symbola, so it's a good idea to install it to save searching through the entire set of fonts.
2
Check out Xah Lee's Font Setup page for more information.
t @flxzr
g alanthird
e Alan Third