为什么 LaTeX 排出来的段落比文字处理软件的更耐看?答案在 文本对齐 底下一层:文字处理软件一行一行地填,而 LaTeX 会先把整段一起权衡,再决定每一行在哪里断开。默认是两端对齐;离开它有两条路——\centering 之类的声明与 center 之类的环境,二者排出的对齐效果完全一样,前后留出的垂直空白却不同。本页顺着一条线索讲下去:两端对齐为什么是默认、\centering 与 center 该怎么选、\raggedright 为什么会悄悄停止断词而 ragged2e 如何补救,以及遇到 Overfull \hbox 警告该怎么办。
两端对齐为什么是默认:整段一起断行
不作任何指定时,LaTeX 会把段落排成 两端对齐,左右都与页边齐平。这个默认值不只是习惯。TeX 并不自上而下地把行填满,而是挑选 让整段总代价(demerits)最小的那一组断行位置。描述这套算法的论文,是高德纳(Donald Knuth)与 Michael Plass 的《Breaking Paragraphs into Lines》(Software: Practice and Experience 第 11 卷,1981 年,第 1119–1184 页)。逐行贪心地填,好处都在前面拿走,代价全压到最后一行;TeX 反其道而行,可以 故意把靠前的一行排得稍差,以免后面出大问题。而这套优化要有腾挪余地,前提是词间空白可以伸缩——这正是两端对齐提供的条件。于是两端对齐成了起点。
机制是具体的。对每一行,TeX 把空白被拉伸或压缩的程度折算成 badness,把断行处的罚分记为 p;再以 l 表示 \linepenalty、b 表示 badness,该行的 demerits 就是 d = (l+b)^2 + p^2。整段的 demerits 是各行之和,外加相邻两行松紧不一(\adjdemerits)、连续两行都以连字符结尾(\doublehyphendemerits)等附加项。高德纳与 Plass 自己形容这些公式「quite arbitrary(相当随意)」——但它们确实好用。另一件值得知道的事:断行器最多跑 三遍。第一遍完全不断词,只有每一行都落在 \pretolerance 之内才接受;第二遍才调入断词模式与更宽松的 \tolerance。也就是说,排得从容的段落根本不会被连字符切开。只有第二遍仍然失败,才会启动使用 \emergencystretch 的第三遍。
\centering 与 center 环境的区别
先说结论:对齐效果 完全一样,唯一的差别是 前后多出的垂直空白。打开 latex.ltx 就能看到,center 的定义是 \trivlist \centering\item\relax——这个环境不过是给 \centering 套了一层不可见的列表;既然是列表,它就会自动与正文之间留出 \topsep 的间隔。flushleft 用同样的方式包住 \raggedright,flushright 包住 \raggedleft。在 10pt 的 article 中实测,center 环境上下各多出约 8pt,而声明什么也不加。
| 声明 | 环境 | 对齐的一侧 | 前后垂直空白 |
|---|---|---|---|
\centering | center | 两边都不齐,行居中 | 声明为零,环境加 \topsep |
\raggedright | flushleft | 左边齐平,右边参差 | 同上 |
\raggedleft | flushright | 右边齐平,左边参差 | 同上 |
\justifying | justify | 左右都齐平,即两端对齐 | 需要 ragged2e;声明为零 |
\begin{figure}[htbp]
\centering % declaration: adds no vertical space
\includegraphics[width=0.6\textwidth]{plot}
\caption{Measured values against the model}
\label{fig:plot}
\end{figure}这正是 figure 里应当用 \centering 而不是 \begin{center} 的原因。浮动体本身上下已经带有间距,再叠上 center 环境的 \topsep,图与题注周围就会显得松垮。同理,tabular 的单元格、\parbox、minipage 以及 \caption 的内容,都用声明来对齐。反过来,若只是在正文中插入一行标语,而且 连同前后空白一起要,那么 center 环境写起来更省事。
声明还有一个容易出事的性质:声明不会开始新的段落。 对齐方式是在段落收尾的那一刻确定的——也就是读到 \par(空行)时——而那一刻生效的设置会应用到 整个段落。所以下面这一行的结果,会出乎多数人的意料。
ww {\centering xx \\ yy} zz
% result: the line "ww xx" comes out centred, "yy zz" does not实际排出来,ww xx 会居中,而 yy zz 从左边距开始。原因分两层。首先,\centering 会悄悄把 \\ 改写 成 \@centercr,而 \@centercr 不是换行,是 \par,即段落结束。于是 xx 在 \centering 仍然生效时结束了段落,这一段便居中。接着 yy 开启新的一段,但 } 关闭分组时 \rightskip 等已经复原,等这一段结束时早已回到两端对齐。使用声明的唯一准则由此而来:要对齐的那一段的结尾——空行,或环境的 \end——必须落在分组之内。
\raggedright 为什么不再断词,ragged2e 又做了什么
原因只有一行代码。\raggedright 把填补行末的胶 \rightskip 设成 0pt plus 1fil,也就是 可以无限拉伸的空白。行末摆着一个能无限拉伸的东西,任何一行的 badness 都是零。于是在 TeX 看来,把单词切开加上连字符 只会增加 demerits,而换不来任何好处。它索性完全不再断词,右端因此参差得厉害。把同一句话排进 4cm 的行宽实测,\raggedright 除了「一个词本身就放不下一行」的情况外一个连字符都不加,各行长度相差极大。
ragged2e 宏包修的正是这一点。它把无限的伸缩量换成 有限的 0pt plus 2em,于是过短的行重新带上真实的 badness,TeX 也就重新开始权衡是否断词。宏包提供首字母大写的命令 \RaggedRight、\RaggedLeft、\Centering,环境 FlushLeft、FlushRight、Center,以及用于切回两端对齐的 \justifying 和 justify 环境。若要让整份文档左对齐同时保留断词,用 \usepackage[document]{ragged2e}:它会在 \begin{document} 处执行 \RaggedRight,并把同样的设置推广到 \parbox 与 tabular 的 p 列。宏包由 Martin Schröder 编写,现由 Marei Peischl 维护。
\usepackage[document]{ragged2e} % whole document flush left, hyphenation kept
% or locally, only where it is needed:
\begin{FlushLeft}
A paragraph set flush left with hyphenation still available,
so the right edge stays gently ragged instead of jagged.
\end{FlushLeft}
\justifying % back to justified text那么什么时候左对齐才真的合适?取决于 行的长度。分栏排版、边注、tabular 里狭窄的 p 列,这些地方一行都很短;强行两端对齐,词间空白就得拉得很开。被拉开的空白会在相邻各行之间连成一片,在版面上形成一道道苍白的通道——排版上称为 白河(rivers)——反而妨碍阅读。行越短风险越大,所以窄栏确实更适合左对齐。双栏论文里常见的折中做法是:正文保持两端对齐,只对图题与脚注使用 \RaggedRight。若是单栏且行长舒服,就让两端对齐待着别动。
如何修掉 Overfull \hbox 警告:\sloppy 与 \emergencystretch
日志里出现 Overfull \hbox (84.8932pt too wide) in paragraph at lines 10--10 这样一行,意思是 TeX 找不到任何一组断行位置能把这一段控制在容差之内,于是它放弃了,让某一行冲出版心。罪魁通常是又长又不可断的东西:URL、拼接起来的标识符、\texttt 里的长词,或者干脆就是栏太窄。先怀疑那一个词,再去怀疑对齐方式。
处理是有次序的。先修那一个词——用 \hyphenation{...} 告诉 LaTeX 该在哪里断,插入自由断点 \-,URL 则交给 url 或 hyperref 的 \url。这才是根治。若警告仍在,再请出 \sloppy:它把 \tolerance 放宽到 9999,\emergencystretch 放到 3em,\hfuzz 放到 0.5pt,实质是 以词间空白变松换取不再溢出 的交易。全文施加会让正文整体显松,所以更稳妥的做法是用 sloppypar 环境只包住出问题的那一段,或者单独设置 \emergencystretch=3em。要回到严格设置,用 \fussy(\tolerance 200、\emergencystretch 0)。而如果窄栏里这类警告接连不断,那本身就是在提示:这一栏该改成左对齐了。
% 1. tell LaTeX where the offending word may be split
\hyphenation{Bib-lio-the-ken-ver-bund}
% 2. relax the line breaker for one paragraph only
\begin{sloppypar}
A paragraph containing a very long unbreakable token.
\end{sloppypar}
% 3. or globally, without going all the way to \sloppy
\emergencystretch=3em为什么不该用 \\ 来结束段落
\\ 只负责断行,并不结束段落;结束段落的是空行,也就是 \par。这个区别与对齐直接相关:前面几节已经说明,对齐是在段落收尾那一刻定下来的。用 \\ 隔开两段文字,后一段 不会被当作新段落——没有首行缩进,也没有 \parskip,只是同一段中间断了一行。更糟的是,在段落末尾写 \\ 再接一个空行,日志里就会塞满 Underfull \hbox (badness 10000) in paragraph at lines 3--4,因为凭空多出了一个空行,又被拉满整行宽度。
那么 \\ 该用在哪里?用在只想断行的地方。 在 center 或 flushright 里分行、在 tabular 里结束一行、在 verse 里分开诗行、把过长的 \caption 折开——这些都是行的边界,不是段落的边界。顺带一提,在 center 块最后一行之后多写一个 \\ 并不会有代价:既不多出空白,也不报警告,因为 \@centercr 会用 \addvspace{-\parskip} 把它抵消掉。分段用空行,断行用 \\——只要守住这条界线,那些看似莫名其妙的对齐故障就会少掉一大半。
哪种对齐用在哪里
判断不从偏好出发,而从「对象是什么」出发。正文段落维持两端对齐;短小的部件——标题、题注、落款行、表格单元——就地对齐。至于用声明还是用环境,只归结为一个问题:你要不要前后那点垂直空白? 按这个顺序决定,同一个问题几乎不会困扰你第二次。
- 图表、表格单元、
\parbox与minipage的内容 用\centering这类声明来对齐;用环境会把\topsep叠在本来就有的空白上。 - 正文中单独一行的标语或题字 用
center环境最自然,因为你要的正是连同前后空白的一整块。 - 西文的左对齐与右对齐 请用
ragged2e的\RaggedRight、\RaggedLeft,而不是标准命令,这样断词才不会被关掉。 - 栏宽很窄时(多栏、边注、
tabular的 p 列)不必死守两端对齐——行越短,左对齐越有利。 - 用声明时,要把段落的结尾放进分组里。 分组内既无空行也无
\end,对齐要么不生效,要么波及你根本不想动的文字。 - 空行结束段落,
\\结束一行。 混淆二者会同时招来Underfull \hbox和消失的首行缩进。