TeX 排版中文字体嵌入问题,兼谈不拘小节的中文字体设计

Garfileo post @ 2008年07月11日 08:31PM in 文档 & 资源 with tags: tex 中文字体
 

本文转载自 yulewang 的博客,原文地址:http://yulewang.spaces.live.com/blog/cns!5C815C994ABB661E!262.entry。本文基本上没有改动,只是将文中出现的一个错字进行了修改。


低于 18 周岁的小朋友们请不要看此文,会被吓到的。如果你和中国的字体公司有联系,请把我所描述的问题转告给他们。

考完作文后,在上海小歇,除了吃喝玩乐加上逛街还有被导师虐着写文章,自己看两眼G词以外,就是在尝试解决中文 TeX 用户几年来一直抱怨的关于 TeX 产生的 pdf 文档中文字体看上去太细的问题。

这个问题的来龙去脉是这样的。很久以前,大家都需要用 dvips 或者 dvipdfm 来转换 TeX 产生的 dvi 文件为 pdf 文件,这个路径很麻烦,需要把一个中文 trueype 字体转为大约一百多个 Type1 字体,然后再把这些字体的 Subfont 给嵌入到文档中。南韩的一个数学家 Cho,因为不满意 TeX 的这种状况,因此写了一个 dvipdfm 程序的扩展,名为 dvipdfmx。dvipdfmx 可以直接嵌入 CID 的 TrueType 和 OpenType 字体,不需要把字体分割成许多个 subfont,产生的 pdf 文件小巧,而且比原先的方法在阅览器下渲染速度快,质量比较高。因此得到了中日韩用户的喜爱。但是许久以来,中文用户一直抱怨一个问题,就是貌似由 dvipdfmx 产生的文档,在 Adobe 公司官方的 Acrobat Reader 下,显得发虚,严重地影响阅读,而相同的字体,如果不用 dvipdfmx,比如使用 cairo、quartz 或者 word2007 进行转化,产生的 pdf 就没有这个问题。如果文章中都是中文,那只不过是难以阅读而已。而如果是中文和英文混排的,而英文部分又比较正常,比如使用了 Times 字体或者 Palatino 字体,则整个 type block 就显得斑驳,一眼看到的都是突出的英文,所以一直以来,我们都建议用户使用本来就细得要命的默认的 Computer Modern 字体,以使得文档不至于失调。

几年来,从来没有人认真地研究过这个问题,南韩的用户都觉得 dvipdfmx 产生的 pdf 质量不错,因为他们使用的随 KTUG 定制的发行版发布的字体本来就比较粗,而中国的用户如果使用 Windows 下的中易公司的宋体,即 simsun,则相当不能忍,方正公司的字体,比如书宋,则稍微好一点,但怎么也好不过 Adobe 公司的 OpenType 字体 Adobe Songti Std Light。若干年前,jjgod 同学写过一篇文章比较了几十个中文字体,他是使用了 dvipdfmx 输出的结果的视觉效果来评价字体的质量,结果一个相当好的字体,方正博雅宋,由于在阅览器中显示过细,被他认为不如方正书宋。而几年以后,这个问题被泛 化,当今,TeX 正在走向国际化,如今的 TeX 已经支持 Unicode 和各种高级的字体格式,也全面从 dvi 时代过渡到 pdf 时代,目前两大炒作得很热的 TeX 引擎,都和 dvipdfmx 扯上了关系。 XeTeX 直接使用了 dvipdfmx 的一个变种,xdvipdfmx 来产生 pdf,而 LuaTeX,则用 dvipdfmx 的代码替换掉部分产原先 pdfTeX 的代码,来产生 pdf。这就导致,目前所有的先进的 TeX 系统,在 CID 字体嵌入方面的代码,是近亲关系,所有产生的中文 pdf,都虚得离谱,LuaTeX 尤甚。

我在去年和今年,一直使用 LuaTeX 引擎和 ConTeXt 格式,写各种包括论文以内的文档,在使用的过程中,发现了不少问题,就开始和开发者交流。几个月来,报告了不少 bug,其中一些,还给出了补丁。此外,我和 ConTeXt 开发者 Hans 等 TeX 专家讨论,试图让最新的 ConTeXt 和 LuaTeX 来支持中文排版。其他事情,还包括为ConTeXt 的用户们提供 FreeBSD 操作系统的 TeX 二进制文件。因此,我和 LuaTeX 与 ConTeXt 的开发者(其实是同一拨人),有很频繁的联系,也彼此保持着不错的关系。开发者们也很勤快地修复着我汇报的各种 bug,这也使我能够很好地使用这些软件进行各种文档(学术论文、技术文档)的排版。今年考完作文后,我暂时可以小歇一下,同时由于 LuaTeX 的开发者们刚刚搞定了一项新功能,mplib,也有空余的时间。因此,我们就有时间来讨论和解决这个由来已久的问题。

我一开始写信给 LuaTeX 的开发者,Taco Hoekwater,之所以不写给 dvipdfmx 开发者 Cho 而发给他,因为我和他熟,而且 Taco 这两年来,勤勤恳恳地写代码,我比较相信他解决问题的效率。有时候一个 bug 提交给他他不到几十分钟就已经 fix 了。结果 Taco 看了好几天,一无所获,于是,Taco 帮助我把信转给了当今世界上几个重要的大牛,准备来专家会诊。这个会诊的医师阵容庞大,技术高超,随便举几个人:dvipdfmx 开发者 Cho,LuaTeX 开发者 Taco,外加 XeTeX 开发者 Jonathan Kew。不久以后,话说解铃还需系铃人,Cho 找到了一个可能的问题。他把 pdf 文件解开,仔细观察 cairo 输出了 LuaTeX 输出中字体嵌入部分的参数,发现某个数值 StemV,相差悬殊。使用编辑器修改解开的 pdf,将 StemV 调整到相同,两个 pdf 文件顿时就差不多了(虽然还有些许差别,但是这个就是最重要的因素之一)。

问题顿时有了突破性的进展,比较 LuaTeX 产生的 pdf 和 dvipdfmx 产生的 pdf,发现 LuaTeX 的 StemV 数值大得多,不久 Taco 就发现,这是 LuaTeX 的某个 bug 导致的。该 bug 当天就得到了修正,这样产生的 pdf 就和 dvipdfmx 一样了。但是修正以后事实上得到的 pdf 依然很细。cairo 产生的 pdf 文件,一律取成了相同的默认数值,所以看上去宋体表现还不错。而 dvipdfmx 的 TrueType 字体的 StemV 数值到底是怎么产生的呢?它经验性地依赖于一个拟合公式:

stemv = (os2->usWeightClass/65)*(os2->usWeightClass/65)+50

其中,os2->usWeightClass 是字体中 pfmtable 中的信息,是一个数值。这个数值在字体设计的时候就被定了下来,一般和字体的 weight 有关:比如 Light 就是 300,500 表示 Medium,而 800 则表示 Extra Bold。该数值决定了 StemV 的数值,也就是说,如果这个字体越粗,那么 StemV 数值就越大,在阅览器中渲染,就会越虚,合情合理。但是当我们打开中易公司的中文字体,方正公司的字体,还有华文字体,我们失望地发现,他们都取了同样的 数值:400。

于是这个问题,如果扯开拟合公式本来结果就偏大不说,其他的就应该怪罪到中文字体设计上来了。像 Simsun 字体,并不比 AdobeSongStd-Light 粗多少,甚至更细,取一个 400 的值本来就不合理。其次,中易字体不管黑体还是宋体,都取相同的数值,怎么都说不过去。相同的也发生在方正字体上,方正宋黑,方正书宋,小标宋,也都取相 同的数值。这个基本上是不可能让软件来自动判断的问题,本该是字体公司仔细勘酌的,现在却被信手赋值。按照现在的状况,软件不可能自动判断这个值,使得黑 体就是比宋体取值大。

解决这个问题,也只能让用户自己设定了,不久以后 LuaTeX 用户可以通过修改 Fonttable 来实现,dvipdfmx 开发者称,今后会在 map 文件中,让用户指定数值。 XeTeX 开发者估计可能会像先前指定伪粗,伪斜一样的语法来定义这个数值,不过目前没有收到任何他的计划。这个估计就是我们能采用的唯一不是办法的办法,不过终归 而言,这个问题被解决了,今后只要仔细调整参数,就能得到渲染效果得当的 pdf 文件。

类似的中文字体乱设参数的例子还有很多,此前 yindian 同学,提到了XeTeX 的一个 bug,导致没有办法产生正确的 pdf,后来发现这个根本不是 bug,完全也是由于字体设计公司乱设字体参数导致的。后来 jjgod 同学 hack 了一下 xdvipdfmx 总算差不多解决该问题。该问题的详细信息,请参考 XeTeX 的邮件列表,该主题内容在http://tug.org/pipermail/xetex/2007-October/007536.html,和后续的 讨论。

中文字体设计不拘小节也让我也想到了另一个问题,用先前,中文用户使用 XeTeX,需要频繁地切换中英文字体,后来 XeTeX 开发者不得不提供了一个机制来让字体切换变得不那么折腾。而我和 ConTeXt 开发者交流中文排版问题,还要煞费苦心地讲怎么切换,需要编程实现复杂的虚拟字体机制来实现。这个都归罪于中文字体普遍地缺乏高质量的英文部分,仔细看看 simsun 或者 simhei 的英文部分,就可以看出有多么夸张了。

如果说这个问题的原因是中国的字体公司,向来没有很好的英文字体设计基础,同时对这个问题也不加以重视,那么中文标点的设计,就没有丝毫的可以开罪 的地方了,这个问题直接导致用户和开发者都非常为难。我们知道,高质量的中文排版,标点并不是占据一个中文字符的位置,而要比中文字符略小。同时,标点之 间需要存在压缩,比如逗号后紧紧跟随的关门引号,需要使用类似 kerning 的特性把两个 glyph 的距离减小。另外,类似破折号和省略号,其实应该放在一个 glyph 中而不应该分开。而现在所有的中文字体的糟糕程度,竟然到所有的标点符号都占用一个中文字符距离的程度。本来这个问题如果中文字体设计得当,使用默认的排 版算法,就基本上能够解决一般的中文的排版问题,而现在糟糕的设计就使得排版软件的设计难上加难。首先我们需要重新定义一系列的新算法和新规则,然后需要 手工赋值去确定标点的大小和两个标点连在一起时候的压缩程度。更麻烦的是,不同字体中的相同的 glyph,比如逗号或者句号,往往会在这个 box 的不同的位置,大小也会千差万别。调好了中易宋体的冒号和开门引号,把相同的数值使用到中易的隶书中,顿时两个符号就会挤在一起,这就使得如果不针对每一 个字体仔细调整,高质量的中文排版就几乎不可能。我寒假和 ConTeXt 的开发者交流中文排版问题时,这个麻烦搞得头都大了,而这个问题本来就是该在字体公司设计字体时就解决的。

排版软件的开发,永远不是一个软件的事情,它牵扯到政府规范,字体设计,文档标准和字体标准的制定。往往如果排版软件不能做出令人满意的结果,很可 能是由于其他非排版软件的因素造成的。Adobe 或者 LinoType 大公司出品的英文字体,往往都会有较高的水准,正是因为设计者已经仔细调整好字体中的各项参数,使得用户使用排版软件默认的方案,就能够做出很好的作品, 偶尔遇到需要的 glyph 找不到,或者某个 kerning 长度不理想,打开 fontforge 之类的字体软件,也能方便快速地调校从而满足自己的需要。中文字体的设计,离开这个标准还很远很远,有很长一段路要走。

文章的最后,感谢参加专家会诊的几位 TeX 开发者,同时,也要感谢为我这次汇报问题提供大量帮助的同学,他们是 lyanry 同学,水木上的 yakun 同学,这两位同学帮助我完成了大部分的 bug 搜集和汇报工作,尤其是 lyanry 同学,帮助我生成了许多测试用的 pdf 文件,他们的技术工作,使得留给我做的仅仅是把这些资料写成英文然后和开发者交流,整个过程就像写 GRE 作文那样简单。