京山游侠

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

Linux 桌面系统字体配置要略

Posted on 2014-06-18 21:40 京山游侠 阅读(...) 评论(...) 编辑 收藏

字体显示效果测试

这一段是为了测试宋体字的显示效果,包括宋体里面自带的英文字体,“This is english,how does it look like?”。这一行是小字。后面几个字是加粗的宋体。标点符号“,。:;!”
  这一段是为了测试黑体字的显示效果,包括黑体里面自带的英文字体,“This is english,how does it look like?”。这一行是小字。标点符号“,。:;!”。微软雅黑是什么样子的呢“,。:!”?
  This paragraph shows how does serif english fonts look like,包括英文里面夹杂中文的显示效果。Is serif same as Times New roman?
  This paragraph shows how does sans-serif english fonts look like,包括英文里面夹杂中文的显示效果。Is sans-serif same as Arial?
  This paragraph shows how does ui fonts look like,还有专门的界面字体,比如Windows中的Tahoma字体:File->New->New Project。
    #include <stdio.h>
    int main(){
      return 0;//很显然这是为了测试等宽字体
    }

    #include <stdio.h>  //观察下面的代码了解monospace是否等于Courier New
    int main(){
      return 0;
    }

 

字体的分类及其用途

在文章最开始的一个部分(也就是这部分之前的内容),我特意写了一些文字用来测试各种字体的显示效果。这些测试文本我在 HTML 源代码模式下经过仔细修改。第 1 段的字体都是设置为宋体,包括其中的英文、小字及加粗的字。第 2 段除了最后一句外,其余的字体都设置为黑体,包括英文及小字;最后一句字体设置为微软雅黑,是为了对比微软雅黑和普通黑体的效果。第 3 段除了最后一句外,其余的字体都设置为 Serif,包括其中的中文;最后一句字体设置为 Times New Roman。第 4 段除了最后一句外,其余的是 Sans-Serif,最后一句是 Arial。第 5 段的字体都是设置为 Tahoma。最后的两块代码,第 1 块代码的字体设置为 monospace,第 2 块代码的字体设置为 Courier New。为什么这样设置,我后面会配合图片逐一讲解。下面,需要先了解字体的分类及其用途。

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 多个像素,字体平滑自然不是问题,根本不需要使用点阵、微调、抗锯齿、次像素等技术。我认为,随着显示器硬件技术的发展,以上技术都将成为浮云。

以上图片是字体测试段落在 Win7 系统中的显示效果。除了标题和小字外,其它的字体都是 12pt。可以看到,12pt 的宋体字和 serif 的英文字体显示为点阵,所以获得了清晰锐利的效果。而黑体、微软雅黑和 sans-serif 字体都没有点阵,但是由于 sans-serif 本身笔画饱满,所以也获得了不错的显示效果。据说微软雅黑每个字造价 100 美元,专为 ClearType 技术而设计,但是从效果上看,并不比普通黑体强多少。

前面讲过,serif 和 sans-serif 是字体的分类名,而不是具体的字体名。Times New Roman 是 Windows 下最经典的 serif 字体,但是从上图可以看出它并不是 serif 字体的首选字体,设置为 serif 的字体和设置为 Times New Roman 的字体显示不一样,但是都是很漂亮的有衬线字体,serif 的中文选择了一种和宋体不同的点阵字体。Arial 字体是最经典的无衬线字体,从图中可以看出设置为 sans-serif 和设置为 Arial 的字体显示效果相同。只是sans-serif 的中文被宋体的点阵代替了。Tahoma 字体的显示效果和 sans-serif 只是宽度上略有不同,但是依然很饱满。

使用 Ctrl+鼠标滑轮 可以缩放网页。将网页放大后显示,可以查看轮廓字体的显示效果,如下图:

(注意:我文中的图片都不是原始大小,可以通过 Ctrl+鼠标滑轮 放大后观看,也可以在新窗口中打开图片观看。)

Linux 中的字体

下面,通过大量的图片直观地感受不同的字体以及不同的字体显示技术会带来什么样的效果。

上图是 CentOS 5 中的显示效果,是不是觉得字体很虚?系统菜单中的字体倒是很锐利。其实在 Fedora 9 之前,Red Hat 系列的 Linux 发行版中文字体显示都很模糊。也正是因为这个原因,我曾经有一段时间偏向于使用中科红旗的 Linux。CentOS 中的字体显示发虚的原因就是因为它的小字没有使用点阵。

小字使用轮廓字体就会发虚,大字使用轮廓字体效果就要好得多。使用 Ctrl+鼠标滑轮 将网页放大,可以看到大字显示很清晰饱满。如下图:

CentOS 5 中的字体显示还有一个奇怪的现象,那就是它认为 serif 的中文应该是楷体,而且它的 sans-serif 也显示的是有衬线字体,很显然这是不对的。而且由于历史的原因,它没有黑体。如下图:

CentOS 系统中对中文的支持主要依赖于 2001 年文鼎向开源界贡献的两套字体:文鼎PL中楷和文鼎PL细上海宋,如下图:

下面来看看 Ubuntu 中的显示效果。从下图中可以看到,Ubuntu 侧重于 sans-serif 和黑体,除了明确指定serif 和 Times New Roman 的字体外,其余全部显示为 sans-serif 和黑体。而且没有点阵。这进一步印证了我前面说的黑体适合屏幕显示、宋体适合打印和印刷。在系统中全部显示黑体虽然不会丢失文字的信息,但是肯定会影响美观。Ubuntu 没有正确地为 monospace 选择等宽字体,但是对 Courier New 倒是选择了一个很漂亮的等宽字体进行替代。

使用 Ctrl+鼠标滚轮 将网页放大,看到清晰饱满的字体,如下图:

对于界面字体,Windows 下有 Tahoma,Ubuntu 下也有专用的界面字体,该字体就叫 Ubuntu,是开源的。自 Ubuntu 14.04 开始,已经没有采用文泉驿的黑体了,而是改用 Droid Sans Fallback,没错,就是 Google 花钱为 Android 系统设计的字体。反正该字体也没点阵,我觉得显示效果也就那样。

下面再来看看 Fedora 20,该系统也是以黑体为主。从下面的图片中可以看到,除了 Times New Roman,其它的字体显示都是一个样。

小字是点阵,放大后,显示为黑体,如下图:

查看 Fedora 20 系统,可以看到它还是安装了 AR PL UMing,也就是宋体,但是它没有使用,而且它没有安装楷体,虽然它的软件源中有 cjkuni-ukai。它使用的黑体是文泉驿系列。

之前发表的几篇随笔都是在 Ubuntu 14.04 下完成的,这次我决定换到 Fedora 20 系统下。晒一下我的桌面,如下图:

从 AR PL 这几个字符可以看出,AR PL UMing 和文鼎贡献的那两套字体是一脉相承的。确实如此,只不过是经过合并、修改、增加了日语韩语及香港常用字型后,更名为 CJKUniFonts,据说含有点阵。从名字可以看出,有了这套字体,中日韩都可以搞定。CentOS 6 的主打字体就是它。如下图,小字有点阵,清晰锐利:

放大后,显示轮廓字体,不过好像缺了黑体,如下图:

这套字体唯一的缺点就是它的标点符号,逗号和句号经常跑到一行的中间,而不是一行的底部。这都不是什么大事儿,其实,微软雅黑的引号也不漂亮,不是吗?

CentOS 6 中已经有了文泉驿,如下图,之所以没有显示黑体不是因为没有黑体,而是因为没有正确配置:

通过上面一系列的截图,可以看到各个操作系统都使用了哪些中文字体,也展现出了各个操作系统在字体配置方面的缺陷。从另一个方面,也反映出了挑选字体的趋势。可以总结如下:

1、Linux 系统中的宋体最早只有文鼎贡献的细上海宋,而且还没有点阵。后来发展到 CJKUniFonts,宋体才有了点阵,它的字体名叫 AR PL UMing,在 Linux 系统上想要使用宋体,就全靠它了。但是 CJKUniFonts 的点阵似乎不太符合我们的习惯,好在文泉驿有非常漂亮的点阵字体,虽然只覆盖了 9pt 到 12pt 的范围。

2、Linux 系统中最开始黑体缺乏,现在大家都认识到了黑体在屏幕显示中具有较好的效果,于是就黑体泛滥了。微软花大价钱请人设计的微软雅黑是黑体,开源字体文泉驿做的也是黑体,Google 花钱请人做的 Droid Sans Fallback 还是黑体。Ubuntu 用黑体,Fedora 也用黑体。可以说,文泉驿选择黑体作为切入点真的是选对了,也确实是我们广大 Linux 使用者的福音。

3、即使是最新的 Linux 发行版,如 Ubuntu 14 和 Fedora 20,其字体设置也不合理,对 serif、sans-serif、宋体、黑体的理解都不正确。所以,对于字体,我们还是得自己配置。

字体配置实战

下面,将以 Fedora 20 为例,自己动手将它配置为正确的显示效果。目前,在 Linux 系统上配置字体的工具是 Fontconfig。

为什么是 Fontconfig

感谢这个时代,曾经混乱不堪的字体配置方法终于被 Fontconfig 一统江湖。在 Linux 中,字体配置曾经各自为政、混乱不堪,XServer、Xft、GTK、GTK2、QT 等等各自采用不同的配置手段,字体引擎也有 Type1、FreeType等。目前,可以认为在 Linux 系统中只需要配置 FontConfig 即可。

XOrg 的官方网站上的文档说明:XOrg 中有两种字体系统,一种字体系统是 XServer 自带的字体系统,另一种就是 Xft,而且 XOrg 官方建议所有的界面库的开发者首选 Xft 字体系统;对于字体引擎,现在只剩下 FreeType 了,Type1 的功能已经合并到了 FreeType 中。FreeDesktop.org 的官方网站上有 Xft、Freetype 以及 Fontconfig 的文档,虽然很简略,但是也提到:Xft1.0 及以前的版本,需要通过 XftConfig 文件来配置字体,从Xft1.1 以后,都采用 Fontconfig 来配置字体;Fontconfig 的文档也说它只负责字体的配置,不负责字体的显示。

So,配置 Linux 中的字体,我们只用理会 Fontconfig,还有疑问吗?

学习 Fontconfig

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

Fontconfig 的功能就是帮助应用程序选择字体并指导字体的显示效果(只能说指导,因为具体的显示由 Xft、Freetype 等说了算),用什么策略选择字体及用什么选项显示字体,通过配置文件来指定。Fontconfig 对配置文件进行两遍扫描,第一遍对应用程序传递给 Fontconfig 的字体列表(称为 pattern)进行操作,通过添加、删除、替换pattern中的字体名,让应用程序得到相应的字体;第二遍对已经选择的字体进行操作,这时一般不改字体名了,而是对抗锯齿(antialias)、微调(hinting)、自动微调(autohint)、微调级别(hintstyle)以及次像素平滑(rgba)等属性进行控制。

Fontconfig 配置文件的语法,这个不用我在这里唠叨,看 man fonts.conf 手册页即可,一点也不难,它的很多元素,如 match、target、test、edit、string、bool、double、const 等,本身就是自解释的,看到这个单词就知道它是什么意思。它的原则就是对每一个 <match...>...</match>,通过 <test...>...</test> 来选择要编辑的元素,然后使用 <edit...>...</edit> 来对该元素进行编辑。

配置 Fedora 20

Fontconfig 首先读取的配置文件是 /etc/fonts/fonts.conf,然后,根据 /etc/fonts/fonts.conf 里面的 <include>...</include> 信息来加载其它的配置文件。Fedora 20 默认是加载 /etc/fonts/conf.d 目录下的所有文件。当然,/etc/fonts/conf.d 目录下的文件太多了,引入了很多其实没有必要的复杂性。对于我这种追求简洁的人,我直接就把它改了,让 /etc/fonts/conf.d 下的文件去见鬼,从我自己的主目录的 fonts.conf.d 目录下加载配置文件吧。如下图,我注释掉了第71行,增加了第72行:

下面正式开始写配置文件。

第一步:将不标准的字体分类命名更改为标准的字体分类命名

英文的字体分类有 serif、sans-serif 和 monospace,中文的分类有“宋体”和“黑体”,但我们不能保证别人在请求字体的时候拼写都是准确的,比如有可能拼写成“sans serif”或“sans”、“mono”等,中文也可能写成繁体“宋體”、“黑體”或者拼音“SongTi”、“HeiTi”等,为了后面配置文件的简洁,这些不标准的分类名要先替换成标准的分类名。

英文的分类名的标准化 /etc/fonts/fonts.conf 这个主配置文件(就是我上面截图的那个文件)已经做了,其完整代码如下:

  1 <?xml version="1.0"?>
  2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
  3 <!-- /etc/fonts/fonts.conf file to configure system font access -->
  4 <fontconfig>
  5 
  6 <!--
  7     DO NOT EDIT THIS FILE.
  8     IT WILL BE REPLACED WHEN FONTCONFIG IS UPDATED.
  9     LOCAL CHANGES BELONG IN 'local.conf'.
 10 
 11     The intent of this standard configuration file is to be adequate for
 12     most environments.  If you have a reasonably normal environment and
 13     have found problems with this configuration, they are probably
 14     things that others will also want fixed.  Please submit any
 15     problems to the fontconfig bugzilla system located at fontconfig.org
 16 
 17     Note that the normal 'make install' procedure for fontconfig is to
 18     replace any existing fonts.conf file with the new version.  Place
 19     any local customizations in local.conf which this file references.
 20 
 21     Keith Packard
 22 -->
 23 
 24 <!-- Font directory list -->
 25 
 26     <dir>/usr/share/fonts</dir>
 27     <dir>/usr/share/X11/fonts/Type1</dir> <dir>/usr/share/X11/fonts/TTF</dir> <dir>/usr/local/share/fonts</dir>
 28     <dir prefix="xdg">fonts</dir>
 29     <!-- the following element will be removed in the future -->
 30     <dir>~/.fonts</dir>
 31 
 32 <!--
 33   Accept deprecated 'mono' alias, replacing it with 'monospace'
 34 -->
 35     <match target="pattern">
 36         <test qual="any" name="family">
 37             <string>mono</string>
 38         </test>
 39         <edit name="family" mode="assign" binding="same">
 40             <string>monospace</string>
 41         </edit>
 42     </match>
 43 
 44 <!--
 45   Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
 46 -->
 47     <match target="pattern">
 48         <test qual="any" name="family">
 49             <string>sans serif</string>
 50         </test>
 51         <edit name="family" mode="assign" binding="same">
 52             <string>sans-serif</string>
 53         </edit>
 54     </match>
 55 
 56 <!--
 57   Accept deprecated 'sans' alias, replacing it with 'sans-serif'
 58 -->
 59     <match target="pattern">
 60         <test qual="any" name="family">
 61             <string>sans</string>
 62         </test>
 63         <edit name="family" mode="assign" binding="same">
 64             <string>sans-serif</string>
 65         </edit>
 66     </match>
 67 
 68 <!--
 69   Load local system customization file
 70 -->
 71 <!-- <include ignore_missing="yes">fonts.conf.d</incllude> -->
 72     <include ignore_missing="yes">~/fonts.conf.d</include>
 73 
 74 <!-- Font cache directory list -->
 75 
 76     <cachedir>/var/cache/fontconfig</cachedir>
 77     <cachedir prefix="xdg">fontconfig</cachedir>
 78     <!-- the following element will be removed in the future -->
 79     <cachedir>~/.fontconfig</cachedir>
 80 
 81     <config>
 82 <!--
 83   These are the default Unicode chars that are expected to be blank
 84   in fonts.  All other blank chars are assumed to be broken and
 85   won't appear in the resulting charsets
 86  -->
 87         <blank>
 88             <int>0x0020</int>    <!-- SPACE -->
 89             <int>0x00A0</int>    <!-- NO-BREAK SPACE -->
 90             <int>0x00AD</int>    <!-- SOFT HYPHEN -->
 91             <int>0x034F</int>    <!-- COMBINING GRAPHEME JOINER -->
 92             <int>0x0600</int>    <!-- ARABIC NUMBER SIGN -->
 93             <int>0x0601</int>    <!-- ARABIC SIGN SANAH -->
 94             <int>0x0602</int>    <!-- ARABIC FOOTNOTE MARKER -->
 95             <int>0x0603</int>    <!-- ARABIC SIGN SAFHA -->
 96             <int>0x06DD</int>    <!-- ARABIC END OF AYAH -->
 97             <int>0x070F</int>    <!-- SYRIAC ABBREVIATION MARK -->
 98             <int>0x115F</int>    <!-- HANGUL CHOSEONG FILLER -->
 99             <int>0x1160</int>    <!-- HANGUL JUNGSEONG FILLER -->
100             <int>0x1680</int>    <!-- OGHAM SPACE MARK -->
101             <int>0x17B4</int>    <!-- KHMER VOWEL INHERENT AQ -->
102             <int>0x17B5</int>    <!-- KHMER VOWEL INHERENT AA -->
103             <int>0x180E</int>    <!-- MONGOLIAN VOWEL SEPARATOR -->
104             <int>0x2000</int>    <!-- EN QUAD -->
105             <int>0x2001</int>    <!-- EM QUAD -->
106             <int>0x2002</int>    <!-- EN SPACE -->
107             <int>0x2003</int>    <!-- EM SPACE -->
108             <int>0x2004</int>    <!-- THREE-PER-EM SPACE -->
109             <int>0x2005</int>    <!-- FOUR-PER-EM SPACE -->
110             <int>0x2006</int>    <!-- SIX-PER-EM SPACE -->
111             <int>0x2007</int>    <!-- FIGURE SPACE -->
112             <int>0x2008</int>    <!-- PUNCTUATION SPACE -->
113             <int>0x2009</int>    <!-- THIN SPACE -->
114             <int>0x200A</int>    <!-- HAIR SPACE -->
115             <int>0x200B</int>    <!-- ZERO WIDTH SPACE -->
116             <int>0x200C</int>    <!-- ZERO WIDTH NON-JOINER -->
117             <int>0x200D</int>    <!-- ZERO WIDTH JOINER -->
118             <int>0x200E</int>    <!-- LEFT-TO-RIGHT MARK -->
119             <int>0x200F</int>    <!-- RIGHT-TO-LEFT MARK -->
120             <int>0x2028</int>    <!-- LINE SEPARATOR -->
121             <int>0x2029</int>    <!-- PARAGRAPH SEPARATOR -->
122             <int>0x202A</int>    <!-- LEFT-TO-RIGHT EMBEDDING -->
123             <int>0x202B</int>    <!-- RIGHT-TO-LEFT EMBEDDING -->
124             <int>0x202C</int>    <!-- POP DIRECTIONAL FORMATTING -->
125             <int>0x202D</int>    <!-- LEFT-TO-RIGHT OVERRIDE -->
126             <int>0x202E</int>    <!-- RIGHT-TO-LEFT OVERRIDE -->
127             <int>0x202F</int>    <!-- NARROW NO-BREAK SPACE -->
128             <int>0x205F</int>    <!-- MEDIUM MATHEMATICAL SPACE -->
129             <int>0x2060</int>    <!-- WORD JOINER -->
130             <int>0x2061</int>    <!-- FUNCTION APPLICATION -->
131             <int>0x2062</int>    <!-- INVISIBLE TIMES -->
132             <int>0x2063</int>    <!-- INVISIBLE SEPARATOR -->
133             <int>0x206A</int>    <!-- INHIBIT SYMMETRIC SWAPPING -->
134             <int>0x206B</int>    <!-- ACTIVATE SYMMETRIC SWAPPING -->
135             <int>0x206C</int>    <!-- INHIBIT ARABIC FORM SHAPING -->
136             <int>0x206D</int>    <!-- ACTIVATE ARABIC FORM SHAPING -->
137             <int>0x206E</int>    <!-- NATIONAL DIGIT SHAPES -->
138             <int>0x206F</int>    <!-- NOMINAL DIGIT SHAPES -->
139             <int>0x2800</int>    <!-- BRAILLE PATTERN BLANK -->
140             <int>0x3000</int>    <!-- IDEOGRAPHIC SPACE -->
141             <int>0x3164</int>    <!-- HANGUL FILLER -->
142             <int>0xFEFF</int>    <!-- ZERO WIDTH NO-BREAK SPACE -->
143             <int>0xFFA0</int>    <!-- HALFWIDTH HANGUL FILLER -->
144             <int>0xFFF9</int>    <!-- INTERLINEAR ANNOTATION ANCHOR -->
145             <int>0xFFFA</int>    <!-- INTERLINEAR ANNOTATION SEPARATOR -->
146             <int>0xFFFB</int>    <!-- INTERLINEAR ANNOTATION TERMINATOR -->
147         </blank>
148 <!--
149   Rescan configuration every 30 seconds when FcFontSetList is called
150  -->
151         <rescan>
152             <int>30</int>
153         </rescan>
154     </config>
155 
156 </fontconfig>
/etc/fonts/fonts.conf

中文的分类名标准化:

 1 <?xml version="1.0"?>
 2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
 3 <fontconfig>
 4     <match target="pattern">
 5         <test qual="any" name="family">
 6             <string>宋體</string>
 7         </test>
 8         <edit name="family" mode="assign" binding="same">
 9             <string>宋体</string>
10         </edit>
11     </match>
12     <match target="pattern">
13         <test qual="any" name="family">
14             <string>SongTi</string>
15         </test>
16         <edit name="family" mode="assign" binding="same">
17             <string>宋体</string>
18         </edit>
19     </match>
20     <match target="pattern">
21         <test qual="any" name="family">
22             <string>黑體</string>
23         </test>
24         <edit name="family" mode="assign" binding="same">
25             <string>黑体</string>
26         </edit>
27     </match>
28     <match target="pattern">
29         <test qual="any" name="family">
30             <string>HeiTi</string>
31         </test>
32         <edit name="family" mode="assign" binding="same">
33             <string>黑体</string>
34         </edit>
35     </match>
36 </fontconfig>
~/fonts.conf.d/01-standardize-chinese-font-class.conf

第二步:对英文字体进行分类

请注意,我只对英文字体进行分类。对英文字体进行分类的目的是为了给相应的英文字体搭配正确的中文,比如字体为 Arial 的英文句子中,如果出现中文就应该用黑体,而字体为 Times New Roman 的英文句子中,如果出现中文当然应该用宋体。等宽的英文字体嘛,whatever,反正中文字都一样宽,哪个顺眼就用哪个吧。对于中文其实也可以分类,然后搭配不同的英文字体。但是我认为,中文的字体中一般都包含有英文字符,用原装的即可,没有必要替换。所以,不对中文进行分类。其代码如下:

  1 <?xml version="1.0"?>
  2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
  3 <fontconfig>
  4 <!--
  5   Serif faces
  6  -->
  7     <alias>
  8         <family>Bitstream Vera Serif</family>
  9         <accept><family>serif</family></accept>
 10     </alias>
 11     <alias>
 12         <family>DejaVu Serif</family>
 13         <accept><family>serif</family></accept>
 14     </alias>
 15     <alias>
 16         <family>Liberation Serif</family>
 17         <accept><family>serif</family></accept>
 18     </alias>
 19     <alias>
 20         <family>Times New Roman</family>
 21         <accept><family>serif</family></accept>
 22     </alias>
 23     <alias>
 24         <family>Times</family>
 25         <accept><family>serif</family></accept>
 26     </alias>
 27     <alias>
 28         <family>Nimbus Roman No9 L</family>
 29         <accept><family>serif</family></accept>
 30     </alias>
 31     <alias>
 32         <family>Luxi Serif</family>
 33         <accept><family>serif</family></accept>
 34     </alias>
 35     <alias>
 36         <family>Thorndale AMT</family>
 37         <accept><family>serif</family></accept>
 38     </alias>
 39     <alias>
 40         <family>Thorndale</family>
 41         <accept><family>serif</family></accept>
 42     </alias>
 43     <alias>
 44         <family>Georgia</family>
 45         <accept><family>serif</family></accept>
 46     </alias>
 47     <alias>
 48         <family>Garamond</family>
 49         <accept><family>serif</family></accept>
 50     </alias>
 51     <alias>
 52         <family>Palatino Linotype</family>
 53         <accept><family>serif</family></accept>
 54     </alias>
 55     <alias>
 56         <family>Trebuchet MS</family>
 57         <accept><family>serif</family></accept>
 58     </alias>
 59 <!--
 60   Sans-serif faces
 61  -->
 62     <alias>
 63         <family>Bitstream Vera Sans</family>
 64         <accept><family>sans-serif</family></accept>
 65     </alias>
 66     <alias>
 67         <family>DejaVu Sans</family>
 68         <accept><family>sans-serif</family></accept>
 69     </alias>
 70     <alias>
 71         <family>Liberation Sans</family>
 72         <accept><family>sans-serif</family></accept>
 73     </alias>
 74     <alias>
 75         <family>Arial</family>
 76         <accept><family>sans-serif</family></accept>
 77     </alias>
 78     <alias>
 79         <family>Helvetica</family>
 80         <accept><family>sans-serif</family></accept>
 81     </alias>
 82     <alias>
 83         <family>Verdana</family>
 84         <accept><family>sans-serif</family></accept>
 85     </alias>
 86     <alias>
 87         <family>Albany AMT</family>
 88         <accept><family>sans-serif</family></accept>
 89     </alias>
 90     <alias>
 91         <family>Albany</family>
 92         <accept><family>sans-serif</family></accept>
 93     </alias>
 94     <alias>
 95         <family>Nimbus Sans L</family>
 96         <accept><family>sans-serif</family></accept>
 97     </alias>
 98     <alias>
 99         <family>Luxi Sans</family>
100         <accept><family>sans-serif</family></accept>
101     </alias>
102     <alias>
103         <family>Tahoma</family>
104         <accept><family>sans-serif</family></accept>
105     </alias>
106 <!--
107   Monospace faces
108  -->
109      <alias>
110         <family>Bitstream Vera Sans Mono</family>
111         <accept><family>monospace</family></accept>
112     </alias>
113     <alias>
114         <family>DejaVu Sans Mono</family>
115         <accept><family>monospace</family></accept>
116     </alias>
117     <alias>
118         <family>Liberation Mono</family>
119         <accept><family>monospace</family></accept>
120     </alias>
121     <alias>
122         <family>Inconsolata</family>
123         <accept><family>monospace</family></accept>
124     </alias>
125     <alias>
126         <family>Courier New</family>
127         <accept><family>monospace</family></accept>
128     </alias>
129     <alias>
130         <family>Courier</family>
131         <accept><family>monospace</family></accept>
132     </alias>
133     <alias>
134         <family>Andale Mono</family>
135         <accept><family>monospace</family></accept>
136     </alias>
137     <alias>
138         <family>Luxi Mono</family>
139         <accept><family>monospace</family></accept>
140     </alias>
141     <alias>
142         <family>Cumberland AMT</family>
143         <accept><family>monospace</family></accept>
144     </alias>
145     <alias>
146         <family>Cumberland</family>
147         <accept><family>monospace</family></accept>
148     </alias>
149     <alias>
150         <family>Nimbus Mono L</family>
151         <accept><family>monospace</family></accept>
152     </alias>
153 <!--
154   Fantasy faces
155  -->
156      <alias>
157         <family>Impact</family>
158         <accept><family>fantasy</family></accept>
159     </alias>
160     <alias>
161         <family>Copperplate Gothic Std</family>
162         <accept><family>fantasy</family></accept>
163     </alias>
164     <alias>
165         <family>Cooper Std</family>
166         <accept><family>fantasy</family></accept>
167     </alias>
168     <alias>
169         <family>Bauhaus Std</family>
170         <accept><family>fantasy</family></accept>
171     </alias>
172 <!--
173   Cursive faces
174   -->
175     <alias>
176         <family>ITC Zapf Chancery Std</family>
177         <accept><family>cursive</family></accept>
178     </alias>
179     <alias>
180         <family>Zapfino</family>
181         <accept><family>cursive</family></accept>
182     </alias>
183     <alias>
184         <family>Comic Sans MS</family>
185         <accept><family>cursive</family></accept>
186     </alias>
187 
188 </fontconfig>
~/fonts.conf.d/02-classify-english-fonts.conf

第三步:所有不认识的英文字体都分类为 sans-serif

世界上字体那么多,不是每一个都想得到的啦。对于所有不认识的字体,都认为它是 sans-serif。其代码如下:

 1 <?xml version="1.0"?>
 2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
 3 <fontconfig>
 4 <!--
 5   If the font still has no generic name, add sans-serif
 6  -->
 7     <match target="pattern">
 8         <test qual="all" name="family" compare="not_eq">
 9             <string>sans-serif</string>
10         </test>
11         <test qual="all" name="family" compare="not_eq">
12             <string>serif</string>
13         </test>
14         <test qual="all" name="family" compare="not_eq">
15             <string>monospace</string>
16         </test>
17         <test qual="all" name="family" compare="not_eq">
18             <string>宋体</string>
19         </test><test qual="all" name="family" compare="not_eq">
20             <string>黑体</string>
21         </test>
22         <edit name="family" mode="append_last">
23             <string>sans-serif</string>
24         </edit>
25     </match>
26 </fontconfig>
~/fonts.conf.d/03-treat-unclassified-fonts-as-sansserif.conf

到这里,所有的分类工作完成。使用 fc-pattern 命令可以看到字体名被替换的过程。如下图:

比如,当应用程序请求字体的 pattern 为“Time New Roman”时,pattern 被更改为“Times New Roman” “serif”;当应用程序请求字体的 pattern 为“微软雅黑,不认识的字体”时,“sans-serif”被添加到了 pattern 的最后面。这也是字体分类的原理,就是将字体的类名添加到列表的后面。在 Fontconfig 的配置语法中,<alias> 是 <match> 的一种简化写法,如果需要测试和替换的只是字体名的话,使用 <alias> 就更简单。如果使用 <alias>,则 <prefer> 表示添加到所选字体的前面,<accept> 表示添加到所选字体的后面,<default> 表示添加到字体列表的最后。在这里选择用 <accept> 而没有用 <default> 的原因是,如果用 <default>,每次都是把分类名添加到最后,遇到比较奇怪的pattern(比如上图中的最后一个测试)就会造成混乱。

第四步:对每一个字体分类,指定自己喜欢的字体

对每一个分类都可以指定一个字体列表,排在越前面的优先级越高。在每一个分类的最后指定一个中文字体的分类,当句子中出现中文时,前面的英文字体中肯定找不到相应的字符啦,这时就会一个一个往字体列表的后面找,直到在最后的中文字体里找到。代码如下:

 1 <?xml version="1.0"?>
 2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
 3 <fontconfig>
 4     <alias binding="strong">
 5         <family>serif</family>
 6         <prefer>
 7             <family>Georgia</family>
 8             <family>Times New Roman</family>
 9             <family>Bitstream Vera Serif</family>
10             <family>DejaVu Serif</family>
11             <family>Thorndale AMT</family>
12             <family>Luxi Serif</family>
13             <family>Nimbus Roman No9 L</family>
14             <family>Times</family>
15             <family>宋体</family>
16         </prefer>
17     </alias>
18     <alias binding="strong">
19         <family>sans-serif</family>
20         <prefer>
21             <family>Arial</family>
22             <family>Verdana</family>
23             <family>Bitstream Vera Sans</family>
24             <family>DejaVu Sans</family>
25             <family>Albany AMT</family>
26             <family>Luxi Sans</family>
27             <family>Nimbus Sans L</family>
28             <family>Helvetica</family>
29             <family>Lucida Sans Unicode</family>
30             <family>BPG Glaho International</family> 
31             <family>Tahoma</family> 
32             <family>黑体</family>
33         </prefer>
34     </alias>
35     <alias binding="strong">
36         <family>monospace</family>
37         <prefer>
38             <family>DejaVu Sans Mono</family>
39             <family>Courier New</family>
40             <family>Bitstream Vera Sans Mono</family>
41             <family>Inconsolata</family>
42             <family>Andale Mono</family>
43             <family>Cumberland AMT</family>
44             <family>Luxi Mono</family>
45             <family>Nimbus Mono L</family>
46             <family>Courier</family>
47             <family>文泉驿等宽正黑</family>
48         </prefer>
49     </alias>
50     <alias binding="strong">
51         <family>宋体</family>
52         <prefer>
53             <family>AR PL UMing CN</family>
54             <family>AR PL UMing HK</family>
55             <family>AR PL UMing TW</family>
56             <family>AR PL UMing TW MBE</family>
57         </prefer>
58     </alias>
59     <alias binding="strong">
60         <family>黑体</family>
61         <prefer>
62             <family>文泉驿正黑</family>
63         </prefer>
64     </alias>
65 <!--
66   Fantasy faces
67  -->
68      <alias>
69         <family>fantasy</family>
70         <prefer>
71             <family>Impact</family>
72             <family>Copperplate Gothic Std</family>
73             <family>Cooper Std</family>
74             <family>Bauhaus Std</family>
75         </prefer>
76     </alias>
77 <!--
78   Cursive faces
79   -->
80     <alias>
81         <family>cursive</family>
82         <prefer>
83             <family>Comic Sans MS</family>
84             <family>ITC Zapf Chancery Std</family>
85             <family>Zapfino</family>
86         </prefer>
87     </alias>
88 
89 </fontconfig>
~/fonts.conf.d/04-prefer-fonts-for-each-class.conf

在这个配置中,依然使用 <alias>,通过把具体的字体名添加到类名的前面来填实字体分类。填实字体分类后,通过 fc-pattern 命令,可以测试 Fontconfig 如何选择字体,如下图:

到这一步,我的 Fedora 系统已经能够正确选择各种分类中的字体了。打开浏览器,用上一篇的开头测试一下字体的显示效果,如下图:

放大后的效果:

从图片中可以看出,经过前面的配置,浏览器已经可以正确区分宋体、黑体,可以区分 serif 和 sans-serif。对于等宽字体,也用上了漂亮的 DejaVu Sans Mono。而且宋体的小字还有点阵。看似比较完美了,但是依然有几个小问题:

1、宋体的小字有点阵,但 AR PL UMing 自带的点阵真心不好看;

2、黑体的小字没有点阵,虽然黑体比较饱满,没有点阵也不会发虚,但是有点阵还是更好,而且应用程序菜单中的中文也从点阵变成了不是点阵;

3、宋体的加粗部分没有正确显示。

所以,还需要进一步的设置工作。

第五步:控制字体的显示属性

上一篇已经论述过,要让字体显示得好看也是一个技术活。所以对字体的显示属性要详细控制啦。Gnome-tweak-tool 只能整体指定少量几个属性,如 hintstyle 和次像素平滑,很显然是不够用的。如下图:

而且我的系统中用来画字的 dpi 都不对,只有 75,如下图(另一个测试字体配置的利器 fc-match 的运行效果):

这样字体画出来肯定偏小。事实上我的显示器 dpi 比 96 都要高 20% 左右。所以得改。为了跟随主流,还是改成 96dpi 好了。

我控制字体属性的思路:

1、著名厂家设计的字体,开 hinting,开源的粗制滥造的字体就用 autohint 好了;目前系统中用的字体似乎名头都很响,所以全局开 hinting,hintstyle 设为 hintslight。

2、英文字体全局开次像素平滑,全局开抗锯齿,如果在使用过程中发现有哪个字体边缘显示为花花绿绿的话,就单独关闭这个字体的次像素平滑。

3、小于 7.5 像素的字就关闭 hinting,这也是目前开源界的主流做法。

4、中文关闭次像素平滑,除非用的是微软雅黑之类专为次像素平滑设计的字体;很显然不在正版 Windows 中用微软雅黑是违法的,所以中文关闭次像素平滑。

代码如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
 3 <fontconfig>
 4   <match target="pattern">
 5     <edit name="dpi" mode="assign">
 6       <double>96</double>
 7     </edit>
 8   </match>
 9   <match target="font">
10     <edit name="hinting" mode="assign">
11       <bool>true</bool>
12     </edit>
13   </match>
14   <match target="font">
15     <edit name="autohint" mode="assign">
16       <bool>false</bool>
17     </edit>
18   </match>
19   <match target="font">
20     <edit name="hintstyle" mode="assign">
21       <const>hintslight</const>
22     </edit>
23   </match>
24   <match target="font">
25     <edit name="antialias" mode="assign">
26       <bool>true</bool>
27     </edit>
28   </match>
29   <match target="font">
30     <edit name="rgba" mode="assign">
31       <const>rgb</const>
32     </edit>
33   </match>
34   <match target="font">
35     <edit name="lcdfilter" mode="assign">
36       <const>lcdlight</const>
37     </edit>
38   </match>
39 
40   <match target="font">
41     <test compare="less" name="pixelsize">
42       <double>7.5</double>
43     </test>
44     <edit name="hinting">
45       <bool>false</bool>
46     </edit>
47   </match>
48   <match target="font">
49       <test name="family" compare="contains"> 
50       <string>AR PL UMing</string>
51     </test>
52     <edit name="rgba">
53       <const>none</const>
54     </edit>
55   </match>
56   <match target="font">
57     <test name="family" compare="contains">
58       <string>正黑</string>
59     </test>
60     <edit name="rgba">
61       <const>none</const>
62     </edit>
63   </match>
64 </fontconfig>
~/fonts.conf.d/05-dpi-and-hinting.conf

再来测试一下,如下图:

第六步:为中文选择点阵字体

上一篇中论述过,小字不用点阵,看起来会发虚。所以中文应该选择点阵。文泉驿点阵正黑不错,系统本身就有安装。文泉驿点阵宋体需要另外安装,如下图:

代码如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
 3 <fontconfig>
 4     <match target="pattern">
 5         <test name="family" compare="contains">
 6             <string>AR PL UMing</string>
 7         </test>
 8         <edit name="family" mode="prepend" binding="strong">
 9             <string>WenQuanYi Bitmap Song</string>
10         </edit>
11     </match>
12     <match target="pattern">
13         <test name="family">
14             <string>文泉驿正黑</string>
15         </test>
16         <edit name="family" mode="prepend" binding="strong">
17             <string>文泉驿点阵正黑</string>
18         </edit>
19     </match>
20     <match target="pattern">
21         <test name="family">
22             <string>文泉驿等宽正黑</string>
23         </test>
24         <edit name="family" mode="prepend">
25             <string>文泉驿点阵正黑</string>
26         </edit>
27     </match>
28   <match target="font">
29     <test name="family"> 
30       <string>文泉驿点阵正黑</string>
31     </test>
32     <test compare="less_eq" name="pixelsize">
33       <double>16</double>
34     </test>
35     <test compare="more_eq" name="pixelsize">
36       <double>12</double>
37     </test>
38     <edit name="antialias">
39         <bool>false</bool>
40     </edit>
41   </match>
42 </fontconfig>
~/fonts.conf.d/06-chinese-bitmap.conf

以上代码通过将文泉驿点阵宋体添加到 AR PL UMing 的前面来实现替换掉 AR PL UMing 自己的点阵,通过将文泉驿点阵正黑添加到文泉驿正黑和文泉驿等宽正黑的前面来显示点阵正黑。文泉驿的点阵只包含 12px 到 16px 的范围,对该范围大小的文泉驿正黑关闭抗锯齿。把控制台字体调到 monospace-12,可以看到控制台中的中文使用点阵,如下图:

写到这里,我不得不提到另外一个问题,就是 <edit...binding="?">...</edit> 中 binding 究竟应该设置为什么的问题。binding 属性有几种选择,分别是 binding="strong"、binding="weak" 和 binding="same"。如果该属性设置不合理,则会出现这样的问题:明明使用 fc-pattern 查看的时候是文泉驿点阵宋体排在 AR PL UMing 前面,是 DejaVu Sans Mono 排在文泉驿等宽正黑前面,但是使用 fc-match 匹配的时候,偏偏首选的是 AR PL UMing 和文泉驿等宽正黑。为什么会这样呢?这是因为 Fontconfig 选择字体的时候不仅仅只看字体名的排列顺序,还要综合考虑一种字体能覆盖的字符集和字体大小范围,所以,在前面的例子中,虽然文泉驿点阵宋体排在 AR PL UMing 前面,但是它覆盖的字体大小范围只有 12px 到 16px,DejaVu Sans Mono 虽然排在文泉驿等宽正黑前面,但是其覆盖的字符集不包含中日韩文,所以 Fontconfig 就会首选排在后面的字体。为了解决这个问题,就必须把binding 属性设置为 strong,这样,不管什么情况都首选排在前面的字体,只有遇到该字体不能胜任的字符时才选择后面的字体。在我的配置文件中,我都是一路 strong 到底。

第七步:对没有包含斜体和粗体的字体自动合成斜体和粗体

代码如下:

 1 <?xml version="1.0"?>
 2 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
 3 <fontconfig>
 4 <!-- 
 5  Artificial oblique for fonts without an italic or oblique version
 6  -->
 7  
 8     <match target="font">
 9         <!-- check to see if the font is roman -->
10         <test name="slant">
11             <const>roman</const>
12         </test>
13         <!-- check to see if the pattern requested non-roman -->
14         <test target="pattern" name="slant" compare="not_eq">
15             <const>roman</const>
16         </test>
17         <!-- multiply the matrix to slant the font -->
18         <edit name="matrix" mode="assign">
19             <times>
20                 <name>matrix</name>
21                 <matrix><double>1</double><double>0.2</double>
22                     <double>0</double><double>1</double>
23                 </matrix>
24             </times>
25         </edit>
26         <!-- pretend the font is oblique now -->
27         <edit name="slant" mode="assign">
28             <const>oblique</const>
29         </edit>
30         <!-- and disable embedded bitmaps for artificial oblique -->
31         <edit name="embeddedbitmap" mode="assign">
32             <bool>false</bool>
33         </edit>
34     </match>
35 
36 <!--
37  Synthetic emboldening for fonts that do not have bold face available
38  -->
39 
40     <match target="font">
41         <!-- check to see if the font is just regular -->
42         <test name="weight" compare="less_eq">
43             <const>medium</const>
44         </test>
45         <!-- check to see if the pattern requests bold -->
46         <test target="pattern" name="weight" compare="more">
47             <const>medium</const>
48         </test>
49         <!--
50           set the embolden flag
51           needed for applications using cairo, e.g. gucharmap, gedit, ...
52         -->
53         <edit name="embolden" mode="assign">
54             <bool>true</bool>
55         </edit>
56         <!--
57          set weight to bold
58          needed for applications using Xft directly, e.g. Firefox, ...
59         -->
60         <edit name="weight" mode="assign">
61             <const>bold</const>
62         </edit>
63     </match>
64 </fontconfig>
~/fonts.conf.d/07-synthetic.conf

从这段代码可以看出,合成斜体的时候使用了矩阵乘法。另一个在 Fontconfig 配置文件中使用矩阵乘法的例子是 /etc/fonts/conf.d 中的第 1 个配置文件 10-scale-bitmap-fonts.conf,它使用矩阵乘法对点阵字体进行缩放,而且如果缩放因子在 0.8 到 1.2 之间则不缩放。阅读系统的配置文件是一个学习的好方法,在我的系统中,该文件就仅供学习了,因为我不需要对点阵字体进行缩放。

最终效果如下图:

放大后:

结果:小字时,宋体黑体都有点阵,大字时宋体黑体分别用不同的轮廓字体,能正确区分 serif 和 sans-serif,加粗的宋体、等宽字体也能正确显示,应用程序菜单中的字体也恢复到了点阵。

总结

展示了 Fedora 20 配置字体的全过程。虽然没有详细讲解 Fontconfig 的语法,但是通过代码、截图和 fc-pattern 以及 fc-match 命令对字体进行测试,让 Fontconfig 的配置不言自明。当然,每个人对字体的感受是不一样的,但是只要掌握了 Fontconfig 的配置方法,适当修改以迎合自己的需求,也是一件简单的事情。也可以将Windows 中的字体借过来使用,微软还是一个很靠谱的公司,它的那几个经典的英文字体是开放的,前面有一个截图中展示有 Fedora 的源中有 mscore-fonts 软件包,直接安装就可以。

 

(京山游侠于2014-06-20发布于博客园,转载请注明出处。)