字体系统 (NFSS / 编码)

当你用 \textbf\sffamily 切换书体时,底层到底发生了什么?本页讲解字体命令 背后的机制。LaTeX 用 五个相互独立的属性(NFSS)指定一个字体,并通过 编码 把输入字符映射到实际字形来排版。命令列表留给单独页面,这里深入低层选择器如何用 \selectfont 生效、OT1 / T1 / TU 等编码的区别,以及为什么在 pdfLaTeX 中要写 \usepackage[T1]{fontenc}

NFSS:字体由五个属性决定

LaTeX2e 的字体选择建立在 NFSS(New Font Selection Scheme,新字体选择方案) 之上。其核心思想是:任何文本字体都完全由五个属性决定。fntguide(LaTeX2e 字体选择指南)把这五个属性定义为:编码(encoding)族(family)系列(series)形状(shape)尺寸(size)

  • 编码 — 字符在字体中的排列顺序(输入字符到字形槽位的映射)。正文中常见的是 OT1(TeX text)和 T1(TeX extended text,也就是 Cork)。下一节详述。
  • — 共享字形骨架的一组字体。Computer Modern Roman 的代码是 cmr,其 Sans 是 cmss,Typewriter 是 cmtt;商业字体中 Times 是 ptm,Helvetica 是 phv,Courier 是 pcr
  • 系列 — 合并字重和字宽的一条轴:m(medium,即默认)、b(bold)、bx(bold extended)、c(condensed)、sb(semi-bold)等。
  • 形状 — 字母的形态:n(直立/罗马体)、it(斜体)、sl(倾斜/oblique)、sc(大写与小型大写)。较少见的还有 ui(人为转正的斜体)。
  • 尺寸 — 设计尺寸,例如 10pt;若省略单位,视为 pt

关键细节是,series 是 把字重和字宽合并成一条轴。在标准表示中,字重从 ulub(ultra light 到 ultra bold),字宽从 ucux(ultra condensed 到 ultra expanded)。两者连接成一个值,不过 m 会省略,除非字重和字宽都是 medium,此时保留单个 m。因此“bold、标准宽度”是 b,“bold extended”是 bx,“medium weight、condensed”是 c

这五个属性写成 encoding/family/series/shape/size,就是 LaTeX 内部的字体正式名称。例如 OT1/cmr/m/n/10 表示“OT1 编码的 Computer Modern Roman,medium、直立、10 点”。你在 overfull box 警告中看到的 \OT1/cmr/m/n/10,正是这五个属性。平时使用的 \textbf 等高层命令,不过是在改写这五个属性中的某一个。

低层选择器与 \selectfont

在高层命令之下,有逐个设置属性的 低层选择器\fontencoding{T1}\fontfamily{ptm}\fontseries{b}\fontshape{it}\fontsize{12}{14}。但有一条关键规则:仅设置它们不会生效,必须调用 \selectfont 才会反映出来。fntguide 警告说:设置字体参数后,在任何后续文本之前必须立即放置 \selectfont 命令。

latex
% 正しい:設定 → \selectfont → テキスト / Correct: set, then \selectfont, then text
\fontfamily{ptm}\fontseries{b}\selectfont Some text.

% 誤り:設定とテキストの間に文字を挟んではいけない / Wrong: no text between a setting and \selectfont
\fontfamily{ptm} Some \fontseries{b}\selectfont text.

一次指定这些属性的捷径是 \usefont{encoding}{family}{series}{shape}。它等价于相应的 \font... 命令序列后接 \selectfont,尺寸沿用当前值。需要临时调用某个特定命名字体时很方便。注意,改变 \fontencoding 后同样需要后接 \selectfont 才会生效。

这些属性 很少需要直接书写\textbf 会在内部调用 \fontseries\sffamily 会调用 \fontfamily,依此类推。命令列表以及命令形式与声明形式的区别,整理在“Font style commands”页面。只有在点名使用特定字体,或把新字体接入类/包时,才会触及低层接口。

默认值宏:\rmdefault 及相关宏

\textrm\rmfamily 选择哪个族 并不是写死的,而是保存在 中。三个宏分别是 \rmdefault(roman)、\sfdefault(sans)和 \ttdefault(typewriter);在 article 类中默认值分别为 cmrcmsscmtt。因此,如果设计者想把正文换成 Times、Helvetica、Courier,并不重写命令,而是替换这些宏。

latex
% 文書全体の3ファミリを差し替える / Re-point the three families for the whole document
\renewcommand{\rmdefault}{ptm}  % roman   → Times
\renewcommand{\sfdefault}{phv}  % sans    → Helvetica
\renewcommand{\ttdefault}{pcr}  % mono    → Courier

正文默认字体本身由 \encodingdefault / \familydefault / \seriesdefault / \shapedefault 四个宏决定,默认是 OT1\rmdefaultmn。由于 \familydefault 指向 \rmdefault,改变 \rmdefault 就会改变正文字体。粗体字重由 \bfdefault 保存(默认 bx);使用不含 bx 粗体的字体时,比如许多 PostScript 字体,可能需要把它降为 b

字体编码:从输入到字形

字体编码 是字形在字体中的排列顺序,也就是约定 每个输入字符对应字体中的哪个槽位(字形)。它是五个属性中的第一个,同时也是唯一一个没有 \textbf 这样 面向作者命令 的属性。正如 fntguide 所说,切换编码是由 fontenc 这样的 包来提供 的。

正文中需要掌握的要点如下。OT1 是 Knuth 的 原始 7 位 “TeX text” 编码(默认)。它只能容纳 128 个字符,因此 éñ 这样的重音字母通过 基字母与重音符号组合\accent)生成。问题就在这个组合:带重音的词不会断词,从 PDF 复制粘贴也容易出错。T1“TeX extended text”,源自 1990 年 Cork 的 TUG 会议,因此也称 Cork 编码)是 8 位、256 字形编码,把每个重音字母作为 单独字形 保存。这样西欧和部分东欧语言就能 正确断词,复制粘贴也正常。

TUTeX Unicode)用于通过 Unicode 码点直接使用系统安装的 OpenType 字体,是 XeLaTeX 和 LuaLaTeX 的默认编码。由于 fontspec 会自动设置它,在 Unicode 引擎中几乎不会手写 TU。此外还有按用途划分的编码:T2A(西里尔文字)、LGR(希腊文字,目前希腊语主要使用的编码)和 TS1Text Companion,正文中版权符号、货币符号等符号字形的集合,由 textcomp 处理)。数学中则分配了 OML(数学斜体)、OMS(数学符号)和 OMX(大型数学符号)。

编码覆盖内容位宽 / 备注
OT1Knuth 原始 “TeX text”(默认)7 位;重音由组合生成 → 不能断词
T1TeX extended text(Cork);西欧语言8 位,256 字形;重音为单独字形 → 可断词
TUTeX Unicode;直接使用 OpenType 字体Xe/LuaLaTeX 默认;由 fontspec 设置
T2A西里尔文字8 位(也有 T2B / T2C)
LGR希腊文字当前希腊语标准的 256 字形编码
TS1Text Companion(文本符号)版权、货币等;由 textcomp 处理

输入编码与字体编码

这里把两个容易混淆的概念分开。输入编码 决定 .tex 源文件中的 字节序列应按什么字符来读取(如 UTF-8),历史上由 inputenc 包负责。字体编码 决定读入后的字符 流入输出字体的哪个字形,由 fontenc 负责。可以把它看作 入口(inputenc)与出口(fontenc) 的关系。

现代 pdfLaTeX 默认把源文件按 UTF-8 读取,因此几乎不再需要显式载入 inputenc。不过字体编码仍然值得设置。fontenc面向 pdfLaTeX 的包;如果使用 XeLaTeX 或 LuaLaTeX,则改用 fontspec(Unicode 引擎默认是 TU,所以这个问题基本不会出现)。

在 pdfLaTeX 中载入 T1

如果用 pdfLaTeX 排版,惯例是在导言区写 \usepackage[T1]{fontenc}。它把文档的字体编码切换到 T1,清除 OT1 默认设置的弊端:带重音的词不能断词、从 PDF 复制文本会乱码。fontenc 文档也说明,它支持法语、德语、意大利语、波兰语等广泛西欧语言,并且有重音字母的词可以由 LaTeX 断词,输出也可以复制粘贴。

如果在 fontenc 的选项中列出多个编码,最后列出的会成为默认值\encodingdefault 被设为它)。例如混用希腊语的文档可写 \usepackage[LGR,T1]{fontenc},这样正文默认仍是 T1,需要时再切换到 LGR

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}   % フォントエンコーディングを T1 に / font encoding -> T1
% pdfLaTeX では入力は UTF-8 が既定(inputenc は通常不要)
% Input is UTF-8 by default on pdfLaTeX (inputenc usually unneeded)
\begin{document}
% T1 ならアクセント付きの語も正しくハイフネーションされる
% With T1, accented words hyphenate correctly
Na\"ive r\"esum\"e, \"uberfl\"ussig, Stra\ss{}e.
\end{document}

低层选择器实例

最后用一个文档确认低层选择器和 \usefont 的用法。\usefont 一次给出五个属性中的四个(encoding、family、series、shape)并当场生效;\fontfamily\selectfont 则逐个设置属性后生效。两者只要包在分组 { } 中,效果就会限制在该范围内。

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}
\begin{document}
% \usefont で 4 属性を一括指定 / \usefont sets four attributes at once
{\usefont{T1}{ptm}{b}{it} Bold italic Times}

% 低水準セレクタを個別に設定し \selectfont で確定
% Set selectors individually, then commit with \selectfont
{\fontfamily{phv}\fontseries{b}\selectfont Bold Helvetica}

% サイズだけ変える(baselineskip は現在値を流用)
% Change only the size (reuse the current baselineskip)
{\fontsize{14}{\f@baselineskip}\selectfont Fourteen point}
\end{document}

这里 {\usefont{T1}{ptm}{b}{it} ...} 选择的是“T1 编码的 Times,粗斜体”;离开分组后会恢复原字体。把内部宏 \f@baselineskip 作为 \fontsize 的第二个参数传入,是只改变字号而不改变行距的惯用法(不能直接改写内部宏,但读取它并传入参数是可以的)。