LaTeX 笔记:NFSS 那点事儿
零、
我就不写成《LaTeX 笔记(一):NFSS 那点事儿》了,省得你们指望还有二……
本文比较靠近 TeXnique 这个主题。想理解本文的内容,至少也得读一下 TeXbook 或者 TeX by topic 之类的。
一、
NFSS 框架是 LaTeX2e 的核心内容,占了 LaTeX2e 源码的 30% 左右的篇幅。如果你不理解它的重要性,还有一个数据:在完全安装的 TeX Live 发行版中,各类字体宏包编写的字体定义文件 (*.fd) 有 3000+。有了 NFSS,大家才能快乐地使用 \bfseries \itshape 等命令切换字体,随心所欲不逾矩了。
那么没有 NFSS 的情况下怎么切换字体呢?如果你像第 0 条里面说的,读过了两本书之一,那么你就知道是这么来的:
% 以 txfonts 宏包的字体 txr 为例
\font\txrm=txr at 12pt
\txrm Hello, \TeX\ and its fonts!
需要定义一个控制序列(命令)代表实际的字体,然后在切换字体的时候用一下。plain TeX 已经定义了一些,如 \bf \it \tt 之类。问题在于,\bf 是直立的粗体,\it 是普通字重的意大利斜体,如果你想要粗斜体,这个字体跟它俩一毛钱关系都没有,必须你自己重新定义个诸如 \bfit 之类的字体命令才行。就连不同字号的字体,都需要重新定义。
那么话说回来,其实 \bfseries \itshape 和 \mdseries \itshape 、\bfseries \upshape 等命令给出的字体也全都不一样啊,这怎么搞的?往下看。
<!-- 往下看之前建议仔细看看 TeX 发行版里两篇重要的文档 encguide 和 fntguide。-->
<!-- 什么,你看完了?-->
<!-- 我也不知道你真看完了还是假看完了,好吧,继续 -->
二、
上面让看的两篇文档,介绍了 NFSS 重要的属性——坐标。一个字体有 encoding / family / series / shape 和字号这五个坐标,比如默认字体,10pt 的 Computer Modern Roman (cmr10),在 NFSS 的坐标是 OT1/cmr/m/n/10。这里就不铺开讲了,需要你真的看完那两篇文档。
哦,你似乎在哪儿见到过这么一串写法?对了,在 Overfull \hbox 等信息里满都是这种写法。好了,是时候告诉你真相了:在调用这个字体前,实际上就有这么个全局的定义:
\global\font\OT1/cmr/m/n/10=cmr10
不过呢直接这样写是错的,因为控制序列的名称必须是字母类(catcode=11 的字符)。正确的写法相当于这样:
\global\expandafter\font\csname OT1/cmr/m/n/10\endcsname=cmr10
\expandafter 和 \csname ...\endcsname 的用法如果不明白,赶紧去看书。
也就是说,真实的字体命令,是由这些坐标拼起来构建的。不同的坐标会构造出来不同的字体命令,你每切换一次坐标,很可能就要生成一个新的字体命令。
三、
真的就是把字体坐标拼起来这么简单?当然不止,LaTeX 需要额外的信息找到上面代码里等号后面要赋值的字体名。把坐标与字体名称建立映射的东西,就是每个字体宏包必不可少的 .fd 文件。这个放到后面穿插着说。
四、
现在要提到 NFSS 里的一个比较核心的命令——\selectfont。在 \bfseries \itshape 或者 \small \large 等的定义里都有它。你光用 \fontseries{bx} 或者 \fontsize{10}{12} 改坐标还起不到改字体的作用,它只是把坐标信息存到了一些内部宏,如 \f@encoding \f@series 等。\selectfont 才是改变字体的实际操作。
那么 \selectfont 主要干了些啥呢?
\DeclareRobustCommand\selectfont{%\ifx\f@linespread\baselinestretch \else\set@fontsize\baselinestretch\f@size\f@baselineskip \fi\xdef\font@name{%\csname\curr@fontshape/\f@size\endcsname}%\pickup@font\font@name\size@update\enc@update}
定义里,前两行先不细看,它的作用是处理行距信息。行距有两种改法:\linespread{***} 和 \renewcommand {\baselinestretch}{***},后者会导致 \baselinestretch 和用内部命令缓存的行距扩展系数 \f@linespread 不一致(前者则没有这个问题),所以在这儿要处理一下。
第 3-4 行就是定义出来所谓的 \OT1/cmr/m/n/10 了,这里定义了一个全局的 \font@name,后面很多地方用得着,另外一个重要的内部宏是 \curr@fontshape,它储存着前四个坐标。
第 6 行则是使用这个 \OT1/cmr/m/n/10 改变字体了;第 7 行起到的作用是更新 \baselineskip 等一系列工作;第 8 行是改了 encoding 之后的一些处理。
重点看第 5 行:
\def\pickup@font{%\expandafter \ifx \font@name \relax\define@newfont\fi}
意思不难,如果 \OT1/cmr/m/n/10 已经定义过,拿来现成用就好,否则需要定义新字体了。接下来看:
\def\define@newfont{%\begingroup\let\typeout\@font@info\escapechar\m@ne\expandafter\expandafter\expandafter\split@name\expandafter\string\font@name\@nil\try@load@fontshape % try always\expandafter\ifx\csname\curr@fontshape\endcsname \relax\wrong@fontshape\else\extract@font\fi\endgroup}
group 之内,第一行影响到向终端输出的信息;第 2-4 行是对 \font@name 内容的一个拆包,把它里头的字体坐标存进 \f@encoding \f@series 等。
后面的一个关键宏是 \try@load@fontshape:
\def\try@load@fontshape{%\expandafter\ifx\csname \f@encoding+\f@family\endcsname\relax\@font@info{Try loading font information for\f@encoding+\f@family}%\global\expandafter\let\csname\f@encoding+\f@family\endcsname\@empty\nfss@catcodes\let\nfss@catcodes\relax\edef\reserved@a{%\lowercase{%\noexpand\InputIfFileExists{\f@encoding\f@family.fd}}}%\reserved@a\relax{\@input@{\f@encoding\f@family.fd}}%\fi}
我们可以看到,这个宏起到的作用就是载入 .fd 字体定义文件了。在此解释两个宏:
- \csname \f@encoding+\f@family\endcsname,形如 \OT1+cmr。它的定义标志着 .fd 文件被载入。它是 .fd 文件里 \DeclareFontEncoding 命令定义出来的。
- \csname \curr@fontshape\endcsname,形如 \OT1/cmr/m/n,它的定义标志着 .fd 文件被载入,并且前四个坐标被 .fd 文件里的 \DeclareFontFamily 命令定义过,这样就可以拿来把最后一块拼图,也就是字号信息拼上。
那么好了,往下走,如果 .fd 文件载入了,各条 \DeclareFontFamily 也过了一遍,仍然找不到对应的字体怎么办?这就表明,字体包里可能没有定义你要的坐标,这时就要进行回退(fallback),这个工作由 \wrong@fontshape 完成。每个 encoding 的定义文件如 t1enc.def 里,都会用 \DeclareFontSubstitution 命令给出用来回退的三个坐标(OT1 的是在 LaTeX2e 内核里给的)。
\wrong@fontshape 的代码就不继续贴了,举个例子,如果我弄了一个错误的坐标出来, \OT1/cmr/xxx/xxx,LaTeX 载入了 ot1cmr.fd 发现没有这个坐标,它就回退到 \OT1/cmr/xxx/n,还找不到,就回退到 \OT1/cmr/m/n。如果进行了回退,你会看到 "Font shape *** undefined, use *** instead" 的提示。
如果找到了前四个坐标的信息,或者回退到了合适的前四个坐标,接下来就要通过字号这个坐标去找字体(TFM)了。这个功能由 \extract@font 完成,它调用了 \get@external@font 去解析 \DeclareFontShape 命令里各种复杂的字号和 tfm 映射关系的定义式,后者内部用了 \try@size@range 完成实际的解析,用 \try@size@substitution 完成字号的 fallback。
五、
啰啰嗦嗦地写完了,不知道有多少人有足够的耐心看到这儿,以及为了看懂它去看更多的书。LaTeX 对字体的这套解决方案也许不是最优的,不过恐怕是无法替代的,不然那乌泱乌泱的字体宏包都得改。
嗯我不是说了有 30% 的代码是 NFSS 嘛?事实上今天讨论到的这部分连 5% 都占不到。其余的大头是被 encoding 和数学字体占了。encoding 的部分,要解决的是不同字体编码下的一些命令和重音的实现,比如 OT1 下的 \"o 是两个字符拼起来的,而 T1 则可以找到单个的字符。数学字体就不在这里说了,要说起来肯定要另开一帖。
\endinput
作者:Louis Stuart
链接:https://zhuanlan.zhihu.com/p/21339128
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
LaTeX 笔记:NFSS 那点事儿相关推荐
- $\LaTeX$笔记:Section 编号方式(数字、字母、罗马)计数器计数形式修改
$\LaTeX$系列根目录: Latex学习笔记-序 IEEE模板中Section的编号是罗马数字,要是改投其他刊物的话可能得用阿拉伯数字,所以可以在导言部分做如下修改(放在导言区宏包调用之后): \ ...
- Latex笔记(一)—— 复杂表格的制作
引言 表格是论文写作中的重要一环,尤其是较为复杂的三线表的制作. 在网上寻找了很久发现没有比较综合的解释方法,因此将查阅的关于复杂三线表的制作的资料总结成了一篇笔记. 由于笔记是用Latex写作的,因 ...
- 【LaTeX笔记12】Latex分栏布局及模板使用
经过三天的学习,记录了12个笔记,所有的代码已经上传到GitHub. 1 分栏布局 分栏布局是我们阅读文献经常见到的排版布局,下面给一种分栏布局的基本方法. % 导言区 \documentclass[ ...
- $\LaTeX$笔记:首字下沉
$\LaTeX$系列根目录: Latex学习笔记-序 首字下沉 \IEEEPARstart{W}{ith} ,第一个参数W会变大,占用两行,第二个参数"ith"变会大写. 如代码 ...
- 【LaTeX】Lyx/LaTeX笔记01
list list中RETURE默认是新起一个新的item,Ctrl-Return可以继续在当前item中新起一行. 快捷键 快捷键 描述 Alt-P 0-6 Part, Chapter, Secti ...
- 奇怪的 Markdown / LaTeX 笔记
记一下日常见到的一些奇怪的 Markdown / LaTeX 用法... Markdown LaTeX LaTeX 数学 1. 运算符 1.1 造运算符: a \operatorname{sin} c ...
- 个人LaTeX笔记(九)
2月20日更 第十一节:LaTeX中的参考文献-BibTeX 这节课讲了参考文献的引用,很有用啊.不过引用方法分为两种首先提出一种: (参考文献样例均来自视频教程,如有侵权等问题请联系删除) \doc ...
- LaTex笔记:参考文献、常用公式符号、图表、注释
目录 一.参考文献 二.公式 快捷处理方法: 小细节:斜体和正体 三.图片 四.表格 五.算法 六.注释 一.参考文献 借助bib文件生成!BibTex相当于是存放所有参考文献的容器,可以无序存放.首 ...
- 读书笔记《Linux那些事儿之我是USB》
第一篇:Linux那些事儿之我是USB Core USB诞生于inel 产生是为了解决前期计算机并口串口的问题,实现一种解决速度,扩展性,易用性的通信方式. 速度:usb2.0高速模式,480MB/s ...
最新文章
- MySQL 语句的执行顺序
- ABCpdf.NET 的简易使用指南
- Qt中TCP服务端编程
- Centos 6.5 64位双网卡绑定
- hdu-4825(01字典树)
- 2021牛客暑期多校训练营3 I Kuriyama Mirai and Exclusive Or 差分 + 二进制分治
- 深度学习(二十五)基于Mutil-Scale CNN的图片语义分割、法向量估计
- virtual memory exhausted: Cannot allocate memory Linux虚拟内存不足,扩展虚拟内存的解决办法
- java框架ssm面试题2016_Java面试-框架篇(SSM-SpringMVC)
- Java自学要多久?
- 最小二乘法系统辨识小结
- 大数据与云计算概论简介
- 算法学习-求平方根函数
- OS - freeRTOS vs Linux
- 利用串口对 89S 系列单片机编程
- Opencv实现去除背景留下前景
- 怎样将手机视频转换成图片?手机怎么把视频做成gif动图?
- B站品牌营销!寻找优质UP主内容共创
- 高质量PM,都用哪些优质的开源项目管理工具
- 获得打气筒的三种方式
热门文章
- 金融风控实战——特征工程上
- 机器学习Sklearn实战——线性回归
- Python基础知识(第九天)
- Python基础知识(第八天)
- Python实现基于朴素贝叶斯的垃圾邮件分类 标签: python朴素贝叶斯垃圾邮件分类 2016-04-20 15:09 2750人阅读 评论(1) 收藏 举报 分类: 机器学习(19) 听说
- 点击按钮测试用例标题_怎么写测试用例?
- Elasticsearch-06 Spring Boot 2.0.9整合ElasticSearch5.6.16
- Java学习笔记(三)--Java主类结构
- java shape用法_Java PShape.scale方法代码示例
- 学习笔记——matplotlib学习