在 Linux 环境下使用中文的朋友恐怕都会遇到过这个问题。如何让 Linux 显示字体时更好 看呢?

可能会有这样的经验,即使 Ubuntu 使用英文版也不能很好的显示汉字。但是将 Ubuntu Desktop 的 /etc/fonts/conf.avail/65-language-selector-zh-cn.conf 启用,或者复 制到自己机器上,就好了。

这几天由于读了 wiki.installgentoo.com 上一些有关上网安全的文章后,又重新回到 Firefox 的怀抱,想比较而言 Chromium 的插件可就少很多,并且多半没 Firefox 的好用 呢。但是有一个非常困扰的情况,在 Linux 中, Chromium 即使遇到了网页指定字体不存在 情况,仍旧能很好的展示(不会使用 X11 默认的难看衬线字体)。而 Firefox 则会出现 字体显示大小不一,字体不一的情况。虽然可以使用 userstyles 插件解决,不过写全局或 单个域名总不是个解决办法。所以这次简单研究一下 fontconfig 吧。

freetype 与 fontconfig

虽然原理上可能相同(某没考察过),不过 Windows 的 GDI 也许和 fontconfig 一样,在 找不到字体的情况下会回到默认的字体上(还记得简体中文那万恶的宋体拉丁字体吗?)。

Linux (或其他 unix-like )系统下将字体文件渲染成点阵位图的主要是 freetype ,而对如何访问这些字体的配置则是通过名为 fontconfig 的配置系统来管理的。它的特点 是通过 XML 配置文件(以及指定的 DTD )来对字体的访问顺序、渲染效果进行控制。

我们来看看控制访问。操作系统中使用字体除了直接指定具体的字体名字外(例如: Microsoft YaHei )。一般都提供了这个几个别名,分别是:

  • serif ,衬线字
  • sans serif 或 sans-serif ,无衬线字
  • monospace ,等宽字

对什么是衬线字不清楚的,简单来说就是除了必要的笔画外还有一些修饰痕迹。详细了解可 看这里

他们都没有具体实际的字体,但都提供了选择。那么如果使用这些字体时,到底实际上使用 了哪个字体,以及如何修改呢?

对于 fontconfig 来说,提供了 fc-match 命令用来查询,例如在某的机器上。

$ fc-match monospace
Menlo.ttc: "Menlo" "Regular"

那么,到底是什么决定了 monospace 使用 Menlo 这个字体呢?

fontconfig 是如何工作的(简要版)

由于某没有读过源代码,所以只能通过使用的经验来解释一下。

自然, fontconfig 是需要通过配置文件来读取的,这些文件都是 XML ,并且语法含义由 DTD 定义(有兴趣的可以去翻一下 DTD 的内容 XD)。

主配置文件在 /etc/fonts/fonts.conf 下,一般会定义字体从哪些目录读取(包括允许 读取个人字体目录 ~/.fonts 等)。 然后会包含 /etc/fonts/conf.d/ 下的文件。而 一般来说,Linux 发行版默认会将其他配置文件放在 /etc/fonts/conf.avail 下会做软 链接,来达到方便地调整。

哦~原来配置文件都是这么来的呀~

再来,包括( include )是按照文件名顺序读取的,开头两位数字从小到大字体读取, 在后面的配置会覆盖前面的文件。所以我们会到看到以 10 、 20 开头的会定义一些通用的 抗狗牙、字体大小等配置。

针对前面提到的 serif 、 sans-serif 以及 monospace 字体别名而言。一般会写在 60-latin.conf 以及 65-nonlatin.conf 中。我们知道一般拉丁字母的字体比较小, 其中不包括那些非拉丁字符对应的字;字体系统也很聪明,如果按顺序读取字体没有该字符 ,则会继续寻找配置里的下一个字体文件中的字。

所以,我们在配置字体时,需要优先把拉丁字符字体放在支持非拉丁字符的字体前面

某在查看了 Gentoo 默认的 65-nonlatin.conf 后发现,中文字体前写的尽是一些日语、 韩语的字体。难怪啦!日语字体里包含一部分汉字,但是又不够多,如果遇见日语字体里不 包含的字,那么就按顺序继续去中文字体里寻找了,所以才会出现同一个页面里,字体不 一致,大小不一的情况

自定义 fontconfig

既然知道了原因,就可以动手开始改了。

但是,好像直接动那个系统配置不太合适吧?没错, fontconfig 为我们提供了两种方 法;一是修改系统级别的自定义文件,/etc/fonts/local.conf 。另外也可以在用户级别 的 ~/.fonts.conf ,具体就根据喜好啦。

我们拿 monospace 这个别名来举例子。原来在 GitLab 等不太关注汉语字体会出现 textarea 与 pre 等元素中等宽字体出现问题。

我们来进行改造,语法内容大致如下,另外两个也可仿照。

<fontconfig>
 <!-- ... -->
  <alias>                        <!-- 注意都是一个别名 -->
    <family>monospace</family>   <!-- 起个名字 -->
      <prefer>                   <!-- 这里定义字体访问的偏好顺序,靠前在上 -->
        <family>Menlo</family>
        <family>WenQuanYi Micro Hei Mono</family>
        </prefer>
      <default><family>DejaVu Sans Mono</family></default> <!-- 这里定义如果全都没找到的情况下默认字体 -->
    </alias>
 <!-- ... -->
</fontconfig>

CJK 混合用户的痛楚

一般来说一个用户在拉丁字符以外,一般只会使用一类 CJK 语言(或字体)。但是遇到像 某这样同时需要兼顾日语以及汉语的就比较麻烦啦。

Linux GUI 程序访问字体本身似乎是没有针对语种进行区分的,导致访问字体只能按照固 定顺序。比如某把文泉驿微米黑放前面的话,由于支持的字符集非常大,日语假名和特有 汉字也会优先使用文泉驿字体,然而有时这个字体不够好看。但是又不能将日语字体放在 汉语前面,因为日语字体里是包含一些汉语字的 orz 。

折中的解决方案是选用一种两种语言均有字符且较为好看的。目前某使用的是 Google 前阵 子刚刚推出的 Noto Sans S Chinese 字体。显示日语汉字与假名效果还能接受,中文 字也 算好看。

不过,总是会有搞不定的时候啦。 XD

Bonus Time

值得一提的是,网页中 html 元素的 lang 做了定义,表示该元素的内容语言。 并且 Firefox 会根据实际情况选用字体。 不过目前发现字体只是对 html 元素生效 ,对页面内的其他元素似乎没用?有待进一步验证。

freetype 补丁

当然,除了使用已经提供的配置来修改渲染效果外,还有直接对 freetype 进行修改的项目 ;自然,这需要比较新版本的 freetype 以及补丁。在 ArchLinux 下可以通过 AUR 中 的 freetype2-infinality 或 freetype2-ubuntu 包来解决;前一个为打过 infinality 补丁集的版本,旨在提供更好的渲染方式以及模拟其他操作系统的显示 效果(例如 MacOSX 与 Windows );后一个则是 Ubuntu 项目中打的补丁版本。在 Gentoo 下,对 media-libs/freetype 打开 infinality USE 即可。

然后针对 /etc/fonts/infinality 里的配置进行调整。 Gentoo 下可以方便的使用

$ eselect lcdfilter

以及

$ eselect infinality

进行修改。

效果与后话

可以在某的配置文件仓库里看到现在正在使用的配置。

这里贴几张目前的效果,还算满意。

汉语

来自维基百科条目

日语

来自维基百科条目

英语

来自维基百科条目

本篇只是浅显地介绍了一下某对 fontconfig 的工作原理的理解,没有读过源代码,不敢说 一定准确。其他有关抗狗牙等渲染上的配置也还没来得及研究。欢迎各位巨巨指教。

以后再补充其他细节吧 XD

__END__