我们都知道(或相信)\unskip
在主垂直模式下这是不可能的,这个说法可以在网站上的各种答案中找到。是的,LaTeX 做了很多工作来\addvspace
解决\addpenalty
这个问题。所以当我在在 multicol 环境中,第二列始终低于第一列一个简单的解决方法\unskip
。
这在主垂直列表中!
因此,查看 TeX 程序本身,条件是如果列表中的最后一项是粘合项,并且该粘合项已经贡献给当前页面,则会触发错误。但是什么时候会发生这种情况?
XXX
\par
\vskip 1cm
\vskip 5cm
\unskip\unskip
XXX
\bye
这种方法非常完美,并且根据 TeXbook 的说法,只有 5 种情况会将内容从最近的贡献移动到当前页面(第 122 页):
以下是可能发生这种情况的时间列表:(a)在段落的开头或结尾,前提是该段落正在添加到主垂直列表中。(b)在该段落中显示的等式的开头或结尾。(c)
\halign
在垂直模式下完成后。(d)在向主垂直列表贡献一个框或惩罚或插入之后。(e)在例程\output
结束后。
不幸的是,它并不十分准确(可能还有一些其他地方对此进行了进一步讨论,但我忽略了)。例如,如果您\vskip
在上面的示例中在后面添加一个空行,那么它会触发错误,即使这\par
显然不会触发或结束任何段落。
我可以想象从情况(e)来创建它,例如,通过让输出例程在主垂直列表中添加胶水,尽管我还没有尝试过。
现实中还有哪些其他情况会引发这种情况?
答案1
查看源代码tex.web
,我收集了以下内容,但我不确定其中任何一条。从(单独的)行
primitive("unskip",remove_item,glue_node);
any_mode(remove_item): delete_last;
我看到\unskip
调用了该过程delete_last
,其中有以下注释tex.web
:
类似地
\lastbox
,此命令在垂直模式下是不允许的(内部垂直模式除外),因为垂直模式下的当前列表会发送到页面构建器。但如果我们碰巧能够在垂直模式下实现它,我们就会这么做。
换句话说,TeX 会尽最大努力移除某些内容,但有时这是不可能的。事实上,delete_last
如果当前模式是,vmode
并且(我相信)没有最近的贡献,则“对现在无法执行操作表示歉意”的代码,除非\unskip
遵循非粘合节点(在这种情况下\unskip
不执行任何操作)。因此,您需要找出何时将内容移出最近的贡献的评估是正确的。
主垂直列表分为两部分:从page_head
到page_tail
表示当前页面,从contrib_head
到contrib_tail
表示最近的贡献。搜索contrib_
,我发现在几个地方使用了变量:
- 在程序中
show_activities
,link(contrib_head)
进行查询,以了解是否有任何最近的贡献; - 该程序
build_page
似乎将材料从最近的贡献移动到当前页面,因此对我们来说至关重要; link(contrib_head):=link(p)
在两个地方使用来向最近的贡献添加节点;q:=new_skip_param(top_skip_code)
然后link(contrib_head):=q
在添加第一个框时将 topskip 添加到页面;- 当在给定节点分页时,通过操作
contrib_head
和,当前页面中后续的内容将放置在最近的贡献之前contrib_tail
; - 默认的输出例程似乎将当前页面放回到最近的贡献中(当然除了已经拆分并放入的页面
\box255
),然后 shipout\box255
; - 在用户定义的输出例程之后也会发生同样的情况(为业务保存
\box255
)。
从中我们可以看出,这build_page
可能就是你想要的。它在以下情况下被调用(标有与您的列表相对应的字母):
- (a)当一个段落以 开始
vmode
(通过水平命令,如\indent
、字母\discretionary
等)时,插入\everypar
到输入流后; - (b)
\everydisplay
在输入流中插入标记列表并设置进入显示模式的各种参数后,如果周围模式为vmode
; - (b) 显示结束后,返回水平模式(并保存语言等),如果周围模式为
vmode
; - (a)
\par
在水平模式下以 结束一个段落后,如果结果模式是vmode
; - (c)当以
\halign
结尾时vmode
; - (d) 在 中插入惩罚后
vmode
; - (d)在程序内
box_end
,如果完成的箱子(或箱子登记册,或......)非空并且要被添加到主垂直列表中(而不是存储,运出或用作领导者); - (d) 当
\insert
或\vadjust
以端基字符结尾时,如果结果模式为vmode
; - (e)在 TeX 完成用户的输出例程后,检查是否为
\box255
空,并将所有当前页面材料放入最近的贡献中(参见上文); - 当遇到
\par
时vmode
; - 插入后,
\hbox to \hsize{}\vfill\penalty-'10000000000
如果遇到原始情况时主垂直列表中仍有一些材料\end
。
与您的列表相比,出现了一些差异。在默认输出例程之后,build_page
似乎没有使用。\end
您的列表中缺少的情况。更重要的是,您的列表中没有明确给出不受限制的垂直模式下的情况\par
(尽管我猜(a)适合它)。这个案例解释了为什么
XXX\par
\vskip 1cm
%
\unskip
删除 后失败%
。与预期相反,\par
在垂直模式下不会被忽略:它将材料从最近的贡献移动到当前页面,但也会重置一些参数(、 和\looseness
)\hangindent
。这让我很惊讶。\hangafter
\parshape
我发现最后一件事。该程序build_page
没有被使用,append_glue
因为该程序append_glue
“至少在一个地方被使用,而那是一个错误”。我不知道错误在哪里。但这解释了为什么\vskip 1cm
TeX 不会将任何材料移动到当前页面。