Jackey's 感悟

Do Research

Monthly Archives: 二月 2009

>Bible – Love

>爱是持久忍耐,又有恩慈;爱是不嫉妒,爱是不自夸,不张狂,不做害羞的事,不求自己的益处,不轻易发怒,不计算人的恶,不喜欢不义,只喜欢真理,凡事包容,凡事相信,凡事盼望,凡事忍耐。如今长存的有信,有望,有爱,这三样,其中最大的是爱……。

>山寨呼吸机

>'山寨呼吸机'的发明是最心酸最无奈的发明,一些卫生工作者应该为此感到耻辱!

>TeX中文字体问题根本原因深究

>

下面是引自yulewang的个人空间的博文,讲述了TeX中文问题的深层次原因,由此能让用TeX写作的人,特别是想写中文的人有更多了解。

我不能肯定原文作者一定是对的,毕竟我没有办法验证其中的事情是否真实,但是我从心里感觉这是真实的,因此,我对国内软件开发人员的技术不够深刻、研究态度不够认真感到惭愧,有时候自己不也是这样,以此为鉴吧。

原文出处 http://yulewang.spaces.live.com/blog/cns!5C815C994ABB661E!262.entry

TeX相关软件中文字体嵌入一个存在已久的问题水落石出,兼谈不拘小节的中文字体设计
低于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作文那样简单。