有人给了我一个盒子,我的目标是尽可能多地了解盒子里胶水的数量。我可以假设这个盒子不是很大,但里面可能装着任意的东西(所以我不能用\unskip
和朋友来解构这个盒子)。
为了明确起见,假设框\mybox
是用 定义的,\newbox\mybox
然后使用 构造\setbox\mybox\hbox to 50pt{a b c}
。为了获取框内容的自然宽度,我可以这样做\setbox0\hbox{\unhcopy\mybox}\showthe\wd0
(然后我会得到21.66669pt
)。
要知道框是否包含无限拉伸,我可以在一个宽度的框内排版它\maxdimen
并检查其不良性(嗯,这并非完全万无一失,但应该没问题),对于无限收缩也类似。
我感兴趣的是了解有限情况下的拉伸或收缩。我的感觉是应该可以通过使用各种方法\badness
来提取它。这是正确的吗?有没有更有效的方法?\setbox0\hbox spread \somedim{\unhcopy\mybox}
\somedim
答案1
简短的回答:我们可以找到盒子的自然宽度,如果它是有限的,那么可以找到确切的拉伸量(否则只能说“它是无限的”),同样,如果它是有限的,那么可以找到确切的收缩量。
为了明确起见,我们假设它\mybox
是一个水平的盒子,例如
\newbox\mybox
\setbox\mybox\hbox spread 5pt{Some text perhaps.}
要找到它的自然宽度,请执行以下操作\setbox0\hbox{\unhcopy\mybox}
:那么答案就是\wd0
框 0 的宽度。
我们只能通过将盒子的宽度设置为不同于其自然宽度并测试 来探测粘连拉伸或收缩的量
\badness
。将盒子的宽度设置为大于其自然宽度会探测拉伸分量,将其设置为小于其自然宽度会探测收缩分量。在这两种情况下,如果相应的分量(宽度较大时拉伸,宽度较小时收缩)是无限的,则 将\badness
是零,这不会告诉我们有关该无限分量的值或符号的任何信息。我看不出有其他方法可以探测这种无限值,所以让我们专注于有限情况。
劣化程度的一般公式相当复杂:在某些情况下它涉及组合100(t/s)^3
其中t
是盒子的自然宽度与要求的宽度之间的差,s
是拉伸量(如果盒子被挤压,则是收缩量)。幸运的是,我们可以只使用一个非常简单的情况。我们测试拉伸量的基本构造是\setbox0\hbox spread 1sp{\unhcopy\mybox\hskip 0pt plus -\stest}
其中\stest
是我们将改变的维度。此盒子的拉伸量是中的量\mybox
减去\stest
。如果为正,则盒子可以拉伸,并且\badness
不会太大(结果是 100 或更小)。否则,盒子未满,因为它甚至无法拉伸
1sp
,并且为\badness
10000(在测试收缩时,我们得到的盒子过满,为1000000)。因此,我们有一个简单的方法来测试对于任何维度\badness
的拉伸量是否\mybox
为>\stest
或 。因此,任务简化为二分法。<=\stest
\stest
\hbadness=1000000
\hfuzz=\maxdimen
\newdimen\stest
\newdimen\smin
\newdimen\smax
\def\find#1#2#3{%
\smin = -\maxdimen
\smax = \maxdimen
\loop
\stest = \smin
\advance \stest by \smax
\divide \stest by 2 % now \stest=(\smin+\smax)/2 truncated to 0
\ifdim \stest = \smax
\advance \stest by -1sp % ensure that \smin<=\stest<\smax.
\fi
\setbox 0 = \hbox spread #2 1sp {%
\unhcopy\mybox
\hskip 0pt #3-\stest
}%
\ifnum \badness > 100 % (shrink/stretch in \mybox) <= \stest
\smax = \stest
\else
\smin = \stest
\advance \smin by 1sp
% since ">\stest" implies ">=\stest+1sp"
\fi
% In both cases, the interval [\smin,\smax] becomes smaller.
\ifdim\smin<\smax
\repeat
\ifdim\smin>\smax\BOOM\fi% cannot happen, I think
\message{^^JThe #1 is
\ifdim\smin=\maxdimen infinite\else\the\smin\fi.}% (\smin=\smax)
}
\setbox0\hbox{\unhcopy\mybox}
\message{^^JThe natural width is \the\wd0 .}
\find{stretch}{}{plus}
\find{shrink}{-}{minus}
答案2
如果\mybox
是 vbox,那么我们几乎可以完全解决问题,方法是保存当前页面、将框内容转储到主垂直列表上、\pagestretch
在输出例程中累积和朋友,然后恢复当前页面。
此方法不能用于测量无限收缩粘连,因为 TeX 不允许在主垂直列表中使用此类粘连。正如 Bruno 指出的那样,您至少可以通过在高度为 的垂直框中将其拆箱并检查 来检查该框是否包含无限收缩-\maxdimen
粘连\badness
。
\newbox\savedpage
\newdimen\savedprevdepth
\newdimen\stretch
\newdimen\filstretch
\newdimen\fillstretch
\newdimen\filllstretch
\newdimen\shrink
\def\savepage{%
\savedprevdepth=\prevdepth
{\output={\global\setbox\savedpage=\box255}%
\holdinginserts=1 \eject}}
\def\restorepage{\unvbox\savedpage \prevdepth=\savedprevdepth}
\def\findglue{%
\savepage
\stretch=0pt
\filstretch=0pt
\fillstretch=0pt
\filllstretch=0pt
\shrink=0pt
\begingroup
\output={%
\global\advance\stretch\pagestretch
\global\advance\filstretch\pagefilstretch
\global\advance\fillstretch\pagefillstretch
\global\advance\filllstretch\pagefilllstretch
\global\advance\shrink\pageshrink
\setbox0=\box255
\null % Keep discardables after page break.
}%
\vsize=\maxdimen
\topskip=0pt
\null % Keep discardables at top of mybox.
\unvcopy\mybox
\eject
\output={\setbox0=\box255}%
\eject % Remove the \null and \penalty10000 remaining on the page.
\endgroup
\restorepage}
% Only needed for the print-out
{\catcode`\p=12 \catcode`\t=12 \gdef\\#1pt{#1}}
\def\decimal{\expandafter\\\the}
\setbox0=\vbox{\boxmaxdepth=0pt \unvcopy\mybox}
\findglue
Height-plus-depth of box:
\the\ht0\
plus \the\stretch\
plus \decimal\filstretch fil
plus \decimal\fillstretch fill
plus \decimal\filllstretch filll
minus \the\shrink
\savepage
和宏\restorepage
假设您没有手动触摸\pagegoal
、\pagetotal
等,并且\topskip
自保存的页面启动以来这些内容没有发生变化。如果不是这种情况,除了页面内容之外,您还应该保存和恢复这些数量。