15

Inspired by this question, I try to use a different font for all emoji characters in the document. In the code sample below, I build the is_emoji function according to unicode standards.

\documentclass{article}
\usepackage{fontspec}
\usepackage{luacode}

\setmainfont{texgyretermes-regular}[ Extension = .otf , BoldFont = texgyretermes-bold, ItalicFont = texgyretermes-italic, BoldItalicFont = texgyretermes-bolditalic, ]

% need to download NotoColorEmoji % https://github.com/googlefonts/noto-emoji/blob/master/fonts/NotoColorEmoji.ttf \newfontface{\notocoloremoji}{NotoColorEmoji}[ Extension = .ttf, Renderer = Harfbuzz ]

{ \notocoloremoji\global\expandafter\let\expandafter\luafontsymbola\the\font }

\begin{luacode*} -- code range for emojis local emoji_range = {{8596, 8600}, {8617, 8617}, {8986, 8986}, {9193, 9195}, {9197, 9197}, {9201, 9201}, {9208, 9209}, {9642, 9642}, {9723, 9725}, {9728, 9728}, {9730, 9730}, {9735, 9740}, {9743, 9743}, {9748, 9748}, {9750, 9750}, {9753, 9755}, {9758, 9758}, {9762, 9762}, {9764, 9764}, {9767, 9768}, {9771, 9772}, {9776, 9782}, {9784, 9784}, {9787, 9790}, {9795, 9798}, {9800, 9810}, {9812, 9821}, {9825, 9825}, {9829, 9829}, {9833, 9849}, {9852, 9852}, {9856, 9860}, {9872, 9872}, {9878, 9878}, {9883, 9883}, {9885, 9886}, {9888, 9888}, {9890, 9893}, {9896, 9896}, {9898, 9898}, {9900, 9902}, {9904, 9904}, {9906, 9915}, {9917, 9917}, {9919, 9922}, {9924, 9924}, {9926, 9926}, {9929, 9932}, {9941, 9959}, {9963, 9966}, {9968, 9968}, {9970, 9970}, {9975, 9976}, {9979, 9979}, {9982, 9984}, {9987, 9987}, {9992, 9995}, {10000, 10000}, {10035, 10035}, {10067, 10068}, {10085, 10086}, {10133, 10134}, {10548, 10548}, {11013, 11014}, {11035, 11035}, {126976, 126978}, {126981, 127181}, {127184, 127230}, {127245, 127246}, {127340, 127342}, {127344, 127344}, {127358, 127358}, {127377, 127385}, {127405, 127460}, {127462, 127486}, {127489, 127489}, {127491, 127502}, {127538, 127545}, {127548, 127550}, {127561, 127566}, {127568, 127568}, {127570, 127742}, {127744, 127755}, {127757, 127757}, {127763, 127764}, {127766, 127767}, {127773, 127773}, {127775, 127775}, {127778, 127778}, {127780, 127787}, {127789, 127790}, {127792, 127792}, {127794, 127794}, {127796, 127796}, {127799, 127817}, {127820, 127822}, {127825, 127866}, {127870, 127870}, {127872, 127890}, {127892, 127892}, {127894, 127894}, {127897, 127898}, {127900, 127900}, {127902, 127902}, {127904, 127939}, {127947, 127949}, {127951, 127954}, {127956, 127966}, {127968, 127970}, {127973, 127983}, {127985, 127985}, {127992, 128006}, {128009, 128010}, {128012, 128013}, {128015, 128015}, {128017, 128017}, {128023, 128040}, {128043, 128061}, {128066, 128099}, {128102, 128106}, {128108, 128108}, {128110, 128171}, {128174, 128180}, {128182, 128182}, {128184, 128234}, {128236, 128236}, {128240, 128243}, {128246, 128246}, {128249, 128251}, {128255, 128257}, {128260, 128262}, {128266, 128275}, {128278, 128298}, {128300, 128300}, {128302, 128316}, {128326, 128327}, {128329, 128329}, {128331, 128333}, {128336, 128346}, {128348, 128358}, {128360, 128365}, {128367, 128367}, {128369, 128369}, {128371, 128376}, {128379, 128389}, {128392, 128392}, {128394, 128396}, {128398, 128398}, {128401, 128403}, {128405, 128405}, {128407, 128418}, {128422, 128422}, {128425, 128431}, {128433, 128433}, {128435, 128442}, {128445, 128448}, {128450, 128451}, {128453, 128463}, {128465, 128466}, {128468, 128474}, {128476, 128477}, {128479, 128479}, {128484, 128486}, {128489, 128493}, {128496, 128497}, {128500, 128504}, {128507, 128510}, {128513, 128517}, {128519, 128519}, {128521, 128524}, {128530, 128531}, {128540, 128541}, {128544, 128548}, {128550, 128550}, {128552, 128554}, {128558, 128558}, {128560, 128562}, {128567, 128575}, {128577, 128579}, {128581, 128590}, {128641, 128641}, {128643, 128644}, {128650, 128650}, {128657, 128658}, {128665, 128665}, {128667, 128672}, {128676, 128676}, {128679, 128684}, {128686, 128688}, {128691, 128692}, {128695, 128695}, {128697, 128701}, {128705, 128708}, {128710, 128713}, {128717, 128718}, {128721, 128721}, {128723, 128723}, {128726, 128726}, {128728, 128734}, {128736, 128740}, {128742, 128743}, {128747, 128747}, {128749, 128750}, {128753, 128753}, {128756, 128757}, {128759, 128759}, {128763, 128763}, {128765, 128766}, {128884, 128894}, {128981, 128990}, {128992, 129002}, {129004, 129022}, {129036, 129038}, {129096, 129102}, {129114, 129118}, {129160, 129166}, {129198, 129278}, {129293, 129294}, {129296, 129303}, {129305, 129309}, {129312, 129318}, {129320, 129326}, {129329, 129329}, {129331, 129337}, {129340, 129341}, {129344, 129348}, {129351, 129354}, {129357, 129358}, {129360, 129373}, {129375, 129386}, {129388, 129391}, {129395, 129397}, {129399, 129399}, {129404, 129406}, {129408, 129411}, {129413, 129424}, {129426, 129430}, {129432, 129441}, {129443, 129443}, {129445, 129449}, {129451, 129452}, {129454, 129454}, {129456, 129464}, {129466, 129470}, {129473, 129473}, {129475, 129481}, {129485, 129486}, {129488, 129509}, {129511, 129534}, {129536, 129646}, {129648, 129650}, {129653, 129654}, {129656, 129657}, {129659, 129662}, {129664, 129665}, {129667, 129669}, {129671, 129678}, {129680, 129684}, {129686, 129703}, {129705, 129710}, {129712, 129717}, {129719, 129726}, {129728, 129729}, {129731, 129742}, {129744, 129749}, {129751, 129790}, {130048, 131068}}

function is_emoji(id) if id < emoji_range[1][1]then return false end -- naive search for _, p in pairs(emoji_range) do if id >= p[1] and id <= p[2] then return true end end return false end

local font_id = font.id("luafontsymbola")

function replace_font(hd) for n in node.traverse_id(node.id("glyph"), hd) do if is_emoji(n.char) then n.font = font_id end end return hd end

luatexbase.add_to_callback("pre_linebreak_filter", replace_font, "replace_font")

\end{luacode*}

\begin{document} minikube v1.15.1 on Ubuntu 20.04\ ✨ Automatically selected the docker driver\ Starting control plane node minikube in cluster minikube\ Pulling base image ... \end{document}

My plan is to change the font of a glyph node to NotoColorEmoji if is_emoji is True. However, I get the following error:

! error:  (file [NotoColorEmoji.ttf]:mode=harf;script=DFLT;language=dflt;) (type
 3): font [NotoColorEmoji.ttf]:mode=harf;script=DFLT;language=dflt; at 600 not f
ound
  • This seems to a problem only with NotoColorEmoji.ttf, because substituting with other fonts does not raise the same problem.
  • I noticed that luaotfload provides fallback options, which might work in this case. However, it involves many low-level APIs. Is it possible for fontspec to make use of fallback?
Alan Xiang
  • 5,227
  • what an unfortunately named font. I read this as 'no-to-color-emoji' rather than 'noto-color-emoji'. – ivo Welch Dec 18 '21 at 18:20
  • 1
    @ivoWelch the font name stands for 'no tofu', the tofu being the ▯ character that is printed when a font does not contain the actually desired character. The Noto fonts are supposed to have all characters so you never get a tofu again. – Marijn Dec 20 '23 at 14:04

1 Answers1

22
\documentclass{article}
\usepackage{fontspec}

\directlua{luaotfload.add_fallback
   ("emojifallback",
    {
      "NotoColorEmoji:mode=harf;"
    }
   )}

\setmainfont{texgyretermes-regular}[
  Extension      = .otf ,
  BoldFont       = texgyretermes-bold,
  ItalicFont     = texgyretermes-italic,
  BoldItalicFont = texgyretermes-bolditalic,
  RawFeature={fallback=emojifallback}
]




\begin{document}
  minikube v1.15.1 on Ubuntu 20.04\\
✨  Automatically selected the docker driver\\
  Starting control plane node minikube in cluster minikube\\
  Pulling base image ...
\end{document}

enter image description here

Ulrike Fischer
  • 327,261
  • That's beautiful, thanks. But I do wonder, why doesn't changing nodes work for NotoColorEmoji? – Alan Xiang Nov 24 '20 at 19:19
  • 2
    Is there a way to add the RawFeature without specifying the main font? – Joan Marcè i Igual Mar 19 '21 at 12:47
  • 2
    Great answer! For specifying multiple fallback fonts, e.g., emoji+CJK, the following worked for me: \directlua{ luaotfload.add_fallback("fallbacks", { "Noto Color Emoji:mode=harf;", "Noto Serif CJK SC:style=Regular;" }) } – andrew Sep 02 '21 at 23:25
  • 1
    @andrew, this is a life-saving command for me, thank you so much! Your comment just made my whole document creation routine a million times easier! But I simplified the command a bit and for Japanese (even though you said "CJK") the font is JP instead of SC: \directlua{luaotfload.add_fallback("FallbackFonts", { "Noto Color Emoji:mode=harf", "Noto Sans CJK JP:" })} – Andrew15_5 Dec 14 '22 at 19:18