即使连字删除了上下文,仍会出现上下文相关的字形

即使连字删除了上下文,仍会出现上下文相关的字形

Alegreya字体有一个非常巧妙的功能,当后面跟着 时,它会用f一个具有更长交叉笔划的变体替换常规字形。结果是 和 中的两个交叉笔划连接在一起,因此序列看起来像连字。我喜欢这个功能。ftftft

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

相关内容