TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question
This is essentially the same as This one which means xetex may not help as much as you want. luatex perhaps.... – David Carlisle Jun 21 '13 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. – Sean Allred Jun 21 '13 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. – David Carlisle Jun 21 '13 at 17:16
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. Jun 21 '13 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. Jun 21 '13 at 19:03
up vote 32 down vote accepted

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

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

share|improve this answer
This is really awesome!! I'm still interested in other solutions (plainTeX (shudder), XeTeX, ConTeXt, etc), but I'm curious -- did you look into the ligaturing hook as described in the comments? Was there a misunderstanding in its purpose, or did it just turn out to be messier than you'd think? – Sean Allred Jun 22 '13 at 13:37
Shouldn't the ff in "different" also be blue? – Charles Staats Jun 22 '13 at 14:03
@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. – Philipp Gesang Jun 22 '13 at 15:10
@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. – Philipp Gesang Jul 3 '13 at 8:00
@LaRiFaRi Yes, I’m being cheap not using the colorstack but injecting pdfliterals instead. – Philipp Gesang Nov 19 '13 at 11:43

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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