AFAIK word space in TeX work more or less like a glue instead of a real character. So it is not possible to kern a character with the space, e.g., to make the pair such as f<space>T
to stay apart a little. pdfTeX
used to have the ability to alter word space (though not exactly the same thing as kerning).
Is there a way to kern characters with word space in LuaTeX?
Update
I have figured out a way to do it. Please comment if there is any improvement can be made.
\documentclass{article}
\directlua{
function kernspace(head)
for space in node.traverse_id(node.id('glue'), head) do
if space.subtype == 13 then % A spaceskip glue
local prev = node.prev(space)
local next = node.next(space)
% Check that the space is between two glyphs
if prev.id == 29 and next.id == 29 then
% Below we increase space by 100pt, demonstration purpose only
% In reality, we can adjust the space according to the prev and next
% In particular, e.g., adjust the skip such that its natural space
% will be increased by the kerning value between prev and next
space.width = space.width + 6553600
end
end
end
return true
end
luatexbase.add_to_callback("pre_linebreak_filter", kernspace, "kernspace");
}
\begin{document}
A V
\end{document}
Update 2 Below is a somewhat more complete, but bit tedious example, in case any one find it useful.
mclass.par.kern = {}
mclass.par.size = {}
function mclass.par.space(head)
for space in node.traverse_id(node.id('glue'), head) do
if space.subtype == 13 then -- spaceskip
local prev = node.prev(space)
local next = node.next(space)
if prev.id == 29 and next.id == 29 then -- glyphs
if prev.font == next.font then
if not mclass.par.kern[prev.font] then -- find and cache the kerning table of this font
local fnt = font.getfont(prev.font)
local seq = fnt.resources.sequences
mclass.par.size[prev.font] = fnt.size
for _,t in ipairs(seq) do
if t.features.kern then
mclass.par.kern[prev.font] = t.steps[1].coverage
end
end
end
if mclass.par.kern[prev.font][prev.char] then
local k = mclass.par.kern[prev.font][prev.char][next.char]
if k then
space.width = space.width + k / 1000 * mclass.par.size[prev.font]
end
end
print(space.width)
end
end
end
end
return true
end
luatexbase.add_to_callback(
"pre_linebreak_filter", mclass.par.space, "mclass_par_space");
The end result is that, as if all spaces are first removed, and glyphs properly kerned together (without ligature). And then a normal space is inserted
Below is an example of showing it works on Arno Pro
\definecolor{grey}{cmyk}{0,0,0,0.5}
\def\test#1{\leavevmode\hbox to 0pt{\color{grey}#1\hss}#1}
\begin{document}
\test{A V}\par
\test{A W}\par
\test{W A}\par
\test{V A}\par
\test{f H}\par
\end{document}
The main limitations are
- It will not work inside boxes where no paragraph. It is added to the
pre_linebreak_filter
. However, it might be possible to add it as another call back to make it work in hbox, etc. - It will not check any additional kernings defined by users through the font loader. However, for well made fonts, I found the need for additional kerning is rare and usually are edge cases that do not involve spaces (such as a single quote followed by a double quote in Minion, which is not kerned).
The way I used to find the kerning table is tedious and look quite inefficient to me. If any one can help, many thanks in advance.
The LuaTeX document says that the font table returned by font.getfont
shall have a characters
table, which is there. And each character hash shall has a sub table names kerns
. However, it appears not there, at least not for the OpenType fonts I tested with.
f T
tof\myftspace T
in the input buffer or adjusting the horizontal node list before line breaking or ..f<space>T
orfT
?f<space>T
f<space>T
. Is it intra-word or inter-word whitespace? If it's inter-word whitespace, please explain what's unsatisfactory (in your view) about the space that gets inserted between "pdf" and "TeX" in "pdf TeX".of That
will look spaced tighter thanA Victor
. It is similar to kerning except it applies to word space instead of tracking. Though your answer did not solve my problem, I think it is useful to may people. Examples of how to use call-back is sparse IMO. (However, for intra-word space in particular, I think an even easier way, and probably more appropriate, is to use font handler to define additional kerning table)