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
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
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
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
(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
(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
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))
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: 你好, 早晨, こんにちは, 안녕하세요 ;; ;; This font requires "Regular". Other Noto fonts dont. ;; ¯\_(ツ)_/¯ (set-fontset-font t 'han "Noto Sans CJK KR Regular") (set-fontset-font t 'kana "Noto Sans CJK KR 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")