第一种方法

第一种方法

尝试构建一个复杂的宏,我设法计算出我需要的 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用任何适当的固定胶水量替换。它不需要在所有位置都使用相同的量。传统上,一半的胶水被放在两边&,但它应该在一个地方也能起作用,因为看起来您希望第一列右对齐,第二列居中,第三列左对齐。

相关内容