在 LaTeX 里写 {\bf\it x},得到的不是粗斜体,而是斜体。把顺序换成 {\it\bf x},消失的反倒是斜体,只剩直立的粗体。这些老式的双字母命令 就是不肯相加,原因是 article.cls 第 493 行里的一个词。读懂那一行,也就一并明白了现代 LaTeX 为什么给每个字体开关都准备了两种形式。本页梳理这些命令如何排布在 字族、字重、字形三个独立的轴 上,何时该用带参数的 \textbf{...}、何时该用按范围生效的声明 \bfseries,以及当你要粗体小型大写字母却拿到普通粗体时,日志到底在说什么——全程附实测数据。
\textbf{...} 与 \bfseries 的区别
同一种切换备有两种写法。以 \text… 开头的命令形式带一个参数,只作用于花括号内部。以 …family / …series / …shape 结尾的声明形式不带参数,从该处一直生效到所在分组或环境结束。 选哪种取决于范围长短:几个词用 \textbf{只有这里},整段或整个环境用 {\bfseries …} 或 \begin{quote}\itshape …\end{quote}。若忘了用分组括住声明,它会一路生效到文档末尾,所以务必核对 { 与 } 的配对。宏包和文档类的内部代码也偏爱声明形式,因为它不额外添加任何东西,把范围管理留给调用方。
% command form: for a short span
This is \textbf{bold} and this is \textit{italic}.
% declaration form: a group limits the scope
Normal here. {\bfseries Bold from here on.} Back to normal.
% declaration applied to a whole environment
\begin{quote}
\itshape The whole quotation is set in italic.
\end{quote}除了范围之外,两种形式还有一个实际差别:命令形式会自动加入斜体校正,声明形式不会。 倾斜字形的外廓向右上探出,而排版宽度并不含这一段,于是紧随其后的直立字母就会相撞。实测 \textit{f}h 为 10.74167pt,{\itshape f}h 为 8.62222pt,这 2.11945pt 的差额整个就是缺失的空白。若用声明形式,就得自己在倾斜段落末尾写上 \/——{\itshape f\/}h 实测 10.74167pt,与命令形式完全一致。这项校正究竟在做什么,强调那一页有详细说明。
字族、字重、字形 — 决定字体的三个轴
LaTeX2e 的字体由三个彼此独立的属性组合而成:字族(family)是字的骨架,字重(series)兼含粗细与宽度,字形(shape)是倾斜或形态。 切换其中一个轴,另外两个保持不变——这种正交性正是整套设计的核心,也正因如此,「保持无衬线而加粗」「保持粗体而变斜」才能顺畅写出。这套方案以 NFSS(New Font Selection Scheme) 之名于 1989 年问世,1993 年推出第二版,1994 年随 LaTeX2e 成为标准;更早的 LaTeX 2.09 根本没有字体属性这个概念。值得记住的是,字重是一个同时涵盖粗细与宽度的轴。这也正是 Computer Modern 的粗体写作 bx(bold extended,既粗又宽)的原因。
- 字族(family) — 罗马体(有衬线)
\textrm/\rmfamily、无衬线体\textsf/\sffamily、打字机体(等宽)\texttt/\ttfamily。 - 字重(series) — 中等(默认粗细)
\textmd/\mdseries、粗体\textbf/\bfseries。属性值是最多两个字母的字符串,把粗细与宽度并写在一起:m(medium)、b(bold)、bx(bold extended)、c(condensed)等。 - 字形(shape) — 直立(默认)
\textup/\upshape、斜体\textit/\itshape、倾斜\textsl/\slshape、小型大写字母\textsc/\scshape。属性值依次为n、it、sl、sc。 - 斜体与倾斜不是一回事。
\itshape调用的是单独绘制的字体(Computer Modern 中为cmti10),\slshape调用的是把直立体机械倾斜后的字体(cmsl10)。字形本身就不同——比如斜体的a是单层结构。
| 键 | 轴与效果 | 命令形式(短范围) | 声明形式(用分组限定) |
|---|---|---|---|
roman | 字族:罗马体(有衬线) | \textrm{...} | {\rmfamily ...} |
sans | 字族:无衬线体 | \textsf{...} | {\sffamily ...} |
typewriter | 字族:打字机体(等宽) | \texttt{...} | {\ttfamily ...} |
bold | 字重:粗体(属性值如 bx) | \textbf{...} | {\bfseries ...} |
medium | 字重:中等(默认,属性值 m) | \textmd{...} | {\mdseries ...} |
italic | 字形:斜体(属性值 it) | \textit{...} | {\itshape ...} |
slanted | 字形:倾斜(属性值 sl) | \textsl{...} | {\slshape ...} |
small caps | 字形:小型大写字母(属性值 sc) | \textsc{...} | {\scshape ...} |
upright | 字形:回到直立(属性值 n) | \textup{...} | {\upshape ...} |
reset all | 把三个轴全部重置为文档默认值 | \textnormal{...} | {\normalfont ...} |
同时套用粗体与斜体
写 \textbf{\textit{...}} 就能得到粗斜体。顺序无所谓,用声明形式时只需并排列出:{\bfseries\itshape ...}。 由于三个轴彼此独立,后写的开关绝不会抹掉先写的。实测确认:在 \textbf{\textit{Y}} 内部,字重保持 bx,只有字形变成 it,于是载入了 Computer Modern 的粗斜体 cmbxti10。三个轴一起用,\textsf{\textbf{\textit{...}}} 就是无衬线粗斜体。不过,「能写出」某个组合与「字体拥有」它是两码事:当所要的字面并不存在时,LaTeX 会替换成相近的——这正是下一节的主题。
% nest the command forms
\textsf{\textbf{\textit{Bold sans-serif italic}}}
% or list the declarations in one group (equivalent)
{\sffamily\bfseries\itshape Bold sans-serif italic\/}
% one word back to the document default inside a sans-serif run
{\sffamily A heading-like line with one \textnormal{normal} word}要把三个轴一次性重置为文档默认值,就用 \textnormal{...}(命令形式)或 \normalfont(声明形式)——当无衬线标题中需要一个词用正文字体时非常方便。只想重置某一个轴,就调用指向该轴默认值的命令:\rmfamily、\mdseries、\upshape。顺带一提值得知道的是:latex.ltx 第 11158 至 11170 行是一连串 \DeclareTextFontCommand 调用,所以这里列出的每一个 \text… 都是由对应的声明机械地造出来的。同一段还定义了 \textulc(回到大小写混排)与 \textsw(花体),只是真正拥有这些字形的字体寥寥无几。
为什么 \bf 和 \it 不肯组合
因为 \bf 的定义里含有 \normalfont。 article.cls 第 490 至 496 行这样罗列这些老式双字母命令——\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}、\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit},\rm、\sf、\tt、\sl、\sc 也是同一格式。也就是说,\bf 不是「变粗体」的命令,而是 「先把三个轴全部重置为默认值,再变粗体」 的命令。排 {\bf\it x} 时,藏在 \it 里的 \normalfont 把先前的 \bfseries 整个丢掉:实测字重从 bx 退回 m,只剩字形 it。反过来 {\it\bf x},字形回到 n,只剩字重 bx。新旧混用也一样:\textbf{\tt x} 最终字族是 cmtt,而字重退回 m,\textbf 被无声地丢弃。
% the old two-letter commands do not stack
{\bf\it x} % italic only -- the bold is thrown away
{\it\bf x} % upright bold -- the italic is thrown away
\textbf{\tt x} % typewriter, medium weight -- \textbf silently lost
% the modern commands do stack
{\bfseries\itshape x}
\textbf{\ttfamily x}这种非正交性并非缺陷,而是原样保留了 1994 年之前 LaTeX 2.09 的字体模型:当时「当前字体」是一个单独的名字而非一组属性,因此每次切换都是整体替换。LaTeX2e 于 1994 年引入了可按轴叠加的现代命令,并通过 \DeclareOldFontCommand 把老命令保留下来,作为 让既有文档仍能编译的兼容层。它们至今仍能无错运行——而这恰恰是麻烦所在:写下 {\bf\it 重要} 的人本意是粗斜体,不看 PDF 就不会发现不对。另外,\DeclareOldFontCommand 的第三个参数是数学模式下的含义,所以 \bf 在公式里表现为 \mathbf。一个命令在正文与公式中含义不同,这是又一条避开它们的理由。新写的代码请一律使用 \textbf / \bfseries 一族。
当日志出现 Font shape ... undefined
这不是拼写错误的报告,而是在说:字体里没有你所要的那个组合的字面。 典型例子是粗体小型大写字母——默认 OT1 编码下的 Computer Modern 没有同时兼具 bx 与 sc 的字面。排 \textsc{\textbf{...}} 时,日志里会成对出现下面这样的 Font shape 行与 using ... instead 行。LaTeX 不会报错停止,而是丢掉小型大写字母、继续用普通粗体排下去。于是你只有在看 PDF 时才会发现「指定的小型大写字母没出来」。同理,\textsw{...} 也会警告 OT1/cmr/m/sw 未定义。
LaTeX Font Warning: Font shape `OT1/cmr/bx/sc' undefined
(Font) using `OT1/cmr/bx/n' instead on input line 8.
LaTeX Font Warning: Some font shapes were not available, defaults substituted.对这条特定的警告,有一行就能解决的办法:加上 \usepackage[T1]{fontenc}。 T1 编码的 Computer Modern(EC 字体)确实带有粗体小型大写字母,因此同样的 \textsc{\textbf{...}} 可以无警告通过;实测加载 [T1]{fontenc} 重新编译后,日志里的字体警告完全消失了。T1 还改善了西文重音字母与断词的处理,所以这类文档迟早都会加上这一行。当警告指向别的组合时,先读日志里那个四段式名字——OT1/cmr/bx/sc,即编码/字族/字重/字形——判断缺的是哪个轴。然后做决定:换成拥有它的字体、把要求降一级,或者接受替换——并把这个决定记录下来。
% OT1 Computer Modern has no bold small caps -> warning + silent fallback
% T1 (the EC fonts) does have it
\usepackage[T1]{fontenc}
\textsc{\textbf{Bold Small Capitals}}
% low-level NFSS: name the four attributes, then commit with \selectfont
{\fontfamily{cmr}\fontseries{bx}\fontshape{sc}\selectfont Explicit}正如最后两行所示,\textbf 这类高层命令在内部会展开为 NFSS 的低层命令。\fontfamily{...}、\fontseries{...}、\fontshape{...} 改写属性,而 \selectfont 才依据此刻这四个属性真正选定字体。仅仅设置属性什么也不会发生;不调用 \selectfont,字体就不会改变。平时无需直接触碰这一层,但当你想按名字调出某个特定字面,或想确认警告里那个四段式名字究竟指什么时,它就派上用场了。编码的含义以及如何加载真实的字体文件,留给字体系统那一页。
日文的字体切换 — 明朝体与哥特体
在 pLaTeX / upLaTeX 中,日文拥有独立于西文的轴,字族一轴有两个成员:明朝体 \textmc / \mcfamily 与哥特体 \textgt / \gtfamily。 定义始于 plcore.ltx 第 1471 行,切换的是日文专属属性 \kanjifamily。有趣的是 \bfseries 作用于日文时的行为。实测显示,日文字族保持 mc(明朝),只有字重变为 bx。而 jy1mc.fd 第 25 行写着 \DeclareFontShape{JY1}{mc}{bx}{n}{<->ssub*gt/m/n}{}:请求「明朝的粗体」时,改由标准字重的哥特体来提供。也就是说,「一加粗就变哥特体」既非偶然也非退让,而是字体定义文件中明文写就的正式替换,如实反映了日文强调历来靠「切换字体」而非加粗的传统。
稿件中的运用规则
直接写进正文的字体命令,请只保留那些承载含义的。 在学位论文或技术文档里,把字体开关当作「稍微调一下外观的小工具」四处播撒,日后就越难修改。「标题加粗」「图注变小」「定义术语一律无衬线」这类规则,应该放进文档类、宏包或你自己的语义宏里,而不是每次都现敲一个 \textbf。中间加一层——\newcommand{\term}[1]{\textsf{#1}}——方针变更时就只需改一行。反过来,对那些「外观本身就是含义」的东西,比如界面上的按钮名或键盘上的按键名,在正文里直接用 \textsf 或 \texttt 完全没问题。判断标准很简单:外观是目的,还是含义的替身。
- 把强调交给
\emph。 一旦把外观选择嵌进正文,日后就再也改不动强调的样子了。 - 长范围用声明形式。 整段引文或整条旁注,就在环境开头声明
\itshape,让环境自己关闭这个分组。 - 组合先在最小示例里试。 先在一个简短文档里排一次
\textsf{\textbf{...}}或\textsc{...},确认日志没有字体警告,再撒进正文。 - 别放任替换不管。 忽视警告,同样的标记可能逐章看起来不同。换字体,或者把要求简化。
- 不要混入旧的双字母命令。 在合作稿件里看到
\bf或\it,就换成\bfseries/\itshape或\textbf/\textit。它们看似能用,实则不会组合。