Overfull / Underfull \hbox

Overfull \hbox警告,不是错误。LaTeX 会一直跑到最后,PDF 也确实生成了——只是其中某一行伸出了右边距。所以第一件该做的事不是盯着日志看,而是打开 PDF 去看那一行。在此基础上,本页依次说明:(71.93637pt too wide) 里的数字量的是什么,in paragraph at lines 4--9detected at line 3 有何区别,\hbadness\tolerance\emergencystretch 中究竟该动哪一个,以及为什么「真正修好」和「只是让警告闭嘴」是两件完全不同的事。它的镜像警告 Underfull \hbox,以及纵向的 Overfull \vbox,同样归本页管。

(71.93637pt too wide) 里的数字量的是什么

它量的是这一行超出 \hsize(栏宽)的量,单位是点。1pt 约合 0.35mm,所以 0.5pt 肉眼根本看不见,而 70pt 大约是一个拇指宽度地挂在纸面外。仅凭这个数字就能定优先级——只超几点没人会注意到,这也是 LaTeX 默认对 0.1pt 以内保持沉默的原因(\hfuzz=0.1pt)。TeX 是以「盒子」为单位排版的,把一行字符横向排在一起的盒子就是水平盒,即 \hboxOverfull \hbox 的含义是:分行时某个盒子的内容比栏宽还宽,而且再也压不下去了。行压不下去的原因几乎总是同一个——这一行里没有任何可以断开的地方

警告的后半段,例如 in paragraph at lines 4--9,告诉你它发生在哪里。这类措辞共有四种,出现哪一种决定了你该去哪里找,所以请用下表对照。表中每一种都是在 TeX Live 2024 的 pdfLaTeX 下用最小文件实测复现的。其中最麻烦的是 has occurred while \output is active,意思是「正在组装页面时」,肇事内容未必对应你写下的任何一行。反方向也有陷阱:裸的 \mbox{...}\makebox[2cm]{...} 无论内容伸出多远,都一条警告也不会给。它们用作填充的 \hss 可以无限收缩,在 TeX 看来盒子总是刚好装下;于是跨过边距的字符就这么印了出来,日志里毫无痕迹。

措辞含义该看哪里
in paragraph at lines A--B发生在普通段落的断行过程中第 A 行到第 B 行的那一段;最常见
detected at line N发生在固定宽度的显式盒子而非段落中第 N 行的 \hbox to\makebox[…][s] 或宏包生成的盒子
in alignment at lines A--B发生在表格对齐中tabular*tabularx 等指定了宽度的表格
while \output is active发生在组装页面的过程中页眉、页脚、脚注、浮动体;行号不可靠

日志会把肇事的那一行给你看——连断字点都标出来

警告正下方,TeX 会把伸出去的那一行内容原样打印出来,而且在它找到的每个断字点插入 -。这正是诊断的关键。下面是把英文灌进 4cm 宽栏时的真实日志:Wis-senschaft-skol-leg 带着连字符,而 electroencephalography- 一个都没有。TeX 会把含有显式连字符的词排除在自动断字之外(扫描在显式连字符处停止)。也就是说这一行确实无处可断,\tolerance 调多高都没用。看日志里的连字符,就能一眼判断断字究竟是在尝试,还是已经彻底放弃——若整行一个连字符都没有,那多半是语言设置错了。

log
Overfull \hbox (13.35585pt too wide) in paragraph at lines 4--9
\OT1/cmr/m/n/10 The Wis-senschaft-skol-leg pub-
 []

Overfull \hbox (80.27266pt too wide) in paragraph at lines 4--9
\OT1/cmr/m/n/10 li-ca-tion de-scribes an electroencephalography-
 []

想在 PDF 上用眼睛确认位置,就给文档类加上 draft 选项。article.cls 里写着 \DeclareOption{draft}{\setlength\overfullrule{5pt}},它会在每一行伸出的位置右侧画一条 5pt 宽的黑色标记。平时看不到这些黑条,是因为 latex.ltx 最后执行了 \overfullrule 0pt,抵消了 INITEX 默认的 5pt。也就是说,黑条只是被关掉了,随时可以打开:如果不想动类选项,在导言区直接写 \overfullrule=5pt 效果相同——记得提交前删掉。

双栏、表格与代码:窄栏为何盛产 overfull

因为栏越窄,不可断词的相对长度就越致命。 在 10cm 宽的栏里,一个 3cm 的词只占行的三分之一;而在 4cm 宽的栏里,它比整行还长,怎么断都必然溢出。同一份稿子一改成双栏就冒出几十条警告,不是文章变差了,而是栏宽减半了。下面这些地方是 overfull 的高发区,若有印象请先从那里查起。认清「窄栏才是病因」会改变对策——与其到处撒 \sloppy,不如重新审视那张表的 p{} 列宽,往往更快。

  • 双栏排版\documentclass[twocolumn]multicol)。栏宽大致减半,原本在单栏下没问题的词会一齐溢出。
  • 表格的 p{}m{}b{}tabularx。列宽是作者自己定的数值,常常配不上里面要放的内容。
  • 逐字环境verbatim\verblistings)。空格与换行都照原稿保留,TeX 根本没有断行的余地。
  • URL、DOI、文件路径、型号、化学名、基因名。 不含空格的长字符串会被当作一个词。
  • 行内公式。 默认情况下公式只能在二元运算符或关系符之后断开,在 \frac 之类结构内部则完全不能断。
  • 脚注与图表标题。 它们有时以比正文更窄的宽度排版,容易以 \output is active 的形式被报出。

消除 overfull hbox:从最优先的做法开始

改写一个词是最好的对策。 这个答案不够花哨,却是任何软件都替代不了的解决办法,而且通常只要五秒钟:把长词换成短的同义词、调整语序、把冗长的型号挪进脚注——盒子就装下了。这个排序是有道理的。TeX 的断行算法一次考察整个段落(高德纳与迈克尔·普拉斯 1981 年论文《Breaking Paragraphs into Lines》),所以改动段落最后一个词,可能连第一行的断法都会变。反过来说,作者挪动一个词,就能大幅拓宽排版的回旋余地。下面按效果与副作用的平衡排列各种对策。

  • 改写。 通常改一个词就够,且毫无副作用。冗长的型号与产品名该放进脚注或表格。
  • 确认语言设置。 德语或法语正文若套用英语断字规则,就找不到可断之处。用 babel\usepackage[ngerman]{babel};在 XeLaTeX/LuaLaTeX 下用 polyglossia\setmainlanguage{german}
  • 让那个词能断开。 在词中放 \- 即可选连字符;\hyphenation{Wis-sen-schafts-kol-leg} 在导言区登记例外。对已含显式连字符的复合词,在连字符后插入 \hspace{0pt} 可重新创造断点。
  • \sloppysloppypar 环境。 以段落为单位放宽词间距。sloppypar 作为环境会在 \end 处收束,比 \sloppy 更安全。
  • 调高 \emergencystretch 实测中仅 \emergencystretch=3em 就把 5 个 overfull 变成 0 个(代价是多出 7 个 underfull)。副作用是词间距变松,远不如文字挂出页面显眼。
  • 加载 microtype 细微的字符突出与字宽伸缩,在同一测试中把超出量从 13.4pt 降到 8.9pt、从 80.3pt 降到 74.9pt。注意它是减少而不是消除
  • 最后手段:\linebreak\mbox 手工指定断点。之后一改句子位置就错,所以只留给交稿前的最终微调。

\tolerance\emergencystretch\hbadness\hfuzz 各管什么

前两个改变排版本身,后两个只改变警告的出现方式。 混淆这一区别,就会出现「以为修好了,其实只是藏起来」的事故:把 \hfuzz 调大,超宽报告确实消失,但纸面上的文字一毫米都没动。TeX 的断行最多分三轮:先在 \pretolerance 范围内不用断字试一次,不行就在 \tolerance 范围内加断字再试,还不行就把 \emergencystretch 的额外伸展量加到每一行上做第三轮。\emergencystretch 只在第三轮起作用,这正是它的定位——山穷水尽时才打出的最后一张牌。LaTeX 的默认值写在 latex.ltx 里:\tolerance=200\hbadness=1000\hfuzz=0.1pt。常用的 \sloppy 也在同一个文件中,其真身只有四条赋值\tolerance 9999\emergencystretch 3em\hfuzz .5pt\vfuzz\hfuzz。所以 \sloppy 并不只是「提高容忍度」,它同时设置了 \emergencystretch——实测中,单独的 \emergencystretch=3em\sloppy 结果完全相同。

参数LaTeX 默认值作用
\tolerance200一行可接受的最大劣度;调高会改变排版结果
\emergencystretch0pt仅在第三轮加入的额外伸展量;会改变排版
\hbadness1000只有劣于此值的行才报 underfull。输出不变
\hfuzz0.1pt不超过此值的溢出不予报告。输出不变
\overfullrule0pt在溢出行旁绘制的黑条宽度;draft 会设为 5pt
preamble
% changes the typesetting -- use these to actually fix boxes
\emergencystretch=3em     % last-resort stretch on the third pass
\usepackage{microtype}    % protrusion and font expansion

% changes only the log -- the page looks exactly the same
\hbadness=10000           % never report Underfull \hbox
\hfuzz=1pt                % ignore overhangs below 1pt

当长 URL 与代码字符串死活不断行

光用 \url{} 包起来常常不够。 在 5cm 宽的栏中实测:裸文本形式的长 URL 溢出 79.0pt;改用 url 宏包的 \url{} 之后仍剩两个盒子,分别是 42.4pt 与 10.8pt。\url{} 只在 /. 之类字符之后断开,因此无法在主机名或长路径片段的中间断行。真正管用的有两招:一是配合 \sloppy,实测 overfull 归零,换来的是 underfull——\sloppy 一贯附带的词间距变松就是代价;二是 xurl 宏包,只加一行 \usepackage{xurl} 就做到 overfull 与 underfull 都为 0,因为 xurl 允许在 URL 的任意位置断开。hyperref 内部会加载 url,断行行为相同;要让链接本身跨行断开则需要 breaklinks 选项。对代码清单而言,对应的开关是 listingsbreaklines=true

preamble
\usepackage{hyperref}   % loads url internally; add breaklinks for split links
\usepackage{xurl}       % allows a break at any point inside a URL
...
\url{https://example.org/proceedings/vol12/paper-0037.pdf}

Underfull \hbox (badness N) 是相反的症状

某一行不够宽,TeX 只好把词间距拉大来填满——这就是 Underfull \hbox。它是两者中较不紧急的,badness 数值可作判断依据。badness 从 0(完美)到 10000(上限,「不能更差了」)。实测中也会出现 1590、2050、7667 这样的中间值,但当一连串 badness 10000 出现时,词间距必定已经肉眼可见地变宽,请去看 PDF。最常见的成因是追杀 overfull 的副作用:\sloppy\emergencystretch 会把 overfull 换成 underfull(上文实测中 5 个变 0 个,同时冒出 7 个 underfull)。另一种成因是在段落中间用 \linebreak\\ 强行制造短行。\linebreak 的含义是「在此断行,但仍要填满行宽」,所以词距会被拉开;\newline\\ 的含义是「这一行到此为止」,因此不会。想要一个刻意的短行,就用 \\ 若只想让消息消失,\hbadness=10000 可以做到,而纸面分毫不变。

Overfull \vbox (… too high)Underfull \vbox

同一套逻辑也存在于纵向,只是把「宽度」换成了「高度」。 Overfull \vbox (56.90552pt too high) 意思是内容装不进一个固定高度的纵向盒子。实测中,像 \vbox to 1cm{\hrule height 3cm} 这样的显式盒子会以 detected at line 3 的形式报出。实际文档里更常遇到的是在组装页面时:放入一个比 \textheight 还高的图或 \rule,就得到了 Overfull \vbox (85.35828pt too high) has occurred while \output is active。看到这种形式,第一个该怀疑的就是大到一页放不下的插图Underfull \vbox (badness 10000) 则相反——纵向的弹性空白被拉伸以填满高度。在 \flushbottom(book 类的默认)下,为了对齐每页底端会拉伸纵向空白,所以内容稀少的页面很容易触发。若不在意,可改用 \raggedbottom,或用 \vbadness\vfuzz 提高报告阈值——同样地,提高阈值不会改变纸面分毫。当插图确实放不下时,\enlargethispage 可以只为该页拉长正文区域。