我在排版 pdf 文档时使用 LuaTeX 及其相关回调(pre_linebreak_filter
、post_linebreak_filter
和mlist_to_hlist
)来分析行和段落。这很正常,但我遇到了图形标题的问题。
对于分成多行的较长标题,一切都按预期工作。pre_linebreak_filter
报告全文,而post_linebreak_filter
报告vlist
各行内容。
如果字幕足够短,可以放在一行中(即不需要换行),则字幕的处理不会产生任何回调。这引出了我的问题:
- 这种行为是否有意为之,是否与其他单行内容(如标题或作者行)形成对比,而这些单行内容会生成这些回调?
- 我怎样才能知道单行字幕是否被处理了?
附言:请注意,标题内容显示在 的输出程序集回调中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_filter
和hpack_filter
通常会带来更好的性能。