LuaTeX 节点库示例:例如将 raggedright 线条转换为 raggedleft 线条等

LuaTeX 节点库示例:例如将 raggedright 线条转换为 raggedleft 线条等

除了发布我的问题中具体示例的正确答案外,请随意留下展示节点库功能的简短代码片段

我是 LuaTeX 节点库的新手,正在学习如何利用它通过 post_linebreak_filter 对行进行后处理。到目前为止,我能够进行非常基本的操作,就像我自己的问题的答案一样这里。接下来,我尝试遍历 hlist 节点,希望改变它们的 leftskip/rightskip 并将 raggedright 文本转换为 raggedleft。我的尝试和错误没有奏效,我在下面发布了我的错误尝试(警告:尝试 1 似乎进入了无限循环,因此不要在主终端中尝试)。有人可以解释一下这些有什么问题吗,并展示正确的代码是什么样子(并附上一些解释)?

% Attempt-1: Set leftskip/rightskip outside, and rebox the contents using hpack?
\directlua{
    function my_post_lb_filter(head,groupcode)
      local HLIST = node.id("hlist") % node.id for a line of text in vertical list
      for n in node.traverse(head) do % For every subnode within paragraph
        if n.id==HLIST then % If its a line of text
            tex.setglue("rightskip",0,0,0,2,2)
            tex.setglue("leftskip",0,65536,0,2,2)
            local b = node.copy(n)
            b = node.hpack(b.head)
            node.write(b)
        end
      end
      return head
    end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}

% Attempt-2 Traverse glue nodes within lines, and surgically update them
\directlua{
    function my_post_lb_filter(head,groupcode)
      local HLIST = node.id("hlist") % node.id for a line of text in vertical list
      local GLUE = node.id("glue")
      local RSKIP = node.subtype("rightskip")
      local LSKIP = node.subtype("leftskip")
      for n in node.traverse(head) do % For every subnode within paragraph
        if n.id==HLIST then % If its a line of text
            for g in node.traverse(n) do % For every subnode within line
                if g.id==GLUE then % If its a glue
                    if g.subtype == RSKIP then
                        node.setglue(g,0,0,0,2,2)
                    end
                    if g.subtype == LSKIP then
                        node.setglue(g,0,65536,0,2,2)
                    end
                end
            end
        end
      end
      return head
    end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}

关于尝试 2 的评论:根据我对 hlist 节点内部的观察,raggedright 行中缺少 'leftskip' 子节点。那么如何才能将 leftskip 子节点添加到这样的行中?而 raggedleft 行中同时包含 'leftskip' 和 'rightskip' 子节点,所以我猜这只是更新它们的问题。

答案1

首先让我们看看您的第一次尝试:

你使用了

            tex.setglue("rightskip",0,0,0,2,2)
            tex.setglue("leftskip",0,65536,0,2,2)
            local b = node.copy(n)
            b = node.hpack(b.head)

这里node.hpack已用于重新打包,以便获取更改的rightskip/leftskip设置,但 TeX 的工作方式并非如此:rightskipleftskip不是作为打包 hbox 的一部分应用的,而是在换行期间应用的。因此,对于这种方法,您必须使用回调linebreak并在那里更改参数。

另外,使用node.write几乎总是会导致回调出现问题。该函数会将一个节点添加到 TeX 正在处理的当前列表中,这有时不是您认为 TeX 正在处理的列表。相反,尝试使用作为传递的列表head。在这种情况下,这恰好是同一个列表,但您正在复制hlist节点并将它们附加在末尾。因此,在处理常规 hbox 节点后,列表的前端不再是末尾,而是后面跟着您复制的节点。然后处理这些复制的节点,从而创建更多副本。这导致了无限循环。

因此第二种方法更有希望。您的代码已经非常接近了,剩下的步骤是:

  • node.subtype仅适用于 whatsit 节点,对于其他节点,您要么对值进行硬编码,要么分析返回的表node.subtypes
  • 我们必须遍历n.head而不是n,否则您永远不会查看列表的实际内容。
  • 我们必须确保左跳过粘合确实存在。我们可以保证左跳过粘合始终是 hlist 中的第一个节点,但如果我们已经有一个左跳过,则在迭代时记住这一点会更安全一些。(这样,如果另一个包添加了某个较早的节点,我们就不会遇到问题)。

    要实际创建节点,我们可以使用node.new,然后将其插入到列表中node.insert_before

  • 这里使用 node.traverse_id 代替 node.traverse 可以简化一些。这样就不需要那么多 if 语句,而且速度也更快一些:
\documentclass{article}
\usepackage{blindtext}
\directlua{
  local HLIST = node.id("hlist") % node.id for a line of text in vertical list
  local GLUE = node.id("glue")
  local RSKIP, LSKIP do
    local gluetypes = node.subtypes("glue")
    for i, n in pairs(gluetypes) do
      if n == "leftskip" then LSKIP = i end
      if n == "rightskip" then RSKIP = i end
    end
  end
  function my_post_lb_filter(head,groupcode)
    for n in node.traverse_id(HLIST, head) do % For every subnode within paragraph
      local leftskip_found
      for g, s in node.traverse_id(GLUE, n.head) do % For every subnode within line
        if s == RSKIP then
          node.setglue(g)
        end
        if s == LSKIP then
          node.setglue(g,0,65536,0,2,0)
          leftskip_found = true
        end
      end
      if not leftskip_found then % We have to add a glue node
        local g = node.new(GLUE, LSKIP)
        g.subtype = LSKIP
        node.setglue(g, 0, 65536, 0, 2, 0)
        g.attr = n.attr % Ensure that attributes have some reasonable value
        n.head = node.insert_before(n.head, n.head, g)
      end
    end
    return head
  end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}
\begin{document}
\showoutput
\raggedright

\blindtext
\end{document}

在此处输入图片描述

你可能会注意到给读者留了一点练习:最后一行目前处于居中状态。解释为什么会发生这种情况并找出解决方法。

相关内容