2009-10-22

不仅仅是为了中文

 
 

Sent to you by l5g via Google Reader:

 
 

via 玩聚SR|最佳 by Emerging World on 10/20/09

玩聚SR还知道:
RT: @CasparAnt: [Google Reader] 不仅仅是为了中文: 声明:在全部文稿未完成之前,谢绝转载! 先问一下,有看过上一篇"这就是 ConTeXt Minimals"? 上一篇在讲述 ConTeXt .. http://bit.ly/3Qm3Hp
monnandtwitter 说 20小时前
当年折腾DocBook就已经半抓狂了,看来TeX果然不是常人能享受的啊
Emerging World发表于2009-10-21 09:35:59

声明:在全部文稿未完成之前,谢绝转载!

先问一下,有看过上一篇"这就是 ConTeXt Minimals"?

上一篇在讲述 ConTeXt Minimals 安装过程中,为了测试 ConTeXt 环境是否可以正常工作,曾给出一个 "Say hello to \CONTEXT" 的英文测试示例,但愿当时你不会擅自向那个示例中自作主张地加入一些中文字符,那样做的话,得到不正确的的排版输出是在所难免的。因为,ConTeXt 默认仅支持英文排版,要让它支持中文,需要再做一些配置并了解更多的知识。

TeX 字体科普

如果对此等内容不大感兴趣,可以直接穿越到下一节。

我们常用的中文字体大部分都是 TrueType 和 OpenType 格式的。TrueType 字体 (简称 TTF) 在 Windows 系统里很常见,OpenType 字体(简称 OTF)在 Adobe 公司的那些围绕 PDF 文档格式的排版软件和阅读器里经常出没。从技术方面来说,OTF 是 TTF 的超集,也就是说如果将 OTF 所包含的一些数据信息给阉掉,它就可以变成 TTF;或者向 TTF 里添油加醋,它就变成了 OTF。

还需要稍微科普一下的就是,TTF 和 OTF 都是轮廓字体,着重于字符的"形",是现今比较流行的字体格式。在计算机纪年的历史早期,广泛使用的是位图字体, 着重于字符的"像"。轮廓字体与位图字体的区别从表象上来说就是:形是可变的,像是固定的。轮廓字体,虽然是按照一定的尺寸来设计的,但是在对字符进行缩 放显示时,通常不会失真。位图字体在只有在其原始设计尺寸的状态下显示才不会失真,通常不能对其随意缩放显示,除非字体设计者为各个缩放级别分别设计字 体。

TTF 和 OTF 原本都不被 TeX 所支持。这是因为 TeX 降生并成长为青少年的时候,TTF 和 OTF 还不知道在哪里。为了充分说明 TTF 和 OTF 是怎样跟 Tex 攀上亲戚的,下面请跟随我再扮演一回考古学家,臆测一下那段曲折的历史。

聪明的 Knuth 在上个世纪七八十年代设计 TeX 的时候,颇有远见地考虑了字体的共性问题,那就是每个字符的轮廓都可以框在一个小盒子里,我们可以将其称为"字形盒子"。TeX 在排版时,只管将这些字形盒子正确地摆放好就可以了。这个原理很像天朝引以为豪的四大发明之一的"活字印刷术"。Knuth 为 TeX 的排版输出设计了一种文件格式——DVI 格式(就是那种扩展名为 .dvi 的文件),这种文件格式记录的自然也是那堆字形盒子的形状尺寸及其所在位置等信息。

TeX 不与具体的字体打交道,但是我们总是要查看排版结果的。排版结果无论是打印输出,还是显示在计算机屏幕上,我们都不希望自己看到的只是 TeX 替我们排列出来的字形盒子。所以针对 DVI 文件的打印机驱动程序以及 DVI 浏览器(比如那个 Yap)会识时务地将具体的字体文件与字形盒子关联起来。每个字形盒子都带着唯一的编码,DVI 文件打印机驱动或者浏览器可以根据这个编码在字体文件中找到相匹配的字形 (glyph),然后将这个字形打印出来或者在浏览器里画出来。

再后来,PS 文件文件格式出来了,并且混成了文档打印标准,TeX 黑客们就创造出可以将 DVI 格式转化为 PS 格式的程序,用来进行文件打印或者屏幕显示。再再后来,PDF 文件格式出来了,并且也混成了文档打印的标准,TeX 黑客们再创造出可以将 DVI 格式转化为 PDF 格式的程序,用来进行文件打印或者屏幕显示。DVI 格式向其它格式转换的过程中,大家玩的是具体的字形嵌入的游戏。

当 Knuth TeX 演进到 pdfTeX 时代,大家就不用再那么麻烦地进行 tex -> dvi -> ps -> pdf 这么一个繁琐的文档生成过程,而是直接由 tex 文稿得到 pdf 文件格式。但是,字形嵌入的问题并没有得到简化。在 pdfTeX 中要使用一款 TTF 字体,还是很繁琐的。首先要从 TTF 中获取字形盒子信息,存成 tfm 文件,还要获取到字形的编码信息,存成 enc 文件,还要生成字形与 tfm 的映射信息,存成 map 文件……这一切,都是要求用户手工进行的。

然后,TeX 继续演进到 XeTeX 和 LuaTeX 时代,这哥俩似乎是异曲同工地将 pdfTeX 使用 TTF 字体的那套折磨用户的做法给隐藏了起来,用户只需要通过几个特定的控制序列,告诉 TeX 引擎他们想使用哪些 TTF 或 OTF 即可。也就是说,XeTeX 和 LuaTeX 都没有去怎么变动 Knuth 定义的 TeX 字体机制,只是分别在内部嵌入了可以从 TTF 和 OTF 中自动获取字形盒子信息的功能,XeTeX 是通过调用 freetype 库来实现这一功能,而 LuaTeX 是通过调用 fontforge 库来实现这一功能 。

上述这段简短的历史,有点个人臆测的成分,我只是凭借一点无知者无畏的勇气将它说出来,请不要完全相信。另外,欢迎知之者帮我指正,多谢!

向 LuaTeX 交代你的字体都藏哪了

要在 ConTeXt MkIV 中使用一款 TTF 或 OTF 字体,理论上你可以将相应的字体文件放到任何一个目录里,但是习惯性的都是放到操作系统定义的一个特定的目录里,譬如 Windows 系统,字体文件通常放在 C:\Windows\Fonts 目录;如果是类 Unix 系统,字体文件通常放在 /usr/share/fonts 目录。无论字体文件放置在哪里,我们都需要将它的位置告诉 LuaTeX。

LuaTeX 是通过一个叫做 OSFONTDIR 的操作系统环境变量来确定用户所用字体的具体位置的。至于如何设置操作系统环境变量,这类知识应当是那些号称可以帮助你掌握 Windows 或 Unix 系统的教程来告诉你的。因此,我假设你已经知道操作系统环境变量的作用,即便是不知道也不要紧,只要跟随下面的步骤进行,就可以将你的字体所在的位置一字 不差的让 LuaTeX 知道。

对于 Windows 用户,假设需要的那些 TTF 和 OTF 字体都在 C:\Windows\Fonts 目录,并假设 ConTeXt Minimals 是采用图形界面方式安装并且安装目录为 D:\contextminimal,那么可以使用记事本打开 D:\contextminimal\setuptex.bat 文件,在其最后一行添加以下代码并保存:

set OSFONTDIR=C:\Windows\Fonts

好了,Windows 用户暂且喝杯水,给我几分钟的时间,也向类 Unix 用户交代一下这件事情该怎么办。

Unix 用户做好准备吧,先假设要使用的字体在 /usr/share/fonts 目录里的某个子目录,比方说 adobe,然后假设 ConTeXt Minimals 被安装在 ~/context 目录,那么打开终端并执行以下命令:

$ echo 'export OSFONTDIR=/usr/share/fonts/adobe' >> ~/context/tex/setuptex

即可完成环境变量 OSFONTDIR 的设定。

字体的 Cache 处理

不的不说,许多的自由/开源软件在易用性上经常受到可爱的用户们的强烈指责,因为在开发者那里很平常的术语被他们不经意地传递到用户那里,就成了咒 语。对于 LuaTeX 载入 TTF 和 OTF 字体的处理过程中,我们已经遭遇了"环境变量",现在还要再遭遇一下"Cache"。

Cache,翻译成中文就是"高速缓冲区",不过在这里,跟硬件里的那些神秘的芯片没有什么关系。对于应用软件而言,所谓的 Cache,只不过是在一些文件中临时存储一些计算数据,以备程序再次使用它们。前面我们臆测过,LuaTeX 是利用 fontforge 库从 TTF 或 OTF 里临时获取字形盒子信息,再将它们传递给 TeX 字体机制,而这个获取字形盒子信息是非常耗时间的,并且通常每个字体只需要处理一次即可,没必要每次加载这款字体都去计算字形盒子。所以,LuaTeX 的开发者 Taco 就搞出了一个 Cache 机制,就是将第一次处理字体时所获得的字形盒子信息保存起来,将来再次使用这款字体时,就省事了。

对于用户而言,可以不必理会其中玄机,只需要知道 LuaTeX 在编译 ConTeXt MkIV 格式的 TeX 文稿时,如果遇到了一款新字体,那么编译速度会很慢,因为它需要 LuaTeX 花费一点时间去 Cache 这款字体。这样,以后再编译使用该字体的文稿时,速度就会快一些了。

在已经通过环境变量 OSFONTDIR 告诉 LuaTeX 那些字体的藏身之处的情况下,可以通过 "mtxrun --script fonts --reload" 命令强制 LuaTeX 立刻就对它能够辨识到的字体进行 Cache 处理。在此再次强调一下如何分别在 Windows 和类 Unix 系统中开启 ConTeXt 工作环境,只有在该环境正确开启之后,ConTeXt Minimals 提供的那些程序才会正常工作。以后,再讲述一些命令用法的时候,就不再赘述如何开启 ConTeXt 工作环境,而是直接给出命令格式以及用法说明。

对于 Windows 用户,要对字体执行 Cache 处理,首先需要开启 ConTeXt 工作环境。再次假设 ConTeXt Minimals 是采用图形界面方式安装的,并且位于 D:\contextminimal 目录,那么先打开命令行提示符窗口,在命令行提示符">"之后输入 "d:\contextminimal\setuptex" 并回车,开启 ConTeXt 工作环境:

然后输入"mtxrun --script fonts --reload" 命令并回车,LuaTeX 便开始在命令行提示符窗口中叽里咕噜的乱讲话,实际上它是在默默地扫掠能够搜索到的字体文件,并提取出它需要的那些信息,这些信息保存在 D:\contextminimal\texmf-cache 目录。

对于类 Unix 用户,要对字体执行 Cache 处理,首先需要开启 ConTeXt 工作环境。再次假设 ConTeXt Minimals 安装在 ~/context 目录,那么打开终端,输入:

$ ~/context/tex/setuptex
$ mtxrun --script fonts --reload

即可完成字体 Cache 处理,生成信息保存在 ~/context/tex/texmf-cache 目录。

用 Adobe 宋体练练手

经过前面的一些小铺垫,现在可以来见识一下 ConTeXt 中究竟如何使用一款 TTF 或 OTF 字体,具体来说是如何使用 Adobe 宋体——AdobeSongStd-Light.otf,这是一款 OTF 字体,从 Adobe Reader 软件包应该可以找到这款字体,从这里或者网络上其他地方下载。

将字体文件 AdobeSongStd-Light.otf 放置于环境变量 OSFONTDIR 所表示的目录中,然后使用  "mtxrun --script fonts --reload" 命令对这个新添加的字体进行 Cache 处理。之后就可以在 TeX 文稿中使用这款字体了。测试文稿内容如下:

  1. \definefont[song][name:adobesongstd]
  2.  
  3. \starttext
  4.  
  5. \song
  6. 终于看到中文了!
  7.  
  8. \stoptext

对于这份 TeX 文稿,无论是采用命令行方式编译,还是在 TeXworks 的 ConTeXt 工作环境中编译,只要按照上述的步骤正确配置,应该可以输出含有"终于看到中文了!"字串的 pdf 文档。

现在,来看看上述 TeX 文稿的第 1 行,认真观摩一下这种控制序列的形式,这是 ConTeXt 控制序列的招牌写法——以参数的形式来调整控制序列的行为。这一行代码的用意是:使用 \definefont 这个控制序列去定义一个新的控制序列 \song,后者用于将文稿中当前使用的字体切换成它所关联的字体——adobesongstd,即刚才进行 Cache 处理的那个 Adobe 宋体。

adobesongstd 这个名字是 LuaTeX 在对 Adobe 宋体进行 Cache 处理时自动生成的字体名。LuaTeX 能够辨识出来的所有字体名,可以使用 "mtxrun --script fonts --list" 命令在 Windows 系统的命令行提示符窗口或者类 Unix 系统的终端窗口中显示出来,但是当字体太多的时候,命令输出信息是不方便查看的。这里,推荐一个 Windows 用户和类 Unix 用户都适用的查看字体名的方法,即使用以下命令,将 "mtxrun --script fonts --list" 命令的输出结果写到当前目录的一个文本文件中保存下来:

mtxrun --script fonts --list >> font-names.txt

然后打开 font-names.txt 文件,第一列信息即为 ConTeXt 可以接受的字体名,第三列信息是对应的字体文件的路径。

第 3 行和第 8 行的控制序列,即 "\starttext ... \stoptext",这是一对前后呼应的控制序列,它所包围的内容即为 ConTeXt  格式的 TeX 文稿(为了叙述简便,后文将简称 ConTeXt 文稿)的正文环境。这种形如 "\startXXX ... \stopXXX" 的控制序列对也是 ConTeXt 控制序列的招牌写法叫做"XXX 环境",譬如 "\starttext ... \stoptext" 表示 "text 环境",即正文环境。

第 5 行出现了那个在第 1 行定义的 \song 控制序列,它的作用是将 ConTeXt 正文环境默认的字体切换为 adobesongstd 字体,它表示从此处开始,往后的文本所使用的字体都是 adobesongstd,除非遇到可以切换为其它字体的控制序列。

第 6 行就是我们要排版的文本。

这个示例只是用来说明在 ConTeXt 文稿中字体使用最基本的方式。真正进行文档排版时,采用这种方式是不现实的。至于为什么,希望下文可以让你明白。

字体的分类与抽象

在排版中,如何灵活的使用字体是一门学问,甚至可以上升到艺术,水很深的说。现在西文(Latin 文)字体上千款,中文也有数百款,这是我个人最井底之蛙的粗略估计。数量多,没关系,人类对于数目众多并且没有精力一一勘察的事物,惯用的手法就是对它们 进行分类。对于字体而言,我认为最大众化的分类,就是将其划分为印刷体和手写体两大类。手写体跟水很深的艺术有些暧昧,我没有多少艺术天分,而且也不是专 业搞排版,所以只专注印刷体。

对于汉字的印刷体,从字型上来分,最为常用的类别是:宋体、黑体和仿宋体。至于其它字型,像楷体、隶书体,我一般都是尽量避免使用,因为我是实用派

宋体,据说是那个让白铁无辜的宋朝卖国贼秦桧发明的,比较适合那时刚流行起来的活字印刷工艺的制版需求。宋体这种字型一直到明朝后期才定型,所以也经常被天朝周边的某些国家和地区称为明体。国人在长期的读书活动中发现,宋体排版的书,看得很顺眼,长时间阅读也不会感觉眼睛疲劳,所以宋体通常用于排版长篇文章的正文部分。

黑体,又称方体或等线体,受西文无衬线体(下面在讲述拉丁文字体时再详细介绍)的影响,于20世纪初在日本诞生。该类字体没有衬线装饰,字形端庄,笔画横平竖直,笔迹全部一样粗细,结构紧密,笔划粗壮醒目,撇捺等笔画不尖,适合排版文章标题以及文章所包含的图表标题。

仿宋体,又称真宋体,是一种采用宋体结构、楷书笔画且字形较为清秀挺拔的字体,传说模仿的是北宋欧阳询的楷书,常用于排版文章的序、跋、注释、图片说明等。另外,天朝特别将这种字体作为工程制图标准字体。个人认为仿宋字体还有一个好处就是打印文档时比较省墨

在走马观花地看过汉字印刷体的常规类别后,我们来看一下西文印刷体的常规类别——衬线字体、无衬线字体和等宽字体。

衬线体,即 Serif。所谓"衬线",意思就是在字的笔画开始及結束的地方有额外的装饰笔画,而且笔画的粗细会因直橫的不同而有不同。从这个定义来看,汉字的宋体、楷体和仿宋体,也算是衬线体。衬线字体反应的是人类早期在一些硬质材料(石碑、陶土、木板等)刻字的状况。衬线体由于比较容易识别,就像是汉字的宋体一样,适用于长篇文章的正文排版。看来许多东西,从实用上来说,不管是天朝还是西方,都很一致。

无衬线体,即 Sans Serif(Sans 在法语中是"无"的意思),是在去除衬线体的那些装饰笔画后,再将笔画的粗细统一起来,汉字的黑体也是如此。无衬线体比较项目,和汉字的黑体类似,适合排版文章的标题以及文章所包含的图表标题。

等宽体,即 Monospace, 顾名思义就是各个字母的宽度都一样,所以说到等宽,汉字就笑了,因为汉字个个都是等宽的。西文的等宽体的设计当初是无奈之举,因为早期的电脑画面显示、打 字机,由于技术的局限,无法进行字母宽度的比例调整,所以需要将每个字符都制作成一样的宽度,从而形成了等宽体。但是,等宽体在那些从事计算机程序设计并 且还需要写文档的人那里很有用,看看下面分别用等宽体与衬线体排版的源程序代码,就会明白前者的作用了。

等宽体的用途与效果
       
等宽体 衬线体

这里值得一提的是,前文说过,汉字的宋体和西文的衬线体适合排版长篇文章的正文,但是很多人 却感觉在计算机屏幕上阅读文章的话,汉字的某种黑体和西文的无衬线体比较适合作为正文字体,因为它们可以让文章的内容看起来更清晰明了。对于这个问题,仁 者见仁,智者见智,大家还是凭感觉来吧。我是认为在 GNU/Linux 的 GNOME 桌面环境里不妨将汉字设置为台湾免费发布的文鼎宋体,而西文可以用一款无衬线的。

有了具体的分类,那么就容易搞一些抽象的字体模型了,这主要是为了方便排版软件的设计。

以 MS Word(假如它算的上是排版软件的话)的中文版来说,它对字体的抽象很简单,只有"中文字体"和"西文字体"这两大类。用户在使用 MS Word 时,只需将中文字体和西文字体分别指定为真正的字体即可,譬如很多人习惯将中文字体设置成宋体,而将西文字体设置为 Times New Roman。其实,不仅仅排版软件如此,如果大家认真用过类 Unix 系统的一些桌面时,经常可以发现这些桌面及其许多应用程序使用的是按照西文字体分类而建立的一种抽象字体模型,用户可以根据自己的喜好,直接去设定 Serif、Sans Serif 和 Monospace 字体

TeX 对字体的抽象,大致上也是按照衬线、无衬线和等宽的分类而设定的,但是它的字体抽象模型划分的更为细致。当年 Knuth 在制作 Plain TeX 格式的时候,为了简化对字体的使用,便设计了 \rm, \tt, \bf, \it, \sl 等用于字体切换的控制序列,并且默认为这组控制序列指定了一组字体。这样对于说英语的 TeX 用户而言,如果不甚讲究排版效果的话,完全可以直接使用这些控制序列来切换字体类型,从而无需考虑它们到底关联到了哪些字体。但是,对于讲中文的我们,如 果要想实现只在控制序列的层面上来切换汉字的字体,那么只有两种方案,一种是去了解一下 TeX 的抽象字体模型,自己写一份可以将字体切换控制序列关联到具体字体的"配置文件",或者直接向那些有能力制作此类"配置文件"的中文 TeX 用户讨一份。

ConTeXt 的抽象字体模型

ConTeXt 的抽象字体模型经历了长期的演变形成了一种叫做 Typescript 的字体模型。在这种字体模型中,字体被划分为衬线、无衬线、等宽以及数学字体四类,其中每一类又可以被细分为正体、粗体、斜体、粗斜体等字型。这些话题可 能坐而论道会很难理解,因为都是一些技术上的事情,结合具体的示例来理解,效果应当更好。

现在,假设我们从某个途径已经获得 Adobe 公司设计的宋体、黑体和仿宋体这三款 OTF 字体,即 AdobeSongStd-Light.otfAdobeHeitiStd-Regular.otfAdobeFangsongStd-Regular.otf,并且它们均已被正确安装,也就是说保证它们都能通过类似这一节中的测试。那么我们利用这三款字体实现对 ConTeXt 的抽象字体模型的具体化,详见下图:

如果采用 ConTeXt 的 Typescript 字体模型来表示上图所描述的抽象字体模型与实际字体之间的映射,体现为内容如下的 ConTeXt 文稿:

\starttypescript[serif][zhfont]
    \definefontsynonym[Serif][name:adobesongstd]
    \definefontsynonym[SerifBold][name:adobesongstd]
    \definefontsynonym[SerifItalic][name:adobesongstd]
    \definefontsynonym[SerifBoldItalic][name:adobesongstd]
\stoptypescript

\starttypescript [sans][zhfont]
    \definefontsynonym[Sans][name:adobeheitistd]
    \definefontsynonym[SansBold][name:adobeheitistd]
    \definefontsynonym[SansItalic][name:adobeheitistd]
    \definefontsynonym[SansBoldItalic][name:adobeheitistd]
\stoptypescript

\starttypescript [mono][zhfont]
    \definefontsynonym[Mono][name:adobefangsongstd]
    \definefontsynonym[MonoBold][name:adobefangsongstd]
    \definefontsynonym[MonoItalic][name:adobefangsongstd]
    \definefontsynonym[MonoBoldItalic][name:adobefangsongstd]
\stoptypescript

\starttypescript[myfonts]
    \definetypeface[myfonts][rm][serif][zhfont]
    \definetypeface[myfonts][ss][sans][zhfont]
    \definetypeface[myfonts][tt][mono][zhfont]
\stoptypescript

 假设该 ConTeXt 文稿被命名为 type-myfonts.tex,并在与之相同的目录里创建一份用于测试 type-myfonts.tex 文件中所定义的抽象字体模型的 ConTeXt 文稿——test.tex,内容如下:

\usetypescriptfile[type-myfonts]
\usetypescript[myfonts]
\setupbodyfont[myfonts,rm,12pt]

\starttext

所有动物都生而平等,但有些动物比其他动物更平等

\ss 所有动物都生而平等,但有些动物比其他动物更平等

\tt 所有动物都生而平等,但有些动物比其他动物更平等

\stoptext

现在,要么在命令行环境中,要么就在 TeXworks 的 ConTeXt 工作环境中去编译上面 test.tex,那么得到如下图所示排版结果的几率肯定远高于中彩票。

上面这两份 ConTeXt 文稿里又出现许多的控制序列,不过结合前面我略费心机而绘制的字体模型示意图,相信不难理解。不过还是略微介绍一下,免得让诸位心神不宁的。

首先,要面对的是 type-myfonts.tex 中的 typescript 环境,即 \starttypscript ... \stoptypscript 控制序列及其所包含的区域。我们要在 typescript 环境中设定 ConTeXt 抽象字体模型与实际字体的对应关系。

控制序列 \definefontsynonym 的 "synonym" 意思是"别名",这个控制序列的作用,顾名思义就是"定义一个别名",是用来建立抽象字体与实际字体对应关系的。比如 \definefontsynonym[Serif][name:adobesongstd]的作用是创建一个抽象字体名称——Serif,然后再将这个名字赋予对应的字体 adobesongstd。 抽象字体名称可以随意来定,上例中之所以使用 Serif, Sans, Mono 等一系列的抽象名称,是因为它们是 ConTeXt 认可的一组标准的抽象字体名,并在 ConTeXt 内部广泛使用。所以,我们只需要将这组标准的抽象字体名与实际所指的字体建立对应关系,那么就完成了 ConTeXt 抽象字体模型的具体化。另外多说一句, \definefontsynonym 所定义的抽象字体名,可以直接在 \definefont 控制序列中当作字体名去定义当前字体切换控制序列。

控制序列 \definetypeface 中的 "typeface"这个英文单词可理解为"字样",因此这个控制序列的作用就是定义一种字样。所谓字样,就是一组样子很相似的字体,它们彼此之间只有正、歪、胖、瘦的区别。上例中,那三个包含了 \definefontsynonym 语句的 typescript 环境,其中每一个都表示一种字样。简单来说, \definetypeface 就是将已经被具体化了的 ConTeXt 抽象字体模型传递到 ConTeXt 内部。这样,ConTeXt 文稿处理程序在遇到 Serif、Sans、Mono 等一系列抽象字体的时候,就会知道它们所指向的是哪款真实的字体。

虽然 type-myfonts.tex 文件从格式上来说是 ConTeXt 文稿,但是由于它在 ConTeXt 字体配置功用方面的特殊性,ConTeXt 就没有将它们与常规的 ConTeXt 文稿等而视之,而是为它们取了个名字,叫做 typescript 文件,并且提供了载入文件内容的控制序列 \usetypescriptfile,以便用户在自己的 ConTeXt 文稿中使用那些已经预先配置好的 typescript 文件。

大致上理解了 typescript 文件结构及其用途之后,可以结合下图去"参悟"一下 test.tex 究竟是如何使用 typescript 文件的。

ConTeXt 弄的这一套 Typescript 模型,初看时比较费电,但是参照上面我略费心机画的图,再略微琢磨一下,就会感觉非常简单。它玩的无非就是个 Tex 宏扩展的把戏而已。

比方说 test.tex 文件的第一行的 \usetypescriptfile[type-myfonts] 语句,无非就是要求 ConTeXt(严格地说应该是 LuaTeX)用 type-myfonts.tex 文件的内容来替换这行语句,但是它却尽力给了我们一个错觉,像是它在"加载" type-myfonts.tex 文件中的内容。

\usetypescript 语句是让某个 typescript 环境生效。虽然我没有剖析过 ConTeXt 的宏代码,不过可以想象地出,一个 typescript 环境相当于定义了一个控制序列,\usetypescript 语句可以调用这个控制序列。这里要注意的就是 \usetypescript 控制序列参数需要与想使之生效的那个 typescript 环境的控制序列参数相匹配。这也就是说,在一份 typescript 文件中,我们可以为 ConTeXt 抽象字体模型指派多套不同的字体,只需要在用的时候使用 \usetypescript 语句讲清楚具体是要用哪个 typescript 环境即可。在 test.tex 中,\usetypescript[myfonts] 表明 test.tex 要使用控制序列参数为 "myfonts" 的那个 typescript 环境。

真正把抽象字体模型具体化结果告诉 ConTeXt 的是 \setupbodyfont。该控制序列的作用是设定正文环境的默认字体,它会根据自个的参数列表去调用相匹配的 typescript 环境中所定义的字样 (Typeface),并且它还可以在自己的参数列表中设置字体的尺寸。

在 test.tex 文件中 \setupbodyfont 调用字样的过程是这样的,首先它会在已经生效的 typescript 环境中查找并调用能够跟它的参数列表中 "myfonts, rm" 这两项参数可以匹配的字样定义,即:

\definetypeface[myfonts][rm][serif][zhfont]

该字样定义会根据自身的后两项参数 "serif" 和 "zhfont" 去调用与之相匹配的 typescript 环境,即:

\starttypescript[serif][zhfont]
    \definefontsynonym[Serif][name:adobesongstd]
    \definefontsynonym[SerifBold][name:adobesongstd]
    \definefontsynonym[SerifItalic][name:adobesongstd]
    \definefontsynonym[SerifBoldItalic][name:adobesongstd]
\stoptypescript

在 test.tex 文件中,正文环境是由三行文字构成的:

所有动物都生而平等,但有些动物比其他动物更平等

\ss 所有动物都生而平等,但有些动物比其他动物更平等

\tt 所有动物都生而平等,但有些动物比其他动物更平等

第一行是正文环境不需要明显进行字体切换的文本,因为前面已经使用 \setupbodyfont 为全文环境设置了默认字体——衬线字体 (roman)。第二行和第三行分别是采用无衬线和等宽字体的文本,\ss 将正文环境默认字体切换成无衬线的,而 \tt 是将当前字体切换成等宽的。在 test.tex 文件中,经过一系列的字体切换之后,如果想将字体再切换成默认的正文字体,那么就可以使用 \rm 来实现。

现在,可以建立这样的认识:

  • 衬线体,英文名 Seirf,通常用罗马 (Roman) 体来表示,所以简称 rm;
  • 无衬线体,英文名 Sans Serif,通常用首字母缩写来表示,简称 ss;
  • 等宽题,英文名 Monospace,但是沿用了早期打字机 (Typewriter) 字体的说法,所以简称 tt。

前面说过,ConTeXt 抽象字体模型是将字体种类划分为衬线、无衬线、等宽和数学四大类,其中每一类又可以分为正体粗体、斜体、粗斜体等字型,那么怎么在正文环境中切换这些字型呢?不再废话了,直接看下面这个从 ConTeXt Wiki 上拿来的表,然后在实践中混乱着用一段时间,一切就明白了。

 

上面讲述的只是使用 ConTeXt 进行普通文本排版的字体基础知识,至于数学字体的如何配置,这个将会在讲述数学公式排版的那篇里讲述。

把很长很长的中文语句截成一段一段的

在掌握前面几节所讲述的字体使用方法之后,可能有人按捺不住就擅自便用 ConTeXt 来排版一篇中文文稿了,他的下场会很惨——中文的文本行从左起开始,碰到页面边界也不停下来,继续英勇地前进,只给我们留下一小段尾巴。这个就是中文的断行问题。

现在,如果让 ConTeXt 对中文进行断行,只需要在自己的 ConTeXt 文稿里添加 \setscript[hanzi] 语句即可。例如下面这份 ConTeXt 文稿就是可以断行的。

\usetypescriptfile[type-myfonts]

\usetypescript[myfonts]

\setupbodyfont[myfonts,rm,12pt]

\setscript[hanzi]

\starttext

明亡后,朝鲜和日本认为中国已亡,日本从此蔑称中国为"支那"。明治维新后,自认为华夏正统的日本曾打着"攘夷主义"的旗号发动甲午战争等对清战争。



\stoptext

好了,中文断行的问题现在就这样简单地解决了。

虽然可以正确断行了,但是这个 \setscript[hanzi] 的控制序列用来断行显得很古怪。不过,我们没必要去管它,也许以后会出现更为友好点的实现中文断行的控制序列。

把两种字体掺和在一起

假如用过 MS Word 中文版,现在可以回想一下,它允许用户单独为中文和西文设定字体,这个功能很不错。虽然中文字体往往也包含了西文字符,但是字符形状设计的很难看,所以在 中文与西文的混合排版中用西文字体去替换中文字体中的西文字符部分,是比较好的解决方案。这个功能在 ConTeXt MkIV 中也是有的,叫做 Fallback 字体,可以用它来解决中西文混合排版问题。

一个 Fallback 字体,可以通过以下三个步骤实现:

  1. \definefontsynonym[song][name:adobesongstd]
  2. \definefontfallback[hereos][name:texgyreherosregular][0x0000-0x0400]
  3. \definefontsynonym[fbsong][song][fallbacks=hereos]

第 1 步就是将 AdobeSongStd-Light.otf 取别名为 song。第 2 步是将一款西文字体 texgyreheros-regular.otf 设定为替换字体,替换区域为 0x0000-0x0400(这是拉丁字母所在的 Unicode 字符编码区域),也就是说我们将要用这款西文字体在这一编码区域的字符去替换 song 体中相应编码区域的字符。第 3 步就是完成替换,得到 fbsong 字体,它是 song 字体与  hereos 字体的组合字体。

现在我们可以利用上述所得的 fbsong 字体去定义一个字体切换控制序列:

\definefont[newsong][fbsong]

这样便可在正文环境中使用这种字体:

\starttext

\newsong 夹杂 English 的中文文本

\stoptext

把上面这三处语句合起来,就可以构成一个很小的 Fallback 字体示例文稿,编译后可得:

上述示例是将一款西文字体 fallback 到中文字体,这样做看起来有些合情合理,但是目前还不实用。这是因为简单的将西文字体"注入"到中文字体,那么在排版西文的时候就是按照中文排版的游戏规 则进行了,这样会让 TeX 丧失一些对西文的特别处理(比如 "~" 带子)。要知道,虽然中文虽然复杂,但是在排版方面要比西文简单的多。简单的得迁就一下复杂的,所以要想达到实用,最好是将中文字体"注入"到西文字体 里,也就是将西文字体的非拉丁字符编码区域(0x0400-0x2FA1F)中的那些字符都换成相应的中文字符:

\definefontsynonym[hereos][name:texgyreherosregular]
\definefontfallback[song][name:adobesongstd][0x00400-0x2FA1F]
\definefontsynonym[fbsong][hereos][fallbacks=song]

\definefont[newsong][fbsong]

\starttext

\newsong 夹杂~English~的中文文本

\stoptext

 不过这样会带来点性能的损失,毕竟是将一款庞大的字体向一款微小的字体注入。

当我们了解了 Fallback 字体机制之后,就可以将它应用到 Typescript 模型的定义中。下面以衬线体为例略作演示,以诸位的智慧,根据下面代码应该很容易杜撰一份属于自己的 Typescript 文件,且看:

\starttypescript[serif][zhfont]
    \definefontsynonym[hereos][name:texgyreherosregular]
    \definefontfallback[song][name:adobesongstd][0x00400-0x2FA1F]
    \definefontsynonym[fbsong][hereos][fallbacks=song]
    \definefontsynonym[Serif][fbsong]
\stoptypescript

需要注意一下的是,可以使用一款字体的别名继续定义新的别名,它们都指向同一款字体,上面这个示例便充分利用了这一特点。

字体设置也可以变得简单

上面,从"使用 Adobe 宋体练练手"一直到"把两种字体掺和在一起", 所讲述的都是 ConTeXt 提供的字体使用方法,可称为官方模式。民间模式也是有的,而且还很好用,特别是对于中日韩 ConTeXt 用户。那就是 Wolfgang Schuster 所写的一个模块——Simplefonts,它是对 ConTeXt 的 Typescript 和 Fallback 的高层封装,目的是为了简化 ConTeXt 字体使用,并且还为中日韩 ConTeXt 用户做了点特别的工作。如果不乐意去写 Typescript 文件,就尝试一下 Simplefonts 模块吧。

先来说一下什么是 ConTeXt 模块。我们在"序幕有点长" 那一篇里说过,TeX 比较出色的地方在于它的宏扩展机制,因为 ConTeXt 是对 TeX 的高层封装,目的是提高其易用性,那么相应地,TeX 那套宏扩展机制的易用性在 ConTeXt 这里也是被改进了,并且对于扩展结果的称谓也没有沿用 TeX 和 LaTeX 所谓的"宏包",而是叫做"模块"。下面借助讲述 Simplefonts 模块的使用,顺便向大家灌输一下 ConTeXt 模块使用方法。

ConTeXt 模块列表中收录了一些优秀的模块,大都是有经验的 ConTeXt 用户编写并共享的,可以从那里找到 Simplefonts 模块并下载其压缩包。将所得压缩包解包后:

  • 如果是 Windows 系统,采用图形界面方式安装的 ConTeXt Minimals,并且安装位置为 d:\contextminimal,那么就将 Simplefonts 解包后所得目录放置于 d:\contextminimal\texmf-local\tex\context\third 目录(如果没有这一目录,就自行建立);
  • 如果是类 Unix 系统,假设 ConTeXt Minimals 被安装在 ~/context 目录,那么就将 Simplefonts 解包后所得目录放置于 ~/context/tex/texmf-local/tex/context/third 目录(如果没有这一目录,就自行建立);

然后,在开启 ConTeXt Minimals 工作环境的情况下,在命令行界面中输入命令:

luatools --generate

该命令的作用是刷新 ConTeXt Minimals 文件"数据库",只有如此才能保证 ConTeXt 程序找得到 Simplefonts 模块所包含的文件。至此,Simplefonts 模块的安装过程讲述完毕,大部分 ConTeXt 模块都可以采用类似方式安装。下面来看如何使用已安装的 ConTeXt 模块。

首先需要在 ConTeXt 文稿里使用 \usemodule 控制序列加载模块:

\usemodule[simplefonts]

然后,就是在文稿里根据自己的需要去调用模块中定义的那些控制序列,下面这个示例展示了 Simplefonts 在中西文混合排版中衬线体、无衬线体和等宽体的对应字体设置:

% 加载模块
\usemodule[simplefonts]

% 中西文衬线体、无衬线体和等宽体的字体设置
\setmainfont[texgyrepagella]
\setsansfont[texgyreheros]
\setmonofont[texgyrecursor]
\setcjkmainfont[adobesongstd]
\setcjksansfont[adobeheitistd]
\setcjkmonofont[adobefangsongstd]

% 设置正文字体默认尺寸
\setupsimplefonts[size=12pt]

% 中文断行
\setscript[hanzi]

\starttext

测试 test 测试

\ss 测试 test 测试

\tt 测试 test 测试

\stoptext

故事刚刚开始

至此,已经完成了三篇有关 ConTeXt 的文章。一直都是慢慢的写,装作很有耐心的写,事实上只想把你劫持到这一小节,然后突然告诉你,这只是个开始。因为我们不是为了了解 TeX 的历史而来的,不是为了安装 ConTeXt Minimals 来的,更不是为了学习如何配置中文字体而来的,这一切都是在准备,我们是为了让 ConTeXt 可以帮助我们排版各式各样的美观的文档而来的。

从下一篇开始,我们将逐步接触使用 ConTeXt 排版的话题,首先要遭遇的就是演示文档的制作。如果手上没有太重要的事情,并且已经被这几篇文章劫持到这里,建议还是忍着再看下一篇吧,怎么也得带点东西回去。

下一篇,将讲述如何使用 ConTeXt 制作演示文档。

玩聚SR 是一个追踪各种社会化媒体,实时发现IT人都在分享和推荐什么的工具。点击阅读科技频道热文。
手机请登录移动版

 
 

Things you can do from here:

 
 

No comments:

Post a Comment