16

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?
2
  • what an unfortunately named font. I read this as 'no-to-color-emoji' rather than 'noto-color-emoji'.
    – ivo Welch
    Commented Dec 18, 2021 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
    Commented Dec 20, 2023 at 14:04

1 Answer 1

24
\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

4
  • That's beautiful, thanks. But I do wonder, why doesn't changing nodes work for NotoColorEmoji?
    – Alan Xiang
    Commented Nov 24, 2020 at 19:19
  • 3
    Is there a way to add the RawFeature without specifying the main font? Commented Mar 19, 2021 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
    Commented Sep 2, 2021 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
    Commented Dec 14, 2022 at 19:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .