LaTeX 的标准 class——article、report、book、letter,再加上年迈的 slides——前三个并不是各自独立写成的。它们是同一份源文件 classes.dtx 用不同选项生成的三个输出。TeX Live 2024 里的 report.cls 和 book.cls 都不到 750 行,而两者的 diff 只涉及 67 行。也就是说,「报告」与「书籍」之间的全部距离,就是这 67 行。本页就来读这 67 行:哪个 class 有章,哪个默认单面或双面,\frontmatter 到底切换了什么,默认选项在哪里分道扬镳——每一条都用真实输出验证,而不是凭记忆复述。
article、report、book 出自同一份源文件
三个 class 印自同一张图纸。打开 article.cls,开头写着「classes.dtx (with options: article)」;report.cls 和 book.cls 也有同样的一行,只是选项名不同。所以想知道它们的差别,跑一次 diff 比读手册更快。article.cls 与 report.cls 的差异是 232 行,report.cls 与 book.cls 之间是 67 行。这两个数字本身就是距离:从 article 到 report 是整整多出一层结构,而从 report 到 book 的改动要小得多。下面各节只讨论这些 diff 中真正出现的内容。
# the header that gives the game away, and the two distances
head -8 "$(kpsewhich book.cls)" | grep classes.dtx
# %% classes.dtx (with options: `book')
diff "$(kpsewhich article.cls)" "$(kpsewhich report.cls)" | grep -c '^[<>]' # 232
diff "$(kpsewhich report.cls)" "$(kpsewhich book.cls)" | grep -c '^[<>]' # 67哪些 class 有 \chapter,以及这个答案还改变了什么
只有 report 和 book 拥有 \chapter,article 没有。在 article 里写 \chapter{...},报错并不会说「这个 class 没有章」,而只是 ! Undefined control sequence.——因为对 LaTeX 来说,不存在的命令和拼错的命令是同一件事。更值得注意的是,这一个差别会同时在四处改变编号方式。report.cls 和 book.cls 里声明了 \newcounter{section}[chapter],于是节号在每一章重置,形式变成 2.1。图、表、公式和脚注的编号同样按章重置(\@addtoreset{equation}{chapter} 等)。而 secnumdepth 的默认值在 article 中是 3,在 report、book 中是 2。
把同样的正文放进两个 class 里排一次,secnumdepth 的差别立刻可见。各给一个 \section、一个 \subsection、一个 \subsubsection:article 输出「1 / 1.1 / 1.1.1」,三层全部编号;report 输出「0.1 / 0.1.1 /(无编号)」。在 report 和 book 中,\subsubsection 默认不编号——「小小节没有编号」这一症状几乎都源于此,在导言区写 \setcounter{secnumdepth}{3} 即可解决。那个「0.1」也不容忽视。如果在 report 或 book 中不写 \chapter 就直接从 \section 开始,章计数器停在 0,于是节号变成 0.1、0.2。文档里没有章,就是这个 class 在提醒你:本该选 article。
% same body, two classes: article numbers three levels, report only two
\documentclass{article} % -> 1 Sec / 1.1 Sub / 1.1.1 Subsub
%\documentclass{report} % -> 0.1 Sec / 0.1.1 Sub / Subsub (unnumbered)
\begin{document}
\section{Sec}\subsection{Sub}\subsubsection{Subsub}
\end{document}
% in report/book, restore the third level with:
% \setcounter{secnumdepth}{3}顺便澄清一个流传甚广的误解:\part 不会重置章编号。 report.cls 和 book.cls 里都只写了 \newcounter{chapter},没有把它绑定到 part 的可选参数。实际上,在第 I 部放两章后开始第 II 部,下一章的编号是第 3 章而不是第 1 章——两个 class 都一样。若希望按部重新编号,必须自己声明,例如在导言区写 \counterwithin{chapter}{part}。\part 的外观倒是因 class 而异:在 article 中它是嵌在正文流里的标题,而在 report、book 中它会另起一页,居中放大,独占一页。
report 与 book 的差别:同一份原稿,5 页还是 9 页
两个 class 的分岔只是一行默认选项。report.cls 写的是 \ExecuteOptions{letterpaper,10pt,oneside,onecolumn,final,openany},book.cls 写的是 ...,twoside,onecolumn,final,openright}。其余一切都是这行的结果。openright 让章从右页(奇数页)开始,必要时就在左页插入空白页。用同一份原稿——两部、三章——分别排一次,report 得到 5 页,book 得到 9 页。多出的四页里有几页完全是空白。对于要印刷装订的成品,这是正确行为;但对于只在屏幕上阅读的 PDF,空白页纯属累赘。这正是实务中常见组合的由来:选 book,然后显式写上 oneside。
diff 里还藏着一个更出人意料的条目:book 没有 abstract 环境。 article 和 report 都定义了摘要环境,而 book.cls 整块都不带,也不定义 \abstractname。把一份原本以 report 编译通过的稿子换成 book,就会停在 ! LaTeX Error: Environment abstract undefined.——「书不附摘要」这一编辑判断被固化进了 class 文件里。默认页面样式也不同:report 用 plain(页脚居中只有页码),book 用 headings(页眉带章节标题)。此外 book 会载入自己的版面文件 bk10.clo。与 article、report 使用的 size10.clo 相比,版心宽度从 345pt 收窄为 4.5in(约 325pt),高度从 43 行减为 41 行,边距变为不对称的订口 0.5in、切口 1.5in。从 report 换到 book,改变的不只是拼版,还有文字所在的那个盒子本身。
| Class | \chapter | 默认选项 | 默认页面样式 | 版面文件 |
|---|---|---|---|---|
article | 无;最上层是 \section | oneside、notitlepage、secnumdepth=3 | plain | size10.clo |
report | 有 | oneside、openany、titlepage、secnumdepth=2 | plain | size10.clo |
book | 有;在 \frontmatter 中不编号 | twoside、openright、titlepage、secnumdepth=2 | headings | bk10.clo(版面不同) |
letter | 无;单位是 letter 环境 | oneside、letterpaper、10pt | firstpage / plain | size10.clo |
slides | 无;单位是 slide 环境 | letterpaper、final | 专用 | sfonts.def / slides.def |
\frontmatter / \mainmatter / \backmatter 到底切换了什么
这三个命令是 book 专有的,而且每一个都同时做两件事。\frontmatter 把页码切换为罗马数字(i、ii、…),同时把 \if@mainmatter 置假,从而停止给章编号。\mainmatter 把页码换回阿拉伯数字并从 1 重新计数,同时恢复章编号。\backmatter 不动页码,只是再次关闭章编号。真正有价值的是第二个作用:前言部分的章虽然没有编号,却仍会进入目录——因此在 book 中不需要那套常见的变通做法,即用 \chapter* 再手写 \addcontentsline。实际编译一下,.toc 文件里会出现 \contentsline {chapter}{Preface}{i} 这样没有编号的行,与正文中带 \numberline {1} 的行并列。
\documentclass[11pt,a4paper]{book}
\begin{document}
\frontmatter % roman page numbers, chapters unnumbered
\chapter{Preface} % -> toc: \contentsline {chapter}{Preface}{i}
\tableofcontents
\mainmatter % arabic, restarts at 1, chapters numbered again
\chapter{Beginning} % -> toc: ... {\numberline {1}Beginning}{1}
\section{Apparatus} % numbered 1.1
\backmatter % page numbers continue, chapters unnumbered
\chapter{Index}
\end{document}\documentclass 的选项,以及各 class 不同的默认值
除 slides 之外的标准 class 都通过 \documentclass[...]{...} 的方括号接受一组共通选项。大多数是作用于整个文档的开关,但真正要紧的是:同一个选项名在不同 class 中默认值不同。上表的「默认选项」一列记录的正是这一点,每个值都原样写在 class 文件的 \ExecuteOptions{...} 行里。
10pt/11pt/12pt— 正文基准字号。三个 class 的默认值都是10pt。- 纸张大小 —
letterpaper(默认)、a4paper、a5paper、b5paper、legalpaper、executivepaper。由于默认是美国 Letter,身处 A4 地区的人每次都要写a4paper。 twoside/oneside— 双面 / 单面版式。只有book默认为twoside。openright/openany— 章从右页开始还是任意页面开始。book用openright,report用openany。article这两个都没有声明——写了也不会报错,只是沉进日志变成LaTeX Warning: Unused global option(s):(这种不对称的原因见「Class options & authoring」)。titlepage/notitlepage— 标题是否独立成页。report和book用titlepage,article用notitlepage。twocolumn/onecolumn— 正文排成双栏还是单栏。三者默认都是onecolumn。fleqn— 行间公式左对齐(默认居中)。leqno— 公式编号放在左侧(默认右侧)。二者都是通过载入一个.clo文件实现的。landscape— 横向纸张。draft— 用黑色线条标出 overfull box(默认是final)。openbib— 以「开放」格式排参考文献。
letter class:一整套不同的命令体系
letter 与另外三个没有血缘关系。它出自自己的源文件而非 classes.dtx,没有 \section、没有 \maketitle、没有标题页,取而代之的是一套书信词汇。发件人信息——\address{...}(自己的地址)、\signature{...}、\name{...}、\location{...}、\telephone{...}——放在导言区,由文件里所有信件共用。一封信就是一个 letter 环境,其参数即收件人地址。正文以 \opening{...} 开始,以 \closing{...} 收尾,之后可选接 \cc{...}(抄送)、\encl{...}(附件)、\ps(附言)。地址内换行用 \\。
\documentclass{letter}
\address{1 Computing Way \\ London} % sender, shared by every letter
\signature{Ada Lovelace}
\makelabels % appends a sheet of mailing labels
\begin{document}
\begin{letter}{Charles Babbage \\ 2 Engine Road} % recipient = argument
\opening{Dear Mr.\ Babbage,}
Thank you for the notes on the Analytical Engine.
\closing{Yours sincerely,}
\cc{The Royal Society}
\encl{Two diagrams}
\end{letter}
\begin{letter}{Mary Somerville \\ 3 Science Lane}
\opening{Dear Mrs.\ Somerville,}
A second letter, same sender, same file.
\closing{Yours sincerely,}
\end{letter}
\end{document}有一行命令雄辩地说明了这个 class 是为哪个年代设计的。在导言区写上 \makelabels,PDF 末尾就会多出一页收件人地址标签。把上面的例子直接编译,两封信占两页,之后附上排列着两个收件地址的标签页,共三页。印在标签纸上,撕下来贴到信封上——那曾是日常工作。反过来说,letter 真正擅长的是同一发件人对多个收件人的批量寄送:有多少收件人就并排放多少个 letter 环境。如果版式要求很具体,比如德语区的 DIN 5008,请改用专门的 class,例如 KOMA-Script 的 scrlttr2。
slides class 究竟是什么:beamer 之前的幻灯片
第五个标准 class slides 至今仍在 TeX Live 里(2024 版为 v2.4b,2022 年更新)。但它并不是「旧版 beamer」。LaTeX 自带的 manifest.txt 把 slides.dtx 描述为「Slides class, etc based on SLiTeX」,正如其名,它是 SLiTeX 的后裔——那是一个曾经独立的程序,用 TeX 制作投影仪用的透明胶片。所以它的词汇不同:一个 slide 环境是一张片子,一个 note 环境是讲者备注,而 overlay 环境字面上就是叠在第一张之上的第二张透明胶片。编译一次,编号会告诉你这一点:幻灯片是「1」,叠在上面的 overlay 是「1-a」,对应的备注是「1-1」。用 \onlyslides{...} 和 \onlynotes{...} 可以从同一份原稿只印幻灯片或只印备注。
实务上的判断很简单:要做新的幻灯片,请用 beamer,不要用 slides。这个旧 class 没有主题,没有现代意义上的逐步 overlay,也没有 PDF 导航;它的输出端从一开始就是印刷,而不是投影驱动。只有在接手旧稿件时,或者想了解 beamer 把 frame 视为「一个逻辑屏幕」这一设计的来历时,才值得去读 slides。
如何选择:从提交要求倒推
选择 class 不是凭喜好,而是从提交要求倒推。不需要章就用 article;分章的技术报告用 report;需要装订、要考虑跨页的稿件用 book;通信文用 letter。如果对方发了指定模板,模板中的 class 已经决定了正文结构、边距和标题处理方式,不要把它改回标准 class。
- 只在屏幕上读的 PDF — 显式写上
oneside。即便选了book,\documentclass[oneside]{book}也能消除空白页。 - 打印并装订 — 有意地选择
twoside和openright,并检查订口处的装订余量。 - 想细致地控制版面 — 标准 class 可供调整的接口很少,改用 KOMA-Script(
scrartcl/scrreprt/scrbook)或 memoir。 - 以日文正文为主 — 标准 class 并未考虑日文排版。pLaTeX / upLaTeX 从 jsclasses(
jsarticle/jsbook)开始,LuaLaTeX 用 ltjsclasses,较新的选择是 jlreq。 - 幻灯片 — 用 beamer,不要用
slides。
中途更换 class 后要检查什么
改写 \documentclass 里的一个词,不是稍微改改外观,而是改变文档结构。如前所见,它会同时牵动章的有无、编号的深度、abstract 是否存在、版心尺寸以及空白页的插入。切换之后,不要因为编译通过就放心,请用眼睛核对以下五点。
| 检查项 | 会发生什么 | 如何修正 |
|---|---|---|
\chapter | 移到 article 会以 ! Undefined control sequence. 停止 | 把章降级为 \section,或继续用 report / book |
abstract | 移到 book 会得到 ! LaTeX Error: Environment abstract undefined. | 删去摘要,或改用 \chapter*{Abstract} 之类 |
secnumdepth | 从 article 迁来后,\subsubsection 的编号消失 | 补上 \setcounter{secnumdepth}{3} |
openright | 换成 book 后章前会插入空白页,总页数增加 | 屏幕阅读用 oneside;印刷则保持原样 |
titlepage | \maketitle 会变成独立页,或与正文同页 | 显式设置 titlepage 或 notitlepage |