京山游侠

专注技术 拒绝扯淡
posts - 63, comments - 402, trackbacks - 2, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

Linux 桌面玩家指南:04. Linux 桌面系统字体配置要略

Posted on 2018-09-19 06:36 京山游侠 阅读(...) 评论(...) 编辑 收藏

特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束。如果某条评论中出现了两个$,MathJax 会将两个$之间的内容按照数学公式进行排版,从而导致评论区格式混乱。如果大家的评论中用到了$,但是又不是为了使用数学公式,就请使用\$转义一下,谢谢。

想从头阅读该系列吗?下面是传送门:

参考资料

  1. Fonts in X11R7.7
  2. The Xft Font Library: Architecture and Users Guide
  3. FreeType 2 官网
  4. Optimal Use of Fonts on Linux
  5. Fontconfig 官网

字体效果预览

宋体12pt:这里是中文,以及标点符号“,。!?:;”This is English. 这一行是6pt。这一行是7pt。这一行是8pt。这一行是9pt。这一行是10pt。这一行是11pt。
  黑体12pt:这里是中文,以及标点符号“,。!?:;”This is English. 这一行是6pt。这一行是7pt。这一行是8pt。这一行是9pt。这一行是10pt。这一行是11pt。
微软雅黑12pt:这里是中文,以及标点符号“,。!?:;”This is English. 这一行是6pt。这一行是7pt。这一行是8pt。这一行是9pt。这一行是10pt。这一行是11pt。
  serif: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
Times New Roman: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
 Georgia: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
sans-serif: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
verdana: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
  Arial: This is English, 这是英文中夹杂的中文。This line is 6pt. This line is 7pt. This line is 8pt. This line is 9pt. This line is 10pt. This line is 11pt.
程序代码:
  #include <stdio.h>
  int main(){
    return 0;//很显然这是为了测试等宽字体
  }

字体的分类及电脑中的字体显示技术

先了解字体的分类及其用途。

1、英文字体分为三类,分别是有衬线字体(serif)、无衬线字体(sans-serif)和等宽字体(monospace)。Serif 是有衬线字体,意思是在字的笔画开始、结束的地方有额外的装饰,而且笔画的粗细会有所不同。Sans-serif 就没有这些额外的装饰,而且笔画的粗细差不多。在传统的正文印刷中,普遍认为有衬线字体能带来更佳的可读性(相比无衬线字体),尤其是在大段落的文章中,衬线增加了阅读时对字母的视觉参照。而无衬线字体往往被用在标题、较短的文字段落或者一些通俗读物中。相比严肃正经的有衬线字体,无衬线字体给人一种休闲轻松的感觉。同时,由于无衬线字体笔画比较饱满,所以比较适合电脑屏幕显示,在印刷和打印中,可以用无衬线字体做标题、加粗字体等表示强调。等宽字体就不用多说啦,主要用于终端字体或编程。

2、中文字体可以参照英文字体进行分类,由于中文都是等宽的,所以就只需要区分有衬线(serif)和无衬线(sans-serif)。中文的宋体、仿宋就相当于英文的 serif,所以用于传统印刷和打印效果比较好。而中文的黑体、楷体、圆体等字体相当于英文的 sans-serif,用于电脑屏幕的显示效果比较好,也可以用在印刷和打印中做标题和粗体字。

3、Serif 字体的经典代表有 Georgia 和 Times New Roman,sans-serif 字体的经典代表有 Arial 和Verdana,monospace 字体的经典代表有 Courier New 和 DejaVu Sans Mono。

电脑中的字体既是一门艺术,也是一门技术。要将屏幕上的字体显示得好看难度可不小。字体可以表示为点阵(bitmap),也可以表示为轮廓(outline)。点阵字体不能缩放,轮廓字体可以随意缩放。

1、要获得锐利清晰的效果,小字必须显示为点阵(bitmap),大字可以显示为轮廓;

2、显示轮廓字体时,为了让字体边缘显得比较光滑,需要对字体边缘进行抗锯齿(anti-alias);

3、为了获得更好的效果,字体设计厂家在设计字体的时候,会对字体进行微调(hinting)。字体微调是一项耗时耗力的工作,所以就产生了自动微调技术(autohint);

4、为了让字体在液晶显示器上获得更好的效果(主要也是为了字体边缘光滑),产生了次像素平滑技术(subpixle),微软的 ClearType 技术也属于次像素平滑技术的一种。所谓次像素,是指每个像素中的单独的 R、G、B 分量,所以次像素平滑用好了,字体边缘看起来会更平滑,如果用不好,字体边缘就会显得花花绿绿。也正是因为 CRT 显示器和液晶显示器每个像素的组成方式不一样,所以在 CRT 显示器中不能开次像素平滑。

5、增加屏幕的 dpi,可以增加画字的像素,从而获得更平滑的显示效果。比如在传统的 96dpi 的电脑显示器上,一个 9pt 的字符用 12 个像素绘制,一个 12pt 的字符用 16 个像素绘制,使用轮廓字体确实很难做到平滑。但是在目前的安卓手机、苹果 iPad 等设备上,高分辨率的润眼屏都在 300dpi 以上,画一个 12pt 的字符可以用 50 多个像素,字体平滑自然不是问题,根本不需要使用点阵、微调、抗锯齿、次像素等技术。我认为,随着显示器硬件技术的发展,以上技术都将成为浮云。

Linux桌面系统字体的历史及现状

这一节很有历史意义。为了向大家充分展示 Linux 桌面系统字体的历史,我安装了 CentOS 5、CentOS 6、CentOS 7、Ubuntu 15.04 和 Ubuntu 18.04,并使用上一节编写的字体测试内容对以上系统的桌面环境的字体显示情况进行了测试。让大家了解 Linux 桌面系统中文字体的五代变迁经历。这五代分别是 CentOS 5 时代的文鼎PL细上海宋和文鼎PL中楷、CentOS 6 时代的 AR PL UMing、CentOS 7 时代的文泉驿黑体、Ubuntu 15.04 时代的 Droid Sans Fallback、Ubuntu 18.04 时代的思源黑体和思源宋体。CentOS 5 发布于 2007 年,比这更早的 Linux 桌面字体的配置是什么样子,以及使用的配置方法是什么,我就不去考究了,前面专门放上几个参考文献的链接,至于更早的字体配置方法,以参考文献中说的为准。

先看上一节的字体测试在 Windows 10 中的显示效果,下图是原始大小:

下图是使用 Ctrl+鼠标滚轮 将字体放大后的效果:

Windows 10 中的显示效果可以当成一个参考标准,虽然网络上仍然还有人对 Windows 的字体渲染技术不太满意,认为跟 MacOS 相比还有差距,但是我已经非常满意了。毕竟 Windows 是一个商业的系统,MicroSoft 是一个有钱的大公司,在字体方面做得还是非常好的。其它在字体方面做得好的公司还有 Apple、Adobe、Google,无一不是有钱的大公司。 这些公司不仅设计了(或者花钱买或者花钱请人设计了)一系列好看的字体,还掌握了很多字体渲染方面的专利。这就使得 Linux 这样的开源系统比较被动了。

从上面两图可以看出,Windows 10 中的字体不管是大号还是小号,都显示得很饱满,而且能够准确区分 serif 和 sans-serif 字体,在 serif 的分类中,又有显示得更好看的 Georgia 和 Times New Roman 字体,在 sans-serif 的分类中,又有显示得更加好看的 Arial 和 Verdana 字体。仔细观看字体并对比,发现在 Windows 10 中, serif 字体不管中英文默认都会以宋体显示,sans-serif 字体不管中英文默认都会以微软雅黑显示。宋体和黑体都是微软买的,中易设计的字体,我们平时用得最多,所以最熟悉。字体比较小时,微软雅黑明显比黑体显示得好看,字体大的时候,微软雅黑和黑体就没有太大的差别了。而且我认为,微软雅黑的引号不好看。宋体字在小字号时,有点阵显示,微软雅黑和 Verdana 在小字号时,也有非常好的 hinting。

Hinting 需要两方面的支持,一是设计字体时,针对字体进行微调,这是一项耗时耗力的工作,所以大公司才搞得起;二是字体渲染时,渲染技术要支持字体中的 hinting 信息。而对 TrueType 字体中的 hinting 进行渲染的技术是有专利壁垒的,所以在早期的 Linux 系统中,使用的 freetype 字体渲染库默认是不支持 hinting 的,如果要开启 hinting 支持,需要自己编译 libfreetype。不过参考文献中说了,如果系统中的 libfreetype 库是 libfreetype6 或以上版本,就说明已经开启 hinting 支持了。现在新一点的 Linux 发行版都满足要求。

没有对比就没有伤害,下面看看 CentOS 5 桌面系统对字体的支持是什么样的,下两图是原始大小:

怎一个模糊了得啊。网页中的字体非常的虚,一点都不饱满,而系统菜单中的字体显示得还比较清晰,因为系统菜单中的字体用的是点阵。基本上可以认为,小字显示为点阵会比较饱满锐利,而显示为轮廓字体则会发虚。下面是使用 Ctrl+鼠标滚轮 放大以后的效果:

将网页放大后,发虚的情况就会好很多,但是和 Windows 下的显示还是没法比。另外,在 CentOS 5 中它不能准确显示 sans-serif 和黑体,所有的字都显示为 serif 和 宋体,更离谱的是,它认为 serif 中夹杂的中文应该显示为楷体。总而言之,CentOS 5 中的字体配置是非常失败的。

当然,这是有历史原因的。CentOS 5 系统中对中文的支持主要依赖于 2001 年文鼎向开源界贡献的两套字体:文鼎PL中楷和文鼎PL细上海宋,如下图:

同时期 Windows 中用的宋体和黑体虽然好,但那是要钱的,在 Linux 中盗用是违法的。而同时期开源界又没有什么好的开源字体,所以只能那么将就了。这时我不得不提一下中科红旗的 Linux,大概也是在这个时期吧,因为字体的原因,我放弃过 Redhat,而选用了中科红旗 Linux。中科红旗 Linux 选用的是和 Windows 一样的中易的宋体,字体清晰锐利,而且中科红旗 Linux 也使用 rpm 管理软件包,所以用着还算顺手。不好的是,我那时对 Linux 字体配置理解不深,写了一些错误的随笔,不过后来都删了,而且这次删随笔致使我的博客排名严重下降,再也没升上来(当然,和我懒没有再继续发高质量博客也有很大关系)。

随着时间的流逝,2011 年,CentOS 6 发布了。在 CentOS 6 中,以上测试字体的内容显示为这样:

放大后,是这样:

从上两张图片可以看到,除了 Arial 显示为 sans-serif 之外,其它的字体都是显示为宋体,而且在小字时有了点阵。和 CentOS 5 相比算是有很大的进步了,因为小字采用点阵后,清晰锐利了不少,发虚的情况没了。不好的是,这个宋体并没有我们习惯的 SimSun 好看,而且逗号句号还不是在一行的偏下的部位,而是跑到了一行的正中。这个宋体是由哪个字体支持的呢?下图可以看到 CentOS 6 中安装的中文字体:

可以看到,CentOS 6 的主打字体是 AR PL UMing。从 AR PL 这几个字符可以看出,AR PL UMing 和文鼎贡献的那两套字体是一脉相承的。确实如此,只不过是经过合并、修改、增加了日语韩语及香港常用字型后,更名为 CJKUniFonts,据说含有点阵。从名字可以看出,有了这套字体,中日韩都可以搞定。而且,CentOS 6 中已经有了文泉驿字体,之所以没有显示黑体不是因为没有黑体,而是因为没有正确配置。

也就是说,至少在 CentOS 6 之前,Linux 桌面发行版都没有认识到黑体字的重要价值。

转眼到了 2014 年,CentOS 7 发布了,这个阶段对应的 Fedora 大约是 20 和 21 这两个版本。在 CentOS 7 中,字体测试显示效果是这样的:

放大后,是这样的:

从上面两张图片可以看出,除了明确指定为 Times New Roman 的字体之外,其它的字体都显示为黑体,而且小字有点阵。它的主打字体是什么呢?请看下图:

黑体选用的是文泉驿,依然安装有 AR PL UMing 作为宋体的支持,但是不作为主打。认识到点阵的重要性和认识到黑体的重要性,可以算是这个时期 Linux 桌面发行版的进步吧。但是,不能正确区分 serif 和 sans-serif,这也是一个巨大的不足吧。

这个时期的 Ubuntu 又是什么样子的呢?我们来看看 Ubuntu 15.04,这是 2015 年的发行版。下面直接连上三图:


我认为,这个时期的 Linux 桌面发行版应该是受到了 Android 手机和苹果 ipad mini 这样的小尺寸高分辨率屏幕的巨大影响,主要表现为认识到了黑体字的重要性,并逐渐放弃了点阵,因为屏幕的 ppi 比较高,点阵就不是必须的了。Ubuntu 15.04 是一种字体打天下,使用的是 Droid Sans Fallback,而这种字体正是 Google 公司为其 Android 系统设计的中日韩字体。这个时期的 Ubuntu,是有进军手机系统的野心的。但是作为 Linux 桌面发行版,不能准确区分 serif 和 sans-serif,也是有硬伤的。

到了 2018 年,Ubuntu 终于放弃了 Unity 桌面,而回归了 Gnome 3,这也标志着其在手机操作系统市场的失败。Ubuntu 18.04 桌面系统显示效果是这样的:



这个阶段,开源领域能够使用的字体也是越来越丰富了。Ubuntu 18.04 中使用的是思源黑体和思源宋体,同时,系统中也安装有 AR PL UMing 和 AR PL UKai。另外,如果有需要,我们随时也可以安装前面提到的文泉驿以及 Droid Sans Fallback。思源黑体和思源宋体的字形以及标点符号还是很漂亮的,可以作为主打字体。Ubuntu 18.04 中小字虽然没有使用点阵,但是其饱满程度和 CentOS 5 相比要强不少,没有明显发虚的感觉,说明不仅字体在进步,字体渲染技术也在进步。Ubuntu 18.04 可以正确区分 serif 和 sans-serif 字体,对它们分别选用宋体和黑体,这已经很令人满意了。

可以这么说,Ubuntu 18.04 可能是目前在字体方面最让人省心的 Linux 桌面发行版,在字体配置方面,我们只需要微调即可。例如,serif 字体显示的是 AR PL UMing,如果更改为思源宋体会更漂亮,Times New Roman 英文字体识别是正确的,但是里面夹杂的中文应该显示为宋体,而不应该是黑体,Georgia 字体应该是 serif 分类的,而不应该是 sans-serif 分类的,其中夹杂的中文也应该为宋体。另外,没有点阵也算是一个小小的缺陷。为什么说是小小的缺陷而不是说硬伤呢?那是因为我认为随着屏幕的发展和渲染技术的进步,点阵已经不是太重要了。

使用 Fontconfig 配置 Linux 桌面字体的语法和流程

目前,在 Linux 系统上配置字体的工具是 Fontconfig。我们要感谢这个时代,曾经混乱不堪的字体配置方法终于被 Fontconfig 一统江湖,我们要配置系统中的字体,只需要学习 Fontconfig 就行了。我在文章的开始给出了参考资料,Fonts in X11R7.7 中说,X11 包含两套字体系统,一套是 The core X11 fonts system,另一套是 Xft fonts system。前者是伴随 X11R1 在 1987 年诞生的,最初只能使用单色的点阵字体,多年来虽小有改进,但是依然很难用。后者是专门为可缩放字体而设计的字体系统,还支持抗锯齿和次像素平滑。该参考文献中还特意指出,虽然 X.org 会继续维护 The core X11 fonts system,但是仍然建议 toolkits author 尽早切换到 Xft 字体系统。什么 toolkits 呢?在 Linux 下无非就是 Qt 和 GTK 而已。[Optimal Use of Fonts on Linux] 中说,从 QT3 和 GTK 2 开始,使用这两种库编写的程序就都是使用 Xft 字体系统了。Xft 只是一个接口,在目前的 Linux 中,普遍使用的 Fontconfig 进行字体的配置,使用 FreeType 进行字体渲染。前面我也给出了参考资料 FreeType 2 官网Fontconfig 官网 ,有兴趣的朋友们可以自己去看。

在这里,我再次强调,玩 Linux 要与时俱进,网络上有很多信息是过时的,要注意鉴别。例如,如果在网络上搜索 Linux 字体安装、配置的文章,如果出来的结果还提及要使用mkfontdirmkfontscale这样的命令,还使用像-misc-fixed-medium-r-normal--10-100-75-75-c-60-iso8859-1这么复杂的字体名称,甚至还提及要使用 xfs 字体服务器,那么这一定是很陈旧的内容了。有多陈旧呢?大概比 CentOS 5 还早 15 年吧。当然,如果你确实需要使用 The core X11 font system,可以阅读我给出的参考文献 Fonts in X11R7.7 ,系统地学习它。在我的 Linux 桌面中,我只需要使用 Fontconfig 就够了。

学习 Fontconfig 的最佳方式是阅读man fonts.conf手册页,其次,就是阅读/etc/fonts/conf.d目录下的配置文件,从实例中学习。

Fontconfig 是一个字体配置工具,在系统中,其它软件是如何和 Fontconfig 交互的呢?可以这么理解,当一个程序需要字体时,它先告诉 Fontconfig 需要什么字体,然后,Fontconfig 根据系统中是否安装有这个字体,以及系统对字体的配置,向这个程序返回一个信息,这个信息中包含有你可以用哪个字体,这个字体的文件是什么,包含哪些属性。程序收到 Fontconfig 的回应后,再调用 FreeType 库来渲染这个字体。这么说有点抽象,下面来一个具体点的例子。就以 Firefox 显示我的这篇随笔为例吧,我的博客设置的字体是 verdana 和微软雅黑,所以 Firefox 就会和 Fontconfig 说:“我要 verdana 和 微软雅黑。” Fontconfig 检查系统中的字体以及配置文件后,发现系统中没有 verdana 和微软雅黑,但是可以使用 DejaVu Sans 和 Noto Sans CJK 代替,就会回答:“请使用 DejaVu Sans 和 Noto Sans CJK,它们的文件分别是什么什么,它们的其它属性分别是什么什么。” 然后 Firefox 就会以 DejaVu Sans 和思源黑体显示我这篇随笔。

把上面的过程进行总结,就是一个程序需要字体时,它先构建一个 pattern,这个 pattern 其实是一个字体列表,而且这个字体列表的每一项并不总是像前面例子中那样只有字体名称,有时还包含一些更详细的要求,例如字体大小、语言、发行者、字符集、是否微调、dpi、像素大小等信息。而对于每一个字体,可以这样表示:

<families>-<point sizes>:<name1>=<values1>:<name2>=<values2>...

而常见的字体属性有哪些呢?请看下表:

属性 类型 描述
family String 字族名称{例如"Microsoft YaHei" "微软雅黑"}
style String 字体风格名称(会覆盖slant与weight){例如"Bold" "Oblique"}
slant Int 倾斜度{0}。分为:Italic(100,斜体), oblique(110,合成斜体), roman(0,正体)
weight Int 粗细程度{80},从0-210,由最细到最粗。例如:light(50,细), regular(80,一般), medium(100,中等), bold(200,粗)
size Double 磅大小(point size),单位是绝对大小的"磅"(=1/72英寸)
pixelsize Double 像素大小(pixel size),单位是显示屏上的"像素"。[pixelsize = (sizescaledpi)/72]
spacing Int 字符间距。分为:proportional(0,变宽), dual(90,双宽), mono(100,等宽), charcell(110,字符单元)
foundry String 字体制造商名称的缩写
antialias Bool 渲染字形(glyph)时是否开启抗锯齿功能[建议对矢量字体设为"true"]
hinting Bool 渲染字形(glyph)时是否开启微调功能(包括内嵌微调与自动微调)[建议对矢量字体设为"true"]
autohint Bool "true"表示只使用自动微调;"false"表示优先使用内嵌微调,但对于没有内嵌微调的字体仍会使用自动微调。[建议设为"false"]
hintstyle Int 微调的程度(同时作用于内嵌微调与自动微调)。分为:none(0,关闭), slight(1,轻度), medium(2,中度), full(3,完全)
file String 字体文件的名称
scalable Bool 字形(glyph)是否可以缩放{True}
dpi Double 目标dpi(像素/英寸)值
rgba Int LCD子像素的排列顺序。分为:unknown(0), rgb(1), bgr(2), vrgb(3), vbgr(4), none(5)
lcdfilter Int LCD filter 的风格。lcdnone(0),lcddefault(1),lcdlight(2),lcdlegacy(3)
lang String 字体所支持的RFC-3066语言的列表{例如"zh-cn"}

我只选择了一些最常用的属性,完整的属性列表请查看 Fontconfig 的文档。Fontconfig 对程序传过来的 pattern 进行扫描和替换,然后把能够满足程序要求的字体列表传回给程序。Fontconfig 还提供很多辅助工具,如fc-listfc-patternfc-matchfc-cache等命令。下面,使用fc-match命令对常用的 serif-12 、 georgia-12 、 sans-serif-12 、 verdana-12 字体进行查询,看看 Fontconfig 返回什么字体,以及这些字体的属性是什么样的。如下图:

结果显示,我们向 Fontconfig 要 serif,Fontconfig 会回应 AR PL UMing,我们向 Fontconfig 要 sans-serif,Fontconfig 会回应 Noto Sans CJK SC。在字体的属性中,antialias、autohint、hinting、hintstyle 等属性都是没有问题的,唯独 dpi 不对,只有 75,所以画一个 12pt 的字只有 12.5 个像素。这不科学,后面我要把 dpi 改成 96。

Fontconfig 对字体列表的替换规则是在 Fontconfig 的配置文件中定义的。Fontconfig 会根据配置文件中的定义,对字体列表进行两次扫描,第一次是针对 pattern 进行替换和修改,第二次是对已经选中的字体的属性进行扫描和修改。Fontconfig 的配置文件是 XML 文件,其语法是自描述的,都是意义非常明确的单词,一看就懂。下面我们根据例子来学习Fontconfig 配置的语法。

首先,Fontconfig 配置文件的最外层 XML 元素一定是:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    ...
</fontconfig>

再看系统中的/etc/fonts/fonts.conf文件,其内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- /etc/fonts/fonts.conf file to configure system font access -->
<fontconfig>

<!-- Font directory list -->

    <dir>/usr/share/fonts</dir>
    <dir>/usr/local/share/fonts</dir>
    <dir prefix="xdg">fonts</dir>
    <!-- the following element will be removed in the future -->
    <dir>~/.fonts</dir>

<!--
  Accept deprecated 'mono' alias, replacing it with 'monospace'
-->
    <match target="pattern">
        <test qual="any" name="family">
            <string>mono</string>
        </test>
        <edit name="family" mode="assign" binding="same">
            <string>monospace</string>
        </edit>
    </match>

<!--
  Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
-->
    <match target="pattern">
        <test qual="any" name="family">
            <string>sans serif</string>
        </test>
        <edit name="family" mode="assign" binding="same">
            <string>sans-serif</string>
        </edit>
    </match>

<!--
  Accept deprecated 'sans' alias, replacing it with 'sans-serif'
-->
    <match target="pattern">
        <test qual="any" name="family">
            <string>sans</string>
        </test>
        <edit name="family" mode="assign" binding="same">
            <string>sans-serif</string>
        </edit>
    </match>

<!--
  Ignore dpkg temporary files created in fonts directories
-->
    <selectfont>
        <rejectfont>
            <glob>*.dpkg-tmp</glob>
        </rejectfont>
    </selectfont>
    <selectfont>
        <rejectfont>
            <glob>*.dpkg-new</glob>
        </rejectfont>
    </selectfont>

<!--
  Load local system customization file
-->
    <include ignore_missing="yes">conf.d</include>

<!-- Font cache directory list -->

    <cachedir>/var/cache/fontconfig</cachedir>
    <cachedir prefix="xdg">fontconfig</cachedir>
    <!-- the following element will be removed in the future -->
    <cachedir>~/.fontconfig</cachedir>

    <config>
<!--
  Rescan configuration every 30 seconds when FcFontSetList is called
 -->
        <rescan>
            <int>30</int>
        </rescan>
    </config>

</fontconfig>

其中,<dir></dir>元素中的内容,就是指定字体的存放目录,从文件中可以看出,如果是我们自己下载的字体,可以直接放到~/.fonts目录中,它是可以识别的;<include></include>元素指定从conf.d目录加载别的配置文件;<cachedir></cachedir>元素指定缓存目录;<selectfont><rejectfont></rejectfont></selectfont>指定哪些字体需要排除,相当于设置黑名单,而<selectfont><acceptfont></acceptfont></selectfont>则明确指定哪些字体必须接受,相当于设置白名单;至于<config><rescan></rescan></config>,就是设置每隔多长时间重新扫描一次配置文件。由此可见,其语法真的是自解释的,一看就懂。关于其中的<match></match>元素,后面再具体讲解。

/etc/fonts/conf.d/目录中,有一个50-user.conf配置文件,其内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <!--
        Load per-user customization files where stored on XDG Base Directory
        specification compliant places. it should be usually:
          $HOME/.config/fontconfig/conf.d
          $HOME/.config/fontconfig/fonts.conf
    -->
    <include ignore_missing="yes" prefix="xdg">fontconfig/conf.d</include>
    <include ignore_missing="yes" prefix="xdg">fontconfig/fonts.conf</include>
    <!-- the following elements will be removed in the future -->
    <include ignore_missing="yes" deprecated="yes">~/.fonts.conf.d</include>
    <include ignore_missing="yes" deprecated="yes">~/.fonts.conf</include>
</fontconfig>

在这个文件中,它还是使用了<include></include>元素,只不过它这次指定的加载配置文件的地方是~/.fonts.conf.d~/.fonts.conf,也就是说,我们可以把自己的配置文件放到自己的主目录下,Fontconfig 能够识别。

在 Fontconfig 的配置文件中,重点是<match></match>元素,它是对 pattern 进行修改的主要场所。它的基本格式是这样的:

<match target="目标">
    <test qual="any|all" name="属性" target="目标" compare="比较">
    </test>
    <edit name="属性" mode="修改方式" binding="绑定">
    </edit>
</match>

<match></match>必须首先包含一系列<test></test>组成的列表,可以为空,然后再包含一系列<edit></edit>组成的列表,可以为空,但是两种列表不能同时为空,而且<test></test>列表必须位于<edit></edit>列表之前。如果"目标"满足<test></test>列表的所有测试条件(当 qual="all" 时)或者列表中的任一测试条件(当 qual="any" 时),那么将被按照<edit></edit>列表中的指令序列进行修改。"目标"的默认值是"pattern",表示此<match>单元针对的是用于匹配的 pattern (第一次扫描)。如果"目标"的值是"font",那么就表示此<match>单元针对的是已被选定的字体(第二次扫描)。如果"目标"的值是"scan",那么就表示此<match>单元针对的是扫描字体以创建内部配置数据的初始化阶段。 <test></test>用于和"目标"的"属性"进行"比较"。"比较"的默认值是"eq"[等于],其他还有:"not_eq"[不等于]、 "less"[小于]、"less_eq"[小于等于]、"more"[大于]、 "more_eq"[大于等于]、"contains"[包含(用于字符串比较)]、"not_contains"[不包含(用于字符串比较)]。<edit></edit>用于修改"目标"的"属性"值列表。"绑定"仅在 name="family" 的情况下才有意义,其默认值是"weak",表示弱绑定;若设为"strong",则表示强绑定;而设为"same",则表示不改变当前的绑定。至于如何修改,则与两个因素有关:(1)"修改方式",(2)此<edit>的"属性"是否与同<match>内某个<test>的"属性"相同。具体如下表所示:

"修改方式" 有相同"属性" 无相同"属性"
"assign"(默认值) 替换第一个匹配的值 替换全部值
"assign_replace" 替换全部值 替换全部值
"prepend" 在第一个匹配的值之前插入 在值列表的首部插入
"prepend_first" 在值列表的首部插入 在值列表的首部插入
"append" 在第一个匹配的值之后添加 在值列表的尾部添加
"append_last" 在值列表的尾部添加 在值列表的尾部添加
"delete" 删除第一个匹配的值 删除全部值
"delete_all" 删除全部值 删除全部值

除了<match></match>元素之外,还有一个<alias></alias>元素,其基本格式是这样的:

<alias binding="绑定">
    <family>"需要替换的字体"</family>
    <prefer>
        <family>"替换后的字体"</family>
        <family>"替换后的字体"</family>
    </prefer>
</alias>

<alias></alias>元素相当于为修改字族名称提供了一种专门的速记法。在<alias></alias>元素中,<prefer>, <accept>, <default>就等于前面"修改方式"中的"prepend"、"append"、"append_last"。

可以使用fc-pattern命令查看 Fontconfig 对应用程序传递进来的 pattern 进行替换后的结果,也可以使用fc-match命令查看 Fontconfig 最终返回给应用程序的字体列表。如下两图:

一般情况下,排列在 pattern 最前面的字体会被选用,如果最前面的字体中没有满足条件的字符,则使用 pattern 中的下一个字体。我们使用这种思路对字体进行制定。例如,如果把 pattern 修改为 " WenQuanYi Bitmap Song" "Noto Serif CJK SC",那么应用程序会优先使用文泉驿点阵宋体,但是当字体的大小超出了文泉驿点阵宋体能够提供的范围时,就会使用后面的思源宋体。再例如,如果把 pattern 修改为 "verdana" "Noto Sans CJK SC",那么应用程序就会优先使用 verdana 显示英文字体,但是当要显示的字符超出了英文字符的范围时,就会使用后面的思源黑体。

但是,binding 的设置会改变上面字体选择的过程。binding="strong"、binding="weak" 和 binding="same" 分别有不同的效果。如果该属性设置不合理,则可能会出现这样的问题:明明使用 fc-pattern 查看的时候是 WenQuanYi Bitmap Song 排在 Noto Serif CJK SC 前面,是 Verdana 排在 Noto Sans CJK SC 前面,但是使用 fc-match 匹配的时候,偏偏首选的是 Noto Serif CJK SC 和 Noto Sans CJK SC。为什么会这样呢?这是因为 Fontconfig 选择字体的时候不仅仅只看字族名称的排列顺序,还要综合考虑一种字体能覆盖的字符集和字体大小范围,所以,在前面的例子中,虽然 WenQuanYi Bitmap Song 排在 Noto Serif CJK SC 前面,但是它覆盖的字体大小范围只有 12px 到 16px,Verdana 虽然排在 Noto Sans CJK SC 前面,但是其覆盖的字符集不包含中日韩文,所以 Fontconfig 就会首选排在后面的字体。为了解决这个问题,就必须把 binding 属性设置为 strong,这样,不管什么情况都首选排在前面的字体,只有遇到该字体不能胜任的字符时才选择后面的字体。在我的配置文件中,我都是一路 strong 到底。

现在的 Linux 桌面发行版在使用 Fontconfig 配置字体的时候,都有固定的流程。我们进入/etc/fonts/conf.d目录看一下,发现里面有 90 多个配置文件,如下图:

每一个文件名前面都有一个数字,数字可以决定配置文件的加载顺序,而 Fontconfig 在对 pattern 进行替换时,就是按照<match></match>元素出现的先后顺序进行的。在该目录下,还有一个README文件,看一下其中的内容,如下图:

其中明确说明了系统字体配置的整个流程。00 到 09 开头的配置文件,用于设置字体文件的存放目录,在 Ubuntu 18.04 中,没有 00 到 09 开头的配置文件。10 到 19 开头的文件用于设置系统中字体的全局配置选项,例如 antialias、hintstyle 等属性,对系统中所有的字体有效。20 到 29 是针对特定字体设置属性,只对配置文件中指定的字体有效。30 到 39 是字体族的替换,用于把某一个字体替换成另外一个字体。40 到 49 是对字体进行分类,这样,当系统中找不到某一个字体时,可以用同一类别的字体进行代替。50 到 59 载入用户自定义的配置文件。60 到 69 是把字体分类进行填实。40 到 49 和 60 到 69 是互相配合的。例如,当应用程序要求的字体是微软雅黑,就会向 Fontconfig 提交一个 pattern "微软雅黑",而 Fontconfig 发现系统中没有微软雅黑字体,他就会对这个字进行分类,微软雅黑肯定是属于 sans-serif 分类的,所以 40 到 49 开头的配置文件就会把 pattern 修改为"微软雅黑" "sans-serif"。然后,到了 60 到 69 开头的配置文件,它发现 pattern 中有"sans-serif",但这只是个分类名,不是一个具体的字体名,它需要对这个分类进行填实,怎么填呢?可以把"Noto Sans CJK SC"放到"sans-serif"的前面。这时,pattern 被修改为"微软雅黑" "Noto Sans CJK SC" "sans-serif"。这就是系统中字体先分类再填实分类的过程,当然 Ubuntu 18.04 中需要分类的字体和用来填实分类的字体更多,这个替换后的 pattern 会更复杂,这是因为 Ubuntu 18.04 是一个面向全球的发行版,它必须考虑到所有可能的语言。而我们自己的系统只需要满足自己的需求就可以了,其实系统中的很多配置文件是可以裁减掉的。如果要看完全不用系统自带的配置文件,全部自己定义字体的过程,可以看我 2014 年写的Linux 桌面系统字体配置要略,里面是用 Fedora 20 做的示范。后来我就变懒了,再改系统的字体我只做加法,只在~/.fonts.conf.d目录中放少数几个配置文件了事。70 到 79 开头的配置文件对可用的字体做进一步的调整,明确指定选择哪些字体或排除哪些字体。80 到 89 定义 scan 阶段对字体属性的修改。90 到 99 定义字体合成。其实在 Ubuntu 18.04 中,70 到 99 开头的配置文件并没有严格遵守这些约定。

系统中的配置文件很多,下面挑少数几个学习以下。先看10-antialias.conf,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!--  Use the Antialiasing -->
  <match target="pattern">
    <edit name="antialias" mode="append"><bool>true</bool></edit>
  </match>
</fontconfig>

这个文件最简单,只有一个<match></match>元素,而且里面的<test></test>元素为空,就是说,这个配置文件将所有字体的 antialias 属性设置为 true。而 20 开头的配置文件都是针对单个字体的属性进行设置的,例如20-unhint-small-dejavu-serif.conf,其内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
<fontconfig>
  <match target="font">
    <test name="family">
      <string>DejaVu Serif</string>
    </test>
    <test compare="less" name="pixelsize">
      <double>7.5</double>
    </test>
    <edit name="hinting">
      <bool>false</bool>
    </edit>
  </match>
</fontconfig>

该配置设置当字体为 DejaVu Serif,pixelsize 小于 7.5 的时候,将 hinting 属性设置为 false。30 到 39 开头的配置文件用于字体替换,其中最复杂的是30-metric-alias.conf,就是对常用的一些英文字体进行交叉替换,这样,当某种字体缺乏的时候,可以马上用另外一个字体替代。这个配置文件太长了,我就不贴了。贴个短一点的,35-arphic-uming-alias.conf,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <alias binding="same">
      <family>AR PL ShanHeiSun Uni</family>
      <prefer><family>AR PL UMing HK</family></prefer>
    </alias>

    <alias binding="same">
      <family>AR PL ShanHeiSun Uni MBE</family>
      <prefer><family>AR PL UMing TW MBE</family></prefer>
    </alias>
</fontconfig>

也就是设置为当 pattern 中有 AR PL ShanHeiSun Uni 时,将 AR PL Uming HK 放到它的前面。这里使用了<alias>简写语法。

40 到 49 开头的配置文件对字体进行分类,这里也大量使用<alias>简写语法。例如45-latin.conf对所有的英文字体进行分类,因为要分类的字体特别多,多以该文件特别长。分类的方法,就是把 serif、sans-serif、monospace 这样的分类名添加到 pattern 的最后。内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!--
  Mark common families with their generics so we'll get
  something reasonable
-->

<!--
  Serif faces
 -->
    <alias>
        <family>Bitstream Vera Serif</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Cambria</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Constantia</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>DejaVu Serif</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Elephant</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Garamond</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Georgia</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Liberation Serif</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Luxi Serif</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>MS Serif</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Nimbus Roman No9 L</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Nimbus Roman</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Palatino Linotype</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Thorndale AMT</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Thorndale</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Times New Roman</family>
        <default><family>serif</family></default>
    </alias>
    <alias>
        <family>Times</family>
        <default><family>serif</family></default>
    </alias>
<!--
  Sans-serif faces
 -->
    <alias>
        <family>Albany AMT</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Albany</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Arial Unicode MS</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Arial</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Bitstream Vera Sans</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Britannic</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Calibri</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Candara</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Century Gothic</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Corbel</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>DejaVu Sans</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Helvetica</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Haettenschweiler</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Liberation Sans</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>MS Sans Serif</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Nimbus Sans L</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Nimbus Sans</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Luxi Sans</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Tahoma</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Trebuchet MS</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Twentieth Century</family>
        <default><family>sans-serif</family></default>
    </alias>
    <alias>
        <family>Verdana</family>
        <default><family>sans-serif</family></default>
    </alias>
<!--
  Monospace faces
 -->
    <alias>
        <family>Andale Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Bitstream Vera Sans Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Consolas</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Courier New</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Courier</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Cumberland AMT</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Cumberland</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>DejaVu Sans Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Fixedsys</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Inconsolata</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Liberation Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Luxi Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Nimbus Mono L</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Nimbus Mono</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Nimbus Mono PS</family>
        <default><family>monospace</family></default>
    </alias>
    <alias>
        <family>Terminal</family>
        <default><family>monospace</family></default>
    </alias>
<!--
  Fantasy faces
 -->
    <alias>
        <family>Bauhaus Std</family>
        <default><family>fantasy</family></default>
    </alias>
    <alias>
        <family>Cooper Std</family>
        <default><family>fantasy</family></default>
    </alias>
    <alias>
        <family>Copperplate Gothic Std</family>
        <default><family>fantasy</family></default>
    </alias>
    <alias>
        <family>Impact</family>
        <default><family>fantasy</family></default>
    </alias>
<!--
  Cursive faces
  -->
    <alias>
        <family>Comic Sans MS</family>
        <default><family>cursive</family></default>
    </alias>
    <alias>
        <family>ITC Zapf Chancery Std</family>
        <default><family>cursive</family></default>
    </alias>
    <alias>
        <family>Zapfino</family>
        <default><family>cursive</family></default>
    </alias>

</fontconfig>

肯定是不可能列出世界上所有的字体的,所以总会有不认识的字体。49-sansserif.conf配置文件就设置将所有不认识的字体,都分类为 sans-serif。内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!--
  If the font still has no generic name, add sans-serif
 -->
    <match target="pattern">
        <test qual="all" name="family" compare="not_eq">
            <string>sans-serif</string>
        </test>
        <test qual="all" name="family" compare="not_eq">
            <string>serif</string>
        </test>
        <test qual="all" name="family" compare="not_eq">
            <string>monospace</string>
        </test>
        <edit name="family" mode="append_last">
            <string>sans-serif</string>
        </edit>
    </match>
</fontconfig>

解释一下就是说,一个 pattern 中既没有 sans-serif,又没有 serif,也没有 monospace,就说明这个 pattern 还没有被分类,没有被分类的原因肯定是出现了前面配置文件中没有见过的字体名称。那么,将 sans-serif 添加到这个 pattern 的最后。相当于把不认识的字体都分类为 sans-seirf。

而对分类进行填实,可以看一下60-latin.conf,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <alias>
        <family>serif</family>
        <prefer>
            <family>DejaVu Serif</family>
            <family>Bitstream Vera Serif</family>
            <family>Times New Roman</family>
            <family>Thorndale AMT</family>
            <family>Luxi Serif</family>
            <family>Nimbus Roman No9 L</family>
            <family>Nimbus Roman</family>
            <family>Times</family>
        </prefer>
    </alias>
    <alias>
        <family>sans-serif</family>
        <prefer>
            <family>DejaVu Sans</family>
            <family>Bitstream Vera Sans</family>
            <family>Verdana</family>
            <family>Arial</family>
            <family>Albany AMT</family>
            <family>Luxi Sans</family>
            <family>Nimbus Sans L</family>
            <family>Nimbus Sans</family>
            <family>Helvetica</family>
            <family>Lucida Sans Unicode</family>
            <family>BPG Glaho International</family> <!-- lat,cyr,arab,geor -->
            <family>Tahoma</family> <!-- lat,cyr,greek,heb,arab,thai -->
        </prefer>
    </alias>
    <alias>
        <family>monospace</family>
        <prefer>
            <family>DejaVu Sans Mono</family>
            <family>Bitstream Vera Sans Mono</family>
            <family>Inconsolata</family>
            <family>Andale Mono</family>
            <family>Courier New</family>
            <family>Cumberland AMT</family>
            <family>Luxi Mono</family>
            <family>Nimbus Mono L</family>
            <family>Nimbus Mono</family>
            <family>Nimbus Mono PS</family>
            <family>Courier</family>
        </prefer>
    </alias>
<!--
  Fantasy faces
 -->
    <alias>
        <family>fantasy</family>
        <prefer>
            <family>Impact</family>
            <family>Copperplate Gothic Std</family>
            <family>Cooper Std</family>
            <family>Bauhaus Std</family>
        </prefer>
    </alias>
<!--
  Cursive faces
  -->
    <alias>
        <family>cursive</family>
        <prefer>
            <family>ITC Zapf Chancery Std</family>
            <family>Zapfino</family>
            <family>Comic Sans MS</family>
        </prefer>
    </alias>

</fontconfig>

这里也大量使用<alias>简写语法。很显然,这只是对英文字体进行填实,不能满足我中文用户的需要。对中文字体进行填实,请看69-language-selector-zh-cn.conf的内容:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>

    <match target="pattern">
        <test name="lang">
            <string>zh-cn</string>
        </test>
        <test qual="any" name="family">
            <string>serif</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Noto Serif CJK SC</string>
            <string>HYSong</string>
            <string>AR PL UMing CN</string>
            <string>AR PL UMing HK</string>
            <string>AR PL New Sung</string>
            <string>WenQuanYi Bitmap Song</string>
            <string>AR PL UKai CN</string>
            <string>AR PL ZenKai Uni</string>
        </edit>
    </match> 
    <match target="pattern">
        <test qual="any" name="family">
            <string>sans-serif</string>
        </test>
        <test name="lang">
            <string>zh-cn</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Noto Sans CJK SC</string>
            <string>WenQuanYi Zen Hei</string>
            <string>HYSong</string>
            <string>AR PL UMing CN</string>
            <string>AR PL UMing HK</string>
            <string>AR PL New Sung</string>
            <string>AR PL UKai CN</string>
            <string>AR PL ZenKai Uni</string>
        </edit>
    </match> 
    <match target="pattern">
        <test qual="any" name="family">
            <string>monospace</string>
        </test>
        <test name="lang">
            <string>zh-cn</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>DejaVu Sans Mono</string>
            <string>Noto Sans Mono CJK SC</string>
            <string>WenQuanYi Zen Hei Mono</string>
            <string>HYSong</string>
            <string>AR PL UMing CN</string>
            <string>AR PL UMing HK</string>
            <string>AR PL New Sung</string>
            <string>AR PL UKai CN</string>
            <string>AR PL ZenKai Uni</string>
        </edit>
    </match> 

</fontconfig>

我们常见的中文字体基本上都在这里了。

最后再学习两个配置文件,一个是70-no-bitmaps.conf,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- Reject bitmap fonts -->
 <selectfont>
  <rejectfont>
   <pattern>
     <patelt name="scalable"><bool>false</bool></patelt>
   </pattern>
  </rejectfont>
 </selectfont>
</fontconfig>

这里出现了一个新元素<patelt name=...>,这个元素配置单个属性,但是其子元素可以是一个列表。上面这个配置文件就是说,凡是 scalable 属性为 false 的字体,都被 reject 掉,也就是不使用任何不可缩放的字体。而我们的 WenQuanYi Bitmap Song 恰恰就是这样一个不可缩放的点阵字体,怎么办?85-xfonts-wqy-1.conf这个配置文件就使用<acceptfont>元素为这个字体开了绿灯,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- WenQuanYi Bitmap Font for CJK users -->
<fontconfig>

<!--
    Enable WenQuanYi Bitmap Song only
-->
    <selectfont>
        <acceptfont>
            <pattern>
                 <patelt name="family"><string>WenQuanYi Bitmap Song</string></patelt>
            </pattern>
        </acceptfont>
    </selectfont>

</fontconfig>

好了,对 Fontconfig 的学习就到这里吧。下面开始实战。

配置 Ubuntu 18.04 的字体

前面提到的所有 Linux 发行版都安装有 Times New Roman 字体,但是没有 Georgia、Verdana、Arial。后面这几个字体是微软公司设计的 webcore 字体,可以免费使用。而中文的 SimSun 和微软雅黑是不能免费使用的,我们只能用思源宋体和思源黑体。思源宋体没有点阵,不过没关系,我们可以使用文泉驿点阵宋体替代。使用如下命令安装文泉驿字体和 webcore 字体:

sudo aptitude install fonts-wqy-microhei fonts-wqy-zenhei xfonts-wqy
sudo aptitude install ttf-mscorefonts-installer

如下两图:

安装完毕之后,我们系统中就多了以下几种字体,如下图:

然后进行配置。思路是这样的:

1、先修改全局配置,将 dpi 改为 96。而 hinting 和抗锯齿是在 gnome-tweak-tool中设置的,还比较令人满意,就不改了。

2、 宋体和 serif 字体都使用思源宋体 Noto Serif CJK SC,但是宋体在小字时需要点阵,serif 在小字时不需要点阵。为什么呢?因为当页面设计者把字体设置为宋体时,他肯定是针对中文设计的,但是如果他设置为 serif,则一定是针对英语设计的,而英文字体往往没有点阵,搭配点阵的宋体并不好看。Times New Roman 和 Georgia 在英文的时候当然显示为 Times New Roman 和 Georgia,但是这两个字体没有中文,所以要把 Noto Serif CJK SC 添加到这两个字体的后面,当这两个字体中夹杂中文时,就显示为思源宋体。同理,如果页面设计者将字体设置为 Times New Roman 或者 Georgia,他肯定是针对英文设计的,所以其中夹杂的中文也不需要点阵。

3、黑体、微软雅黑、Verdana、Arial 和 sans-serif,完全不需要做任何修改,保持 Ubuntu 18.04 默认的配置就行了,不需要做任何修改,也不要试图给 sans-serif 的中文添加点阵,理由还是和上一条中说的一样,中文使用点阵是很漂亮的,但是和没有点阵的英文搭配时,就不是那么和谐了。

所以,我们只需要两个配置文件就行了。根据上一节的学习我们知道了,只需要把配置文件放到~/.fonts.conf.d/目录下就行了,为了配合系统字体配置文件的命名规律,我把它命名为10-dpi.conf68-serif.conf,在第一个文件中,把 dpi 改成 96,在第二个文件中,针对 serif、Times New Roman、Georgia、宋体和 SimSun 进行配置,如下。

使用 gnome-tweak-tool 设置 hinting 和抗锯齿,如下图:

更改 dpi 的配置文件~/.fonts.conf.d/10-dpi.conf的内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!--  Use the Antialiasing -->
  <match target="pattern">
    <edit name="dpi" mode="assign"><double>96</double></edit>
  </match>
</fontconfig>

更改好 dpi 之后,使用fc-match查看字体,可以看到 12pt 的字的 pixelsize 是 16,能用更多的像素去渲染一个字,效果当然要好一些。如下图:

配置宋体及 serif 的文件~/fonts.conf.d/68-serif.conf的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
<fontconfig>
    <match target="pattern">
        <test name="family">
            <string>serif</string>
        </test>
        <edit name="family" mode="assign" binding="strong">
            <string>Noto Serif CJK SC</string>
        </edit>
    </match>
    <match target="pattern">
        <test name="family">
            <string>宋体</string>
        </test>
        <edit name="family" mode="assign" binding="strong">
            <string>WenQuanYi Bitmap Song</string>
            <string>Noto Serif CJK SC</string>
        </edit>
    </match>
    <match target="pattern">
        <test name="family">
            <string>SimSun</string>
        </test>
        <edit name="family" mode="assign" binding="strong">
            <string>WenQuanYi Bitmap Song</string>
            <string>Noto Serif CJK SC</string>
        </edit>
    </match>
    <match target="pattern">
        <test name="family">
            <string>Times New Roman</string>
        </test>
        <edit name="family" mode="append" binding="strong">
            <string>Noto Serif CJK SC</string>
        </edit>
    </match>
    <match target="pattern">
        <test name="family">
            <string>Georgia</string>
        </test>
        <edit name="family" mode="append" binding="strong">
            <string>Noto Serif CJK SC</string>
        </edit>
    </match>
</fontconfig>

配置完成后的效果:

这是我认为的最完美的效果。宋体、Serif 都显示为思源宋体,Georgia 和 Times New Roman 只有中文显示为思源宋体,而且,只有明确将字体指定为宋体或 SimSun 的字,才有点阵,而指定为 Serif、Georgia 和 Times New Roman 的字,没有点阵。黑体、微软雅黑、sans-serif 都显示为思源黑体,Verdana 和 Arial 的中文部分显示为思源黑体,都没有点阵。

为什么说如果页面设计者指定字体为宋体时最好用点阵呢?以新浪网首页为例就可以说明问题。如果我们的系统中宋体没有点阵,页面效果是这样的:

仔细看这个页面的效果,上半部分是分类导航,页面设计者指定的字体是微软雅黑,所以没有点阵显示得也很饱满,但是下半部分是新闻标题,页面设计者指定的字体是宋体,显示就很虚。如果我们的系统中宋体有点阵,看起来就会漂亮不少,效果是这样的:

为什么说黑体、微软雅黑以及英文字体不需要点阵呢?以我自己的博客为例就可以说明问题。我的博客字体用的是 verdana 搭配微软雅黑。如果设置了黑体显示点阵(这里使用文泉驿点阵正黑),效果是这样的:

当使用点阵时,中文字还是显示得很漂亮。但是,一和英文字体搭配,就不是那么协调了。如果都不用点阵,效果如下图:

中文和英文都很饱满,而且饱满程度基本一致,看起来非常协调。这就是我在 Ubuntu 18.04 中的最终配置,看博客和写博客都很舒服哦。

总结

这篇随笔篇幅很长。先是用了很大的篇幅来展示各 Linux 桌面发行版的字体配置情况,充分展示了 Linux 桌面字体的历史变迁,还是很值得一看的。其次又用了很大的篇幅来介绍 Fontconfig 的配置语法,以及 Linux 桌面发行版中字体配置的思路和流程。最后,才在 Ubuntu 18.04 中进行字体配置实战,实战只占了很少的篇幅。但是,我认为前面的铺垫都是非常有意义的,我们只要充分掌握了 Fontconfig 的配置语法、Linux 桌面字体配置的思路和流程,那么,在字体配置方面,真的就可以为所欲为了。

版权申明

该随笔由京山游侠在2018年09月19日发布于博客园,引用请注明出处,转载或出版请联系博主。QQ邮箱:1841079@qq.com