这Alegreya
字体有一个非常巧妙的功能,当后面跟着 时,它会用f
一个具有更长交叉笔划的变体替换常规字形。结果是 和 中的两个交叉笔划连接在一起,因此序列看起来像连字。我喜欢这个功能。f
t
f
t
ft
f
但是,当行尾连字符通过拆分为 而删除上下文时,也会选择具有较长交叉笔划ft
的字形变体f-t
。现在f
变体看起来完全是错误的和奇怪的。
\documentclass{article}
\usepackage{fontspec}
\setmainfont{Alegreya}%
[Path = /usr/local/texlive/2015/texmf-dist/fonts/truetype/huerta/alegreya/,
UprightFont = Alegreya-Regular.ttf]
\begin{document}
\parbox{0pt}{\hspace{0pt}for after}
\end{document}
第一个单词for
说明了f
当 后面没有 时 应该是什么样子t
。显然,这里发生的事情是,首先f
用字体的f.t
变体替换 ,然后LaTeX
通过引入连字符打断了序列,现在显然“太晚了”,字符f
无法恢复到其常规字形变体。
所以现在的问题是:有没有办法告诉 LaTeXf
在后面跟着换行连字符时选择常规字形变体?
我曾经lualatex
编译过上面的示例,并且如果可能的话我打算坚持使用该引擎。
答案1
我不知道该功能在字体中是如何实现的,但我猜 Luaotfload 缺少对它的支持。以下代码只是针对此问题的快速修复,理想情况下应该在 Luaotfload 中修复。
完整代码如下,下面提供一些解释:
\documentclass{article}
\usepackage{fontspec}
\usepackage{luacode}
\begin{luacode*}
local hlist_id = node.id "hlist"
local glyph_id = node.id "glyph"
local utfchar = unicode.utf8.char
local hyphen_char = 45
local break_subtype = 0
charreplaces = {}
local function get_char(c)
local x = tonumber(c)
if not x then
return string.byte(c)
end
return x
end
function char_replace(old, new)
local old = get_char(old)
local new = get_char(new)
print(old, new)
charreplaces[old] = new
end
local function get_next(n)
if n and n.id == glyph_id then
return n
elseif n then
return get_next(n.prev)
end
end
local function get_last_glyph(head)
local last = node.tail(head)
return get_next(last)
end
local function fix_end_f(head)
for n in node.traverse(head) do
if n.id == hlist_id then
local last = get_last_glyph(n.head)
if last and last.subtype == break_subtype and last.char == hyphen_char then
local prev = get_next(last.prev)
local c = prev.char
print("Last char: ", c)
if charreplaces[c] then
print("Replacing", c)
prev.char = charreplaces[c]
end
end
end
end
return head
end
luatexbase.add_to_callback("post_linebreak_filter", fix_end_f, "fix_end_f")
\end{luacode*}
\newcommand\CharReplaceAtEnd[2]{
\luaexec{char_replace(\luastring{#1},\luastring{#2})}
}
\CharReplaceAtEnd{983056}{f}
\setmainfont{Alegreya}%
[Path = /usr/local/texlive/2015/texmf-dist/fonts/truetype/huerta/alegreya/,
UprightFont = Alegreya-Regular.ttf]
\begin{document}
\parbox{0pt}{\hspace{0pt}for after}
for after
\end{document}
节点处理回调用于修复字符。因为我们需要在换行后应用修复,所以我们需要使用post_linebreak_filter
。
local hlist_id = node.id "hlist"
local glyph_id = node.id "glyph"
local utfchar = unicode.utf8.char
local hyphen_char = 45
local break_subtype = 0
我们在那里定义了一些常量,在回调中,我们需要处理节点列表。post_linebreak_filter
此列表包含hlist
节点,每个节点包含一行。我们需要从行中获取最后一个字形,以测试它是否是换行符连字符,如果是,则获取前一个字形,将其打印到标准输出并测试它是否在替换表中。如果是,我们可以用正确的字符替换错误字符。
local function fix_end_f(head)
for n in node.traverse(head) do
if n.id == hlist_id then
local last = get_last_glyph(n.head)
if last and last.subtype == break_subtype and last.char == hyphen_char then
local prev = get_next(last.prev)
local c = prev.char
print("Last char: ", c)
if charreplaces[c] then
print("Replacing", c)
prev.char = charreplaces[c]
end
end
end
end
return head
end
为了使界面更加用户友好,提供了一个 LaTeX 宏:
\newcommand\CharReplaceAtEnd[2]{
\luaexec{char_replace(\luastring{#1},\luastring{#2})}
}
它可以这样使用:
\CharReplaceAtEnd{983056}{f}
我们如何找到该值983056
?在 LaTeX 输出中搜索Last char
。它将打印换行符连字符之前的所有字符。
答案2
另一种解决方案是使用Renderer = Basic
:
\documentclass{article}
\usepackage{fontspec}
\setmainfont{Alegreya}[Renderer = Basic]
\begin{document}
\parbox{0pt}{\hspace{0pt}for after}
\end{document}
Renderer = Basic
但是,这会导致字体的字距调整不正确,因为此选项会阻止luaotfload
解释基于类的字距调整:https://github.com/khaledhosny/libertine/issues/3#issuecomment-133849174