\lastnodetype 在 luaTeX 中无法按预期工作

\lastnodetype 在 luaTeX 中无法按预期工作

我目前正在试验linebreak_filterluatex,当使用它来更改/替换换行算法时,\lastnodetype命令停止工作。

MWE 如下:

\def\partest{%
   \directlua{%
     function hpackparagraph  (head) 
       return node.hpack(head)
     end
     callback.register("linebreak_filter", hpackparagraph)
  }%
  \par
}

\setbox0\vbox{ABC\par
  {\tracingonline1\showboxbreadth\maxdimen\showboxdepth\maxdimen\showlists}%
  \showthe\lastnodetype
}

\setbox0\vbox{ABC\partest
  {\tracingonline1\showboxbreadth\maxdimen\showboxdepth\maxdimen\showlists}%
  \showthe\lastnodetype
}

简而言之,该\partest命令改变了段落分隔算法,简单地将未分隔的段落打包到水平盒子中,并将该水平盒子不加改变地返回到垂直列表(独立使用时没用,但这是能显示我的问题的最短版本)。

因此如果你运行这个你会得到:

This is LuaTeX, Version beta-0.70.1-2011061421 (rev 4277) 
 restricted \write18 enabled.
(./lua-bug.tex

### internal vertical mode entered at line 13
\hbox(6.83331+0.0)x469.75499, glue set 427.9494fil, direction TLT
.\whatsit
..\localinterlinepenalty=0
..\localbrokenpenalty=0
..\localleftbox=null
..\localrightbox=null
.\hbox(0.0+0.0)x20.0, direction TLT
.\tenrm A
.\tenrm B
.\tenrm C
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
prevdepth 0.0, prevgraf 1 line
### vertical mode entered at line 0
prevdepth ignored

! OK.
l.14 ...h\maxdimen\showboxdepth\maxdimen\showlists
                                                  }%
? 
> 1.
l.15   \showthe\lastnodetype
                            
? 

### internal vertical mode entered at line 18
\hbox(6.83331+0.0)x41.8056, direction TLT
.\whatsit
..\localinterlinepenalty=0
..\localbrokenpenalty=0
..\localleftbox=null
..\localrightbox=null
.\hbox(0.0+0.0)x20.0, direction TLT
.\tenrm A
.\tenrm B
.\tenrm C
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
prevdepth ignored
### vertical mode entered at line 0
prevdepth ignored

! OK.
l.19 ...h\maxdimen\showboxdepth\maxdimen\showlists
                                                  }%
? 
> -1.
l.20   \showthe\lastnodetype

如您所见,\lastnodetype第一个测试的结果1与预期一致 (hbox)。但第二个测试的结果为 (hbox),这-1意味着当前列表没有节点。

这当然是不正确的,因为它确实在我的原语返回的 hbox 里面有一个 hbox linebreak_filter。我们可以观察到的两个列表之间的唯一区别是,在第一个列表中,hbox 的宽度为 469pt,而在第二个列表中,它的宽度是其自然宽度,但这是预料之中的,因为它是这样打包的。

我猜想标准段落生成器放置的节点列表正在更新数据结构,\lastnodetype但如果应用了这样的过滤器,则不会发生这种情况,这感觉像是一个错误。还是我在这里遗漏了一些基本的东西?

更新

与问题一致,\lastbox在应用的情况下linebreak_filter,该命令也会返回一个空框,即\setbox0\lastbox \showbox0无法返回当前列表中的最后一个对象。

答案1

我现在相当肯定这是 当前实现中的一个错误linebreak_filter。似乎发生了以下情况:当 TeX 构建列表(在本例中为垂直列表)时,它会通过指向该列表的头部和尾部的指针来跟踪它。现在,当段落被分成几行时,这些行会通过换行算法附加到当前垂直列表中,并且指针 tail会更新为指向此列表的新末尾。但是,如果linebreak_filter取代 TeX 的算法,则该过滤器的材料会附加到列表中,但tail指针不会改变。因此在我的示例中,它仍然指向段落之前的节点(当段落开始 vbox 时,该节点恰好为 nil)。

幸运的是,这个tail指针可以从 Lua 代码中访问和修改,所以有一种方法可以解决这个问题。但是,我们无法在 中执行此操作linebreak_filter,因为调用过滤器的当前 luatex 代码恰好需要这个尾部错误的因为它做了一些最后的检查和改变。

但是我们可以做的是使用后续文件post_linebreak_filter来为我们完成工作,因为到那时所有这些事情都已经发生了。因此解决方法如下:

function hpack_paragraph  (head)
  h =  node.hpack(head)
  return h
end

function fix_nest_tail (head)
  tex.nest[tex.nest.ptr].tail = node.tail(head)
  return true
end

callback.register("linebreak_filter", hpack_paragraph)
callback.register("post_linebreak_filter",fix_nest_tail)

第二个过滤器只是更新 tail当前语义嵌套中的 TeX 指针,使其成为换行产生的结果的尾部。有点讨厌,但至少它允许linebreak_filter在当前实现中使用。

如果您想知道这段代码的作用:它无需进行任何进一步的操作即可更改输出,以便每个段落都作为一行返回(并删除诸如 fromitemize或类似环境的缩进),并且脚注或\vadjusts 之类的插入内容保留在这些水平盒子内。因此,它还需要更多功能才能变得有用,但请发挥您的想象力。

更新

Hans Hagen 向我指出,正确的方法linebreak_filter通常需要设置更多变量,即诸如prevdepth和之类的变量prevgraf,并可能添加表示基线跳过的粘合节点。与指针问题相比,tail这些可以直接在过滤器中设置,因此更完整的解决方案将如下所示:

function oneliner(head)
    local h = node.hpack(head)
    local d = tex.baselineskip.width - tex.nest[tex.nest.ptr].prevdepth - h.height
    tex.nest[tex.nest.ptr].prevdepth = h.depth
    tex.nest[tex.nest.ptr].prevgraf  = 1
    local n
    if d < tex.lineskiplimit then
        n = 1
        d = tex.lineskip
    else
        n = 2
    end
    local s = node.new("glue_spec")
    local n = node.new("glue",n)
    s.width = d
    n.spec = s
    return node.insert_before(h,h,n)
end

callback.register("linebreak_filter",oneliner)

-- fix the bug with the tail pointer not being updated:

function fix_nest_tail (head)
  tex.nest[tex.nest.ptr].tail = node.tail(head)
  return true
end

callback.register("post_linebreak_filter",fix_nest_tail)

对于我所考虑的用例,这种额外的编码是不必要的,因为无论如何我都想存储这些框以供日后使用,但如果有人想编写一个真正的换行算法,这种额外的工作肯定是需要的,所以在这里评论一下是有意义的。

相关内容