术语表

LaTeX 的术语难懂,不是因为词汇艰深,而是因为它们顶着日常词汇的外表,内里却完全不同。“宏包”不是 npm 的包,“类”不是面向对象的类,“glue(胶)”也不是胶水。更妙的是,本术语表的主角——引擎与格式、类与宏包、盒子与胶与 kern——都可以在自己机器上亲自验证。在 TeX Live 2024 上,pdflatex 不过是指向 pdftex 的符号链接,而 latex 指向的也是同一个 pdftex。本表对每个词条,都从“新手必然弄错的地方”讲起。

引擎与格式的区别:为什么 pdflatex 就是 pdftex

引擎是真正运行的程序,格式则是预先装入该程序的一整团宏。这不是抽象说法,在文件系统里就能看到。查看 TeX Live 2024 的 bin 目录,pdflatex 是指向 pdftex 的符号链接,latex 指向的也是同一个 pdftex。同样地,xelatex → xetexlualatex → luahbtex,而 platexuplatex 都是 euptex。换句话说,“六个 LaTeX 命令”实际上只有 四个二进制程序。那么 latexpdflatex 差在哪里?只差启动时载入的 .fmt 文件——一份预先处理好的宏转储。latex.fmt 是 8,221,690 字节,pdflatex.fmt 是 8,221,444 字节,两者只相差 246 字节。

shell
# 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真正运行的程序本体(texpdftexxetexluatexeuptex)。输出格式、字体机制和字符处理由它决定
format预先载入的宏体系(plain TeX、LaTeX、ConTeXt),固化为 .fmt 文件
.fmt格式的转储文件,放在 texmf-var/web2c/<引擎名>/ 下,由 fmtutil 生成
pdfLaTeX引擎 pdftex 加 LaTeX 格式的组合。并不存在名为 pdfLaTeX 的程序
LaTeX2e1994 年以来的现行 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,所以那里不需要这道手续。

latex
% 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 处理的最小单位。字形相同而类别码不同,就是不同的记号
catcode0 到 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) 里的正是这个数
spscaled point,TeX 内部的最小单位,1pt = 65536sp。所有长度都以这种整数保存,因此不会累积舍入误差

DVI 是什么:“设备无关”为什么不带字体

DVI(device-independent,设备无关)是 TeX 本来就要产生的输出格式:一份只写着「哪个字符、用哪种字体、放在哪个坐标」的指令清单,其中并不包含字形轮廓。把差别量一量,结果相当惊人。在 TeX Live 2024 上排同一份只有一行的文档,latex 产生的 DVI 是 300 字节pdflatex 产生的 PDF 是 17,081 字节——五十七倍有余。翻开 DVI 会看到 fntdef1 27: cmr10:字体只是被名字引用。对 PDF 运行 pdffontsCMR10 显示为 emb yes sub yes——轮廓已嵌入,而且只保留实际用到的字符。因此 DVI 单独无法可靠显示,必须先由 dvipdfmxdvips 解析字体,才成为可分发的成品。也正是这份轻巧、以及它留给后段的自由,让日文的 upLaTeX → dvipdfmx 路线至今仍在日常使用。

术语含义,以及容易搞混的地方
DVIdevice-independent,只按名字引用字体的中间格式。无论显示还是分发都需要转换
PDF用于分发和印刷的最终形态:字体已嵌入,还可携带书签、链接与元数据
PostScript页面描述语言,由 dvips 从 DVI 生成,至今仍用于印刷厂的投稿
pt / bpTeX 的 pt 是英美式点,1in = 72.26999pt。PDF 与 PostScript 用的是 bp(big point),1bp = 1.00374pt。每英寸 72 点的是后者
MWEminimal 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),在全球镜像间同步
TDSTeX Directory Structure,规定 texmf 内部布局的约定。手动放置文件时依它行事
texmf存放实际文件的目录树。它有多棵,其中 texmf-local 的优先级高于 texmf-dist
kpathsea / ls-R文件查找库及其索引。用 kpsewhich 查询,用 mktexlsr 重建索引
TeX Live / MiKTeX两大主要发行版。TeX Live 每年整体更新一次,MiKTeX 的特色是运行时按需下载缺失的宏包
tlmgrTeX Live 的宏包管理命令,用于更新单个宏包或追加安装

日文排版术语:JFM、和文与欧文、禁则

日文 TeX 有一套只排西文时永远碰不到的词汇。其核心是一个设计决定:把和文与西文当作两条完全独立的系统来处理。pTeX 系列引擎用和文字体排汉字与假名,用欧文字体排拉丁字母,并在两者交界处自动插入四分之一 em 的空白(\xkanjiskip)。描述和文字体尺寸的是 JFM(Japanese Font Metric),其中不仅记录字宽,还记录两个标点相邻时该收紧多少。禁止行首出现「、」、行末出现「(」之类的规则叫 禁则处理,在 TeX 中以罚分值(penalty)表达。注音(ルビ)竖排是日文特有的另外两项需求,都由类或宏包负责。这些工具在实务中如何取舍,交由日文排版的相关页面处理。

术语含义,以及容易搞混的地方
JFMJapanese Font Metric,和文字体的度量定义,除字宽外还包含标点收紧的规则
pTeX / upTeX为日文排版扩展的 TeX 引擎;upTeX 是内部改用 Unicode 的后继版本
kinsoku禁则处理:规定哪些字符不得置于行首或行末,在 TeX 中以罚分值表达
\xkanjiskip在和文与西文交界处自动插入的空白,默认约为四分之一 em,其值可调
ruby注音假名,小字标在正文上方(竖排时在右侧),由类或宏包提供