PostScript入门(2)-基础概念
上一章,我们简单介绍了PostScript语言,以及如何在Windows、Linux上运行PostScript。PostScript是个页面描述语言,也是个编程语言。本章将介绍PostScript的语言基础概念和图形基础概念,为以后的几章做个铺垫。
语言基础概念
从编程语言的角度来看,PostScript是十分简单的。PostScript是一种RPN,即“逆波兰式”(Reverse Polish Notation),或称“后置记法”。学过数据结构的人想必不陌生吧。简单来说就是操作数在前,操作符在后。一般我们使用“中置记法”,比如 1 + 2,加号写在两个操作数1和2的中间;但在PostScript中,要把加号放在最后,写成“1 2 add”。这是PostScript 的基础。
栈
后置记法最大的好处就是处理方便。学过编译原理的同学应该不会忘记中置记法中的表达式歧义问题带来的痛苦吧(比如处理1+2*3,就必须用某种方式告诉计算机是先计算1+2还是先计算2*3),但在后置记法中就不会有这个问题。只需用一个栈,依次处理表达式中的符号,''遇到操作数就压栈,遇到操作符就从栈顶弹出相应数量的操作数进行计算,并把计算结果压栈。'' 例如,要计算(1+2)*(7-4),用PostScript表示如下:
1 2 add 7 4 sub mul ==
末尾的“==”表示显示栈顶元素。这行代码可以直接在GhostScript的提示符下运行,结果如下所示:
GS>1 2 add 7 4 sub mul == 9
下面来解释一下运行过程:
处理的符号 | 操作 | 栈的内容(右边为栈顶) |
1 | 压栈 | 1 |
2 | 压栈 | 1 2 |
add | 栈顶两元素出栈,计算加法,计算结果压栈 | 3 |
7 | 压栈 | 3 7 |
4 | 压栈 | 3 7 4 |
sub | 栈顶两元素出战,计算减法,计算结果压栈 | 3 3 |
mul | 栈顶两元素出栈,计算乘法,计算结果压栈 | 9 |
== | 栈顶一元素出栈并显示 | <空> |
可见,这种方式完全不需要对表达式进行语法分析,所以PostScript解析器可以做得相当简单。实际上,PostScript语言中的确有这样一个栈,用于存放操作符和操作数并进行计算,称为“操作数栈”。
在GhostScript中,栈的大小会显示在操作数之后。例如,可以逐个地输入上述表达式(PostScript语言中,空白和换行是没有意义的,所以一条命令可以分成几行写,也可以写在同一行上,效果相同):
GS>1 GS<1>2 GS<2>add GS<1>7 GS<2>4 GS<3>sub GS<2>
“GS”后面的数字就是栈中的操作数个数。“pstack”命令可以查看栈的内容,例如在上面 GS<2> 的状态下执行结果为:
GS<2>pstack 3 3
另外,这里的“操作数”和“操作符”的概念也可以理解为“参数”和“函数”,计算后的结果可以认为是函数的“返回值”。
数据类型
PostScript中的数据类型有数字、字符串、数组和过程。
数字
PostScript支持整数和实数。整数部分为0的小数可以省略整数部分,如:
.123 % 表示 0.123
此外还有进制计数法和科学计数法。进制计数法写成“基数#值”,例如
16#ff % 表示16进制的0xff,即255 8#777 % 表示8进制的0777,即511 7#33 % 连7进制都可以使用 2#3 % 出错,因为2进制中没有3这个符号
科学计数法写成“尾数E指数”,例如
1E10 % 表示1e+10 1.234E-2 % 表示0.01234
字符串
字符串用圆括号表示,例如
(abcdefg) % 表示字符串"abcdefg" (a(b)c) % 表示字符串"a(b)c",圆括号内成对的圆括号失去特殊意义 (a\(bc) % "a(bc",单个圆括号需要转义 (a\nb) % "a<换行>b"
转义符的形式有:
\t | TAB符号 |
\n | 换行(line feed) |
\r | 回车(carriage return) |
\f | 换页(form feed) |
\b | 退格 |
\( | 左圆括号 |
\) | 右圆括号 |
\\ | 反斜杠 |
\ddd | 用八进制表示的字符 |
注意转义符中没有十六进制表示(\xnn)。
此外,也可以直接在字符串中加入换行:
GS>(abcd efg) GS<1>pstack (abcd\nefg)
如果想在代码中让字符串换行,但并不想在字符串中加入换行,可以在行尾使用 \ 符号:
(This is a \ string \ that has no \ newlines)
的值为
(This is a string that has no newlines)
字符串还可以用十六进制表示,只需将十六进制数写在尖括号中。例如:
<616263> % 表示"abc" <6d 6e 6f> % 表示"mno",中间加入空格或换行不起作用
布尔值
PostScript支持 true 和 false 两个布尔值。例如:
GS>true false GS<2>pstack false true
数组
数组是一系列操作数的排列,用方括号括起来。数组中的数据可以是任何类型,甚至可以是另一个数组。
[ 367 28.4 (abc) true ] % 四个不同类型元素的数组 [ 1 2 add 7 4 sub ] % 值为[3 3]。数组中甚至可以做运算,数组值为运算结果 [ 21 [ 53 74 ] [ 60 [ 53 48 ] 99 ] 18 ] % 可以任意嵌套 [ ] % 空数组
过程
过程是一系列操作符和操作数的排列,放在大括号中,用于自定义操作符。例如:
{ dup mul } % 计算平方。dup表示复制栈顶元素,mul表示栈顶两元素相乘
实际应用中需要使用def操作符将过程定义到名字(见下文)上,如:
GS>/square { dup mul } def % 定义 square 为 { dup mul } GS>3 square == % 调用 square 9
字典
字典是一系列成对的名字和值,相当于Perl语言中的哈希表,或者Java中的HashMap。
名字
名字是任何不是数字的符号,相当于一般编程语言中的标识符。名字可以由除了空格和特定保留字符((、)、[、]、<、>、{、}、/和%)以外的任意字符组成,例如abc、$def、ghi-jkl都是有效的名字。名字甚至可以以数字开头,例如 1Z 是有效的名字,但这种习惯有时会遇到问题(如 1E10 是个实数)。
通常,名字出现时会被当作操作符,PostScript会去“调用”该名字对应的过程。那么怎样给名字定义“过程”呢?这就需要使用“字面意义的名字”(literal name)和 def 操作符。在名字前面加上 / 符号,可以告诉PostScript,我们要定义这个名字了,别把它当作操作符,当作操作数压进栈里就行了。def 操作符则从栈顶弹出两个元素,并把第二个元素(字面意义的名字)定义为第一个元素(过程)。例如:
/square { dup mul } def % 定义 square 为 { dup mul } 3 square % 调用 square,相当于 3 dup mul
这里还要说明一下,所谓“调用”并不是像C语言那样跳转到函数定义的位置去执行。 PostScript中的过程实质上是一系列的操作符和操作数,在“调用” 一个名字时,PostScript简单地用该名字对应的那个过程来替换该名字,然后依次执行。例如上面的 3 square,PostScript在遇到 square时,从以前的定义中找到 square 相当于 { dup mul },于是将 square “展开” 成 dup mul,再依次执行,其效果跟直接写 3 dup mul 是相同的。
注释
PostScript中使用 % 符号表示注释,% 之后的内容会被忽略。
图形基础概念
作为页面描述语言,PostScript的主要功能就是绘图和显示文字,那么有几个图形方面的基础概念是必须要了解的。
用户空间
用户空间是PostScript用于表达点和线的位置的坐标系。它的原点(0, 0)位于左下角, x轴向右,y轴向上,相当于数学中的第一象限。
用户空间的默认单位是点(point)。一个point相当于1/72英寸,这个值比真正打印机上的point(等于1/72.27英寸)稍稍短一些。
实际应用中,可以作以下定义,以便在PostScript程序中使用英寸、毫米等比较直观的单位。
/mm { 2.834646 mul } def % 毫米 /inch { 72 mul } def % 英寸 1 inch 20 mm moveto % 应用例
路径
路径是页面上一系列直线和曲线的组合。它并不是可见的线条,只是假想的一条路径而已。通过 stroke 和 fill 操作符可以描边或填充路径(用过Photoshop就能理解),使之可见。
图形状态
图形有许多状态(如当前颜色、当前路径、线的粗细、坐标系等)。程序有时需要临时改变图形状态,此时应该将当前的图形状态保存到图形状态栈(graphics state stack)中,画完之后再恢复状态。保存和恢复可以通过gsave和grestore操作符来实现。
建议继续学习:
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:charlee 来源: idv2
- 标签: PostScript
- 发布时间:2010-07-27 23:20:42
- [54] IOS安全–浅谈关于IOS加固的几种方法
- [53] android 开发入门
- [52] 如何拿下简短的域名
- [51] 图书馆的世界纪录
- [49] Oracle MTS模式下 进程地址与会话信
- [49] Go Reflect 性能
- [47] 【社会化设计】自我(self)部分――欢迎区
- [46] 读书笔记-壹百度:百度十年千倍的29条法则
- [36] 程序员技术练级攻略
- [27] 视觉调整-设计师 vs. 逻辑