11

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?
12
\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

3
  • That's beautiful, thanks. But I do wonder, why doesn't changing nodes work for NotoColorEmoji?
    – Alan Xiang
    Nov 24 '20 at 19:19
  • 1
    Is there a way to add the RawFeature without specifying the main font? Mar 19 at 12:47
  • 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 2 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.