人们经常被教导“\\
导致换行而不是段落中断”和“变体\\*
还可以防止分页”。
(我在此确认egreg
用户警告“\\*
不应使用”。)
但\\*
经常不起作用!什么时候不起作用\\*
?如何修复?
以下代码生成一个脚注示例,然后生成一个正文示例。请注意,\\*
在正文中使用可能更为常见。
\documentclass{article}
\usepackage{lipsum}
\begin{document}
\lipsum[1-4]\footnote{
no asterisk \\ asterisk \\*
no asterisk \\ asterisk \\*
no asterisk \\ asterisk \\*
no asterisk \\ asterisk \\*
no asterisk \\ asterisk \\*
% the pagebreak happens here
last line}
\lipsum[5]
\lipsum[6-8]
\lipsum[2]
Line 1 \\*
% the pagebreak happens here
Line 2 \\*
Line 3 \\*
Line 4 \\*
Line 5 \\
Line 6
\end{document}
答案1
理论
\\*
如果在垂直模式下使用,则会引发错误;在水平模式下,它会执行以下操作:
\unskip % remove any preceding spaces
\vadjust{\penalty 10000}% \nobreak in vertical mode after this line
\penalty 10000 % \nobreak in horizontal mode
\hfil % fill current line
\penalty-10000 % force line break
因此看起来好像\nobreak
两行之间有一个垂直模式,可以防止分页。
但是,如果 TeX 将段落的行放在垂直列表中会发生什么?
它在框之间插入行间粘连(取决于
\baselineskip
、\lineskiplimit
和的值\lineskip
)。不可丢弃项目后的粘连将构成断点。因此 TeX 在行间粘连之前添加了一个惩罚来控制分页。这个惩罚加上
\interlinepenalty
一些根据上下文而定的其他惩罚(\clubpenalty
、\widowpenalty
、\displaywidowpenalty
、\brokenpenalty
)。那么分页点就是这个惩罚,而不是行间粘连。
通过添加惩罚项,\vadjust
可以将其放在当前行之后,在行间惩罚项和行间粘连之前。因此,我们有:
\hbox % the line
\penalty10000 % \nobreak via \vadjust
\penalty\interlinepenalty+... % interline penalty
\vskip % interline glue
\hbox % next line
因此,如果行间惩罚小于 10000,尽管使用了 ,我们也会有一个断点。如果行间惩罚为 10000 或更大,那么无论是否使用\\*
,我们都不会有一个断点。\nobreak
\\*
然而,有一种情况\\*
确实会阻止换行。也就是说,“TeXbook”并不总是说实话:有一种情况是,TeX 确实不是添加行间粘连。它试图变得聪明,并将行间惩罚合理化为零。它可能会认为:行间粘连之后\hbox
——我们有一个断点;惩罚为零——既不好也不坏,因为已经有一个断点,我们不需要它。在附录 A 中可以找到练习 14.26 的答案:
(当总惩罚为零时,就像本例中的第 3 行和第 4 行之间一样,实际上没有插入任何惩罚。)
然后\nobreak
开始\\*
:
\hbox % line box
\penalty 10000 % of \\* via \vadjust
\vskip <interline glue>
\hbox % next line
瞧,令人惊讶的是,分页符被阻止了。当然,这种过度优化并不是一个“错误”(事实上,TeX 按照定义是没有错误的),我们称之为“设计缺陷”。;-)
为了说明,下面是一个用于实验/研究的 ini-TeX(或纯 TeX)示例。宏\NPB
模拟\\*
:
% iniTeX or plain TeX
\showboxbreadth=10000
\showboxdepth=10000
\tracingonline=1
\catcode`\{=1
\catcode`\}=2
\font\rm=cmr10
\rm
\baselineskip=12pt
\parfillskip=0pt plus 1fil\relax
\hsize=2in
\vsize=4in
\interlinepenalty=1
\clubpenalty=-1
\widowpenalty=150
\def\NPB{% \\*
\unskip
\vadjust{\penalty10000}%
\penalty10000\hfil\penalty-10000 %
}
\noindent
A\NPB B\NPB C
\scrollmode
\showlists
\end
控制台输出:
### vertical mode entered at line 0
### current page:
\glue(\topskip) 0.0
\hbox(6.83331+0.0)x144.54, glue set 137.03998fil
.\rm A
.\penalty 10000
.\glue 0.0 plus 1.0fil
.\penalty -10000
.\glue(\rightskip) 0.0
\penalty 10000
\glue(\baselineskip) 5.16669
\hbox(6.83331+0.0)x144.54, glue set 137.45663fil
.\rm B
.\penalty 10000
.\glue 0.0 plus 1.0fil
.\penalty -10000
.\glue(\rightskip) 0.0
\penalty 10000
\penalty 151
\glue(\baselineskip) 5.16669
\hbox(6.83331+0.0)x144.54, glue set 137.31776fil
.\rm C
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
total height 30.83331
goal height 289.07999
prevdepth 0.0, prevgraf 3 lines
! OK.
l.28 \showlists
第一行之间的内容\hbox
:
\penalty 10000
\glue(\baselineskip) 5.16669
第一个惩罚来自\vadjust{\penalty10000}
。行间惩罚总计为零:\interlinepenalty
+ \clubpenalty
= 1 - 1 = 0
第二种情况:
\penalty 10000
\penalty 151
\glue(\baselineskip) 5.16669
行间惩罚不被抑制:\interlinepenalty
+ \widowpenalty
= 1 + 150 = 151。此惩罚允许在此处分页。
实践
问题在于自动插入的。它通常小于 10000,并且即使其前面有(= )\interlinepenalty
也允许分页。\nobreak
\penalty10000
下面使用了一种解决方法。不是结束行,而是结束段落。然后需要逆转和避免\parskip
和的影响:\parindent
\documentclass{article}
\usepackage{lipsum}
\newcommand*{\NLS}{%
\par
\nobreak
\vspace{-\parskip}%
\noindent
\ignorespaces
}
\begin{document}
\lipsum[1-4]\footnote{
no asterisk \\ asterisk \NLS
no asterisk \\ asterisk \NLS
no asterisk \\ asterisk \NLS
no asterisk \\ asterisk \NLS
no asterisk \\ asterisk \NLS
last line}
\lipsum[5]
\end{document}
然而,这仍然是一种变通方法,并没有为 提供正确的实现\\*
。通过将一个段落拆分为两个段落,插入 之前和之后的换行符最多\par
增加两个行间惩罚,\widowpenalty
或。也就是说,如果增加或现在将行间惩罚增加到 10000,\clubpenalty
则可以作为副作用禁止额外的分页符。\widowpenalty
\clubpenalty
另一种情况问题较少:如果插入 的换行符\par
会变成\clubpenalty
或\widowpenalty
,那么这个惩罚现在就消失了。但由于我们已经禁止了 的分页符\nobreak
,因此额外的惩罚损失并不重要。