# How can I highlight ligatures?

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

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",
color.r,
color.g,
color.b)
local pop     = "0 g"
return push, pop
end

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)
end
end
end
end
return hd
end

local hook    = "post_linebreak_filter"
local active  = false

color_ligatures.enable = function ()
if active == false then
active = true
end
end

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

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, }
end


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
\def\startcolorligatures{%
\directlua{documentdata.color_ligatures.enable()}%
}

\def\stopcolorligatures{%
\endgraf %% paragraph-based callback
\directlua{documentdata.color_ligatures.disable()}%
}

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


Usage:

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

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

%% c) change color to blue
\setligaturecolor{0}{0}{bb}
different color a ff b ffi c ffl d
\stopcolorligatures

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

\bye


Output:

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

-
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. –  phg 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. –  phg Jul 3 '13 at 8:00
@LaRiFaRi Yes, I’m being cheap not using the colorstack but injecting pdfliterals instead. –  phg Nov 19 '13 at 11:43