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",
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
luatexbase.add_to_callback (hook, cbk, "my.color_ligatures")
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 % 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
\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.
\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.ligaturing
callback. I've never used it so can't say for sure.