尝试构建一个复杂的宏,我设法计算出我需要的 vbox 内容。现在,我希望\unvbox
这个框允许分页,并希望它包含的每个元素都居中。
我找不到在每个元素的左侧和右侧添加胶水的方法。这可能吗?还有其他选择吗?
编辑:我想要处理的典型盒子是这种类型:
\vbox{
\halign{\hfil#&\hfil#\hfil&#\hfil\cr
aa & b & cc\cr
d & eee & f\cr}
}
(我并不想修改它们,因为我需要\wd
在某个时候访问它们)。
答案1
第一种方法
该代码是纯 TeX,但也可以在 LaTeX 中使用;该宏\centervbox
将框寄存器号作为参数并输出相同的框,但相对于边距居中。
\catcode`\@=11
\def\centervbox#1{\begingroup\global\setbox\@ne=\box\voidb@x
\setbox\z@=\copy#1\relax\docentervbox}
\def\docentervbox{%
\setbox\z@=\vbox{\unvbox\z@ \setbox\tw@=\lastbox
\skip@=\lastskip\unskip
\global\setbox\@ne=\vbox{
\vskip\skip@
\hbox to\hsize{\hfil\box\tw@\hfil}
\unvbox\@ne}}%
\ifdim\ht\z@=\z@
\def\next{\unvbox\@ne\endgroup}%
\else
\let\next\docentervbox
\fi
\next}
\catcode`\@=12
\vsize=5\baselineskip
\setbox4=\vbox{
\halign{\hfil#&\hfil#\hfil&#\hfil\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
d & eee & f\cr}
}
\centervbox{4}
\bye
在示例中,我使用了框寄存器 4,但是如果你说\newbox\mybox
然后
\setbox\mybox=\vbox{...}
那么你可以说\centervbox\mybox
。盒子寄存器保持不变(如果它不是盒子寄存器 1,但使用它会非常糟糕的事情)。
对第一种方法的注释
我们假设已经有一个\vbox
在框寄存器中构建的框,类似于,\setbox\mybox=\vbox{...}
并且该框仅包含水平框和它们之间的粘合。过程是将此框复制到临时框(编号 0);我们还保留框寄存器 1,我们将始终全局操作它,首先确保它为空。然后我们开始递归:在每一步中,我们从框 0 中检索底部水平框并将其转移到框 1 中;我们还转移行间粘合。但是,检索到的框放在与其一样宽\hsize
且居中的框内。
当框 0 的高度为零时,递归结束。此时我们\unvbox
将寄存器 1 装箱。
第二种方法
另一种方法是排版两次对齐,首先收集其自然宽度,然后使用所需的前言:
\newdimen\alignlen \newtoks\preamble
\long\def\centeralign#1#2{\par\preamble{#1}%
\setbox0=\vbox{\tabskip=0pt\halign{&##\cr#2\crcr}}%
\alignlen=\wd0
\begingroup\tabskip=0pt plus 1fil
\halign to\hsize{\tabskip=0pt \the\preamble\tabskip=0pt plus 1fil\cr
#2\crcr}\endgroup}
\bigskip
\centeralign{\hfil#&\hfil#\hfil&#\hfil}
{aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
aa & b & cc\cr
d & eee & f\cr}
这需要稍微不同的语法,但不是那么复杂。首先,对齐排版在一个带有标准前导的框中,只不过是“打印每个条目”。长度寄存器\alignlen
加载宽度。然后对齐排版在每侧都有无限粘连,带有所需的前导。由于 在\halign
任何框之外,因此可以将其拆分到各个页面。这种方法的优点是\noalign
可以将命令放在 之后\cr
,说明惩罚和垂直跳过。
对第二种方法的评论
如果居中框是,\halign
我们可以使用不同的方法。首先,我们\halign
在框内排版一个简单的前言,&#\cr
意思是“任意数量的列,左对齐”;这将提供测量对齐的自然宽度的可能性,如所要求的那样。
然后我们排版与请求的前导码对齐,但\tabskip
两侧有无限可拉伸的粘连。前导码已保存在令牌寄存器中\preamble
,我们只需通过添加\tabskip=0pt
将适用于所有列间距的开头和右侧间距的无限粘连规范来增强它。
第三种方法
新形成的盒子里除了可以携带跳过和盒子外,还可以携带 kerns 和 penalties。其他物品不能一起携带。
\catcode`\@=11
\newif\ifboxended
\def\centervbox#1{\begingroup\global\setbox\@ne=\box\voidb@x
\global\boxendedfalse
\setbox\z@=\copy#1\relax\docentervbox}
\def\docentervbox{%
\setbox\z@=\vbox{\unvbox\z@
\ifcase\lastnodetype
% char node (can't remove)
\or
% hlist node
\setbox\tw@=\lastbox
\def\next{%
\global\setbox\@ne=\vbox{
\hbox to\hsize{\hfil\box\tw@\hfil}\unvbox\@ne}}%
\or
% vlist node
\setbox\tw@=\lastbox
\def\next{%
\global\setbox\@ne=\vbox{
\hbox to\hsize{\hfil\box\tw@\hfil}\unvbox\@ne}}%
\or
% rule node (can't remove)
\or
% ins node (can't remove)
\or
% mark node (can't remove)
\or
% adjust node (can't remove)
\or
% ligature node (can't happen)
\or
% disc node (can't happen)
\or
% whatsit node (can't remove)
\or
% math node (can't remove)
\or
% glue node
\skip@=\lastskip\unskip
\def\next{\global\setbox\@ne=\vbox{\vskip\skip@\unvbox\@ne}}%
\or
% kern node
\dimen@=\lastkern\unkern
\def\next{\global\setbox\@ne=\vbox{\kern\dimen@\unvbox\@ne}}%
\or
% penalty node
\count@=\lastpenalty\unpenalty
\def\next{\global\setbox\@ne=\vbox{\penalty\count@\unvbox\@ne}}%
\or
% unset node (can't happen)
\or
% math mode node (can't remove)
\else
% empty list
\def\next{\global\boxendedtrue}
\fi
\next}
\ifboxended
\def\next{\unvbox\@ne\endgroup}
\else
\let\next\docentervbox
\fi
\next}
\catcode`\@=12
节点类型列表可以在 e-TeX 文档 ( texdoc etex
) 中找到。特殊情况 -1 是列表为空时,我们使用它来结束递归。
答案2
\halign
是纯文本编码,因此最好在每个元素的右侧和左侧添加粘连,方法是使用纯文本样式。因此
\vbox{
\halign{\quad\hfil#&\quad\hfil#\hfil\quad&#\hfil\quad\cr
aa & b & cc\cr
d & eee & f\cr}
}
可以完成工作;\quad
用任何适当的固定胶水量替换。它不需要在所有位置都使用相同的量。传统上,一半的胶水被放在两边&
,但它应该在一个地方也能起作用,因为看起来您希望第一列右对齐,第二列居中,第三列左对齐。