LaTeX 的术语难懂,不是因为词汇艰深,而是因为它们顶着日常词汇的外表,内里却完全不同。“宏包”不是 npm 的包,“类”不是面向对象的类,“glue(胶)”也不是胶水。更妙的是,本术语表的主角——引擎与格式、类与宏包、盒子与胶与 kern——都可以在自己机器上亲自验证。在 TeX Live 2024 上,pdflatex 不过是指向 pdftex 的符号链接,而 latex 指向的也是同一个 pdftex。本表对每个词条,都从“新手必然弄错的地方”讲起。
引擎与格式的区别:为什么 pdflatex 就是 pdftex
引擎是真正运行的程序,格式则是预先装入该程序的一整团宏。这不是抽象说法,在文件系统里就能看到。查看 TeX Live 2024 的 bin 目录,pdflatex 是指向 pdftex 的符号链接,latex 指向的也是同一个 pdftex。同样地,xelatex → xetex、lualatex → luahbtex,而 platex 与 uplatex 都是 euptex。换句话说,“六个 LaTeX 命令”实际上只有 四个二进制程序。那么 latex 和 pdflatex 差在哪里?只差启动时载入的 .fmt 文件——一份预先处理好的宏转储。latex.fmt 是 8,221,690 字节,pdflatex.fmt 是 8,221,444 字节,两者只相差 246 字节。
# TeX Live 2024: six commands, four binaries
$ ls -l latex pdflatex xelatex lualatex platex uplatex
latex -> pdftex
pdflatex -> pdftex
xelatex -> xetex
lualatex -> luahbtex
platex -> euptex
uplatex -> euptex| 术语 | 含义,以及容易搞混的地方 |
|---|---|
engine | 真正运行的程序本体(tex、pdftex、xetex、luatex、euptex)。输出格式、字体机制和字符处理由它决定 |
format | 预先载入的宏体系(plain TeX、LaTeX、ConTeXt),固化为 .fmt 文件 |
.fmt | 格式的转储文件,放在 texmf-var/web2c/<引擎名>/ 下,由 fmtutil 生成 |
pdfLaTeX | 引擎 pdftex 加 LaTeX 格式的组合。并不存在名为 pdfLaTeX 的程序 |
LaTeX2e | 1994 年以来的现行 LaTeX。通常说“LaTeX”指的就是它 |
LaTeX3 | 重写内核的长期项目。它已作为 expl3 内置于现行 LaTeX,许多宏包都在使用 |
类与宏包的区别:.cls 和 .sty 差在哪里
类决定文档“是什么”,宏包给文档“加功能”——而且一份文档只能有一个类,宏包却可以有任意多个。这就是全部区别。从技术上说,两者都是充满宏定义的文本文件,.cls 与 .sty 的内容本身并没有本质差异。真正区分它们的只是载入方式,而语法把规矩说得很明白:\documentclass{article} 一次,\usepackage{...} 想写几次就几次。所以,当你拿不准“分两栏”属于类的事(\documentclass[twocolumn])还是宏包的事(multicol)时,就问自己:是文档的种类变了,还是只是多了一项功能。此外,.dtx 是宏包的原稿(源码与文档合写在一起的格式),.ins 则是从中抽出 .sty 的说明书。
| 扩展名 | 内容,以及何时产生 |
|---|---|
.tex | 你自己写的原稿。只有它才是应当纳入版本控制的“本体” |
.cls | 类文件,由 \documentclass 载入且只能有一个 |
.sty | 宏包,可用 \usepackage 载入任意多个 |
.dtx / .ins | 宏包作者的原稿,以及据此生成 .sty 的指令文件 |
.aux | 交叉引用、目录和引用的备忘。正因为有它,才需要编译两次 |
.log | 本次运行的完整记录。像 Missing character 这样的警告有时只出现在这里 |
.bib / .bbl | 文献数据库,以及据此排成的参考文献表 |
宏、记号与类别码:TeX 读取字符的顺序
TeX 并不是直接读字符,而是先给每个字符贴上一个 0 到 15 的“类别码”(catcode),再据此处理。\ 是 0(命令的开始),{ 是 1,} 是 2,$ 是 3,& 是 4,# 是 6,^ 是 7,_ 是 8,空格是 10,字母是 11(letter),数字和多数符号是 12(other),~ 是 13(active),% 是 14(comment)。字符加上它的类别码,就是 TeX 处理的最小单位——记号(token);而宏不过是给一串记号起的名字。有一处很吃这套:在文档正文里,@ 的类别码是 12,也就是普通符号,不能成为命令名的一部分。因此 \@ifnextchar 这类内部命令在正文中无法调用,\makeatletter 的作用正是暂时把 @ 改成 11。而在宏包(.sty)内部它本来就是 11,所以那里不需要这道手续。
% Ask TeX for the catcode of a character; the answer lands in the log.
\showthe\catcode`\@ % > 12. (in the document body: "other")
\showthe\catcode`\A % > 11. (letter)
\showthe\catcode`\~ % > 13. (active)
\makeatletter
\showthe\catcode`\@ % > 11. (now a letter, so \@ifnextchar works)
\makeatother| 术语 | 含义,以及容易搞混的地方 |
|---|---|
token | 字符与其类别码的组合,是 TeX 处理的最小单位。字形相同而类别码不同,就是不同的记号 |
catcode | 0 到 15 的角色编号,可用 \catcode 改写;verbatim 正是靠它让命令失效 |
macro | 给一串记号起的名字,用 \newcommand 定义。它是替换而非函数调用:参数不会求值,而是原样粘贴进去 |
expansion | 把宏替换成其内容的过程。错误报告在展开处而非定义处的行号,原因就在这里 |
robust / fragile | 在内容会被写出再读回的场合(移动参数:标题、脚注)不会坏掉的命令是 robust,会坏掉的是 fragile。现代 LaTeX 已把大部分命令做成 robust |
盒子、胶与 kern:排版的三个零件
在 TeX 眼中,排好的一页不是由文字或段落构成的,而只由三样东西构成:盒子,以及填在盒子之间的胶(glue)与 kern。一个字符是带有宽度、高度和深度的小盒子;一行是把盒子横向排开的盒子(\hbox);一页是把行纵向堆叠的盒子(\vbox)。盒子之间放的是 胶——可以伸缩的空白。它不是一个宽度,而是「自然宽度、可伸量、可缩量」的三元组:10pt 的 Computer Modern 中,词间空白自然宽 3.33333pt,可伸 1.66666pt,可缩 1.11111pt。正是这份弹性使两端对齐成为可能,也让 TeX 能够给一行的松紧打分(badness)。相对地,kern 是不能伸缩的固定间隙,而且不能成为换行点。因此 \hbox to 60pt{a\kern 10pt b} 撑不满宽度,会报出 Underfull \hbox (badness 10000);而换成可伸的胶,同一个盒子就悄无声息地填满了。
| 术语 | 含义,以及容易搞混的地方 |
|---|---|
box | 具有宽度、高度和深度的矩形。\hbox 横向排列,\vbox 纵向堆叠。把深度(基线以下)单独计量是 TeX 的特色 |
glue | 可伸缩的空白,由自然宽度、可伸量、可缩量三者构成。它同时也是换行、换页的候选位置 |
kern | 固定宽度的间隙:既不伸缩,也不作为换行点。用于字距调整,例如 \TeX 内部的负 kern |
badness | 衡量胶被强行伸缩到何种程度的指标,取值 0 到 10000,10000 最差。Underfull \hbox (badness 10000) 里的正是这个数 |
sp | scaled point,TeX 内部的最小单位,1pt = 65536sp。所有长度都以这种整数保存,因此不会累积舍入误差 |
DVI 是什么:“设备无关”为什么不带字体
DVI(device-independent,设备无关)是 TeX 本来就要产生的输出格式:一份只写着「哪个字符、用哪种字体、放在哪个坐标」的指令清单,其中并不包含字形轮廓。把差别量一量,结果相当惊人。在 TeX Live 2024 上排同一份只有一行的文档,latex 产生的 DVI 是 300 字节,pdflatex 产生的 PDF 是 17,081 字节——五十七倍有余。翻开 DVI 会看到 fntdef1 27: cmr10:字体只是被名字引用。对 PDF 运行 pdffonts,CMR10 显示为 emb yes sub yes——轮廓已嵌入,而且只保留实际用到的字符。因此 DVI 单独无法可靠显示,必须先由 dvipdfmx 或 dvips 解析字体,才成为可分发的成品。也正是这份轻巧、以及它留给后段的自由,让日文的 upLaTeX → dvipdfmx 路线至今仍在日常使用。
| 术语 | 含义,以及容易搞混的地方 |
|---|---|
DVI | device-independent,只按名字引用字体的中间格式。无论显示还是分发都需要转换 |
PDF | 用于分发和印刷的最终形态:字体已嵌入,还可携带书签、链接与元数据 |
PostScript | 页面描述语言,由 dvips 从 DVI 生成,至今仍用于印刷厂的投稿 |
pt / bp | TeX 的 pt 是英美式点,1in = 72.26999pt。PDF 与 PostScript 用的是 bp(big point),1bp = 1.00374pt。每英寸 72 点的是后者 |
MWE | minimal working example,仍能复现问题的最小文档。提问时对方首先会向你索要它 |
CTAN、TDS、texmf 与 kpathsea:文件到底在哪里
CTAN 是全世界的仓库,TDS 是仓库里的摆放规则,texmf 是本地的实物,kpathsea 则是从中找东西的机制——这四者串成一条链。汇集到 CTAN(Comprehensive TeX Archive Network)的宏包,按照名为 TDS(TeX Directory Structure)的统一布局规则展开到 texmf 目录树中:.sty 放进 tex/latex/<宏包名>/,文档放进 doc/latex/<宏包名>/。正因为有这条规则,文件在任何发行版里的位置都一样。不过 TeX Live 2024 的 texmf-dist 里排着 276,953 个文件,每次运行都遍历目录是不可能的。于是 kpathsea 会先读一份名为 ls-R 的索引。手动装了宏包却依然找不到时,通常只是这份索引过期了——用 mktexlsr 重建即可。想知道实际用到的是哪个文件,kpsewhich article.cls 一行就能回答。
| 术语 | 含义,以及容易搞混的地方 |
|---|---|
CTAN | 宏包的中央归档站(ctan.org),在全球镜像间同步 |
TDS | TeX Directory Structure,规定 texmf 内部布局的约定。手动放置文件时依它行事 |
texmf | 存放实际文件的目录树。它有多棵,其中 texmf-local 的优先级高于 texmf-dist |
kpathsea / ls-R | 文件查找库及其索引。用 kpsewhich 查询,用 mktexlsr 重建索引 |
TeX Live / MiKTeX | 两大主要发行版。TeX Live 每年整体更新一次,MiKTeX 的特色是运行时按需下载缺失的宏包 |
tlmgr | TeX Live 的宏包管理命令,用于更新单个宏包或追加安装 |
日文排版术语:JFM、和文与欧文、禁则
日文 TeX 有一套只排西文时永远碰不到的词汇。其核心是一个设计决定:把和文与西文当作两条完全独立的系统来处理。pTeX 系列引擎用和文字体排汉字与假名,用欧文字体排拉丁字母,并在两者交界处自动插入四分之一 em 的空白(\xkanjiskip)。描述和文字体尺寸的是 JFM(Japanese Font Metric),其中不仅记录字宽,还记录两个标点相邻时该收紧多少。禁止行首出现「、」、行末出现「(」之类的规则叫 禁则处理,在 TeX 中以罚分值(penalty)表达。注音(ルビ) 与竖排是日文特有的另外两项需求,都由类或宏包负责。这些工具在实务中如何取舍,交由日文排版的相关页面处理。
| 术语 | 含义,以及容易搞混的地方 |
|---|---|
JFM | Japanese Font Metric,和文字体的度量定义,除字宽外还包含标点收紧的规则 |
pTeX / upTeX | 为日文排版扩展的 TeX 引擎;upTeX 是内部改用 Unicode 的后继版本 |
kinsoku | 禁则处理:规定哪些字符不得置于行首或行末,在 TeX 中以罚分值表达 |
\xkanjiskip | 在和文与西文交界处自动插入的空白,默认约为四分之一 em,其值可调 |
ruby | 注音假名,小字标在正文上方(竖排时在右侧),由类或宏包提供 |