Table of Contents
- 1. 语句表达是逆波兰式
- 2. 一些特别的符号
- 3. 函数和过程可以看作 C 语言中的宏
- 4. 执行脚本可以看作解析流的过程
- 5. 栈平衡
- 6. 调试
- 7. 中文字体
- 8. 指令隐含的对栈的影响
- 9. 示例程序
网上有PostScript 的教程,并且还有参考手册,这里只是记录在学习该语言过程中的一些 心得体会。
学此语言主要是为了创建一个只带页码的 pdf,配合 pdftk 给输入 pdf 添加页码。当然, 加页码有其他手工方法,但我就是想找一个能够自动化的方式,考察之后选择使用 ps2pdf 和 PostScript 脚本的方式。关于使用 pdftk 修改 pdf 页码的方法可见上一篇文章。
学完之后,关于 PostScript 映像最深的是 "PostScript是面向堆栈的语言"
。这句话乍
一看也没什么,但在实践中才能领会其真义。
1. 语句表达是逆波兰式
"a * b" 在PostScript 表示成 " a b mul",非常便于操作数和操作符入栈。当然操作数和 操作符分别压入不同的栈,三个基本栈分别是操作数、操作符、字典。
2. 一些特别的符号
- << k1 v1 k2 v2 … >> 表示字典
- [ v1 v2 v3 … ] 表示数组
- (abcde…) 表示字符串
- { s1 s2 } 表示过程,也可表示数组,因为过程也是数组
- % 表示注释
- / 表示名字
3. 函数和过程可以看作 C 语言中的宏
PostScript 使用 def 来定义常量,也用其定义变量和过程," /fontsize 14 def " 定义 了一个名字是 fontsize 的常量。 " /tmpstr 6 string def " 定义了一个名字是 tmpstr 的变量, "/mm2inch { 72 mul 25.4 div } def " 定义了一个名字是 "mm2inch" 的函数。
函数的参数需要提前入栈,可以是上一步的结果自动入栈,也可以直接当作操作数写出来被 自动入栈。返回值同理,只要代码中的名字不是操作符或者前文定义的过程,就会被当作操 作数压入栈。
4. 执行脚本可以看作解析流的过程
换行并不重要,更多的是便于程序员阅读。解释器分析的脚本的过程中,大致是先将所有宏 (常量、函数)展开,然后是一个搜索操作符的过程,不是操作符的当作操作数压入栈,找 到的操作符需要参数的从栈中弹出,然后执行。
5. 栈平衡
写脚本的过程要非常关注栈平衡,否则执行结果就可能出问题。这里举例说明,下面定义了 一个函数,将一个数据传入,将数字对应的字符输出出来。PostScript 其实并不难,但在 写定个函数的过程中还是差点作出 PostScript 无法完成所需功能的判断而放弃学习。其中 注释了一个句子和正式使用句子有一些不同,搞明白其中道理基本上一通百通。
/num2glyph { % 准备后面要用的临时变量,即将参数复制了几遍 dup len -1 0 { pop dup } for len -1 0 {10 exch exp 2 -1 roll exch div floor cvi 10 mod glyphlist exch get glyphshow } for %下面这句话值得研究 %len -1 0 { dup 10 exch exp exch 2 add -1 roll exch div floor cvi 10 mod } for } def
注释的句子中求余数后未采取下一步动作,余数还在栈中, for 循环中下一步还要取之前 压入栈的数据,在使用 roll 指令时,第一个参数是通过 add 指令计算出来的,而正式使 用的句子中,由于求余后的数被后面的操作使用了,不存在于栈中,所以 for 循环中 roll 的每一个参数是固定值 2。
6. 调试
PostScript 是可以使用 gs 等可交互的解释器进行调试的,执行后指令后可以使用
stack
指令查看堆栈中的数据,从而判断指令参数等是否正确。
7. 中文字体
网上有贴子说要显示中文需要特殊字体并将文件存为 gb2312 编码。我是没有测试成功的, 最后阅读 PostScript 手册基本上肯定的一件事是,字体分成好几种,而我要使用的宋体是 不能使用 show 指令显示的,也无法使用 stringwidth 来计算显示字符串所需要的长度的。
我将宋体字体文件复制到与脚本一个文件夹,并在脚本中使用 "(simsun.ttc) 14 selectfont " 选择字体,同时 gs 命令要加参数 "-p", 这样就要以正常使用该字体了。
#这个句子可以将 input.ps 转换成 output.pdf gs -P -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=output.pdf input.ps
关于使用 Unicode 编码,PostScript 不支持 " (我爱你) show " 或者 " (我爱你) glyphshow " 这种操作,原因是中文在脚本中的表示他并不了解,可以通过字符名称进行访 问,当然无法直接显示字符串。
所以,计算字符所占宽度,我也只是采用了取巧的方式,即在看不见的地方先记录所在位置, 然后将要输出的字符输出一遍,再查看当前位置,可计算出输出字符串所占长度。如果谁知 道更加优雅的方式,欢迎留言告知为谢。
8. 指令隐含的对栈的影响
一般情况,计算有结果的指令会将结果压入栈。
for 指令会在每一遍循环前将当前 index 压入栈。
stringwidth 和 currentpoint 计算结果是 wx 和 wy 两个数,均会压入栈。
拿不准的指令最好阅读手册去了解指令的参数和输出。
9. 示例程序
%!PS /pagewidth_by_mm 210 def /pageheight_by_mm 297 def /left_margin_by_mm 28 def /right_margin_by_mm pagewidth_by_mm 26 sub def /bottom_start_by_mm 24 def /fontsize 14 def /maxpagenum 20 def /mm2inch { 72 mul 25.4 div } def /height_start { bottom_start_by_mm mm2inch } def /pagewidth { pagewidth_by_mm mm2inch } def /pageheight { pageheight_by_mm mm2inch } def /left_start { left_margin_by_mm mm2inch } def /right_margin { right_margin_by_mm mm2inch } def << /PageSize [pagewidth pageheight] >> setpagedevice (simsun.ttc) fontsize selectfont %/Helvetica 12 selectfont /glyphlist [ /zero /one /two /three /four /five /six /seven /eight /nine ] def /tmpstr { 6 string } def /len { 10 tmpstr cvrs length 1 sub } def /glypharray { 6 array } def /num2glyph { dup len -1 0 { pop dup } for len -1 0 {10 exch exp 2 -1 roll exch div floor cvi 10 mod glyphlist exch get glyphshow } for %下面这句话值得研究 %len -1 0 { dup 10 exch exp exch 2 add -1 roll exch div floor cvi 10 mod } for } def /pstrlen { 0 -50 moveto currentpoint pop exch /hyphenmonospace glyphshow num2glyph /hyphenmonospace glyphshow currentpoint pop exch sub } def /mkpage { dup dup 2 mod 1 eq { pstrlen right_margin exch sub height_start moveto } { left_start height_start moveto } ifelse /hyphenmonospace glyphshow num2glyph /hyphenmonospace glyphshow showpage } def 1 1 maxpagenum { mkpage } for
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议 进行许可。
标签:10,mm,学习心得,PostScript,指令,def,exch From: https://www.cnblogs.com/bu-wu-zheng-ye/p/18215350