Skip to content

The TEXbook 知识点总结

🕒 Published at:

阅读背景

《The TEXbook》是由 TeX 作者 DONALD E. KNUTH 编写的一本关于 TeX 设计的书, 介绍了 TeX 在设计时所考虑到的细节。OLeX 在词法分析和语法部分需要仔细研究此部分的内容, 从而消除对 TeX 的分析差异。(之前都是针对大量模板进行的实现, 多多少少会有兼容性和语法忽略的问题)

TeX 是一门注重排版的语言, 并且让用户不用过度关心于排版本身。本篇博客将注重于对 TeX 令人疑惑的细节探索, 追求在语法上和原理上总结 TeX 这门语言的 “奇异” 之处。

排版

书籍与普通的排版并不完全相同, 尤其是在符号的差别上, 计算机的 ASCII 码并不是专门为书籍出版所发明, 比如 引号 有左引号、右引号、无方向引号... 因此, TeX 在此方面做了很多的考虑。

比如双引号 可以输入为 ‘‘, 可以输入为 ’’

连字符(-) -、短破折号(-) --、破折号(——) ---、减号(-) $-$, 这四个符号其实并不一样。TeX 会完全自己解决这个问题, 而用户并不必要担心。

TeX 可以通过 \lq\rq 键入左右引号。为了键入 ’” 并且消除歧义, 使单双引号间距不过大或过小, 可以键入 ’\thinspace’’ 得到想要的结果。

TeX 控制系列

键盘可以输入的字符比较少, 为了解决这个问题 TeX 使用反斜杠 \ 进行 “转义”。举个例子:

tex
\input MS

上面这行命令的意思为: 读取 MS.tex 文件, \input 就是一个控制系列(命令), 再举个例子:

tex
George P\olya

渲染为: George Pólya

控制系列类型:

  • 第一类: \input; 由 \ + 多个字母 + 空格或非字母字符, TeX 一般把 [a-zA-Z] 看作字母, 数字 [0-9] 并不看作字母 (OLeX 在实现上进行了差异化处理, 未严格按照 TeX 进行实现)
  • 第二类: \’; 由 \ + 一个单个非字母组成, 区别是不必将控制系列与后面的字母分开, 第二类控制系列在转义符后面永远只有一个符号!

第一类控制系列后面的空格会被忽略掉, 并不看作为 “实际” 空格。但是, 出现在第二类控制系列后面会看作是实际空格

TeX 把多个连续空格看作单个空格。键入控制空格解决此问题 \ (控制空格)

TeX 把 <return> 看作同 \ (控制空格), 控制空格属于第二类控制系列。

TeX 的第二类控制系列的实现在 OLeX 中也没有严格实现, OLeX 借助于 HTML 和一部分的 SVG 进行实现, 所以并没有完全严格按照 TeX 的规范, 为了消除 TeX 本身可能语法带来的歧义导致给用户带来心智负担。

TeX 大约能执行900个控制系列, 是 TeX 内置语汇的一部分。(OLeX 通过自行设计渲染器结构对内置语汇和宏包进行统一实现)。需要注意的是, TeX 的控制系列是大小写敏感的, 并且是一一对应的映射关系。大约 300 个控制系列为 原始控制系列

TeX 的高级控制系列可以进行计算 (OLeX 或许在更远期的规划里面, 会对此部分的内容进行实现。)

\show\input -> \input=\input, \show\thinspace

->

text
> \thinspace=macro:
-> \kern .16667em .

\show 的结果被保存在 log 文件中。

plain TeX 大约有 600 个基本控制系列, 加上 300 个左右的原始控制系列, 构成了TeX 基本的文本排版控制。

字体

默认情况下, 不定义其他字体将使用 roman 字体。

text
\rm --> Roman
\sl --> Slanted
\it --> Italic
\tt --> Typewriter
\bf --> Bold

字体的外形会随着大小而改变, 单位设计使用 points(即pt)。

text
10 points --> \tenrm
9 points --> \ninerm
10 points slanted --> \tensl

对于字体名称的处理, \font\cs=<external font name> 把一个特殊字体的有关信息载入内存; 在控制系列 \cs 将在排版时选择那个字体。Plain TeX 最初只定义了 16 个字体, 可以使用 \font 把系统字体库中字体为 TeX 所用。

因为对于字体设计来说, 随着字号的不同并不是单纯的放大或缩小, 因此放缩字体还是存在与调整字号大小不同之意义的。通过 \font\cs=<external font name> at <desired size> 命令实现。

另一种方法是, 相对于设计尺寸给出放大因子。规定为整数, 等于放大比例乘上 1000.1200 即是放大 1.2 倍。\font\manifiedfiverm=cmr5 scaled 2000。Plain TeX 提供了缩略命令, \magstep0 放大因子为 1000、\magstep1 放大因子为 1200, \magstep2 为 1440 ... \magstep5

\magstephalf 放大 1.2 倍。

编组

编组即是使用一对 {} 对内容进行包围的操作, 实现隔绝渲染环境, OLeX 对编组进行了实现。但是为了更好的歧义消除效果, 对于 OLeX 来说, 还是更推荐使用 \CommandName{} 的形式, 会在语法上对非控制系列分组进行考虑实现

类似于 {\it centered} 这种组内语句, \it 对其**后面组内的文本渲染(局部作用域)**都起作用, 这一点需要注意。

需要注意: TeX 里面有个控制系列 \/ 可以作为 unslanted 转化过渡。

编组还有一个作用: 将文本的多个单词看作一个单一对象!(OLeX 在词法分析上已经对此部分进行了实现, 需要修改语法分析部分, 并且对不正确编组进行修正)

编组创造了局部作用域的环境。 有时候为了突破当前局部作用域, 可以通过定义加上前缀 \global 而实现。例如, TeX 把当前页码放在一个叫 \count0 的寄存器中, 输出一页增加页数。放在组中可以避免上下文影响; 但是对于输出来说 \count0 为局部变量, 会产生掩盖, 命令 \global\advance\count0 by 1 可以实现组内生命周期结束, 对寄存器进行保留。\global 使其后紧接的定义适用在所有存在的组, 无所谓深度。

OLeX 在生命周期通过上下文对此特性进行实现, 并且通过上下文进行传递。

除了上述模块结构的方法, 另一种方法为原始的 \begingroup \endgroup, 但需要保证实际执行文本必须是嵌套组, 组不能交叉!

定义控制系列 \beginthe<block name> \endthe<block name>, 只有嵌套才是合法的。

运行 TeX (跳过此部分)

运行 TeX 程序会生成一个 .dvi 文件, 不依赖于任何设备, 可以在几乎所有的印刷设备打印出来。

~ 称为带子, (OLeX 暂不对此部分进行实现), 对应书 p17~28。

TeX 工作原理 (跳过实现)

  • 一个 <return> 与一个空格类似
  • 一行中的两个空格看作一个 (合并空格操作)
  • 一个空行表示一段的结束

上面的规则并不准确

TeX 可以见到直接由键盘输入的字符行中字符有 256 个, 可以分为 16 类:

类别意义字符
0转义符\
1组开始{
2组结束}
3数学环境$
4表格对齐&
5换行<return>
6参数#
7上标^
8下标_
9可忽略字符<null>
10空格
11字母[a-zA-Z]
12其他字符不在上下文的其他字符
13活动符~
14注释符%
15无用符<delete>

OLeX 支持的字符远超上述所述内容, 换行使用 \n 规范化解决了 CRLF 的问题。

字符输入

TeX 通过 % 对文本进行注释, 通过 \% 键入 %

TeX 可以通过 \char<number> (number: 0~255) + 空格, 得到相应的字符。比如 \%=\char37, TeX 内部表示字符基于 ASCII 码, 说到底还是通过**“特殊”**的控制系列