Emacs 27 gained support for Cairo for font rendering and this is supposed to let me render emojis but I can't find out how to make it work. I just about managed to set up some fontsets but the emoji which are made out of multiple characters don't work. Is it possible to make them work?
2 Answers
You need to use a font with support for that. This is what I do:
(if (>= emacs-major-version 27)
(set-fontset-font t '(#x1f000 . #x1faff)
(font-spec :family "Noto Color Emoji")))
-
-
No, but it's simple. Ah, now I see that the question explicitly says "which are made out of multiple characters". I missed that, thinking it was a question just about getting emojis to work at all.– pstDec 11, 2020 at 23:27
-
1I just wanted plain emoticons. And this worked. +1! But I think your range is too wide; following en.wikipedia.org/wiki/Unicode_block I changed it to
(set-fontset-font t '(#x1f600 . #x1f64f)
Most of the other blocks dont seem to work. Ive not done any special setup ofNoto Color Emoji
– RusiFeb 17, 2022 at 7:03
Step 1 is to make sure that you have Cairo and Harfbuzz enabled. You can run this elisp and check the results to test this:
(featurep 'cairo) ; should evaluate to t
(frame-parameter (selected-frame) 'font-backend) ; should be a list starting with ftcrhb
I'm not sure about Windows or MacOS support. I think for Windows, you possibly need harfbuzz
as the font backend and for MacOS you don't get any choice and I think things may work automatically.
Step 2 is to ensure you have a font which supports emoji, and that font is used for emoji by your fontset. I did this with an Emacs lisp function:
(defun init-my-font ()
(set-face-font
'default
(font-spec
:family "<your default font>"
:size 10.3
:weight 'normal
:width 'normal
:slant 'normal))
(set-face-attribute 'default nil :height 103)
;; emoji font
(set-fontset-font
t 'symbol
(font-spec
:family "Noto Color Emoji"
:size 10.3
:weight 'normal
:width 'normal
:slant 'normal))
;; fallback font
(set-fontset-font
t nil
(font-spec
:family "DejaVu Sans Mono"
:size 10.3
:weight 'normal
:width 'normal
:slant 'normal)))
If you use Emacs without a daemon, I think it is sufficient to call this function in your init file by writing (init-my-font)
. But I use a daemon and it seems to require that you wait until you have a gui frame before setting up the fonts. I have this hook for that:
(add-hook
'server-after-make-frame-hook
(let (done)
(lambda ()
(unless done
;; still set done to true even if we hit a bug (otherwise we
;; can never open a frame to see the problem)
(setq done t)
(init-my-font)))))
Note that this snippet requires lexical scoping in your init file.
This should cause some emoji to be displayed but not the “ligatures” made of multiple emoji, for example country flags, skin tone modifiers, or family emoji. To support them, we need to specify what the sequences start with, a regex for the rest of the sequence, and an instruction for how Emacs should figure out which glyphs to draw. For the instruction we use font-shape-gstring
, which tells Emacs to ask the font how to combine things (but the problem is knowing how Emacs decides which font from the fontset to use. I'm not really sure but I think it picks the font for the first character.) This config catches some things that won't form ligatures however that isn't a problem—they just get shaped into multiple glyphs as they normally would.
(require 'cl)
;; setting up composition functions for emoji modifiers
(dolist (items `(((?🇦 . ?🇿) [".[🇦-🇿]+" 0 font-shape-gstring])
((?🏳 . ?🏴) [".[️🌈⚧☠-]*" 0 font-shape-gstring])
(?⃣ ["[#*0-9]️⃣" 2 font-shape-gstring])
;; TODO: I can't make keycap sequences work because I
;; think they're trying to shape with the wrong font.
,@(mapcar (lambda (range) (list range [".?[🏻-🏿]?[️♂♀]*️?" 0 font-shape-gstring]))
(concatenate 'list "☝🎅🏇👂👃👦👧👼💏💑💪🕴🕵🕺🖐🖕🖖🙇🚣🛀🛌🤏🤞🤟🤦🤽🤾🥷🦻👯❤"
'((?⛹ . ?✍) (?🏂 . ?🏄) (?🏊 . ?🏌) (?👆 . ?👐)
(?👫 . ?👮) (?👰 . ?👸) (?💁 . ?💇) (?🙅 . ?🙇) (?🙋 . ?🙏)
(?🚴 . ?🚶) (?🤘 . ?🤜) (?🤰 . ?🤹) (?🤼 . ?🤾) (?🦵 . ?🦹)
(?🧍 . ?🧏) (?🧒 . ?🧟))) )
(?🧑 [".?[🏻-🏿]?[⚕⚖✈❤️🌾🍳🍼🎄🎓🎤🎨🏫🏭👦-👩💋💻💼🔧🔬🚀🚒🤝🦯🦰-🦳🦼🦽🧑]*" 0 font-shape-gstring])
((?👨 . ?👩) [".?[🏻-🏿]?[⚕⚖✈❤️🌾🍳🍼🎄🎓🎤🎨🏫🏭👦-👩💋💻💼🔧🔬🚀🚒🤝🦯🦰-🦳🦼🦽🧑]*" 0 font-shape-gstring])
,@(mapcar (lambda (str) (list (elt str 0) (vector str 0 'font-shape-gstring)))
'("😶🌫️" "🐈⬛" "🐕🦺" "🐻❄️" "👁️🗨️" "😮💨" "😵💫"))))
(set-char-table-range
composition-function-table
(car items)
(list (cadr items))))
Possibly, the above snippet will not survive StackExchange’s formatting, so here it is in base64 of UTF-8:
Ozsgc2V0dGluZyB1cCBjb21wb3NpdGlvbiBmdW5jdGlvbnMgZm9yIGVtb2ppIG1vZGlmaWVycwooZG9saXN0IChpdGVtcyBgKCgoP/Cfh6YgLiA/8J+HvykgWyIuW/Cfh6Yt8J+Hv10rIiAwIGZvbnQtc2hhcGUtZ3N0cmluZ10pCiAgICAgICAgICAgICAgICAgKCg/8J+PsyAuID/wn4+0KSBbIi5b77iP4oCN8J+MiOKap+KYoPOggKAt86CBv10qIiAwIGZvbnQtc2hhcGUtZ3N0cmluZ10pCiAgICAgICAgICAgICAgICAgKD/ig6MgWyJbIyowLTld77iP4oOjIiAyIGZvbnQtc2hhcGUtZ3N0cmluZ10pCiAgICAgICAgICAgICAgICAgOzsgVE9ETzogSSBjYW4ndCBtYWtlIGtleWNhcCBzZXF1ZW5jZXMgd29yayBiZWNhdXNlIEkKICAgICAgICAgICAgICAgICA7OyB0aGluayB0aGV5J3JlIHRyeWluZyB0byBzaGFwZSB3aXRoIHRoZSB3cm9uZyBmb250LgogICAgICAgICAgICAgICAgICxAKG1hcGNhciAobGFtYmRhIChyYW5nZSkgKGxpc3QgcmFuZ2UgWyIu4oCNP1vwn4+7LfCfj79dP1vigI3vuI/imYLimYBdKu+4jz8iIDAgZm9udC1zaGFwZS1nc3RyaW5nXSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChjb25jYXRlbmF0ZSAnbGlzdCAi4pid8J+OhfCfj4fwn5GC8J+Rg/Cfkabwn5Gn8J+RvPCfko/wn5KR8J+SqvCflbTwn5W18J+VuvCflpDwn5aV8J+WlvCfmYfwn5qj8J+bgPCfm4zwn6SP8J+knvCfpJ/wn6Sm8J+kvfCfpL7wn6W38J+mu/Cfka/inaQiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKCg/4pu5IC4gP+KcjSkgKD/wn4+CIC4gP/Cfj4QpICg/8J+PiiAuID/wn4+MKSAoP/CfkYYgLiA/8J+RkCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKD/wn5GrIC4gP/Cfka4pICg/8J+RsCAuID/wn5G4KSAoP/CfkoEgLiA/8J+ShykgKD/wn5mFIC4gP/CfmYcpICg/8J+ZiyAuID/wn5mPKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoP/CfmrQgLiA/8J+atikgKD/wn6SYIC4gP/CfpJwpICg/8J+ksCAuID/wn6S5KSAoP/CfpLwgLiA/8J+kvikgKD/wn6a1IC4gP/CfprkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICg/8J+njSAuID/wn6ePKSAoP/Cfp5IgLiA/8J+nnykpKSApCiAgICAgICAgICAgICAgICAgKD/wn6eRIFsiLuKAjT9b8J+Puy3wn4+/XT9b4oCN4pqV4pqW4pyI4p2k77iP8J+MvvCfjbPwn4288J+OhPCfjpPwn46k8J+OqPCfj6vwn4+t8J+Rpi3wn5Gp8J+Si/Cfkrvwn5K88J+Up/CflKzwn5qA8J+akvCfpJ3wn6av8J+msC3wn6az8J+mvPCfpr3wn6eRXSoiIDAgZm9udC1zaGFwZS1nc3RyaW5nXSkKICAgICAgICAgICAgICAgICAoKD/wn5GoIC4gP/CfkakpIFsiLuKAjT9b8J+Puy3wn4+/XT9b4oCN4pqV4pqW4pyI4p2k77iP8J+MvvCfjbPwn4288J+OhPCfjpPwn46k8J+OqPCfj6vwn4+t8J+Rpi3wn5Gp8J+Si/Cfkrvwn5K88J+Up/CflKzwn5qA8J+akvCfpJ3wn6av8J+msC3wn6az8J+mvPCfpr3wn6eRXSoiIDAgZm9udC1zaGFwZS1nc3RyaW5nXSkKICAgICAgICAgICAgICAgICAsQChtYXBjYXIgKGxhbWJkYSAoc3RyKSAobGlzdCAoZWx0IHN0ciAwKSAodmVjdG9yIHN0ciAwICdmb250LXNoYXBlLWdzdHJpbmcpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgJygi8J+YtuKAjfCfjKvvuI8iICLwn5CI4oCN4qybIiAi8J+QleKAjfCfproiICLwn5C74oCN4p2E77iPIiAi8J+Rge+4j+KAjfCfl6jvuI8iICLwn5iu4oCN8J+SqCIgIvCfmLXigI3wn5KrIikpKSkKICAoc2V0LWNoYXItdGFibGUtcmFuZ2UKICAgY29tcG9zaXRpb24tZnVuY3Rpb24tdGFibGUKICAgKGNhciBpdGVtcykKICAgKGxpc3QgKGNhZHIgaXRlbXMpKSkpCg==
You can decode it with the command base64 -d
.
-
-
1Running the snippet "setting up composition functions for emoji modifiers" gives
Symbol's function definition is void: concatenate
in Emacs29 at least. Fixed it by adding a(require 'cl)
before the whole sexp.– lboJan 9 at 12:40 -
-
1Note that emacs-28 now has an
emoji
script, so you can set the font for symbols and emoji to be different. And it has built-in support for the various multi-character emoji such as country flags– rpluimJan 9 at 15:38