Linux 下字体配置
本文参考:
用 fontconfig 治理 Linux 中的字体
Linux字体美化实战(Fontconfig配置)
Linux 上的字体配置与故障排除
利用 fontconfig 对 linux 下的字体进行配置。分享我自己的配置方案,尽量把各类问题处理好。
字体的分类
字体的数量可以说是成千上万,但一般在电脑上显示的基本为以下这三类
- monospace [等宽]
等宽字体是指字符宽度相同的字体,用于需要字符严格对齐的场合,例如控制台和源代码。与此相对,字符宽度各不相同的字体称为比例字体(其余四类字体都是)。不过,对于中文字体而言,并不存在等宽与比例的差别,因为所有中文字都是等宽的。中文字体中的“等宽”指的是字体的西文部分是等宽的,2个字母对应1个汉字。
- sans-serif [无衬线]
是指笔画末端没有修饰(衬线)的字体,通常用于屏幕显示。中文的黑体与圆体就属于此类字体。
- serif [有衬线]
是指笔画末端有修饰(衬线)的字体,通常用于打印。中文的宋体与仿宋就属于此类字体。
我们要做的字体配置主要就是针对上面这三类字体。
选字体
有了目标,下面就是选一个自己喜欢的字体了。不过,对于中文字体,目前免费商用的中文字体越来越多了,除了 google 主导的 Noto 系列字体,以及 Adobe 主导的思源系列字体以外。还有阿里巴巴普惠体、OPPO Sans、HarmonyOS Sans 和 MiSans。
对于编程字体,可以选择的余地就多多了,像是 Source Code Pro,Consolas,Menlo 等等。我最终也是选择了广受好评的 Fira Code 。
小结一下,我的选择是:
- 系统UI:MiSans
- 无衬线:西文 DejaVu Sans,中文 MiSans
- 衬线:西文 DejaVu Serif,中文 方正书宋
- 等宽:西文 Fira Code,中文 方正中等线
需要注意的是,方正书宋,方正中等线都是个人免费使用,但是不能商用,需要自行去方正官网拿到授权。而 Misans 则是小米的免费字体。
关于 emoji,我选择了 Debian 系统默认会装上的 Noto emoji 。
另外,少不了人见人爱的图标字体 Nerd Fonts。我是下载使用了 FiraCode Nerd Fonts。
这里就有小伙伴开始好奇了,如何让西文和中文使用不同的字体呢?
在 Windows 下,我们可以选择合成字体,即将各类字体打包到一起。例如更纱黑体就是由思源黑体和西文字体 Iosevka 整合而来的。这种字体的好处就是方便,直接选择使用即可。但是缺点也是显而易见,就是打包太麻烦了,引入 Iosevka 要打一次包,想要支持 Nerd Fonts,又要打一次包。如果是别人帮你提供好的合成字体那还好说, 从网上下载、从软件仓库安装就完事了,自己打包的话真的工作量巨大。
而在 Liunx 下,我们只需要配置 fontconfig 就好了,无论想怎么搭配都可以实现,听起来是不是特别酷😎。可惜的是,有一些程序对 fontconfig 支持并不完善,这就达不到我们想要的效果。(说的就是你,Chrome😠)
fontconfig
在我们开始正式配置前,还是有必要了解一些基本的知识。这里我就简单介绍一下,如果想要深入了解的话可以看看双猫大佬和金步国大佬的文章,里面详细介绍了 Linux fontconfig 工作原理。
字体的属性
字体有很多属性,常用的有字族(family)、倾斜(slant)、字重(weight)。后两者合一起叫样式(style)。
字族就是它的名字啦。一个字体文件,可以提供多个字体族名 (family)。比如 Debian 用户安装 fonts-noto 后,系统端增加了 NotoSans-Regular.ttf 等字体文件,文件会提供一系列字体名,它们是一个意思。我们可以运行 fontconfig 提供的命令行工具 fc-list 去查看系统上已安装的字体已经它们对应的字体族名。
倾斜就是斜不斜,英文叫 Roman、Italic 或者 Oblique、Italic 是专门的斜体写法(更接近手写样式), Oblique 是把常规写法倾斜一下完事。
字重就更简单了,就是笔划的粗细。常见的有 Regular、Normal、Medium、Bold、Semibold、Black、Thin、Light、Extralight 等。
通用字族名
很多时候,程序并不在乎用户具体使用的是哪款字体,像很多网站的 CSS 那样把各个平台的常见字体全部列出来太傻了,又容易出问题。所以,人们发明了“通用字族名”,也就是 sans-serif (sans)、serif 和 monospace (mono) 这些。它们不是真实存在的字体,而是分别指示程序去使用无衬线、衬线、等宽字体。那么桌面程序又是如何知道具体使用哪些字体呢?它只需要去查询 fontconfig 就行了。由于它们必定要经过 fontconfig 的查询流程后才能使用字体,所以我们可以通过 fontconfig 的配置去精准控制程序使用的字体。
如何调试
传入环境变量FC_DEBUG=4即可,例如:
1 | FC_DEBUG=4 firefox |
fontconfig 就会打印调试信息,其中可以看到:
1 | FcConfigSubstitute editPattern has 8 elts (size 16) |
除了启动一个程序来看它字体的调用日志,我们也可以手动调用。例如,我想看 monospace 在系统里被修改成了什么字体,就可以执行:
1 | FC_DEBUG=4 fc-match 'monospace' |
打印出的调试信息会很长,我们主要看几个部分:
第一部分,Add Rule,指已添加的配置文件规则。这里面也包含了家目录下的配置文件,可以找来看看被解析成了什么。
第二部分,在 Add Rule 之后,迎来了最关键的、我们应当关心的 FcConfigSubstitute Pattern,它包含了 font pattern。(s) 和 (w) 分别代表强弱绑定;prgname 代表程序名,此时就是 fc-match。至于 lang,由于没有对 fc-match 指定语言,所以默认是 en。
接下来有很多条 FcConfigSubstitute editPattern,代表对 font pattern 的替换操作。但是必须当规则匹配的时候,也就是 Rule Set 不是 No match 的情况下,才执行 FcConfigSubstitute editPattern。那么,又应该怎么看 FcConfigSubstitute editPattern 呢?主要看 family,因为 family 代表着字体匹配顺序。它就是配置文件中的
最后应该关心 FcConfigSubstitute donePattern,这是 fontconfig 执行完字体替换后的结果。
配置文件
整个配置文件由如下几个部分依次拼接而成:
- 目录设置(
<dir>
,<cachedir>
,<include>
) - 杂项设置(
<config>
) - 扫描阶段(
<match target="scan">
) - 匹配阶段(
<alias>
,<match target="pattern">
) - 渲染阶段(
<match target="font">
)
想要实现合成字体的效果,一个最简单的思路,本文也基于该思路:不让程序使用某个具体的字体,而是使用通用字体族名 (Generic Font Family)。比如,让程序使用 sans-serif,也就是默认的无衬线字体。
我们要关心第四个部分,即匹配阶段,使用 fontconfig 配置如下:
1 | <match target="pattern"> |
这种 font stack 的方式,即可让程序按照以下顺序渲染字体:
Misans —> DejaVu Sans Book -> Noto Color Emoji
这里的 <test>
就是条件判断,mode="prepend"
指在前添加,binding="strong"
则是强绑定
开始配置
我们的思路就是就是修改默认的字族,让其成为我们想要指定的字体。然后将所有程序的字体配置改为通用字体族名:sans-serif,serif,monospace。
单用户使用,则配置文件在 ~/.config/fontconfig/fonts.conf;多用户使用则配置文件为 /etc/fonts/local.conf 。
设置默认字体
1 | <!-- Default system-ui fonts --> |
对 system-ui,sans-serif,serif,monospace 设置优先显示的字体。在这里我让 system-ui 默认为无衬线。注意,system-ui 必须在最前。由于 fontconfig 对 font pattern 的操作是按顺序执行的,所以必须先让 system-ui 能优先以 sans-serif 显示,然后才是对 sans-serif 的操作。
覆盖西文字体
如果去观察 Noto Sans CJK 这个中文字体,会发现它的西文部分的字形其实和 Noto Sans 不一样,虽然它们都以 Noto 自称。中文字体携带的英文字符有可能十分糟糕,特别是 Windows 自带的 SimHei,也就是中易黑体,它的英文相当糟糕。另外,微软雅黑的字重实在是太少了,对于设计师来说很不友好。而各种流行的英文字体支持很多字重。
此处仅在英文状态下将 MiSans 替换为 DejaVu Sans Book字体。
1 | <!-- Replace english fonts--> |
浏览器字体问题
有些程序,主要是浏览器程序,居然只使用 font pattern 结果中的首个字体,比如 Chrome(以及衍生的Chromium),虽然 Chrome 接受了我们指定的西文字体,但是它忽略了紧接其后的中文字体,即使配置采用了强绑定!然后中文字体又不知道它 fallback 到哪去了,可能会出现你想要的中文字体,也可能不是。我们可以指定程序来渲染。
1 | <!-- 替换浏览器字体--> |
这里是 msedge 主要是我平时都用 edge 而不是 Chrome。可惜由于 Chromium 在 Linux 上小问题实在是太多了,还是老老实实的用 firefox 吧。
在所有情况下,除了程序名为 msedge 的情况下,优先使用 Fira Code 显示西文,再用 MiSans 显示中文。虽然我不能让 msedge 使用 Fira Code,但它一定能用上 MiSans 显示中文。
替换任意字体
当系统里已经安装了一些不需要的字体,但又不想删除或者屏蔽它怎么办呢?替换掉 font pattern 就可以了。
我这里则是用方正书宋来替换普通的宋体。
1 | <match target="pattern"> |
字体渲染参数
1 | <!--rendering options--> |
这里主要设置了一些字体的渲染方式:
autohint:优先使用内嵌微调
hinting:开启微调
hintstyle:微调的程度,轻微
antialias:开启抗锯齿功能
lcdfilter:LCD filter 的风格,默认
rgba:LCD 子像素的排列顺序,rgb
这里就直接抄作业了。
不能解决的问题
Linux 不强迫程序必须使用特定的依赖,而是程序主动选择了约定俗成的依赖。老话重谈,程序可以自由选择完全遵守 fontconfig,也可以选择部分使用 fontconfig 的配置,或者完全不遵守它。这也导致了对一些程序无法实现字体的修改。以及上面提到的 chrome 对 fontconfig 并不是很好,或许面对这种程序,就需要合成字体的出场了。