TeX Live 2009 的 LaTeX 中文支持

最近刚发布的 TeX Live 2009 是对中文支持有较大改进的一个版本,这里简单介绍一下。

首先,一如 2007, 2008 版本,TeX Live 2009 有中文版本的安装使用指南,强烈建议在提问前把相关章节好好看看,这份指南主要叙述的是和中文无关的一些通用的问题。

然后 TeX Live 2009 的中文支持主要分为两部分,基于 XeTeX 的,和基于传统 CJK 宏包的。(我这里只讨论 LaTeX 的,如果你希望尝试 ConTeXt 和 luatex,可以参考 Li Yanrui 的 blog,他有很详细的介绍。)

基于 XeTeX 的方案

这是我们推荐的方案,又可分为高层与低层两种,分别适用于不同需求的用户。

首先,安装所需的宏包:

sudo tlmgr install xecjk ctex

高层的方案是使用 ctex 宏包自带的文档类,例如原来用 article 文档类的就改用 ctexart 文档类,原来用 book 文档类的就改用 ctexbook 文档类,这样绝大部分你会遇到的中文问题,比如字体设置、hyperref 的调用、章节标题的设置等等,都自动为你解决了。详情可以看 ctex 宏包的文档。下面是一个小例子:

\documentclass{ctexart}
\begin{document}
你好,TeX Live 2009!
\end{document}

默认情况 (winfonts) 下,你需要宋体 (SimSun)、仿宋 (simfang)、黑体 (simhei)、楷体 (simkai)、隶书 (LiSu)、幼圆 (YouYuan) 这六套 Windows 字体,如果你的字体不全,可以编辑 ctex-xecjk-winfonts.def 文件 (用 kpsewhich 来找) 来修改设置,也可以选择 Adobe Reader 等软件所带的 Adobe Song Std, Adobe Heiti Std, Adobe Kaiti Std, Adobe Fangsong Std 四款字体,这时需要给 ctexart 加上 [adobefonts] 选项。又或者,你可以不用这些预置的字体,使用 [nofonts] 选项,然后参考 ctex-xecjk-winfonts.def 文件,自己定义对应各个 CJK 字体族的字体。

低层的方案则是用原来你自己的宏包,结合 xeCJK 宏包来配置字体,下面是一个小例子:

\documentclass{article}
\usepackage{xeCJK}
\setCJKmainfont{SimSun}
\begin{document}
你好,TeX Live 2009!
\end{document}

当然,你得有 SimSun 这个字体。

不管选用高层的还是低层的,都可以直接用 xelatex 命令直接编译你的文档。

传统 CJK 方案

传统 CJK 方案主要是为了方便使用以前的文档存在的。也分为两种,高层方案也是使用 ctex 宏包,低层方案和原来的文档一样,直接用 CJK 宏包。

使用 ctex 宏包的例子和前面用 ctexart 的那个完全一样:

\documentclass{ctexart}
\begin{document}
你好,TeX Live 2009!
\end{document}

不过需要注意的是,你还得安装 zhmetrics 宏包。另外,如果文档保存的编码是 GBK,必须添加 [GBK] 选项,否则用 UTF-8 编码的也建议用 [UTF8] 选项。

可以看出,用高层方案最大的好处是文档代码可以最大限度地不修改就能换用不同的 TeX 引擎。

低层方案与原来的做法的区别是,需要载入 zhwinfonts,使用 zhwinfonts 定义的字体,而不是原来自己去生成字体的映射文件,这样要简便很多:

\documentclass{article}
\usepackage{CJK}
\input zhwinfonts
\begin{document}
\begin{CJK}{GBK}{zhsong}
你好,TeX Live 2009!
\end{CJK}
\end{document}

注意,这里用了 zhsong 这个字体族而不是 song,为了避免和 CJK 宏包原来定义的冲突,我们定义的所有字体族都带 zh 前缀。

对于上述这两种情况,你都可以使用 latex 编译,用 dvipdfmx 生成 pdf。

除了上述的 zhmetrics, ctex, xeCJK 宏包以外,我们还在 CTAN/TeX Live 中提交了 zhspacing, CJKpunct, zh-luatex 等宏包,ctex-faq, lshort 中文翻译等文档,以上工作都需要感谢参与维护 ctex-kit 的全体开发者,为 ctex-doc 贡献内容的全体作者,也欢迎你参加到我们的工作中来,帮助进一步改进 TeX 的中文支持。

闲聊文本渲染技术的近期发展

在今年 7 月的 GUADEC 上 Behdad Esfahbod 做了一个题为 State of Text Rendering 的讲座,系统地综述了当前文本渲染技术的现状,顺带强调主要由他开发的 harfbuzz-ng 是未来发展的方向,4 个月过去了,最近文本渲染技术有了什么发展呢?这里谈谈我的一些印象和见解。

首先,harfbuzz-ng 到底想做成什么样子?我们知道底层的字体格式支持,开放的有 FreeType 一枝独秀,各平台私有的有 Win32 的 GDI font, Mac OS X 有 ATS 和 CGFont,上层的文本布局排版引擎,现在各家自有一套到两套:Windows 的 DirectWrite 和 Uniscribe;Mac OS X 有 Core Text 和 ATSUI,有 NSLayoutManager;GTK+ 有 pango,都是比较成熟的接口了,那 harfbuzz-ng 是要取代他们吗?

不是,也完全说不通,毕竟 pango 就是 Behdad Esfahbod 自己维护的,没理由拆自己的台。但是开放的 pango 等平台一直缺失的部分是 OpenType 复杂排版特性的支持,这一点 FreeType 没来得及解决,也因为和排版引擎关系太紧密,所以没法完全靠 FreeType 这种“字体格式解析库”来解决。harfbuzz-ng 要做的,正是在原来的 FreeType OpenType 排版代码的基础上,构建一个类似 ICU LayoutEngine 的、有简洁的 C API 的、支持 OpenType 特性的,而且还要比 pango 底层一些的库。

那不是和 ICU 重合了吗?ICU 一来是 C++ 的 API 用着比较累,而来确实也比较笨重,移植的时候够受的。ICU 虽然在近期版本里也开始提供 C 的排版引擎 API,但也只是试验性的。

所以总的看起来,Linux 下的文本渲染层次还真是够多的:GTK+ → pango/cairo → harfbuzz-ng → FreeType,同时 harfbuzz-ng 还可能用到 ICU, Graphite,在 Mac OS X 上还要用到 Core Text 和 Core Graphics。

细分层带来的好处就是不同的需求可以选择合适的 API 来实现:比如你要在图形界面上显示一段文字,就用 GTK+ 的 label;要绘制小段的文本,就用 cairo 和 pango;要高效率的、轻量地绘制大量的文本,就用 harfbuzz-ng 来自己攒一个排版引擎 — 大部分浏览器都是这样,只不过在没有 harfbuzz-ng 的情况下,或者要直接用 FreeType,不得不多写很多代码,或者就用 pango 这种重量级的,性能又得不到保证。

在 8 月份,harfbuzz-ng 的 API 提议已经出现,并在不断讨论中完善 (要我说,本来这个星球上有经验参与讨论的也最多不超过 20 个人,不如大家到一个屋子里开个会搞定得了)。

XeTeX 的作者 Jonathan Kew 虽然仍然在开发 TeXworks,但工作的重心已经放到了 Mozilla Firefox 3.6 的 WOFF 格式和 OpenType 复杂排版支持上,最近的一个视频里 Jonathan 提供了一个很棒的概览,有这样的一些特性之后,在浏览器里做一些认真的文本排版才可能成为现实 — 可惜浏览器仍然缺乏好的断行算法实现。这部分工作其实就是基于 harfbuzz-ng 的。而 WebKit-GTK 版本也开始了基于 harfbuzz-ng 的实现。

当然了,畅想了一番美好未来之后还是要回到现实,目前我想做的一个项目,是开发一套能跨 iPhone OS 和 Mac OS X 的、轻量级的排版引擎,专门给文本阅读器用。这个工作的出发点是:

  1. iPhone 上缺少 (按我的标准) 足够好的文本阅读器,而既然我已经开始写 Textus 了,不如把它移植到 iPhone 上;
  2. iPhone 上没有 Core Text 这个层次的框架,要渲染文本要么用 NSAttributedString,要么用 WebView,他们的额外开销都太多,太重量级了;
  3. 轻量的方法有神秘的 private API CGFontGetGlyphsForUnichars,如果不愿意用 private API 呢,就只好像 cocos2d-iphone 那样自己去实现 cmap 载入的代码,非常痛苦;
  4. 即使这样,也不支持复杂的 OpenType 排版特性,甚至简单一点的连字 (ligature) 都不支持,对于一个文本阅读器这是不能忍受的。

所以觉得写个这样的引擎会比较有用:

  • 做到后端独立,比如可以选用 ICU (iPhone OS 上), Core Text (Mac OS X 上), harfbuzz-ng
  • 能够支持 OpenType 复杂排版特性
  • 至少支持好大部分西方字符和 CJK 字符
  • API 尽可能的简单

最近刚刚开始做一些试验,比如用 Core Graphics 完成了一个简单的 CGFontInstance 给 ICU 用,这在目前还没看到类似的工作。

文档标记语言的一点思考

近来在水木上讨论文档标记语言的优劣,想起前些天查资料时正好又碰到了一个熟悉的名字: Scribe,于是多花了一点时间查找,没想到找到的资料仍然非常稀少。

Scribe,是 Brian Reid 在 Carnegie Mellon 的 Ph.D Thesis,也是对文档格式研究中所绕不开的一个重要的名字 (另一个,当然是 Don Knuth 的 TeX),这个项目大概在 1976 年开始,1980 年 Reid 发表这篇 Scribe: A Document Specification Language and its Compiler 以后,就甚少有什么改进了,尽管 Reid 的论文中也研究了 Knuth 的工作,但可以认为 Scribe 和 TeX 是同时代的作品。从对后续文档格式的影响说,Scribe 还更大一点,我们熟悉的 SGML (XML 的前身),BibTeX 都受了 Scribe 的影响。在 1982 年,Reid 还因为这一贡献 (Ground-breaking text-formatting language.) 获得了 Grace Hopper Medal

可是 Scribe 在应用上的成就却远远地被 TeX 所抛离,TeX 的生命力,被证明持久至今,而 Scribe 现在却在网上连一个正式的文档格式规范都找不到,一篇超过两页的详细描述都找不到!这是为什么呢?从 wikipedia 的文章中我们或许可以猜到一些隐情。

与 Knuth 慷慨地将 TeX 放入 public domain 不一样,1979 年 Reid 就把 Scribe 卖给了一个叫做 Unilogic 的公司,后来这个公司还持续和 Carnegie Mellon 大学就 Scribe 的知识产权问题扯皮,直到 CMU 放弃。最愚蠢的还不在这里,Scribe 居然在免费提供的 Scribe 程序中安插了后来被称为“time bomb”的东西,也就是说,90 天后如果用户不购买,这个软件就会执行自毁程序。

从 Scribe 如此猥琐的历史看,这个软件、这套格式最终为大众所抛弃,实在一点也不奇怪。

TeX Live 的新包管理架构

临近年末,TeX Live 2008 的发行就成为了一个有趣的话题,我们都知道 TeX Live 2007 最大的改进是 XeTeX 的集成,那 TeX Live 2008 呢,会有什么新东西?

LuaTeX? 有可能,LuaTeX 恐怕是今年来在 TeX 引擎开发上的又一创新,不过虽然今年 LuaTeX 的底层代码已经有了很大的完善 (参考这个由开发者 Hans Hagen 在 TUG 2007 上的演讲),但上层结构: TeX 的排版格式定义:Mark IV (下一代的 ConTeXt,基于 LuaTeX) 和 LuaTeX 兼容的 LaTeX2e 格式都未趋于稳定。当然,可能 LuaTeX 和相关格式会被包含在 TeX Live 2008 中,但多半会作为试验性特性来介绍。

事实上,自从 TeX Live 2007 发布后,TeX Live 的开发者们就开始讨论如何改进 TeX Live 的包管理架构,因为 TeX Live 无法像 MiKTeX 那样在线更新宏包是最为使用者所诟病的。毫无疑问,我们需要一个更灵活、更方便的包管理架构。今年 10 月的意大利 TUG 的会议 GuIT 2007 上,这个架构的主要开发者 Norbert Preining 就对此作了一个较为详细的介绍: TeX Live’s New Infrastructure

对急于想了解这个新架构大要的朋友,这里是我的一点理解。

在 2007 和以前的 TeX Live 中,使用 TPM 文件来组织软件包,这是一种 XML 格式的文件,有一套 Tpm.pm Perl 模块进行管理,TPM 的主要问题是:

  • 混合了机器生成的动态信息和人工写入的静态信息
  • 在许多不同的 TPM 中,同一个包的版本、授权和描述多次重复出现,来自不同时间的 TeX Catalogue 数据,因为缺乏及时的维护,导致很多数据事实上是过时的。
  • 因为 TeX Live 使用 shell script 来安装软件包,而用 shell 来解析 XML 显然不现实,所以我们还需要从 TPM 文件生成 shell 比较好解析的 lists 文件

所以这套新的底层结构的目的是:

  • 分离动态信息与静态信息
  • 消除 lists 文件生成的必要
  • 能够通过 web 安装单个软件包的更新
  • 提供更好的文档组织,吸引更多围绕 TeX Live 的开发

最终的设计是这样的,围绕三种类型的文件进行开发: tlpsrc, tlpobjtlpdb

tlpsrc 是人工编辑的软件包描述文件,有包括 name (包名称), category (分类), catalogue (TeX Catalogue 名), shortdesc (短描述), depend (包依赖) 等诸项属性,每项都是简单的 UTF-8 文本,通过换行符分隔。

tlpsrc 中还通过 pattern 的形式描述安装这个软件包需要执行的操作,pattern 的设计主要为了描述的简洁和跨平台,详细内容可以看这份文档

latex.tlpsrc 这个文件为例:

name latex
category Package
srcpattern d texmf-dist/source/latex/base
runpattern d texmf-dist/makeindex/latex
runpattern d texmf-dist/tex/latex/base
docpattern d texmf-dist/doc/latex/base

tlpobj 文件则是自动生成的,用于实际安装时的文件清单,类似如下:

name bin-dvipsk
category TLCore
revision 4427
docfiles size = 959434
 texmf/doc/dvips/dvips.html
 ...
runfiles size=1702468
 texmf/dvips/base/color.pro
 ... 
 texmf/scripts/pkfix/pkfix.pl
binfiles arch=i386-solaris size=329700
 bin/i386-solaris/afm2tfm
 bin/i386-solaris/dvips
 bin/i386-solaris/pkfix
binfiles arch=win32 size=161280
 bin/win32/afm2tfm.exe
 bin/win32/dvips.exe
 bin/win32/pkfix.exe

可以看出,这个文件的内容已经按照不同架构、不同类型组织好了,安装程序只需要按类别复制文件就可以了。

tlpdb 文件则是所有 tlpobj 文件的连接。

目前,TeX Live 的这套新结构已经完成了一个 Perl API 的参考实现,它将作为跨平台的 TeX Live 软件包安装程序的基础,但还有许多剩余的工作要做,包括改进效率、转换原来的 ctan 描述为新的 tlpsrc 描述,等等,而我目前在开发的是一套 C 的 API,目的是方便其他语言 binding 的开发,统一实现细节,并提供最好的处理性能。

如果你有兴趣,可以参考:

TODO List 的乐趣在于写

而不在于完成它。