以下的内容介绍了一种标准的屠龙之技。

我是走上了科研之路后才接触PostScript,因为一些专门软件(大都年代久远)会生成.ps文件,但我对PostScript一直是一知半解,一般用CorelDraw进行编辑。最近玩Octave的时候又涉及了PostScript,又研究了一下,有一些心得,写出来。

下列的操作的环境是Windows 8.1,用到的软件主要是GhostScript,版本有点老,是9.16。GhostScript可以从官网下载,地址是

https://ghostscript.com/download/gsdnld.html

还有一个软件是GSview,可以很方便显示PostScript相关的文件,包括.ps.eps.pdf,还可以格式转换,等等。下载地址

http://pages.cs.wisc.edu/~ghost/gsview/get50.htm

还涉及了一些字体,你未必有,不过方法是相通的。

配置CID font

首先,在GhostScript里使用字体的「正道」是将字体编译到可执行文件(gs.exegswin32c.exe)里,再或者是将字体转换为PostScript可使用的格式,不过这些方法都不够方便。GhostScript自带的字体又能满足所有人的需要,所以开发者就搞了CID font,可以「动态」的使用字体而不必将字体嵌入可执行文件。这种方法当然有很大的缺点,因为它严重依赖电脑上安装的字体,换一个电脑甚至是换一套环境就不行了,可相比前面说的两种方法,这种方法实现起来还是相对容易。

CID font通过GhostScript的lib\cidfmap加载电脑上安装的字体。安装GhostScript的时候好像会提示是否生成CID,如果没有生成或出现错误也可以手动安装,方法是

1
2
cd C:\gs
.\bin\gswin64c -q -dBATCH -sFONTDIR=c:/windows/fonts -sCIDFMAP=./lib/cidfmap ./lib/mkcidfm.ps

注意,我把GhostScript安装到了C:\gs\而不是默认的目录。

打开cidfmap可以看到里面是类似这样的内容

1
2
/SimHei << /CSI [(GB1) 2] /Path (c:/windows/fonts/simhei.ttf) /SubfontID 0 /FileType /TrueType >> ;

还有这些

1
/AdobeHeitiStd-Regular /SimHei ;

这些内容告诉GhostScript如何找到系统里的字体。这种方式生成字体有个缺点,因为它生成出来的都是mkcidfm.ps里定义好的字体,如果想添加新的字体,就需要手动了。

比如如下的几段定义了“Adobe Caslon Pro”系列字体:

1
2
3
4
5
6
/ACaslonPro-Regular << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-regular.otf) /SubfontID 0 /FileType /TrueType >> ;
/ACaslonPro-Bold << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-bold.otf) /SubfontID 0 /FileType /TrueType >> ;
/ACaslonPro-BoldItalic << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-bolditalic.otf) /SubfontID 0 /FileType /TrueType >> ;
/ACaslonPro-Italic << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-italic.otf) /SubfontID 0 /FileType /TrueType >> ;
/ACaslonPro-Semibold << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-semibold.otf) /SubfontID 0 /FileType /TrueType >> ;
/ACaslonPro-SemiboldItalic << /CSI [(Identity-H) 0] /Path (c:/windows/fonts/acaslonpro-semibolditalic.otf) /SubfontID 0 /FileType /TrueType >> ;

其中需要解释的是/CSI [(Identity-H) 0],可以理解成它定义了这个字体使用的字符集(术语是“CMap”),也就是告诉GhostScript这个字体是根据什么编码的,只有正确定义了才能。如果是中文,它的取值可以有如下的这些

  • GBK2K-H
    Modified GB 18030-2005 character set, encoding according to GB 18030-2005

  • UniGB-UTF8-H
    Modified ISO 10646:2003 (Unicode 4.1), UTF-8 encoding

  • UniGB-UTF16-H
    Modified ISO 10646:2003 (Unicode 4.1), UTF-16 encoding

  • UniGB-UTF32-H
    Modified ISO 10646:2003 (Unicode 4.1), UTF-32 encoding

  • Adobe-GB1-5
    New Identity CMap

具体可以看参考资料里Adobe的PDF文件[1]

这里面给出最重要的信息是,如果我的.ps文件是UTF-8编码的,那么,我需要使用UniGB-UTF8-H

以实例来解释。默认的cidfmap文件里我喜欢的字体一个都没有,所以我手动添加一些

1
2
3
4
/FZXSS << /CSI [(GB1) 5] /Path (c:/windows/fonts/fzss_gbk.ttf) /SubfontID 0 /FileType /TrueType >> ;
/STKaiti-Regular << /CSI [(GB1) 5] /Path (c:/windows/fonts/stkaiti.ttf) /SubfontID 0 /FileType /TrueType >> ;
/STXihei-Regular << /CSI [(GB1) 5] /Path (c:/windows/fonts/stxihei.ttf) /SubfontID 0 /FileType /TrueType >> ;
/STFangsong-Light << /CSI [(GB1) 5] /Path (c:/windows/fonts/stfangso.ttf) /SubfontID 0 /FileType /TrueType >> ;

添加的四种字体分别是方正新书宋、华文楷体、华文细黑和华文仿宋[2]。把里面已经有的字体注释掉,可以用下列的代码做替换

1
2
3
4
5
6
7
8
9
/SimHei /STXihei-Regular ;
/HeiTi /STXihei-Regular ;

/SongTi /FZXSS ;
/SimSun /FZXSS ;

/KaiTi /STKaiti-Regular ;

/FangSong /STFangsong-Light ;

也就可以方便的使用我喜欢的四种字体了。

怎么知道这些设置是否正确工作呢?把下面的代码存为UTF-8编码的.ps文件[3],用GSview打开就可以看效果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%!PS-Adobe-3.0

/testshow {
x y moveto
/Times-Roman findfont 14 scalefont setfont show
findfont 14 scalefont setfont
(中文字体测试镕基) % <---- sample Chinese text here
show
/y y 28 sub def
} def

/x 100 def /y 700 def

/SongTi-UniGB-UTF8-H (SongTi-UniGB-UTF8-H ) testshow
/FangSong-UniGB-UTF8-H (FangSong-UniGB-UTF8-H ) testshow
/KaiTi-UniGB-UTF8-H (KaiTi-UniGB-UTF8-H ) testshow
/HeiTi-UniGB-UTF8-H (HeiTi-UniGB-UTF8-H ) testshow

showpage
%%Trailer
%%EOF

用PostScript显示中文的效果

PostScript入门

PS其实是一门编程语言,维基百科里有基本的介绍。下面是我的一些理解

  1. PostScript类似于汇编,是一种原始的语言,所以和现代的编程语言很不一样。
  2. 可以理解为操作和现代语言的顺序相反,比如在末尾写def表示定义。
  3. 括号类似于其他语言里的引号,表示字符串。
  4. 我们所熟悉的函数加参数调用,在PostScript里是用原始的压栈来实现的。/SongTi-UniGB-UTF8-H (SongTi-UniGB-UTF8-H ) testshow在栈里面压了两个内容,而在对应的“函数”中,第一行的操作堆栈是平衡的,所以没用上栈里面的东西,而第二行show之前需要一个字符串,解释器就从堆栈中取出了字符串(SongTi-UniGB-UTF8-H )findfont又从栈中取出了/SongTi-UniGB-UTF8-H
  5. 一般PostScript文件是自动生成的,由专门的软件读取和处理,很少手写和编辑……

修改Octave生成的PostScript文件的字体

学了PostScript后我们可以实战一下。Octave生成的.ps文件里有如下的内容定义了字体

1
2
<</BaseFont/Helvetica/Type/Font
/Subtype/Type1>>

我开始以为这是路径,其实不是的,这里面的/Helvetica定义了字体,把它换成我们喜欢的字体就可以了,比如
换成

1
2
<</BaseFont/ACaslonPro-Regular/Type/Font
/Subtype/Type1>>

就用了Adobe Caslon Pro字体。

已知问题

没法用CID font使用冬青黑体,似乎思源黑体也不行,因为它们的CMap是私有的?不知道。估计没有人知道。

如果你能坚持看到这里,谢谢你。如果我的理解有偏差,欢迎批评指正。


  1. Adobe关于CJK和CMap的介绍. PDF下载

  2. 日语的TeX Wiki关于GhostScript的介绍. https://texwiki.texjp.org/?Ghostscript%2FWindows

  3. 一个网易博客的介绍. http://guoyoooping.blog.163.com/blog/static/13570518320101291442176/