我最近有关于如何获得“不间断垂直空间”?是大多已解决,但看起来好像使用\needspace
与布局引擎所做的不一致。
也就是说,我使用时\needspace
,所有东西看起来都应该可以放进去,但它仍然被移到了下一页。通过查看当前位置和剩余空间,甚至目测,都可以确认这一点——当分页符前的列中有五英寸可用空间时,两英寸的物体应该很容易放进去。
为了尝试解决这个问题,我需要确保我理解了什么\needspace
是这个问题,以及如何解决。
\newcommand{\needspace}[1]{%
\begingroup
\setlength{\dimen@}{#1}%
\vskip\z@\@plus\dimen@
\penalty -100\vskip\z@\@plus -\dimen@
\vskip\dimen@
\penalty 9999%
\vskip -\dimen@
\vskip\z@skip % hide the previous |\vskip| from |\addvspace|
\endgroup
}
如果我理解正确的话,这个宏:
- 存储参数(必须是长度)。
- 跳下这段距离(
\z@\@plus\dimen@
...作为橡胶长度?) - 稍微鼓励当前位置休息一下(负面惩罚...... TeX 不会需要在这里休息一下,就像
\newpage
强制一样,但是\penalty- 100
说没关系),然后跳回原来的距离。
到目前为止,我想我知道发生了什么。然后它做了几乎相同的事情:
- 跳过所要求的距离。
- 非常强烈地(
\penalty 9999
几乎是“永远不要在这里中断”)阻止中断。 - 跳回。
- “零跳过”为“跳过清理”提供了可以安全食用的东西。
...现在我不太确定我是否明白发生了什么。
第一部分对我来说是有意义的。向下移动请求的量,探测这是否是一个好的中断点(负惩罚;如果是好的地方就会中断),然后再向上移动。如果没有中断,我们最终会回到开始的地方,请求的空间应该是可用的。如果有中断,那\vskip
真的什么也不做。
为什么要做第二次?
探索 1:MWE
我现在有一个 MWE 可以用来探索这个主题。
\documentclass[letterpaper,10pt,twoside,twocolumn]{article}
\usepackage{lipsum}
\usepackage{tcolorbox}
\usepackage{fp}
\usepackage{needspace}
\usepackage[top=1in,bottom=1in,right=1in,left=1in]{geometry}
\makeatletter
\newlength\egd@nsc@space
\newlength\egd@nsc@needed
\newsavebox\egd@nsc@box
\newenvironment{needspacecalc}[1][0pt]{
\setlength{\egd@nsc@space}{#1}
\begin{lrbox}{\egd@nsc@box}
}{
\end{lrbox}
\setlength{\egd@nsc@needed}{\dimexpr\ht\egd@nsc@box+\egd@nsc@space\relax}
\marginpar{\the\pagetotal\quad\the\pagegoal\quad\the\egd@nsc@needed}
\needspace\egd@nsc@needed
\noindent\usebox\egd@nsc@box
}
\makeatother
\newcommand{\testbox}{
\medskip\par
\begin{needspacecalc}[1.5\baselineskip] % "need 1.5 lines after the box
\begin{tcolorbox}
TColorBox stuff goes in here.
\vskip4\baselineskip\mbox{}
\end{tcolorbox}
\end{needspacecalc}
\medskip\par
}
\raggedbottom
\title{Suppressing Page Breaks MWE}
\begin{document}
\maketitle
\lipsum[4]
\lipsum[4]
\lipsum[4]
\lipsum[4]
\testbox
\lipsum[4]
\bigskip
\lipsum[5]
\testbox
\lipsum[5]
\end{document}
环境needspacecalc
将其内容存储在 中lrbox
,测量该框的高度,将请求的量添加到此高度,然后\needspace
在输出 之前调用lrbox
。 (这又回到了我最初的问题,但在这里使用起来很方便。)
虽然不会\marginpar
在needspacecalc
这里投入生产,但很高兴看到正在发生的事情。
当我按照写的内容运行它时,我得到的几乎都是我所期望的。在左列的底部,我处于 528pt 中的 485pt 位置(全部截断),我想要 104pt,所以显然没有空间,我的块移到了下一列。在右列的底部,我处于 528pt 中的 410pt 位置,我想要 104pt,然后... 块被移动到下一列。可能是因为 410+104 = 514,这有点接近......?无论如何,它被移动了。
如果我\needspace
像这样改变
\penalty -100\vskip\z@\@plus -\dimen@
%\vskip\dimen@
%\penalty 9999%
%\vskip -\dimen@
\vskip\z@skip % hide the previous |\vskip| from |\addvspace|
我得到了相同的结果。我有点预料到了这一点,因为看起来注释掉的指令没有做任何尚未完成的事情……但也许它们做了一些不适用于此处的事情。
但是,无论\needspace
这三行是否存在或被注释掉,如果我改变它,它实际上根本\testbox
不使用环境needspacecalc
\newcommand{\testbox}{
\medskip\par
%\begin{needspacecalc}[1.5\baselineskip] % "need 1.5 lines after the box
\begin{tcolorbox}
TColorBox stuff goes in here.
\vskip4\baselineskip\mbox{}
\end{tcolorbox}
%\end{needspacecalc}
\medskip\par
}
第二块做出现在列的底部,后面是两行文本。曾是有空间,但只有当我没有尝试先保留它。
(如果您感到困惑,而且这不是因为我的写作,那么您并不孤单......)
答案1
几个月前我看了needspace
,并阅读精彩的论文将段落分成几行,所以分享我所理解的(在我忘记一切之前)。
回想一下:\needspace{length}
当我们想说:“下一个 [长度] 的材料应该保持在一起,而不是在分页符上分裂时,应该使用。为了具体起见,我们假设我们使用参数(例如)\needspace
进行调用42pt
。然后,通过查看定义(包含在问题中),您可以看到它导致以下粘合(跳过)和框的序列:
\vskip 0pt plus 42pt
(理想长度0pt
和拉伸性的胶水42pt
)\penalty -100
(负数惩罚,表明这是一个好的破门地点)\vskip 0pt plus -42pt
(理想长度0pt
和拉伸性的胶水-42pt
)\vskip 42pt
(胶水准确42pt
,无拉伸或收缩)\penalty 9999
(高额罚款)\vskip -42pt
(确切的胶水-42pt
)\vskip 0pt
(这个仅适用于查看的宏\lastskip
,如果您附近没有使用此类宏,则可以忽略)
为了理解此项目序列,您需要了解以下几点:
根据 TeX 破坏规则,在上述项目中,仅在惩罚(或一个)之后立即允许破坏
-100
,9999
或者在第一个胶水之后如果在其前面有一个盒子(不可丢弃的项目)。TeX 插入断点后,它会丢弃紧随其后的所有惩罚和粘合。
对于分页符(与段落中的换行符不同),TeX 不使用全局算法(“最佳拟合”),而是在本地为每个页面做出最佳决策(“最佳拟合”)。更详细地说:每次 TeX 向页面添加项目(比如:行)时,它都会计算在该页面处分页的成本(使用不良度和惩罚)。当页面内容过满时或者当遇到≤-10000的惩罚时,TeX知道它不能继续在页面上堆积更多的东西,并在目前看到的最佳(成本最低)位置拆分页面。(剩下的内容将成为下一页的材料。)
因此,对于上面的项目列表,分页符有四种允许的可能性,以及每种情况下会发生什么情况:
无分页符
在这种情况下,没有中断,惩罚无关紧要,胶水巧妙地相互抵消。(这很重要,没有引入任何奇怪的事情。)
第页后分页符
\vskip
这种情况永远不会发生(即使允许),因为这比紧接着发生以下情况更糟糕
\penalty -100
:在这种情况下(与这种情况相比),TeX 会42pt
在第一页上获得额外的可拉伸性(这会降低不良程度)和负面惩罚。分页符后
\penalty -100
在这种情况下,
42pt
额外的可拉伸性被添加到第一页(以便在必要时它可以是参差不齐的底部),而在新的页面上,所有的前导粘连都被删除,因此其余的项目将被忽略。分页符后
\penalty 9999
这也永远不会发生(尽管勉强允许),同样是因为
\penalty -100
出于类似的原因,这比在之后中断更糟糕。(练习:证明或提出反例。)
好吧,我们可以在 处中断,也可以不中断,但是我们的项目列表如何实现:在下一个 (大约) 内不中断的\penalty -100
目标?好吧,考虑以下两种情况:needspace
42pt
该页面还有(远远)超过
42pt
剩余的内容(也就是说,
42pt
可以塞入的内容比页面可以塞入的内容多,而不会使页面变得过满。)在这种情况下,TeX 会考虑上述列表中的两个或三个合法断点,记下它们的成本(位于 的那个\penalty -100
是最好的),然后继续,最终可能会在通常的位置断页。这是因为在 处断页\penalty -100
会获得一点可拉伸性(降低不良性)和惩罚,代价是必须将页面拉伸至少剩余的长度42pt
(增加不良性)。(这个成本可能会抵消收益,但不能保证,这就是为什么needspace
说“大约”,也是你看到你所看到的内容的原因。)因此可能的结果:上述“无分页符”的情况(但“在 处断页”的\penalty -100
情况也仍然有点可能,这是 errs 的方向needspace
)。该页面剩余空间
42pt
不足在这种情况下,一旦 TeX 看到
\vskip 42pt
(列表中的第四项),页面已经满了,因此一旦 TeX 看到下一个\penalty 9999
(允许的断点,尽管很糟糕)它就会选择迄今为止看到的最佳断点。可能的结果:\penalty -100
上面的“分页符在 ”情况,或者可能只是一些更早的断点。
我希望现在您可以明白为什么序列中的每个项目都是必要的:实际的“如果没有42pt
剩余空间则强制分页”是通过 实现的\vskip 42pt\penalty 9999
,\penalty -100
前一个旨在为执行此操作提供一个良好的断点(如果需要),在\vskip 0pt plus 42pt
它之前添加以便为第一页提供足够的拉伸,而其他的(负高度和拉伸)是在不进行分页的情况下添加的以抵消这些的影响。
\tracingpages=1
您可以通过编译文档并查看日志文件来看到这些审议的痕迹。
广告
虽然上述案例最终取决于用于分页的贪婪算法,但其精神与 Knuth-Plass 中许多优雅的排版问题解决方案类似。将段落分成几行论文中的各种问题——作者行、不规则的右对齐和居中文本、排版源代码或不同宽度的索引——都可以通过这些“神奇”的方框、胶水和惩罚序列来解决。(这些也出现在TeXbook作为双重危险弯举练习和附录 D:肮脏的伎俩,但论文中更具说明性。)在展示这些解决方案之后,有一些“代数”可以帮助您自己提出类似的构造。整篇论文读起来很有趣。最新版本(符号更接近 TeX,例如使用~
for ties 而不是&
)出现在书中数字排版,但你也可以在网上找到早期版本,其中的精彩想法都在那里。
编辑:
有了这种理解,我们可以调整宏以更好地适应我们的偏好。例如,如果我们不喜欢在第一页不必要地中断:
- 为什么要用 -100 作为惩罚?如果无法满足所需长度,那么已经保证会分页。通过分页,TeX 已经获得了可伸缩性;为什么要用负惩罚进一步提升它?因此,您可以尝试将惩罚改为 0 甚至略微正的值。
- 为什么要在第一页上添加这么多的可拉伸性,让这个中断如此令人向往?可拉伸性不必与传递给的尺寸长度完全相同
\needspace
;您可以减小它。如果您希望页面与底部齐平,您甚至可以将其完全删除,并让 TeX 根据需要拉伸第一页。
在您的 MWE 中,其中
\needspace
用104.86pt
调用,如果我们将惩罚从 -100 增加到 43(保持可拉伸性相同),或者将可拉伸性从104.86pt
降低到 ≤92.81pt
(惩罚为 0)或 降低到 ≤(保持惩罚为 -100),则可以避免中断(在76.7pt
第二个块之前)。[插入对“有一个包可以解决这个问题”现象的抱怨,而不是通过向用户提供信息来赋予他们权力,让他们自己想出解决方案,这使得系统不那么神秘。但通常前者会获胜;这并不是 LaTeX 所特有的。
你可能不需要
needspace
!如果你正在计算一些你想避免破坏的材料(一个盒子或段落或其他任何东西)的长度,并使用\needspace
该长度,那么你可能可以通过在其后添加适当的胶水和惩罚序列来获得更好的结果。
答案2
首先,主要的假设是错误的:\vskip 0pt plus \dimen@
与 非常不同\vskip \dimen@
。第一个是 0pt 空间,可以(但不需要拉伸)。
现在让我们尝试一下如果删除定义的某些部分会发生什么:
\documentclass[]{book}
\makeatletter
\newcommand{\needspace}[1]{%
\begingroup
\setlength{\dimen@}{#1}%
\vskip\z@\@plus\dimen@
\penalty -100
\vskip\z@\@plus -\dimen@
% \vskip\dimen@
% \penalty 9999%
% \vskip -\dimen@
\vskip\z@skip % hide the previous |\vskip| from |\addvspace|
\endgroup
}
\begin{document}
Without the second vskip needspace doesn't do anything:
\needspace{100cm}
blbl
\end{document}
\documentclass[]{book}
\makeatletter
\newcommand{\needspace}[1]{%
\begingroup
\setlength{\dimen@}{#1}%
% \vskip\z@\@plus\dimen@
% \penalty -100
% \vskip\z@\@plus -\dimen@
\vskip\dimen@
\penalty 9999%
\vskip -\dimen@
\vskip\z@skip % hide the previous |\vskip| from |\addvspace|
\endgroup
}
\begin{document}
Without the first vskip needspace works as expected:
\needspace{100cm}
blbl
\end{document}
但
在日志中收到一条消息:
Underfull \vbox (badness 10000) has occurred while \output is active []
因为断点是前\vskip (在粘连之前通常是一个断点)
\documentclass[]{book}
\makeatletter
\newcommand{\needspace}[1]{%
\begingroup
\setlength{\dimen@}{#1}%
\vskip\z@\@plus\dimen@
\penalty -100
\vskip\z@\@plus -\dimen@
\vskip\dimen@
% \penalty 9999%
\vskip -\dimen@
\vskip\z@skip % hide the previous |\vskip| from |\addvspace|
\endgroup
}
\begin{document}
Without the second penalty needspace doesn't work:
\needspace{100cm}
blbl
\end{document}
在这种情况下,在 TeX 开始考虑分页符之前,两个 \vskip\dimen@ 会相互抵消。
结论
您需要 \vskip 0pt 加上拉伸来填充第一页并避免页面不足
您需要 \vskip \dimen@ 来获取所需的分页符
你需要第一个罚球作为破发点
您需要第二个惩罚来避免两个 vskips 相互抵消。
答案3
这就是我保留空间的方式。当\vspace
在行中间使用时,它会等到行末才实现。请注意,\vspace
在页面顶部或底部会忽略。因此,如果\rule
延伸得太远,它要么在下一页结束,要么\vspace
被忽略(不确定是哪个)。
\documentclass[letter]{article}
\usepackage{showframe}
\usepackage{lipsum}
\newcommand{\needspace}[1]{\rule{0pt}{#1}\vspace{-#1}\vspace{-\parskip}\par}
\begin{document}
\rule{1pt}{40\baselineskip}% 41 will force a break.
\needspace{5\baselineskip}
\lipsum[1]
\end{document}