PostScript(PS/EPS格式)讲解
 作者:bobob
 
一、PostScript概述
Postscript既是一种页面描述语言,也是一种高级解释型脚本语言。由于它与设备的无关性,使得它无论在那种平台上,都能忠实的再现原貌,从而被广泛应用于打印出版行业,同时由于它是一种解释型脚本,所以它也可以像一般编程语言一样用来解决某些问题。
和我们熟悉的pdf文件相比,两者有几个明显的区别:
1.       pdf有严格的文件结构(文件头,所有对象,交叉引用表,文件尾,线性化pdf也有固定格式)和文档结构(由catalog穿起来的逻辑结构),ps则没有这些;
2.       ps的数据类型有十几种,pdf只有8种;
3.       ps有一般编程语言所具有的结构控制条件语句,比如if,ifelse,for,forall,loop,以及function等等,而pdf没有;
4.       pdf最终是要显示出来给别人看的,ps不仅限于这个目的,它还可以做为脚本实现一定非显示的功能;
5.       ps语言中的标准操作符有400多个,有的一个操作符有好几种使用方式(参数类型和数量不同),而且ps没有保留字,这些标准操作符完全可以通过ps脚本改变其原来的含义;
6.       ps文件一般都是没有加密的,所以文件很大,pdf相对要小很多。
 
通常,一个postscript语言的阅读器,应该包含以下几个大部分:扫描器,解释器,操作数栈,执行栈,字典栈,图形状态栈,虚拟内存区,字体处理块,颜色处理块,以及最后的输出功能,下面将详细说明。
 
二.Postscript详细介绍
 
1.基本类型的数据结构
简单 OBJECTS  复合 OBJECTS  
boolean  array  
fontID  dictionary  
integer  file  
  gstate (LanguageLevel 2)  
name  packedarray (LanguageLevel 2)  
null  save  
operator  string  
real     
 
Array:能容纳不同类型的对象;可以通过index来访问其中的元素;对array的访问必须有下表安全检查,array长度有最大实现限制;要支持嵌套;值的存储符合复合对象的特征。
String:它的元素值必须是0-255之间;长度受程序实现的限制;要能处理转义字符’\’;值的存储要符合复合对象的特征。
Dictionary:用来存储键-值对,要实现在字典中插入一个条目;能查询一个键并得到它关联的值;字典在创建的时候要指定最大条目数,当插入一个条目的时候超过最大条目数,ps level1返回一个dictfull错误,level2以上则自动扩展,最大条目数受字典实现的限制;能支持和字典相关的操作符的实现;值的存储要符合复合对象的特征。
File:解释器和运行环境之间用来交流的一个可读或可写的字符流。要支持像磁盘文件这样可以永久保存的类型,也要支持动态生成的类型;一个file对象要创建并打开一个文件;支持其他操作符的读写操作;支持类似read,readline,write,writeline等等的操作;
Save:save操作符获取当前local虚拟内存的状态快照并返回描述此快照的save object,restore则把local虚拟内存的状态恢复到save产生的快照状态。Restore要实现以下功能:丢弃自从对应的save以来所有在local虚拟内存上产生的对象,归还占用的空间;恢复save时local虚拟内存中的所有复合对象(不包括string);隐式调用grestoreall操作符,把graphics state的状态恢复到save的时候的状态;关闭自save以来所有打开的文件(local虚拟内存起作用的时候打开的)。Restore不会影响操作数栈,字典栈,执行栈,以及global虚拟内存。Save 和 restore可以嵌套。
Gstate:一组图形控制参数,分为两大类:设备无关的包括CTM,position,path,clipping path,clipping path stack,color space,color,font,line width,line cap,line join,miter limit,dash pattern,stroke adjustment;设备无关的包括color rendering,overprint,black generation,undercolor removal,transfer,halftone,flatness,smoothness,device。要实现以上特性的存取操作。
 
2.扫描器。
 
这个是实现浏览器的基础,从字符流中按照postscript语法解析成一个一个的对象,ps‘编码方式有3种:ASCII, binary token, 和 binary object sequence。其中ASCII的编码方式很普遍,所以只考虑这种方式的编码。扫描器要能识别一下几种ps元素:
A.空白符。除了注释和串中的以外,空白符当作分割对象的单位,连续的空白符当作一个处理;CR和LF以及CR+LF都当作换行处理。
B.注释。除了在字符串中,%之后直到换行的所有字符。
C.数字。包括3中类型:符号整数(比如0,+5,-3),实数(比如-.002,34.5,-3.62,123.6e10, 1.0E-5,1E6,-1.,0.0),指数(比如8#1777,16#fff,2#100)。
D.string。有3种形式:()包括的字面文本;<>包括的十六进制编码文本;<~~>包括的ASCII-Base85编码数据。其中()中的数据处理依据以下原则:如果()内包含配对的(),则不需要特殊处理;如果有单个的(或),用\来处理;用\处理的其他转义字符。
E.名字。由常规字符组成又不能解释为数字的词法单位就当作名字处理。除了空白符和界定符之外的所有字符都可以出现在名字中。/引导一个字面名字,但它本身不是名字的一部分。
F.数组。[和]界定一个数组,从[开始收集元素,]则构造一个包含这些元素的数组对象。[和]是操作符。
G.过程。{和}界定一个可执行数组。
H.字典。<<和>>构造一个字典,过程和[]几乎一样,里面包括key1 value1 key2 value2… keyn valuen这样的对,构造后的字典由解释器放入字典栈。
 
3. operand stack,execution stack,dictionary stack,graphic state stack,clipping path stack。
 
这5个栈和后面的虚拟内存以及gstate是postscript执行环境的主要部分。
A.操作数栈。
可以存放任意postscript对象,因为postscript是操作数在前,操作符在后的语法规则,所以解释器遇到操作数都是先压入操作数栈,直到遇到操作符再从栈顶拿操作数。同时,操作数栈中还可以存放执行的中间结果。操作数栈直接被ps解释器控制,大多数ps操作符可以直接push和pop操作。
B.执行栈。
存放可执行的对象,当解释器延迟当前可执行对象去执行另外一个对象的时候,就把当前可执行对象压入执行栈,等执行了之后再执行它。这个也相当于postscript程序的调用堆栈。受ps解释器控制,ps程序可以读,但是不能写。
C.字典栈。
只存放字典对象,从最底往上,3个对象依次是systemdict,globaldict,userdict。其中systemdict是一个只读的字典,定义了所有标准的操作符(400多个)globaldict和userdict包括了用户自己在虚拟内存中定义的一些变量和操作符,配合save等操作符使用。直接受ps解释器控制,但是只能保存dictionary对象,且最下面的3个字典(systemdict,globaldict,localdict)不能被pop,只有bengin,end,clearstack可以改变dictionary stack。
典型示例:
/average {add 2 div} def
40 60 avergage
执行步骤:
1.把名字average和过程{add 2 div}压入操作数栈
2.遇到def后,查找字典栈,如果这个操作符没有被重新定义,则它的意义是把操作数栈栈顶的两个元素(average和{add 2 div})pop出来,在当前字典中添加一个条目,key是average,value是{add 2 div}。
3.把40和60依次压入操作数栈。
4.遇到average,在字典栈中查找,执行它关联的值(这里是){add 2 div}。这个步骤的执行过程是先执行add,从操作数栈栈顶pop出两个数(40和60),相加,然后把结果100压入操作数栈;遇到2压入操作数栈;遇到div,在字典栈中查找,如果它没有被重新定义,则pop出操作数栈栈顶的两个数,做除法运算,然后把结果50压入操作数栈。
D.图形状态栈。
用来维护一组控制文本和图像显示状态的参数。
设备无关类参数:
1.CTM。当前转换矩阵,把位置从用户坐标转换到设备坐标。
2.position。用户空间当前点的坐标。
3.path。由path构造操作符创建并增加,并隐含的做为类似fill,clip,stroke等操作符的参数。
4.clipping path。设置当前的裁剪区域。
5.clipping path stack。一个clipping path的stack,存放由clipsave保存而且没有被cliprestore释放的clipping path。
6.color space。要被解释的颜色值的种类,比如Device Gray。
7.color。描绘操作符使用的颜色,和color space有关,一般1-4个值。
8.font。用字典来表示当前字体的绘图形状集合。
9.line width,line cap,line join,miter limit。线条相关的特性。
10.dash pattern。线条被stroke的时候,线的虚实类型描述。
       设备相关参数:
1.color rendering。如何把CIE-based color 转换成device color 的一组参数。
2.overprint。是否覆盖周边颜色。
3.black generation。RGB转换成CMYK的时候,用来产生黑色的一个过程。
4.undercolor removal。计算CMY减少的总量,用产生的黑色做补偿。
E.clipping path stack。由clipsave和cliprestore控制的一个栈,存放clipping path。由于这个栈是graphics state stack的一部分,所以grestore和setgstate会替换整个clipping path stack。
 
4.解释器。
在扫描器分析出一个对象和它的属性、类型的时候,解释器就要负责解释它。主要包括一下几个方面:
A.对注释的处理。遇到不是串中的%时,过滤掉%到换行符(或文件结束符)之间的所有字符,当作一个空白字符处理。
B.对数字的处理。分析出来的数字一律压入操作数栈;
C.对名字的处理。名字有3类:前面有/的文法名字,无前缀的可执行名字,带前缀//的立即替换名字。第一种名字压入操作数栈,第二种名字压入执行栈,第三种名字由解释器查找字典,找到后压入操作数栈。
D.对字典的处理。当<<执行时,把一个对象压入操作数栈,>>执行时这个字典会被创建,然后压入字典栈。
E.对数组和打包数组的处理。解释器遇到数组时,会顺次执行数组里面的元素。
F.对过程对象的处理。解释器遇到过程对象的时候并不会立即执行,而是先压入操作数栈,等到明确被调用时才会被执行。
 
5.虚拟内存。
虚拟内存是psotscript中的一个特殊概念,没有规定物理实现方式,但不管怎么实现,都要符合一下原则。虚拟内存包括4个部分:
A.local虚拟内存。Local虚存是一个类似栈的存储池。在它上面申请内存或修改变量都受制于save和restore。一般而言,local虚拟内存主要是维护当前页面用到的一些参数和变量,作用范围是当前页面(save和restore之间)。
B.global虚拟内存。Global虚拟内存是一个没有固定规律的存储池,在一个程序中,在global虚存中申请内存或修改变量不受save和restore影响。一般是维护整个文件级别的一些参数,用来保证每页开始的时候初始状态都是相同的。
C.local和global之间的交互。包括local和global状态的转变,local和global之间存储数据的相互操作的实现等等。
D.Local虚拟内存在save-restore机制影响下的行为。当一个restore发生时,自上一个save以来,所有在local虚拟内存中申请的内存都会被释放,所有打开的文件都要关闭,同时会恢复gstate的状态。 

解决方案 »

  1.   

    在我的blog里面同步,欢迎大家去我的blog看看
      

  2.   

    请问各位高手:
    newpath
     0 setlinecap 
     1 setlinejoin 
     0.120000 setlinewidth 
     1.000000 0.000000 1.000000 0.000000 setcmykcolor 
     136.570837 471.347424 moveto 
     136.920837 471.953632 0.699992 239.999610 300.000390 arcn 
    stroke中的136.920837 471.953632 0.699992 239.999610 300.000390 arcn 是在画什么,arcn前面的五个参数都是代表什么,另外arc与arcn有什么区别?