LuaTeX 单行字幕回调

LuaTeX 单行字幕回调

我在排版 pdf 文档时使用 LuaTeX 及其相关回调(pre_linebreak_filterpost_linebreak_filtermlist_to_hlist)来分析行和段落。这很正常,但我遇到了图形标题的问题。

对于分成多行的较长标题,一切都按预期工作。pre_linebreak_filter报告全文,而post_linebreak_filter报告vlist各行内容。

如果字幕足够短,可以放在一行中(即不需要换行),则字幕的处理不会产生任何回调。这引出了我的问题:

  1. 这种行为是否有意为之,是否与其他单行内容(如标题或作者行)形成对比,而这些单行内容会生成这些回调?
  2. 我怎样才能知道单行字幕是否被处理了?

附言:请注意,标题内容显示在 的输出程序集回调中pre_output_filter。但是,段落从属信息在此阶段已经丢失。因此,这对我没有帮助。

聚苯硫醚:举个例子,只需添加

\begin{figure}
  \centering
  \includegraphics{ FILENAME }
  \caption{ TEXT }
\end{figure}

在普通文章中,文本可以放在一行中,也可以不放在一行中。回调通过 TeX 源中的以下代码链接

\directlua{ luatexbase.add_to_callback("pre_linebreak_filter", pre_callback, "pre_callback") }
\directlua{ luatexbase.add_to_callback("post_linebreak_filter", post_callback, "post_callback") }

在单独的 Lua 文件中,以下函数处理回调

pre_callback = function (head)
  texio.write_nl("PRE CALLED")
  -- Recursive inspection of head
end

post_callback = function (head)
  texio.write_nl("POST CALLED")
  -- Recursive inspection of head
end

对于短标题文本和长标题文本,pre_linebreak_filter和均post_linebreak_filter报告图像本身(以whatsit子类型为 41、40、42、14 的节点形式)并在日志文件中输出“CALLED”。但仅对于长标题,标题文本本身在单独的回调中报告(因此“PRE CALLED”和“POST CALLED”在日志文件中出现两次)。对于短标题,不会为标题文本引发回调,“PRE CALLED”和“POST CALLED”仅出现一次。

聚苯乙烯树脂:我正在使用 LaTeX2e <2011/06/27> 和 LuaTeX 0.76.0。

答案1

如果字幕足够短,可以放入一行(即不需要换行),则字幕的处理不会产生任何回调。

正如 /u/egreg 所解释的那样一条评论,不同之处在于在某些情况下,不会创建段落。因此,水平列表上不会调用换行算法;因此,pre_linebreak_filter不会触发回调。您可以通过将文本放入 hbox 或 vbox 来观察这种差异:

\hbox {Horizontal list only, without line break.}         \par
\vbox {Vertical list, triggers the line break mechanism.} \par

仅第二行受到换行的影响。

这种行为是否有意为之,是否与其他单行内容(如标题或作者行)形成对比,而这些单行内容会生成这些回调?

这种行为确实是有目的的:LuaTeX 提供了另一个回调——hpack_filter每当 TeX 打包一个 hbox 时调用。您可以按照与 相同的方式使用它pre_linebreak_filter,尽管回调在 Lua 端接收了包含有关 TeX 当前所处状态的信息的进一步参数。


为了说明问题,我添加了一些示例代码(使用 Luatex 0.78.2 测试)。首先是 Lua 文件;其文件名应仅在扩展名上与下面的 TeX 代码不同:

local traverse_nodes      = node.traverse
local iowrite             = io.write
local tableconcat         = table.concat
local utfchar             = unicode.utf8.char

local nodecodes           = table.mirrored (node.types())
local glyph_t             = nodecodes.glyph
local glue_t              = nodecodes.glue

local cbk = function (hd, group, size, pack)
  --[[--
    Demo function collecting text from glyph nodes in the list starting
    with ``hd``, printing them to stdout.
  --]]--
  local text = {
    "\n["
    .. (group == "" and "par" or group)
    --- the line below shows more info from the hpack_filter context
    --.. (size and (":" .. tostring (size) .. ":" .. pack) or "")
    .. "] "
  }
  for n in traverse_nodes (hd) do
    local ntype = n.id
    if ntype == glyph_t then
      text [#text + 1] = utfchar (n.char)
    elseif ntype == glue_t then
      text [#text + 1] = " "
    else -- print node type
      text [#text + 1] = "<" .. nodecodes [ntype] .. ":" .. tostring (n.subtype) .. ">"
    end
  end

  text [#text + 1] = "\n"
  iowrite (tableconcat (text))
  return true
end

luatexbase.add_to_callback ("pre_linebreak_filter", cbk, "document.nodeprinter")
luatexbase.add_to_callback ("hpack_filter",         cbk, "document.nodeprinter")

一些 Latex 代码显示了不同的情况:

\documentclass    {scrartcl}
\usepackage       {luatexbase}
\RequireLuaModule {lualibs}
\directlua        {dofile "\jobname.lua"}

\begin {document}

  \hbox {Horizontal list only, without line break.}         \par
  \vbox {Vertical list, triggers the line break mechanism.} \par

  \begin {figure}
    \input ward
    \caption {Some brief caption.} %% same as with the hbox above
  \end {figure}

  This is some \hbox{ordinary} paragraph material.

\end {document}

您还可以方便地克隆以下代码: 要点


通常,在处理文本节点时,您需要在两个回调中注册您的函数。不过,有一些注意事项。

hpack_filter主要问题影响执行顺序:在将段落移交给换行机制之前,在单独的步骤中调用。例如,在以下代码片段中:

This is some \hbox{ordinary} paragraph material.

hpack_filter在将文本的其余部分传递到之前, 包含文本“普通”的水平框会在 中进行处理 pre_linebreak_filter。根据您想要实现的目标,这可能会变得一团糟。

解决这个问题的方法是不依赖于hpack_filter并递归遍历节点列表,例如:

local traverse_nodes      = node.traverse
local iowrite             = io.write
local stringformat        = string.format
local stringrep           = string.rep
local nodelength          = node.length
local count_nodes         = node.count

local nodecodes           = table.mirrored (node.types())
local hlist_t             = nodecodes.hlist
local vlist_t             = nodecodes.vlist

local rec_cbk rec_cbk = function (hd, _, depth, where)
  --[[--
    Recursively walk a paragraph node list and print basic info.
  --]]--
  if not depth then
    iowrite "\n"
    depth = 1
    where = "par"
  end
  iowrite (stringrep (" ", depth) ..
           stringformat ("[%s:%d] %d nodes, %d glyphs\n",
                         where, depth,
                         nodelength (hd),
                         count_nodes (glyph_t, hd)))
  for n in traverse_nodes (hd) do
    local ntype = n.id
    if ntype == vlist_t then
      rec_cbk (n.head, nil, depth + 1, "vbox")
    elseif ntype == hlist_t then
      rec_cbk (n.head, nil, depth + 1, "hbox")
      --- else pass
    end
  end
  return true
end

luatexbase.add_to_callback ("pre_linebreak_filter", rec_cbk, "document.recursenodes")

使用上面的代码并排版一些稍微复杂一点的东西:

  This is some \vbox {\hbox {significantly}
                      \hbox {less}
                      \hbox {ordinary}} paragraph material.

现在将按照自然顺序遇到水平盒子和垂直盒子材质。

不幸的是,如果你想处理浮动字幕,这不是一个选项。此外,结合使用pre_linebreak_filterhpack_filter 通常会带来更好的性能。

相关内容