postscript(转载)

介绍

POSTSCRIPT语言是打印机页面描述的一种程序设计语言。他拥有着广泛的图形操作,并且可以以任意方式,包含变量,函数,以及过程的任意组合

POSTSCRIPT页面描述是由解析器运行的程序(gs),postscript程序通常是由其他应用程序生成的。

1.1、POSTSCRIPT作为页面语言
POSTSCRIPT具有大量的图形运算符,通过他可以更加详细的描述你的页面。

文本的字体,位置,方向,尺寸。
几何图形的大小,方向,宽度,直线和曲线的位置,任何大小形状颜色的空间填充。

1.2、POSTSCRIPT作为编程语言

堆栈和运算

POSTSCRIPT 编程语言和其他编程语言一样,适用于各种各样的数据,例如数字,数组,字符串,以及字符。这种处理数据的方法被称为POSTSCRIPT对象。
操作数据的方法有多种多样,例如:将数据放入变量名,通过变量名称进行处理。POSTSCRRIPT语言拥有变量,但也可以通过堆栈直接操作数据。

2.1、POSTSCRIPT堆栈
堆栈是一段用于处理数据的内存,是一种先入后出的存储原理。

将数字入栈

12 6.3 -99

1.将12入栈
2.将6.3,入栈,12压栈
3.将-99 入栈,前两个一次类推

2.2、运算

加: 5 27 add => 32
减: 8.3 6.6 sub => 1.7
除: 13 8 div => 1.625
除返回整数: 25 3 idiv => 8
除返回余数: 12 10 mod => 2
乘: 6 8 mul => 48
取反: -27 neg => 27

组合运算:

6 + (3 ÷ 8)

  1. 3 8 div 6 add
    将3,8入栈,除运算,0.375,再讲6 入栈,加运算,得6.375
  2. 6 3 8 div add
    将6,3,8,入栈,8为栈顶,取3,8,进行除运算,结果再与6 进行加运算

8 - (7 × 3)

  1. 7 3 mul 8 exch sub
  2. 8 7 3 mul sub

exch:交换栈的前两项空间数据

清除所有栈数据: 6 8 12 32 clear => 空
复制堆栈顶部的数据: 6 dup => 6 6
删除堆栈顶部数据: 17 8 pop => 17
旋转数据: 7 8 9 3 1 roll => 9 7 8 : 789依次入栈,1代表从顶到底,798,978
7 8 9 3 -1 roll => 897 : -1代表从低端到顶端,879, 897

2.3、交互运算符
== 将堆栈顶端的数据打印出来,并且删除。
pstack 将整个栈中的数据打印数来,保留数据。

绘图

3.1、画线

  1. 线
newpath
144 72 moveto
144 432 lineto
stroke
showpage

我们首先调用newpath,初始化当画笔状态。

x y moveto,将画笔的移动到x,y的位置
x y lineto,以x,y为终点进行画线
stroke将构建的路径绘制到当前页面
showpage打印当前页

  1. 交叉线
newpath
72 360 moveto
144 72 rlineto
144 432 moveto
0 -246 rlineto
storke
showpage

x y rlineto:将画笔位以x,y进行位移

  1. 正方形
newpath
    270 360 moveto
    0 72 rlineto
    72 0 rlineto
    0 -72 rlineto
    -72 0 rlineto
4 setlinewidth
stroke showpage

setlinewidth:设置线的宽度

newpath
270 360 moveto
0 72 rlineto
72 0 rlineto
0 -72 rlineto
-72 0 rlineto
closepath
4 setlinewidth
stroke showpage

closepath:使图形闭合(不使用的时候在拐角的地方会出现一个小缺口)

3.2、填充图形

newpath
270 360 moveto
0 72 rlineto
72 0 rlineto
0 -72 rlineto
-72 0 rlineto
closepath
fill showpage  

fill:填充图形

newpath
270 360 moveto
0 72 rlineto
72 0 rlineto
0 -72 rlineto
-72 0 rlineto
closepath
.5 setgray
fill showpage

gray:设置透明度

多图的叠层

newpath
    252 324 moveto
    0 72 rlineto
    72 0 rlineto
    0 -72 rlineto
    -72 0 rlineto
closepath
.5 setgray
fill showpage
 
newpath
    270 360 moveto
    0 72 rlineto
    72 0 rlineto
    0 -72 rlineto
    -72 0 rlineto
closepath
.5 setgray
fill showpage
 
newpath
    288 396 moveto
    0 72 rlineto
    72 0 rlineto
    0 -72 rlineto
    -72 0 rlineto
closepath
.5 setgray
fill showpage

3.3、总结
closepath 闭合图形
lineto 绘制直线
moveto 设置绘制的起始点
newpath 初始化绘制方式
rlineto 移动
fill 绘制填充图形
setgray 设置透明度
setlinewidth 设置线的宽度
stoke 绘制路径
showpage 打印当前页

程序变量使用

4.1、变量定义
POSTSCRIPT 变量
变量的定义是将比那两名和值用def进行关联类似

/ppi 75 def   %将ppi定义为75
/ppi ppi 1 add def  %将ppi + 1的值定义为ppi ppi = 75 + 1 = 86

POSTSCRIPT 程序块

/inch {72 mul} def

4.2、使用程序和变量

方块

/box
{
72 0 rlineto
0 72 rlineto
-72 0 rlineto
closepath
}def
 
newpath
252 324 moveto box
.4 setgray fill
/inch {72 mul}def
 
/box
{
newpath moveto
1 inch 0 rlineto
0 1 inch rlineto
-1 inch 0 rlineto
closepath
}def
 
/fillbox
{
setgray fill
}def
 
3.5 inch 4.5 inch box
0 fillbox
2.75 inch 5 inch box
.4 fillbox

文本打印

5.1、POSTSCRIPT字体
字体是具有统一规格的字符集。其中包含数百个字符集,包含熟悉的TIMES和HELVETICA
使用POSTSCRIPT字体
在你打印文本之前,你需要指定所需的字体,这个过程需要三个步骤
1、查找字体的信息。保存在当前环境中,他用来提供特定字体的形状,信息。
2、将字体缩放。
3、将缩放的额字体设置为当前字体,然后打印出来

%!PS-Adobe-3.0
 
/Times-Roman findfont 15 scalefont setfont
 
72 200 moveto (typography) show
 
showpage

/Times-Roman findfont
查找名称为Times-Roman的字体,并把它保存到当前程序中

15 scalefont
设置字体大小为15号的Times-Roman字体

setfont
根据已经保存好的规格,对字体进行设置

5.2、动态设置字体大小

%!PS-Adobe-3.0

/scaleTimes % stack:scale
{
/Times-Roman findfont
each scalefont setfont
}def
 
6 scaleTimes
72 200 moveto (typography) show
showpage

/Times-Roman findfont 入栈
each scalefont setfont 入栈
6 scaleTimes意义为
6 /Times-Roman findfont each scalefont setfont
each 将 6 和 /Times-Roman 交换位置得
/Times-Roman findfont 6 scalefont setfont

默认支持的字体字体:
Times-Roman
Times-Bold
Times-Italic
Times-Boldltalic
Helvetica
Helvetica-Bold
Helvetica-Oblique
Helvetica-Boldltalic
Courier
Courier-Bold
Courier-Oblique
Courier-BoldOblique

%!PS-Adobe-3.0
 
/MainFont
    /STSong-Light--GB-EUC-H findfont 15 scalefont def
/SloganFont
    /Helvetica-Oblique findfont 7 scalefont def
/OwnerFont
    /Helvetica  findfont 10 scalefont def
 
/rightshow
{
    dup stringwidth pop
    150 exch sub
    0 rmoveto
    show
}def
 
/CardOutline
{
    newpath
    90 90 moveto
    0 144 rlineto
    252 0 rlineto
    0 -144 rlineto
    closepath
    .5 setlinewidth
    stroke
}def
 
/doBorder
{
    99 99 moveto
    0 126 rlineto
    234 0 rlineto
    0 -126 rlineto
    closepath
    2 setlinewidth
    stroke
}def
 
/Diamond
{
    newpath
    207 216 moveto
    36 -54 rlineto
    -36 -54 rlineto
    -36 54 rlineto
    closepath
    .8 setgray
    fill
}def
 
/doText
{
    0 setgray
    90 180 moveto
    MainFont setfont
    (AAA丶死刘) rightshow
 
    90 168 moveto
    SloganFont setfont
    ("The Club of Lonely Hearts") rightshow
 
 
    216 126 moveto
    OwnerFont setfont
    (Sam Spade) show
 
    216 111 moveto
    (Owner) show
}def
 
 
CardOutline
doBorder
Diamond
doText
 
showpage

/STSong-Light--GB-EUC-H 为中文打印(注:中文打印时候要求文件格式为gb2312,否则字库不识别,打印出来是乱码)

stringwidth 获取当前字符串的宽度,寄存起为字符串宽度 ,字符串内容,宽度在顶端
  dup stringwidth pop
  150 exch sub
  0 rmoveto
  show
意义为:
  获取字符串宽度,并将(150 - 字符串宽度的值,0)进行偏移,即:打印字符串在150处右端对其

图形转换

6.1、坐标系变换

POSTSCRIPT图形操作是在一个坐标系中,这个坐标系被称为用户坐标系或用户空间,该坐标系独立于任何物理设备。POSTSCRIPT在用户空间中进行绘制,并将结果传输到特定的打印机设备(设备空间)中。
在程序中,通常使用默认的坐标系。在这个空间中,原点是在页面的左下角,量度是1/72英寸长度。
然而,用户空间是可塑性的,他的坐标系可能是在位置,方向,大小变化后的。

6.1.1、用户空间的平移

translation是从一个地方运动到另一个地方。这个运动是原点的运动。例如:

100 200 translate

将会把原点右移100个单位,上移200个单位,随后的操作都是根据这个新的原点进行测量。

下面的程序将说明这个转化效果。

/Times-Roman findfont 30 scalefont setfont

/square        %一个填充的方块 

{

newpath      

0 0 moveto

90 0 lineto     %定义正方形的路径

90 90 lineto

0 90 lineto

closepath fill  %进行填充

6 92 moveto     %文本显示

(A Box) show } def

square              %绘制一个方块

200 250 translate   %将原点移动x + 200 y + 250

square              %绘制第二个方块

200 250 translate   %原点再次移动x + 200 y + 250

square              %绘制第三个方块

showpage

这个程序定义了一个方块,然后通过原点变换获得了三个方块。
注:原点的变换是相对于当前原点而不是起始原点。

6.1.2、用户空间的旋转

rotate是从栈中取出一个数然后进行逆时针旋转坐标轴。(旋转的单位是度)

/Times-Roman findfont 30 scalefont setfont

/square

{

newpath       

0 0 moveto

90 0 lineto   

90 90 lineto

0 90 lineto

closepath fill

6 92 moveto    

(A Box) show } def

square            

300 150 translate  

60 rotate           %逆时针旋转90度

square            

300 150 translate  

60 rotate           %逆时针旋转60度

square             

showpage

在此,我们只是改变了坐标系的位置,方块实际上的定义从未改变。

6.1.3、用户空间的缩放

scale运算符允许更改坐标单元的大小,这个运算符获取两个参数,一个x缩放,一个y缩放,例:

3 3 scale

执行此操作,将会将x,y放大为原来的三倍。

在此通过小方块例子

/Times-Roman findfont 30 scalefont setfont

/square

{

newpath       

0 0 moveto

90 0 lineto   

90 90 lineto

0 90 lineto

closepath fill

6 92 moveto

(A Box) show } def

square

300 150 translate

1.5 1.5 scale      %扩大1.5倍

square

300 150 translate

0.5 2 scale        %x 缩小到原来的1/2 y变为原来的2倍

square

showpage

6.2、绘图状态

目前为止,我们工作在当前的绘图状态中,构成当前环境包括(路径,点,灰度值,字体,线宽,用户坐标系)。

6.2.1、保存当前的图形状态

有时候我们需要保存当前的图形状态,以便以后可以随时得到他。

例如:如果想要打印一个填充的图形,我们就必须要构建一个适合的路径,然后填充他。不幸的是fill操作符将会清除当前的路径。所以在执行fill前应先保存当前的路径,在fill之后再恢复保存的路径就可以了。

保存和恢复图形状态的操作符是gsave和grestore。

gsave操作符将当前图形状态的副本保存在图形状态的堆栈中。这个堆栈最多可保存32个图形状态(包括当前图形状态)。

grestore操作符恢复最新的gsave图形状态。包括当时的所有特征,路径,灰度,线宽,用户坐标系。

下面是一个五角星来演示这些操作符的使用

/starside

{

72 0 lineto

currentpoint translate

-144 rotate } def

/star %stack: x y

{

moveto

currentpoint translate

4 {starside} repeat

closepath

gsave

.5 setgray fill

grestore

stroke }def

200 200 star

showpage

在这个程序,定义了两个步骤

starside绘制了组成该星星的一条直线。

star根据坐标点填充星星。

添加一条水平线

72 0 lineto       %星星的边长为72

将坐标系原点移动到水平线的末尾

currentpoint translate

再讲坐标系顺时针旋转144度

-144 rotate

repeat:这个运算符需要两个参数

一个数字(例子中的4)

一组用括号括起来的运算(starside)

意思为执行指定运算4次

4 {starside} repeat

然后跟上结束符,完成路径绘制

closepath          %画笔回到起点

进行图形的填充(填充操作会清空路径信息所以要提前保存,不保存的话1.路径被清空,不会显示变沿线以及中间的五边形,灰度清空所有线的灰度值会一样,看不出轮廓)

gsave

.5 setgray fill      %将灰度值设置为0.5进行填充

grestore         %恢复保存的默认状态 

以200 200的坐标点绘制当前图形,然后提交当前页面到纸张。

200 200 star

showpage

6.3、曲线

通常图形图像不仅仅是由直线构成,在postscript中可以使用运算符来定义任何期望的曲线。

6.3.1、圆弧

在本节中我们将讨论圆弧曲线arc

arc操作符是将圆弧添加到当前路径,他需要五个参数

弧的曲率中心的x,y坐标、曲率半径、以及从正x轴逆时针测量的弧的起始和结束角。

例:中心在100 150 半径为36 从逆时针45到90

100 150 36 45 90 arc

arcn运算符和arc类似,只是在方向上相反,为顺时针

100 150 36 45 90 arcn

第一条线是画了一个圆弧

第二条线是先将图龟移动到300 365 然后以40度起点到60度起点画弧

newpath

300 400 54 40 140 arc stroke

newpath

300 365 moveto

340 345 54 40 140 arc stroke

showpage

6.3.2、椭圆

圆可以认为是一个0到360的弧线

椭圆可以认为是将坐标系非均匀缩放之后的圆

/doACircle

{ 
  0 0 54 0 360 arc stroke } def /doAnEllipse {
  1 .75 scale        %横坐标不变,纵坐标变为原来的0.75   doACircle            %绘制圆     stroke } def
300 500 translate doACircle
4 {0 -72 translate        %不断缩放坐标系 doAnEllipse} repeat
showpage

6.3.3、圆角

相交线经常通过圆角进行连接,postscript的arcto运算符提供了一种方法。

操作员除了当前点意外需要两个点和一个半径

可以这样理解:以起始点开始,第一个点为线段连线,再将第一个点和第二个点连线,组成相交的一个角,以r为半径的圆相切与这两个直线,最后形成的弧度即为圆角。

987748-20181122154344856-806305519.jpeg

例:列出了两个圆角,分别为两条线段的角度,相同,第一条线段的长度相同,第二条线段的长度不同,可以看到,两个弧度的大小是一样的。

/DrawAnX

{

3 3 rmoveto

-6 -6 rlineto

0 6 rmoveto

6 -6 rlineto

stroke

}def

50 50 moveto DrawAnX

50 150 moveto DrawAnX

150 150 moveto DrawAnX

50 50 moveto

50 150 150 150 36 arcto

4 {pop} repeat

200 50 moveto DrawAnX

200 150 moveto DrawAnX

400 150 moveto DrawAnX

200 50 moveto

200 150 400 150 36 arcto

4 {pop} repeat

stroke

showpage

注意:在圆角绘制完成后,当前的图龟停留在最后一次相切的圆角的末端。以第一个圆弧为例,

起始点 50 50

圆规确定的点是 50 150 150 150 36

此时在arcto运算符执行后有一组数据保留在了堆栈中,分别是圆角的起始点和终止点。

起始点 50,150 - 36 = 114

终止点 50 + 36 = 86,150

这四个点对于我们来说是没有必要的,所以,我们要将其从堆栈中删除。

例:打印一个logo

% ------------ Define Procedures -------------

/Helvetica-Bold findfont 27 scalefont setfont

/fourpops               %四次出栈

{

4 {pop} repeat } def

/background             %绘制背景

{

0 18 moveto

0 72 108 72 18  arcto fourpops

108 72 108 0 18 arcto fourpops

108 0 0 0 18    arcto fourpops

0 0 0 72 18     arcto fourpops

fill } def

/moon

{

.6 setgray          %绘制logo中的小圆

81 45 18 0 360 arc fill

} def

/omaha                  %绘制字体

{

1 setgray

0 -1 moveto

1 2 scale               %更改坐标系比例

(OMAHA) stringwidth pop %获取OMAHA字符串的宽度

108 exch sub 2 ``div`      `%108 - (字符宽度 / 2)作为x的偏移量

0 rmoveto               %将图龟移动到该点进行字符串显示(目的为让字符串显示在logo的正中央)

(OMAHA) show } def

255 465 translate

background

moon

omaha

showpage

6.4、总结

repeat

重复执行程序n次

rotate

逆时针方向旋转坐标系

scale

x,y方向缩放坐标系

translate

将坐标原点进行移动

grestore

恢复上一次保存的图形状态

gsave

保存当前图形状态

arc     x y r ang1 ang2 -> —

逆时针添加圆弧

arcn    x y r ang1 ang2 -> —

顺时针添加圆弧

arcto    x1 y1 x2 y2 r -> xt1 yt1 xt2 yt2

绘制圆弧,参数五个,两点一个半径,返回值为圆弧的起始点和终止点

currentpoint — -> x y

返回当前点的坐标

条件语句

POSTSCRIPT语言中有许多操作符用于制定程序内流的控制。我们在前一章使用了一个repeat运算。所有的控制操作符都使用了之前简要提到的对象类型,即函数,用于我们调用。

7.1、函数

函数(既过程)是一个数组,其内容由解析器执行。

当解析器在程序中遇到一系列对象(值和名称)时,它执行与这些指令相应的操作,将对象放在堆栈上,查找和执行操作符和过程。

但是如果一系列对象用括号括起来,则不会立即执行,而是放在堆栈上。

86 23 add

该行使解析器将数字86和23相加

{86 23 add}

当执行置放时,操作符添加一个程序块,然后放在堆栈上,函数通常跟字面名称,后边跟def运算符,def运算符将其与当前所保存的名称相连(这就是函数的定义)。

7.2、条件语句

7.2.1、比较

POSTSCRIPT提供了一套比较完整额比较运算符,这些比较栈上的前两个项目,可以是任何类型,返回一个布尔类型的对象在堆栈上。他们的等效数学符号为:

eq =;  ne ≠;  gt >;  lt <;  ge ≥; le≤;

7.2.2、if

例:检查是否到达当前行的末尾

/chkforendofline
{  currentpoint pop     %获取x的位置
    612 gt              %是否大于612
    {0 -12 translate 0 0 moveto} if
}def

执行取出当前的x,比较看是否大于612(即本行的末尾),如果大于,则将原点移动到下一行的开头,当前点移动到原点的位置。

编写一个程序,对字符串进行简单的格式打印:检查该字符串的长度是否适合当前行,如果不适合,则移动到新的行,然后进行打印。

在这里定义了三个变量:左边距,右边距,当前文本的垂直位置,以及单独文本的垂直距离。

首先复制要打印的字符串,然后使用stringwith计算其长度,然后取出当前的x的位置,将两个值相加,看是否大于右边沿,如果大于,跳转到下一行进行打印。

% -------------- Variables ---------------
/LM 72 def              %定义左边沿
/RM 216 def             %定义右边沿
/ypos 720 def           %当前文本的垂直位置
/lineheight 14 def      %一行字符的高度
                        % of text
     
% ------------- Procedures ---------------
/newline                %跳转倒下一行
{   ypos lineheight sub     %720 - 14 即为下一行的垂直位置
    /ypos exch def          %将当前文本的垂直高度刷新为新的值
    LM ypos moveto } def    %将当前点移动到新的起始点
 
/prtstr                 %打印字符串
{   dup stringwidth pop     %取出打印的字符串的宽度
    currentpoint pop        %取出当前画笔x的位置
    add RM gt               %相加看是否大于右边距
    {newline} if            %条件符合,跳转到下一行
    show } def              %进行打印
 
%------------- Main Program --------------
/Times-Italic findfont 13 scalefont setfont
 
LM ypos moveto
(If ) prtstr (you ) prtstr (tell ) prtstr
(the ) prtstr (truth, ) prtstr (you ) prtstr
(don’t ))prtstr (have ) prtstr (to ) prtstr
(remember ) prtstr (anything. ) prtstr
(- Mark ) prtstr (Twain ) prtstr
showpage

7.2.3、ifelse

这个运算符需要堆栈上有三个对象:一个布尔值和两个执行语句。

如果为真,则执行第一个,否则执行第二个。

bool {op1} {op2} ifelse

例:使用ifelse生成了一组灰度交替逐渐减少高度的重叠梯形。

% ------- Variables & Procedures ---------
/scalefactor 1 def
 
/counter 0 def
 
/DecreaseScale
{   scalefactor .2 sub
    /scalefactor exch def } def
 
/IncreaseCounter
{   /counter counter 1 add def } def
 
/trappath                  
{   0 0 moveto 90 0 rlineto
    -20 45 rlineto -50 0 rlineto
    closepath } def
 
/doATrap
{   gsave                   %保存当前环境
    1 scalefactor scale     %设置当前坐标轴的压缩比例
    trappath                %绘制梯形
    counter 2 mod           %取余(看是否能被2整除)
    0 eq {.5} {0} ifelse   
    setgray fill            %设置灰度,然后进行填充
    grestore } def          %恢复环境
 
% ------------ Begin Program ----------
250 350 translate
 
5
{   IncreaseCounter         %标记当前的次数
    doATrap                 %绘制梯形
    DecreaseScale           %标记当前的坐标轴y的压缩比例
    0 20 translate          %每次将原点向上移动20个单位
    } repeat
showpage

7.3、循环

在POSTSCRIPT中有三个操作符用来建立和控制循环程序。我们已经使用过repeat操作。

7.3.1、for

这个运算符实现了计数循环。该操作符需要四个对象:

起始值、地增量、最终值、重复的过程。

for操作在每次执行过程之前将计数器的当前值放在堆栈上。

例:将字幕k在页面上每12个单元打印一次,

/Times-Italic findfont 10 scalefont setfont
 
100 100 translate
 
0 12 600 {0 moveto (k) show } for
 
showpage 

数字的变化不一定是整数,例如打印一个zip字符出现阴影效果

/Times-Italic findfont 30 scalefont setfont
 
/printZip
{ 0 0 moveto (Zip) show} def
320 400 translate
 
.95 -.05 0
{   setgray
    printZip
    -1 .5 translate } for
 
1 setgray printZip
 
showpage

7.3.2、loop,exit

许多程序需要重复一个不确定的数目,一段时间或者永远,或者满足某种条件,这时候就要用到重复。。。直到。。。这种。

POSTSCRIPT提供了一对运算符:loop,exit。

loop操作符重复执行其过程,直到遇到退出条件。退出导致程序离开包含该操作符的最内层循环。退出操作符还会终止for,repeat,forall操作符启动的循环。

例:此程序无限打印howdy。

{(Howdy ) show} loop

例:为了了解loop-exit如何一起工作,以下程序在当前页面绘制了几个圆

/pagewidth 8.5 72 mul def
 
/doCircle
{  xpos ypos radius 0 360 arc stroke} def
 
/increase-x
{  xpos radius add
    /xpos exch def } def
 
/lineofcircles %stack: radius y
{  /ypos exch def
    /radius exch def
    /xpos 0 def
    {   xpos pagewidth le
        {doCircle increase-x}
        {exit} ifelse
    }loop
} def
 
% --------------- Begin Program -----------
10 400 lineofcircles
30 400 lineofcircles
90 400 lineofcircles
showpage

你可能感兴趣的:(postscript(转载))