LaTeX 会先把文字、数学公式和图形都装进 盒子,再把这些盒子排到页面上。盒子是带有宽度、高度和深度这三个尺寸的刚性矩形;断行器不会查看盒子内部,而是把它当作一个不可分割的整体来处理。理解了这种“先装进盒子”的思路后,移动文字、重叠内容、加框以及制作分栏,都会显现为同一机制的不同用法。
盒子模型
在 TeX 内部,所有已经排好的素材都是带尺寸的盒子。一个字符是盒子,一个单词是盒子,一行是盒子,整页也是盒子。LaTeX 生成行和段落时,会用可伸缩的空白把这些盒子粘接起来,并在最佳位置断行、分页。关键在于:一旦素材被打包成一个盒子,它的内容就不会再被拆开。 盒子内部不会发生断行;整个盒子会作为一个单位移动到下一行。
LaTeX 的盒子大致分为两类。第一类是 LR 盒子(left-to-right box):它只把内容排成一行,绝不会在中途断行;如果横向太长,就会伸出边界。\mbox、\makebox 以及带框的 \fbox 都属于 LR 盒子。第二类是 段落盒子:它会在预先指定的宽度内折行,因此可以容纳多行,甚至多个段落。\parbox 和 minipage 环境就是段落盒子。
这种区别对应于 TeX 的 模式。LR 盒子的内部处于 LR 模式,只把输入从左到右排下去,不会断行。段落盒子的内部会切换到 (内部)段落模式,断行机制就像普通正文一样工作。只要留意哪个命令会进入哪种模式,就能清楚看出为什么某些内容不会换行,或者为什么某个盒子会向下增长。
LR 盒子:打包成一行
最基本的 LR 盒子是 \mbox{...}。它把内容封进一个盒子,并且 禁止断行和断词。如果有不希望被拆开的短语,比如产品名 Mac OS X、电话号码或很长的化合物名称,用 \mbox 包起来就会始终留在同一行。它也适合做细小调整,例如让数值和单位保持在一起。
想自己指定宽度时,使用 \makebox[width][position]{text}。[width] 设定盒子的宽度,[position] 选择内容在盒子中的位置:l 为左对齐,c 为居中(默认),r 为右对齐,s 会拉伸词间空白,使这一行在整个宽度内两端对齐。除了 \textwidth 这样的普通长度,宽度还可以使用 表示内容自然尺寸的特殊长度:\width(内容宽度)、\height 和 \depth,以及 \totalheight(高度加深度)。因此,\makebox[2\width]{word} 会生成一个宽度为该词两倍的盒子。
特别有用的是 零宽盒子 \makebox[0pt][position]{text}。因为盒子的宽度为 0,LaTeX 会排出内容,但 不会推进当前的水平位置,所以内容可以叠印到后续材料上。使用 [c] 时内容会以当前位置为中心向左右伸出,使用 [r] 时向左伸出,使用 [l] 时向右伸出。删除线、重叠印字和边注都利用这一机制;简写命令 \rlap 和 \llap(分别是向右、向左伸出的零宽盒子)也是同样的原理。
要画边框,可以使用 \fbox{text} 和 \framebox[width][position]{text},它们会在盒子的四边画线。\framebox 的 [width][position] 与 \makebox 完全相同。边框的外观由两个长度控制:线条粗细 \fboxrule(默认 0.4pt),以及边框和内容之间的间距 \fboxsep(默认 3pt)。可以用 \setlength 修改;例如 \setlength{\fboxsep}{0pt} 会让边框紧贴内容。这些都是单行 LR 盒子,因此若要给多行内容加框,需要与下面的段落盒子组合使用(更复杂的边框请参见“框线”)。
Do not break \mbox{Mac OS X} here.
% a 4cm box, contents flush right, then framed
\framebox[4cm][r]{right} \\[1ex]
% zero-width box: the word overprints what follows
X\makebox[0pt][l]{\,/} Y % prints an X/ over the gap before Y在这个例子中,\mbox 包住的短语始终保持在一行,\framebox[4cm][r] 把内容右对齐放在 4 cm 宽的边框中,最后的零宽盒子则在 X 后面叠印一条斜线。
段落盒子:parbox 与 minipage
当你想固定宽度并让内容自动换行时,就要使用段落盒子。较轻量的是 \parbox[position]{width}{text},它会在给定的 {width} 内把内容作为一个段落来折行。[position] 指定 这个盒子相对于周围行的对齐位置:c(默认,盒子中心对齐到该行中心)、t(盒子首行的基线对齐到周围基线)或 b(盒子末行的基线对齐)。还可以写成 \parbox[position][height][inner-pos]{width}{text},进一步指定高度和内部垂直对齐(t/c/b/s),但 \parbox 只能处理 单个段落,不能放多个段落或列表。
功能更强的段落盒子是 minipage 环境:写作 \begin{minipage}[position][height][inner-pos]{width} … \end{minipage},参数含义与 \parbox 相同,但其内容会像 一小页独立页面 一样处理。它可以包含多个段落、itemize 等列表,甚至 verbatim;在段落盒子中它是真正的主力。需要注意的是,内部的 段落缩进(\parindent)会重置为 0;如果需要,可以用 \setlength{\parindent}{1em} 自行设置。
minipage 还有自己独特的 脚注处理。在其中使用 \footnote 时,脚注不会出现在页面底部,而是出现在 minipage 正下方,并以 a, b, … 这样的小写字母标记(使用专门的 mpfootnote 计数器)。当你希望表格或框内说明的注释自成一体时,这很方便。另一方面,minipage 不能在内部放置浮动体(figure/table),也不会跨页拆分,它必须作为一个盒子完整放入页面。
minipage 最常见的用法是 并排分栏。把两个 minipage 连续放置,并在中间加入 \hfill 这样的空白,就能得到简单的两栏布局,适合把图和说明文字左右并排,或把两张图片并排放置。把两者都设为 [t],即使两个盒子的高度不同,顶端也能整齐对齐(更正式的放置方法请参见“浮动体与放置”)。
\noindent
\begin{minipage}[t]{0.48\textwidth}
Left column. This minipage wraps text within 48\%
of the text width, and can hold several paragraphs,
lists, and even its own footnote.\footnote{Local note.}
\end{minipage}\hfill
\begin{minipage}[t]{0.48\textwidth}
Right column, top-aligned with the left one because
both use the optional \verb|[t]| argument.
\end{minipage}两个 minipage 各占正文宽度的 48%,中间的 \hfill 会吸收剩余空间,使它们横跨整行。因为两者都带有 [t],即使行数不同,顶端也会对齐。左侧盒子中的 \footnote 会带着小写标记出现在 minipage 下方,而不是页面底部。
移动与画线:raisebox 和 rule
要在垂直方向移动盒子,可以使用 \raisebox{lift}[height][depth]{text}。{lift} 为正时内容上移,为负时内容下移。巧妙之处在于可选的 [height] 和 [depth]:它们会 告诉 LaTeX 假装这个盒子具有指定的高度和深度,因此周围行距会根据声明值而不是实际绘制的外观来计算。例如,\raisebox{0pt}[0pt][0pt]{a big symbol} 会实际画出较大的内容,却声明高度和深度都为零,从而可以插入装饰而不扰乱行距。这里同样可以使用 \height、\depth、\totalheight 等特殊长度。
\rule[lift]{width}{height} 会画出一个实心填充矩形(黑盒)。{width} 和 {height} 指定它的尺寸,可选的 [lift] 调整它相对于基线的位置(为负时向下)。它非常适合画水平线,例如 \rule{0.5\linewidth}{0.4pt} 会得到一条宽度为行宽一半的细线。
\rule 的另一面是 支柱(strut)。零宽的 \rule{0pt}{height} 是一个不可见盒子,不打印任何内容,却会 预留相应高度,适合强制最小行高,或给表格单元格留出空间。在一行开头放入 \rule{0pt}{2.6ex},这一行就保证至少有 2.6 ex 高。LaTeX 自带的 \strut 实际上正是 \rule[-0.3\baselineskip]{0pt}{\baselineskip},也就是一个零宽支柱,覆盖当前行距在基线上下的完整高度和深度。这是防止表格线和文字挤在一起的经典技巧。
% a centered horizontal rule
\noindent\hfil\rule{0.5\linewidth}{0.4pt}\hfil
% a strut forces a taller line / roomier table cell
\begin{tabular}{|l|}
\hline
\rule{0pt}{2.6ex}Tall, uncramped row \\
\hline
\end{tabular}
% align two differently sized boxes on a common baseline
big \raisebox{-0.4ex}{\Huge A} small保存并重复使用盒子
如果要反复使用同一份材料,可以 只排版一次,把它保存到盒子里,之后再取出来使用。首先用 \newsavebox{\boxname} 声明一个保存用盒子(寄存器)。然后用 \sbox{\boxname}{text} 或 \savebox{\boxname}[width][position]{text} 填入内容;内容真正被排版只发生在这一刻一次。在需要的地方写 \usebox{\boxname},就会原样输出已保存的盒子。对于长表格或复杂图形这类要在多处使用、排版成本较高的材料,这既能加快处理,也能保证外观完全一致。
保存用盒子本身是 LR 盒子,因此如果要保存多行内容,需要先用 \parbox 或 minipage 包起来。也可以使用环境形式 \begin{lrbox}{\boxname} … \end{lrbox},它更适合保存包含 verbatim 的内容。注意,\sbox 是稳健的(robust),而 \savebox 是脆弱的(fragile),所以在章节标题等“移动参数”中使用后者时要小心。
要测量盒子的尺寸,可以使用 \settowidth{\len}{text}、\settoheight{\len}{text} 和 \settodepth{\len}{text}。它们会把内容排版后的宽度、高度或深度存入指定的长度寄存器。需要按照盒子尺寸精确画边框或下划线时,这些命令很有用。
\newsavebox{\mylogo}
\sbox{\mylogo}{\fbox{\textbf{Draft}}} % typeset once
% reuse the identical box as many times as you like
Header: \usebox{\mylogo} \dots\ Footer: \usebox{\mylogo}
% measure a box into a length, then rule under it
\newlength{\w}
\settowidth{\w}{Signature}
Signature\par\rule{\w}{0.4pt}\sbox 那一行把 “Draft” 加框并只排版一次,两个 \usebox 调用取出的都是同一个已保存盒子。下半部分用 \settowidth 测量 “Signature” 的宽度,再用 \rule 画出正好同宽的下划线。
盒子命令速览
| 命令 | 类型 | 作用 |
|---|---|---|
\mbox | LR 盒子 | 将内容固定在一行;不允许断行或断词 |
\makebox | LR 盒子 | 指定宽度和位置;[0pt] 以零宽叠印 |
\framebox | LR 盒子 | 带框的 \makebox(\fbox 是不指定宽度的形式) |
\parbox | 段落盒子 | 在固定宽度内折行的单个段落盒子 |
minipage | 段落盒子 | 可容纳段落、列表和脚注的小页面 |
\raisebox | 变换 | 上下移动内容;可声明虚假的高度和深度 |
\rule | 填充盒子 | 实心矩形;零宽时成为支柱(strut) |
\usebox | 存储 | 输出通过 \newsavebox + \sbox 保存的盒子 |