XeTeX 的一个中文处理脚本

其实这个东西早就该写了,只不过我既是一个懒人,也是一个看不惯不完美东西的人,所以一直不好意思去写。

For the impatient: [脚本在这里](http://www.newsmth.net/att.php?s.460.239183.807.py)

[XeTeX](http://scripts.sil.org/xetex) 是 Johnathan Kew 一开始在 Mac OS X 上开发的一套 TeX 系统的扩展,主要的特点是使用 ATSUI 支持了操作系统 native 的字体,而后使用 ICU 加入了对 complex scripts 等多语言排版的直接支持,算是 TeX 世界中很振奋人心的更新,我也一直在关注着,因为没有 Mac 用,所以不大了解详情。

直到最近 Johnathan 公布了 Linux 系统下的 XeTeX,结合 fontconfig 和 freetype、ICU 一起支持了 Linux 系统下的 native fonts 和多语言排版,并在 Jin Wan Cho (dvipdfmx 的作者之一) 的帮助下扩展了 dvipdfmx,支持 xetex 的“.xdv”输出。所以近期在我的 Arch Linux 上,好好把玩了一阵子 XeTeX (中间的经历,在 [newsmth](http://www.newsmth.net/) 的 [TeX](http://www.newsmth.net/bbsdoc.php?board=TeX) 版和朋友们提过),虽然发现有诸多不便,但仍然觉得这是一个很值得发展的东西。

最大的不便在于 mixed scripts typesetting,即所谓多种语言混杂的排版,因为目前 XeTeX (其实 TeX 也是,只不过无论 [CJK](http://cjk.ffii.org/) 还是 [CCT](ftp://ftp.cc.ac.cn/pub/cct/) 都用一种比较特别的方法绕过了这个问题,此处按下不表) 无论是哪一段文字,都只有唯一的一种字体,你无法给它指定一个复合字体 (combined font),而复合字体的概念在 DTP 软件中是很常见的 (比如 FrameMaker, InDesign, Scribus 都支持),这就造成了混杂排版的困难:每当语言变化了,就必须手动切换字体,比如用 XeLaTeX,通常使用的是 Will Robertson 写的 fontspec 来选择字体,设置正文的字体用:

\setromanfont[字体特性]{字体名称}

上面这里推荐使用汉字的字体,才能比较好的处理中文的断行 (考虑标点符号的避头尾)。

\XeTeXlinebreaklocale=”zh”
\XeTeXlinebreakskip=0pt plus 1pt

然后定义一个英文字体,比如:

\newfontinstance\rmfont{Garamond Premr Pro}
\newcommand{\nc}[1]{{\rmfont #1}}

这样在文档中写的时候是:

中文啦啦啦 \nc{blahblahblah} 中文啦啦啦 \nc{blahblahblah}

一行字还不觉得,英文多了就会很麻烦。目前在 XeTeX 的 mail list 上,已经多次有人 (包括我 ^^|) 提到了这个问题,其实反复思考,这真的不是一个很容易处理的问题,尤其是不仅仅考虑中文和英文两种语言,而是数十种相互字符集范围有交叠的语言时,设计就更费斟酌了,Johnathan 表示他在关注这个问题,但还没有到集中力量解决它的时候,所以我才在这里贴这么一个很 dirty 的 script,其实发现这个问题以后,我反复想了几天,即分析了 XeTeX 的源代码,也去信和 CCT 的作者张林波老师讨论了,最后才决定采用预处理这个方法,觉得这是目前最不伤筋动骨,又能迅速解决问题的方法。

这个脚本就是用来处理中英文混杂的排版,在英文前后自动插入 \nc{} 用于切换字体,你可以在 preamble 定义 \nc 代表的字体。处理时,最 tricky 的部分是中文的标点,其实这一部分在 Unicode 中的代码分布是 [Fullwidth Latin Characters](http://www.unicode.org/charts/PDF/UFF00.pdf) 部分,根本不属于汉字部分。

这是用 Python 写的一个很简单的小工具,从 `\begin{document}` 开始处理,到 `\end{document}` 结束,目前还*不支持*任何命令,所以在正文部分如果有 `\title{中文标题}` 这样的东西,别指望它能正确处理。

此外还有一个功能是在两行连续的中文之间插入 % 以保证源文件中的手动换行不会导致输出结果中出现空白。

这还是一个非常非常 experimental 的东西。欢迎测试,我希望有时间认真写一个 tokenizer 作这个处理,现在的处理是基本上不依赖上下文的,所以能力很弱。另外,还可能在以后加上标点符号的 kerning 处理,类似 CJKpunct 的功能。

当然,我很希望有更多中文的 XeTeX 爱好者和我一同来完成这个工作,:)

FreeType 2.2.1 的一点测试

最近 [FreeType 2.2.1](http://download.savannah.gnu.org/releases/freetype/)
刚刚发布,尽管由于 API 的变化 (许多内部函数被隐藏起来了),多数的 distro
并未把系统使用的 FreeType 升级到这一版本——我相信要过很长一段时间才有可能这
么做。但使用源代码编译的版本,我做了一点小小的对比分析。

因为精力有限,只对 FreeType 2.1.10 (目前绝大多数发行版正在使用的版本)
进行了比较,试图给出基于 FreeType 的程序 (如 fontconfig) 应当如何配置字体
才能获得最好的效果的一点建议。而事实上,font rasterizer (字体光栅化工具)
必须和其他的 rasterizer 比较才能看出明显的区别来,例如 ATSUI, Win32 GDI
等等。

同时,我也只对随 Windows 附的 simsun.ttc 做了测试。如果你有兴趣,欢迎分享
更多的测试结果。

测试的截图在 [flickr](http://www.flickr.com/photos/jjgod/tags/freetype/) 上。

经过比较,我们可以给出这样一些建议:

1. 必须打开 anti-alias,gamma 才会起作用。
2. gamma = 0.0 时启用 sRGB (次像素反锯齿) 模式,这种模式要比默认的
gamma = 1.0 显示得更清晰锐利一些。
3. 绝大多数情况下,打开 hinting 能达到更好的效果。
4. 小于 12px 的情况下,关闭 anti-alias,打开 hinting 能使字体勉强可读,
但仍然建议绝对不要使用这么小的字体。
5. 12px 以上,即 12-16px, 18px 的汉字必须使用嵌入的 bitmap。事实上,AA +
hinting 的汉字要到 28px 以上才可以称为比较可读。

来写 ikiwiki 的试用报告

大家都知道,无聊的人特点之一是见到好玩的东西就找来玩一玩,[ikiwiki](http://ikiwiki.kitenet.net) 就是新近发现的有趣东西,把我的兴趣:[Subversion](http://subversion.tigris.org), [Markdown](http://daringfireball.net/projects/markdown), wiki 的概念整合到了一起,算是 wiki 程序里边思路比较另类的,本来看到要装一堆 Perl Module,不愿意去折腾,但 [hlb](http://hlb.yichi.org/blog) 说等我的试用报告 (笑),就试着装了装看,发现真的很好玩,值得推荐,所以把装的时候遇到的问题流水帐地记下来,或许对你有用。

说了这么多,那 ikiwiki 究竟是什么呢?它其实是一套 Perl 程序,虽然是用作 wiki,但和一般的 CGI/mod_perl 的 wiki 程序不一样的是,它把所有的 wiki 数据文件用 Subversion 来管理,这样文件的历史记录、备份和并行控制就可以直接把 Subversion 的功能搬来用,这是 ikiwiki 最大的特点。

此外,ikiwiki 有趣的地方还在于它使用了 Markdown 的语法来作标记,相比常用的 [wikitext](http://en.wikipedia.org/wiki/Wikitext),我觉得 Markdown 实在是人性化得多的标记方式。在 Markdown 的基础上,ikiwiki 做的两个额外的处理就是 wiki 的链接 (CamelCase 或者手动指定) 和[子页面](http://ikiwiki.kitenet.net/subpage.html)的创建。

ikiwiki 还有些好玩的功能,比如当作 [blog](http://ikiwiki.kitenet.net/blog.html) 来用、支持[反向链接](http://ikiwiki.kitenet.net/backlinks.html) 等等,你可以在它的 [features](http://ikiwiki.kitenet.net/features.html) 页面中看到,不过我这里是还没有尝试过啦。

哦,顺便提一句,ikiwiki 的作者 [Joey Hess](http://kitenet.net/~joey/) 是一位 [Debian](http://www.debian.org) Hacker,也是 [alien](http://kitenet.net/~joey/code/alien.html), [debconf](http://kitenet.net/~joey/code/debconf.html) 的作者。

下面开始讲实际的东西。

[安装](http://ikiwiki.kitenet.net/install.html)
—-

我测试的环境是 Arch Linux 2.6.16, Perl 5.8.8, lighttpd 1.4.11, subversion 1.3.1。

首先,ikiwiki 需要以下这些 Perl Module:

1. CGI::Session
2. CGI::FormBuilder
3. HTML::Template
4. Mail::Sendmail
5. Time::Duration
6. Date::Parse
7. HTML::Scrubber

我的经验是,还需要 HTML::Parser,此外,Markdown 可以使用 Text::Markdown 这个 Module。

用 cpan 装完以后,就可以[下载](http://ikiwiki.kitenet.net/download.html)一份 tarball,解压。此时,因为我们使用的是 Text::Markdown 提供的 markdown,所以 Ikiwiki/Render.pm 需要作一点小的修改,找到 Markdown::Markdown 那一行,改为:

use Text::Markdown ‘markdown’;
$content = markdown($content);

然后执行

perl Makefile.PL
make
make install

就完成的安装。现在可以试试 ikiwiki 命令有没有反应了。

配置
—-

接下来是[配置](http://ikiwiki.kitenet.net/setup.html)过程,视你希望实现什么功能而有不同的繁琐程度。

第一步是 (先装好 Subversion) 创建给 wiki 使用的版本仓库:

svnadmin create /svn/wikirepo
svn mkdir file:///svn/wikirepo/trunk -m create

然后从这个仓库中 checkout 一份当前的版本作为 working copy,放在自己的 home directory 下边:

svn co file:///svn/wikirepo/trunk ~/wikiwc

这样你就可以用默认的模板和配置构建你的第一个 ikiwiki 了:

ikiwiki –verbose ~/wikiwc/ ~/public_html/wiki/ \
–url=http://host/~you/wiki/

`~/public_html/wiki/` 是你的 Web server 分配给你的 HTML 文档目录。完成上边这一步之后,你就可以在这个目录中找到生成的 `*.html` 文件 (别忘了 ikiwiki 是一个“wiki compiler”),如果配置好了 web server 的 user directory 功能,就可以在 http://localhost/~username/wiki 看到你的 wiki 了。

这里简单说说 lighttpd 的这个配置,默认的 lighttpd 配置文件在 /etc/lighttpd/lighttpd.conf,首先,在 `server.modules` 变量中打开 `”mod_userdir”` 模块,然后设置 `userdir.path`,以上面的情况为例,就应该是:

userdir.path = “public_html”

当然,输出的目录也不一定要是你的 home directory,只要你的 web server 和你都能访问到的地方都可以啦。

接下来就可以做真正有趣的定制工作了,默认用来创建 wiki 的文件在 `/usr/share/ikiwiki/basewiki/`,你可以从里面复制一份到 `~/wikiwc/` 作为自己的版本,比如:

cp /usr/share/ikiwiki/basewiki/index.mdwn ~/wikiwc # 复制首页 (Markdown 标记的)
svn add ~/wikiwc/index.mdwn # 加入 svn
$EDITOR ~/wikiwc/index.mdwn # 修改首页文件内容
svn commit ~/wikiwc/index.mdwn -m customised # commit 到 svn repo 中

你发现 Subversion 在这里派上用场了,凡是编辑 wiki 文件其实都在 Subversion 中维护着,这样 Subversion 就能够跟踪每步更改了。

可是我们现在的方式还很原始耶,不能在浏览器中直接编辑 (也许某些人就喜欢这样),每次要打长长的一串命令,于是,一个叫做“配置文件”的东西出来拯救我们了。

先把 ikiwiki 源文件目录下的 doc/ikiwiki.setup 复制一份到你喜欢的地方。然后开始修改这个文件。里面像 wikiname, adminemail 这样的看名字就知道是什么意思。下面是各项的简短说明,详细的可以看 [Usage](http://ikiwiki.kitenet.net/usage.html)。

* srcdir: 你放 index.mdwn 文件的那个目录
* destdir: 你输出 html 的那个目录
* url: wiki 的 URL
* cgiurl: 用于自动更新 wiki 的 cgi 程序的路径,这个程序是 ikiwiki 自动生成的,看下面的解释
* svnrepo 和 svnpath: 和前面创建的 repo 符合,在我这里是 /svn/wikirepo 和 trunk

接下来就是最重要的两个设置,为了实现在线编辑,ikiwiki 可以自动生成一个 (编译好的) C CGI 程序,这个程序的位置就在 cgi wrapper 部分指定,你需要设置为一个你的 web server 可以访问得到,而且又能作为 CGI 程序执行的地方,比如我放在 `~/public_html/wiki/ikiwiki.cgi`,那么 lighttpd 对应的配置是:

1. 打开 mod_cgi
2. 在 `cgi.assign` 的设置中,加入 `”.cgi” => “”` (为空表示不使用其他程序解释,直接执行)

同时,这里设置的位置也必须和前面的 cgiurl 属性对应,如果根据我前面的这个设置,对应的 cgiurl 就是 `http://localhost/~username/wiki/ikiwiki.cgi`。

问题在于,只是编辑了还不行,编辑后的结果得生成静态的 HTML 我们才能读啊,CGI 不负责这一步,它是通过一个 svn post-commit 的 hook 程序实现的,这个程序,同样是一个编译好的 C 程序,由 ikiwiki 自动生成,放在哪里应该是无所谓的,不过我这里是放在 `/svn/wikirepo/hooks/post-commit`。

这样就算是设置完毕了,执行 `ikiwiki –setup 配置文件路径/配置文件名` 可以生成上面提到的两个程序。

小结
—-

其实小结就是要说些坏话… 目前 ikiwiki 还没开始对 UTF-8 的支持,如果你是直接用编辑器来编辑文件的话,那不会有问题,可是如果希望通过 CGI 来在线编辑,它就搞不定了,在它的 TODO List 中也提到了目前[对付](http://ikiwiki.kitenet.net/todo/utf8.html)的方法,如果要全面解决,还是得期待 2.0 啦。

另外,ikiwiki 这样虽然很好玩,尤其是作为个人信息管理的工具,但恐怕还是不能胜任大型的 wiki,像 wikipedia 这样,同时有成百上千人在编辑,编辑后都要 svn commit… 哦,我不敢想象了。

发现一件有趣的事情,如果按照 ikiwiki 网站上给出的[图片](http://ikiwiki.kitenet.net/logo/ikiwiki.png)把第二个 k 反向,这又是一个反着读也一样的名字 🙂

Why Cairo?

因为想写一个 lightweight & fast 的文本布局引擎,这两天在留意一些新的图形 API。其实有些都不算很新了,像 [Anti-Grain Geometry](http://www.antigrain.com/)、[Amanith](http://www.amanith.org) 和 [Xara](http://www.xaraxtreme.org/)。

曾被誉为“Linux 图形未来希望”的 [cairo](http://www.cairographics.org) 广受诟病的是它的效率,尽管从一开始 cairo 便宣称将会利用 glitz 这样的 backend 实现硬件加速的矢量图形绘制,从而达到软件绘制无法达到的效果。结果现在戏剧性的是,cairo 比所有这些用软件绘制的引擎都慢得多

所以才有人写了这么一篇 [Why Cairo?](http://weblogs.mozillazine.org/tor/archives/2006/04/why_cairo.html),意思大概说得很清楚了,Mozilla 选用 cairo 的借口现在看来是非常苍白无力的,比如说 cairo 引以为傲的“bring vector graphics to print”有多少人需要把网页输出到 PDF/PS?如果最基本的页面渲染都做不到高效,谈何页面印刷的高效?

所以我觉得啊,rendering model 好当然不错,cross-platform 性能好也很好,但 cairo 是不是中 gnome 社群的毒太深了?什么东西都来搞个 backend,结果最后每个 backend 都半死不活的,没错,也许某天 David Reveman 搞定了 xgl 腾出手来整 glitz 了,可 David Reveman 就算搞定了 glitz,也未必能在 win32 下用啊。

… 今天测试的结果是,pango 用 cairo 作 backend,cairo 用 win32 backend,结果渲染一行字 (4 个字母),要半秒钟。嗯,没看错,就是 0.5s。

效率还是很重要的呀,虽然某位大人物说过:

预优化是万恶之源。

Layout Engine 的文档

真的是.. a mess,估计就像 pango maillist 上有的人说的,有能力使用 layout engine 的人都能够直接看 reference 写程序,没能力的都去用 GUI Toolkit 提供的 high-level API 了。

最近看的几个 layout engine 包括:[Uniscribe](http://www.microsoft.com/typography/developers/uniscribe/), [Graphite](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&cat_id=RenderingGraphite), [Pango](http://www.pango.org) 和 [ICU](http://icu.sourceforge.net/userguide/layoutEngine.html)。最惨的是它们的定位和功能还不一样,有些相互之间还有牵扯。

就是这样居然还有人能把 Graphite, ICU 和 ATSUI 集成起来[给 TeX 用](http://scripts.sil.org/xetex),考虑到 TeX 还是用 WEB (Pascal 的 literate programming 版本) 语言写成的,这真是让人崇拜得五体投地的[强者](http://www.unicode.org/iuc/iuc22/b051.html)啊。

(看了一下强者的 biography,原来自从我出生开始就在 SIL 工作了.. 那么这样我比较不自卑一点。)

更新:看完这篇 [mail](http://mail.gnome.org/archives/gtk-i18n-list/2004-December/msg00010.html),我又很高兴了 🙂