君子剑的密室

Friday, July 27, 2007

貌似Consolas目前在Linux下的效果还是不太好

Freetype的渲染算法和cleartype的差别还是蛮大的,以至于像consolas这种为cleartype技术量身定做的字体,在freetype之下效果并不太理想,尤其是和在win下的效果进行了对比之后……
推荐,还是继续使用bitstream vera sans mono/dejavu sans mono字体吧……

Sunday, July 22, 2007

Emacs unicode branch(emacs23)的字体管理

Emacs从Unicode Branch(所谓的emacs23)开始支持XFT,从而实现了字体渲染的Antialias效果和新的字体描述方式(与之相对地,之前的emacs使用X Logical Font Description,一个完整的字体描述涉及到15个域,比如-adobe-courier-medium-r-normal--8-80-75-75-m-50-iso8859-1)。因此,emacs的字体配置也需要相应改变。

另外,和其他的支持XFT的程序(库)不同的是,emacs并没有采纳Fontconfig字体管理子系统,所以emacs并不能像那些程序那样把具体的字体替换管理工作交给系统(Fontconfig子系统)完成(比如,gvim可以完全放心的使用gnome/kde系统的monospace虚拟字体,而不需要关心实现的细节)。我在这里简要介绍一下Fontconfig子系统。用户的机器上往往包含有很多套字体,这些字体的风格、用途、码字集都往往各不相同。所以,针对任意一个码字,系统需要知道该用那个字体去显示(如果有多个字体包含有这个码字),或者该替换成哪个字体去显示(如果当前使用的字体并不包含这个码字)。这就是Fontconfig的功能。举个例子,因为绝大多数的中文字体都含有常见的Latin字符,同时又因为几乎所有的中文字体的Latin字符的效果都不如专门的西文字体,所以用户往往会倾向于用特定的西文字体来显示西文字符,和只使用中文字体来显示中文字符。这时,用户就需要Fontconfig(或者类似的机制)来自动完成这件事情。

Emacs的字体设置可以出现在三个地方:命令行参数、X Resources文件,以及.emacs。我这里只给出最后一种方式。字体设置的思路很简单:对于常规的西文字符使用特定的西文字体(某个用户所喜爱的mono字体),对于中文字符和中文标点使用特定的中文字体(某个用户所喜爱的中文字体),对于其他的漏网之鱼则使用其他字体来补全(往往是一些很不常见的字符)。

(set-default-font "Consolas-13")
;; 这是选定的西文字体。用户可以替换为任何自己想要的mono字体,比如
;; Bitstream Vera Sans Mono-10

(set-fontset-font (frame-parameter nil 'font) 'unicode '("Microsoft YaHei" . "unicode-bmp") 'nil 'append)
;; 类似的,可以把Yahei替换为其他中文字体
;; 'unicode表示针对所有的unicode字符集
;; 'append表示这个字体设置只作为前一个设置的补充,而非强制替代
;; 因为这个是第一个fontset设置,所以这个参数并非必要

(set-fontset-font (frame-parameter nil 'font) 'unicode '("Arial Unicode MS" . "unicode-bmp") 'nil 'append)
;; 这里使用Arial Unicode MS字体作为补漏
;; 注意最后的'append。没有这个参数的话,很多Yahei和Arial Unicode MS
;; 共有字符将会由后者显示

以上就简单地实现了模拟Fontconfig的效果:西文字符和标点用Consolas,超出的部分首先用Yahei来显示(因为Yahei含有CJK ExtA,所以这部分字符也会正常显示),剩余的某些字符再用Arial Unicode MS来显示。

Friday, July 20, 2007

聊聊sam的structural regular expression

sam是Rob Pike在上世纪90年代初鼓捣出来的编辑器,一般来说,被认为是更接近ed精髓的全屏编辑器。同时,因为sam在设计之初就融入了鼠标的支持(当然,仍然可以以行编辑器的模式运行),所以没有什么在文本区内四处移动的命令(也就是说,没有vi的hjkl w b f t等类似的命令),同时也没有mode(用户的输入会根据是在命令窗口还是文本窗口而被区分为命令还是文本)。

sam和ed相类似的是它也有一个明确的dot概念,始终指向“当前”区域。比方说,当sam利用a命令附加一段文本时,其起始位置就是当前的dot;而当其附加完毕后,dot就会被更新为这段新的文本。再比如,当sam用/.../命令查找某个目标时,dot就会被更新为这个目标(如果找到的话)。因为有了这个dot命令,所以sam的各种命令就可以被级联起来执行:前一个命令操作dot,结束后会提供一个新的dot,后面的命令则依此类推。打个比方,c是更改命令,所以/hello/c/world/就会把前一个命令(查找hello)的dot更改为world,并将world标为新的dot(也就是把hello替换为world)。

再简单说两个命令,就可以聊聊结构化的regexp了。

一个是x命令,格式为x/.../。其作用是在某个文本区域(事先给定)查找所有出现过的某目标,并依次将dot设置为该目标。换言之,x命令提供了一种遍历机制。所以,x/hello/c/world/(,和vi的%含义类似)会讲整个文本中的hello替换为world。

第二个命令是g,格式也是g/.../。和x唯一不同的是,这个命令只查找,却不设置dot。换句话说,它提供的是一个遍历性的条件机制:如果找到...就...。所以,,g/hello/c/world/有可能会把整个文本区域弄成只剩一个单词world,如果文本中原本含有hello的话。

嗯,好了,现在来说说结构化的regexp。用Pike自己给的例子吧。

假设有个电话簿文本文件,里面的内容是
Herbert Tic
44 Turnip Ave., Endive, NJ
Male
201-5555642

Norbert Twinge
16 Potato St., Cabbagetown, NJ
201-5553145
Female

...

每个人的记录是由名称/地址/性别/……/电话之类的项目组成,每个记录的项目个数和顺序也不一定一样,记录和记录之间由空行隔开。假设,这个时候要找某个人的电话号码(假设是要Norbert Twinge的),就可以用下面的命令来得到:, x/(.+\n)+/ g/^Norbert Twinge$/ x/^[0-9]*-[0-9]*\n/ p
第一部份,x/(.+\n)+/相当于将文本按记录分割,依次将dot设置为各个记录;
第二部分g/^Norbert Twinge$/找到该人对应的记录,但是此时的dot仍然没变(是该人的整条记录);
第三部分则只在这个记录范围内匹配出电话号码项。

可以看出,这里的关键就在于,x和g一个查找并且更新dot,而另一个只查找不更新dot从而在文本中建立起了一种结构,并依次完成功能。

Pike在其论文中曾经说过,传统的UNIX工具总是将文件看作由行组成的,并且所有的操作都是在这样一种结构下进行。而sam则可以临时提供其它的结构,因此在某些问题上便更有优势一些。