I'd like to highlight all the ligatures used in a document similar to the image below


as an attempt to show off some of TeX's tender loving care for the font being used.

Is there a way to do this automatically? My guess is that it would be much easier in XeTeX, but any automatic solution is welcome.

  • This is essentially the same as This one which means xetex may not help as much as you want. luatex perhaps.... Commented Jun 21, 2013 at 17:06
  • @DavidCarlisle - it would seem so -- since ligatures are essentially just another glyph in the font, no different from 'normal' letters (to my understanding). LuaTeX raises interesting possibilities, since it can perhaps peer into the fonts being used and then 'mark' ligatures, inserting \color tokens as necessary... (but wouldn't that still break the input stream, as feared in the linked Q?) Just thinking out loud. Perhaps this needs to be a two-pass process - once to generate marked TeX (perhaps from Python/Lua/etc.) and once to compile the document. Commented Jun 21, 2013 at 17:11
  • If you are prepared to modify the font you could use a .vf file and add colour specials into the vf description of each ligature. Would be quite a bit of work and you'd have to do it for each font. Commented Jun 21, 2013 at 17:16
  • 3
    There is some code on site that works to suppress ligatures (here and here). It might be a starting point or modified to color them rather than suppressing them.
    – Scott H.
    Commented Jun 21, 2013 at 18:58
  • It might even be dead easy with the ligaturing callback. I've never used it so can't say for sure.
    – Scott H.
    Commented Jun 21, 2013 at 19:03

1 Answer 1


Using Luatex, you can insert color literals to highlight ligatures. Save this snippet as color-ligatures.lua:

documentdata                  = documentdata or { }
documentdata.color_ligatures  = { }
local color_ligatures         = documentdata.color_ligatures

color_ligatures.color         = { r = 0xee/255,
                                  g = 0x31/255,
                                  b = 0x09/255, }

local stringformat            = string.format

local copynode                = node.copy
local insertnodeafter         = node.insert_after
local insertnodebefore        = node.insert_before
local newnode                 = node.new
local traversenodes           = node.traverse
local traversenodetype        = node.traverse_id

local nodecodes               = nodes.nodecodes

local glyph_t                 = nodecodes.glyph
local disc_t                  = nodecodes.disc
local hlist_t                 = nodecodes.hlist
local whatsit_t               = nodecodes.whatsit
local pdf_literal_t           = 8 -- change to 16 on TexLive 2016 (luatex 0.95)

local get_color = function ()
  local color   = color_ligatures.color
  local push    = stringformat ("%.3g %.3g %.3g rg",
  local pop     = "0 g"
  return push, pop

local pdf_literal = newnode(whatsit_t, pdf_literal_t)

local cbk = function (hd)
  local pushcolor, popcolor = get_color()
  local push, pop = copynode(pdf_literal), copynode(pdf_literal)
  push.mode, push.data = 1, pushcolor
  pop.mode,  pop.data  = 1, popcolor

  for line in traversenodetype(hlist_t, hd) do
    local hlist = line.list
    for n in traversenodes(hlist) do
      --- locate ligatures
      if  n.id == glyph_t and n.components then
        --- surround with color literals
        local before, after = copynode(push), copynode(pop)
        hlist = insertnodebefore(hlist, n, before)
        hlist = insertnodeafter (hlist, n, after)
      elseif n.id == disc_t then
        local replace = n.replace
        if replace and replace.components then
          local before, after = copynode(push), copynode(pop)
          hlist = insertnodebefore(hlist, n, before)
          hlist = insertnodeafter (hlist, n, after)
  return hd

local hook    = "post_linebreak_filter"
local active  = false

color_ligatures.enable = function ()
  if active == false then
    luatexbase.add_to_callback (hook, cbk, "my.color_ligatures")
    active = true

color_ligatures.disable = function ()
  if active == true then
    luatexbase.remove_from_callback (hook, "my.color_ligatures")
    active = false

color_ligatures.set_color = function (r, g, b)
  color_ligatures.color = { r = (tonumber(r, 16) or 0) / 255,
                            g = (tonumber(g, 16) or 0) / 255,
                            b = (tonumber(b, 16) or 0) / 255, }

Now you can define some macros that toggle the callback on demand by wrapping the functions enable() and disable(). For example:

\input luaotfload.sty  % in latex: \usepackage{luaotfload}
\directlua{dofile "color-ligatures.lua"}

%% uncomment the next line for node mode (default in luaotfload):
%\font\mainfont="file:Iwona-Regular.otf" at 30pt \mainfont

%% enabling/disabling the colorizer

  \endgraf %% paragraph-based callback

%% changings colors (expects octets in hexadecimal)
\def\setligaturecolor#1#2#3{%%-> rgb values
  \directlua{documentdata.color_ligatures.set_color("#1", "#2", "#3")}


%% demo
%% a) inactive
before a ff b ffi c ffl d

%% b) activate, using default color
during a ff b ffi c ffl d

%% c) change color to blue
different color a ff b ffi c ffl d

%% d) inactive again
after a ff b ffi c ffl d



result of demo

EDIT: Recognize ligatures differently so it works in node mode as well.

  • 2
    @Sean Allred I don’t use Xetex, so I can’t help you there. Context otoh requires only minor modifications since it is Luatex based as well. did you look into the ligaturing hook? -- the ligaturing callback is for inserting ligatures but if you want to colorize them, they need to be present already. Thus, the choice of the post_linebreak_filter. Commented Jun 22, 2013 at 15:10
  • 1
    @therese that’s an issue when using Latex with Luaotfload 2.2. It is worked around in the dev version but has not made it on CTAN yet. Removing those spaces should help to some extent. Commented Jul 3, 2013 at 8:00
  • 1
    @LaRiFaRi Yes, I’m being cheap not using the colorstack but injecting pdfliterals instead. Commented Nov 19, 2013 at 11:43
  • 1
    @PhilippGesang I've been using your script extensively in the past, thanks a lot! But some updates of the past years seem to have broken it. I get error message: color-ligatures.lua:41: You cannot set field data in a node of type whatsit. also: \newpage ...k \@nobreakfalse \everypar {}\fi \par \vfil \penalty -\@M Could you have a look at it?
    – Huugo
    Commented Sep 5, 2016 at 21:54
  • 1
    @Huugo: I could fix the error message color-ligatures.lua:41: You cannot set field data in a node of type whatsit. with following change: color-ligatures.lua:24 local pdf_literal_t = 16 (change the value 8 to 16 for pdf_literal_t).
    – Hotschke
    Commented Dec 13, 2016 at 14:53

You must log in to answer this question.

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