首先要对板上的同好们说一声抱歉。上次回答完问题之后﹐就消失了很长时间。一方面是
课业繁重﹔一方面是 TeX Live 2014 就要出来了﹐手头几个宏包急着更新(希望能随
TeX Live 2014 发布)﹔一方面是手头在修一本书﹐关于 GRE 考试的填空教程﹐工程
比较浩大﹐不过好在今天完成了它手稿的的四分之三﹔另外是最近自己身体欠佳﹐去了几
次医院。
虽然有诸多理由﹐不过还是要对大家说一声抱歉。真的没想到一拖就拖了这么久。
今天来详细说一说关于 TeX 处理中文字的一些事情﹐会比较长﹐希望大家做好心理准备。
下文中﹐一些术语的说法可能和台湾方面不大一样﹐尽我所能总结如下﹕
macro 宏 宏
package 宏包 宏套件
file 文件 档案
如果文章中有看不懂的术语﹐欢迎大家回复补充。
## 关于 TeX 系统的一点基础知识
板上同好们水平较高﹐所以分不清发行版、宏包、格式、引擎这些概念的应该是少数﹐不
过这篇文章之后也会在大陆的知乎网上发布﹐为了统一﹐所以一并讲了吧。
TeX 家族是一个很庞大的概念﹐虽然 TeX 程序自己很小﹐庞大的原因﹐主要是后来人们
不断地对它的功能进行扩展导致的。
TeX 家族最开始的概念是引擎。Knuth 老爹开发出的 TeX 是用 WEB 语言写的﹐这是一
个文学编程的语言﹐需要用 Pascal 来编译──不过现在的 Pascal 编译器也是不能编
译它的﹐版本不匹配──比较的繁琐﹐效率也不够高。于是有人开发出了 WEB2C 这个小
工具来将 WEB 语言的代码转成 C 语言﹐这样能提高不少效率。
众所周知﹐TeX 最开始是 Knuth 老爹为了自己的 TAOCP (不知道的请上网爬文)的排
版而创作的﹐所以当 Knuth 老爹自己对 TeX 满意之后﹐就不乐意修改它了。然而尽管
TeX 是 Knuth 老爹和其他人(他的学生)一起做出来的东西﹐Knuth 老爹却坚持只有
自己才能修改名为 TeX 的东西。所以当 TeX 流传开来之后﹐大家发现 TeX 功能上有
不如人意的地方的时候﹐只能自己在 TeX 的基础上做一些增强版。比如﹐著名的
e-TeX, 还有大家熟悉的 pdfTeX, XeTeX, LuaTeX 等。不是所有的扩展版都包含了
T, E, X 这三个字母﹐比如以前有一个引擎叫做 Omega, 它支持 16 种文字顺序方向﹐
十分神奇。每一个 TeX 引擎都有一套自己的原语﹐不过所有的增强版引擎﹐几乎都兼容
原始的 TeX 的原语──语法一致﹐但功能可能变强大了。
这些增强版﹐包括最原始的 TeX 在内﹐都被称为**引擎**。引擎是承担最终排版工作的
可执行程序。
让我们回到最开始的 TeX 上面来。众所周知﹐TeX 是一个引擎﹐同时也是一种宏编程语
言的名字。Knuth 老爹的原始版 TeX 一共有三百多个最原始的宏﹐我们把它们叫做
TeX 原语(TeX primitive)。其他所有的宏都是通过他们构建出来的﹐所以从理论上
说﹐只要掌握了这三百多个原语﹐就能让 TeX 帮我们解决一切问题了。不过最原始的东
西往往是强大的﹐但同时是不方便使用的。为了方便使用﹐我们就要在这些原语的基础上
为常用的功能编写宏。
有一些宏非常常用﹐所以如果每次都加载这些宏会繁琐且缓慢。所以 Knuth 老爹允许人
们将常用的宏编译成为格式(format, 扩展名为 fmt)。Knuth 老爹自己设计了一个格
式﹐称为 plain TeX, 加上原语﹐他一共有 900 多个命令。因为它太基础﹐用得非常
频繁﹐所以大多数时候人们讲到 TeX 语言的时候﹐指的是 plain TeX 构成的那些指令
集合﹐而不是 TeX primitives.
TeX 是面向排版的﹐所以许多命令晦涩难懂──通常写文章的作者不需要知道这些细节﹐
于是﹐刚刚获得图灵奖的 Leslie Lamport 博士在 plain TeX 的基础上又编写了一些
宏﹐并编译为格式﹐取名为 LaTeX. La 就是他姓氏的头两个字母﹐仿照中国古代的叫法
这就是所谓的莱氏 TeX. 所以你看﹐不管是中国人还是外国人﹐都想着光宗耀祖呢。
LaTeX 优秀之处在于﹐它将内容与格式分开﹐使得作者不需要﹐或者只需要知道很少的排
版细节就能排出漂亮的文章。
除掉 plain TeX 和 LaTeX, 常见的格式可能就只有 ConTeXt 了。
在格式的基础上﹐人们发现实现某一些特定的功能的时候﹐使用的宏命令总是一样的。所
以如果把它们打包起来﹐就会变得很方便。所以出现了宏包(package)这个东西。宏包
总是和格式对应﹐LaTeX 格式的宏包往往不能用于使用 plain TeX 格式书写的文章之
中。LaTeX 中最常用的几个宏包可能是﹕amsmath, geometry, hyperref, xcolor,
graphicx, booktabs 等。
除掉这些东西﹐人们还开发了一些辅助工具。比如﹐名为 BibTeX 的工具﹐就是用来生
成参考文献的﹔名为 makeindex 的工具﹐就是用来生成索引的。另外对于引擎、格式、
宏包、工具﹐用户要使用它必须有一份说明文档来指引进步。所有这些东西﹐零零总总﹐
加起来有成千上万个文件﹐如果让用户去一一下载安装﹐一则不方便﹐二则不方便控制
版本。所以就有人/组织/公司将他们全部打包在一起。比较著名的发行版有 Windows
系统下的 MikTeX, 跨平台的 TeX Live, 为中文做过优化的 CTeX (大陆吴凌云研究
员)﹐cwTeX (台湾吴聪敏教授)等。
至于 TeXworks, WinEdt, TeXstudio, TeXmaker 这些东西﹐只是编辑代码的工具﹐
和 TeX 系统并没有直接的关系。
总的来说﹐引擎承担排版工作﹐对应一套原语﹔格式对应一套书写规则﹔宏包对应一定
的功能﹔发行版则是所有东西的大杂烩。如果用汽车来做比喻﹐那么发行版是整个汽车﹐
引擎是发动机和传动系统﹐格式是方向盘、刹车、离合器这些控制系统﹐宏包则是音响、
车窗之类的各种功能的小物件。
## 说说字体
所谓排版﹐就是要把每一个东西摆在合适的区域﹐然后在这个区域上画出特定的形状。
理解了这个概念﹐我们就知道﹐不管是排版文字﹐还是排版图形﹐所做的工作都是一样
的﹐差别只在于最终画什么。对于 TeX 来说﹐画出什么样的形状其实是不重要的﹐毕
竟这个形状是给我们人类看的。所以 TeX 完全可以只关注什么区域放什么﹐至于这些
东西长什么样﹐完全可以交给阅读器去处理。
因此﹐我们可以很容易地想到﹐一个字体应该可以分成两个部分﹕描述尺寸的和描述形
状的。前一个部分我们称之为 metrics, 后一个部分我们称之为 glyph. 对于
METAFONT 生成的字体﹐确实就是有这两个部分的﹐前一个部分扩展名是 tfm, 后一
个部分的扩展名是 pk. 不过并不是所有的字体格式都将两个部分分开﹐比如
OpenType 字体﹐字符的尺寸信息和形状信息全都保存在一个文件里面。
在 TeX 原语中﹐调用字体的命令是 \font. 这个命令是后续各种字体调用命令的基础﹐
包括 xeCJK 的 \setCJKmainfont 等命令﹐都是在它的基础上建立起来的。
Knuth 老爹的 TeX 系统﹐限于当时的情况﹐只支持自家的 METAFONT 生成的 tfm
文件。什么意思呢﹖(Knuth) TeX 的原语 \font 只认识 tfm 文件﹐别的什么 ttf,
otf 统统不认识。这无疑是不方便的﹐毕竟现代优秀的字体大都是 ttf 或者 otf
格式。于是 XeTeX 应运而生﹐它的原语 \font 认识 ttf, otf 这些格式﹐所以能够在
读取安装在系统上的这些格式的字体──只要想办法让它能够找到就行了。另一个支持
ttf, otf 字体的引擎是 LuaTeX, 不过继承自 pdfTeX 的 LuaTeX, 它的 \font 命令
也不能直接读取 ttf, otf 格式的字体文件﹐需要使用名为 callback 的库来间接调用。
这也就是为什么使用 LuaTeX 处理字体会比 XeTeX 慢的缘故了﹐虽然 LuaTeX 相较
XeTeX 有不少优势﹐但是读取字体这一块﹐确实是比不上。
说完了单个字符﹐我们再来说说字符在字体里边的情况。
可以想象﹐字符在字体文件里边是按照某种顺序排列在一起的。那么﹐假设你就是 TeX,
你想要获得 A 这个字符﹐你怎么找到它在字体文件里的位置呢﹖没错﹐A 的 ASCII 编码
是 65, 如果字体文件是按照 ASCII 编码的顺序排列的﹐我们只需要找到第 66 个(从 0
开始)字符就可以了。
遗憾的是﹐事情并没有这么简单。一则编码有许多种﹐字体里不一定要使用某一种特定的编
码﹔二则东亚文字有许许多多﹐各地区使用的编码也不尽相同﹐必须协调好各部分之间的关
系。于是 LaTeX 规定了名为 NFSS (新字体选择机制)的方案﹐字体属性分成五个方面﹕
编码(encoding)﹐比如 TeX text, TeX extended text;
字族(family)﹐比如 Roman, Typewriter;
字重(series)﹐比如 bold, narrow, medium;
字形(shape)﹐比如 Italic, Small Caps;
字号(size)﹐比如 10pt, 11pt.
明确了这些﹐你才能精确地说明白你到底需要哪一个字形﹐如何才能找到特定的字符。这些
东西你可能不熟悉﹐但是如果你用过一段时间的 LaTeX, 几乎能说你肯定见过。LaTeX 的
关于溢出盒子的警告一般是这样写的﹕
Overfull \hbox (3.80855pt too wide) in paragraph at lines 314