PostScript学习:另一种缩写为PS的技术
1.前言
PostScript是一种编程语言,直译为"后处理脚本"[相对印刷过程而言],学名为页面描述语言。更为详细的解释见维基百科,以及其翻译版百度百科。
值得一提的是,PostScript于1985年由Adobe推出,而Adobe的软件Photoshop 1.0版本于1990发布,两者的缩写均为PS。虽然就知名度而言,PhotoShop更为人所熟知,但是在Adobe的发家史中,PostScript扮演着非常重要的作用,时至今日PostScript的衍生技术PDF已经是一项ISO标准。
老实说,关于PostScript的基础知识,大家看维基百科的介绍以及附录的四本书就够了,四本书分别是:PostScript语言参考,PostScript语言教程与手册,PostScript程序设计,数学图表:几何与PostScript手册 。本文只是介绍PostScript的简单绘图知识。
大多数人开始接触PostScript大概是因为LaTeX,参考如何在论文中画出漂亮的插图?中 地铁风 的回答。LaTeX中常用的矢量图格式是EPS[Encapsulated PostScript],不过对PDF格式的支持应该是最好的。大多数与LaTeX相关的绘图与渲染都是基于PostScript技术的,如PSTricks、TikZ /PGF、MetaPost以及Asymptote。Texample.net上有大量关于Tikz的例子。Asymptote官网也有大量 asy的例子。
2.简单例子
在学习之前,你需要两个软件:GhostScript和SumatraPDF。GhostScript是PostScript的一个开源解释器;SumatraPDF是一个文档阅读器,默认支持PDF,结合GhostScript可以支持PostScript。
HelloWorld的例子,保存到文本文件HelloWorld.ps, 用SumatraPDF打开:
%!PS /Courier % name the desired font 20 selectfont % choose the size in points and establish % the font as the current one 72 500 moveto % position the current point at % coordinates 72, 500 (the origin is at the % lower-left corner of the page) (Hello world!) show % stroke the text in parentheses showpage % print all on the page
下面给出C语言生成PostScript图案的一个简单例子:
1 #include <math.h> 2 #include <stdio.h> 3 void init(FILE* fp) 4 { 5 int w=640,h=480; 6 fputs("%!PS-Adobe-3.0 EPSF-3.0\n",fp); 7 fprintf(fp,"%%%%BoundingBox: 0 0 %d %d\n",w,h); 8 fputs("/rgb {setrgbcolor} def\n",fp); 9 fputs("/np {newpath} def\n",fp); 10 fputs("/cp {closepath} def\n",fp); 11 fputs("/mt {moveto} def\n",fp); 12 fputs("/ll {lineto} def\n",fp); 13 fputs("/st {stroke} def\n",fp); 14 fputs("/lw {setlinewidth} def\n",fp); 15 fputs("/line {np mt ll st} def\n",fp); 16 } 17 void close(FILE* fp) 18 { 19 fputs("showpage\n%%EOF",fp); 20 fclose(fp); 21 } 22 void line(FILE* fp,float x[],float y[],int n) 23 { 24 fprintf(fp,"0 0 1 rgb\nnp\n%.3f %.3f mt\n",x[0],y[0]); 25 for(int i=1;i<n;i++){ 26 fprintf(fp,"%.3f %.3f ll\n",x[i],y[i]); 27 } 28 fprintf(fp,"st\n"); 29 } 30 #define N 628 31 int main() 32 { 33 34 FILE* fp=fopen("main.ps","wb"); 35 init(fp); 36 // 37 float x[N],y[N],t; 38 for(int i=0;i<N;i++){ 39 t=-3.14+i/100.0; 40 x[i]=200*sin(3*t)*cos(t)+320; 41 y[i]=200*sin(3*t)*sin(t)+240; 42 } 43 line(fp,x,y,N); 44 // 45 close(fp); 46 return 0; 47 }
如图所示,更多的例子可以看之前提到的四本书,三维绘图请看第四本数学图表:几何与PostScript手册:
另一个简单例子,可以作为一个练习,更多例子见我的GitHub:LearnPostScript。
3.复杂一点的例子
这个例子需要一点三维旋转矩阵的知识,代码仅作演示:
1 #include <math.h> 2 #include <time.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #define Random rand()%255 6 #define Length(x) (sizeof(x)/sizeof(x[0])/3) 7 const float PI=3.1415926536; 8 typedef float vector3[3]; 9 //三维点数组结构 10 typedef struct { 11 int length; 12 float *x, *y, *z; 13 }PointArray ; 14 // 15 //三维点初始化 16 PointArray Array3d(int n) 17 { 18 PointArray r; 19 r.length = n; 20 r.x = (float*)malloc(n*sizeof(float)); 21 r.y = (float*)malloc(n*sizeof(float)); 22 r.z = (float*)malloc(n*sizeof(float)); 23 for (int i = 0; i < n; i++) { 24 r.x[i] = r.y[i] = r.z[i] = 0.0; 25 } 26 return r; 27 } 28 //三维点通过数组赋值 29 PointArray eval(float *a, int n) 30 { 31 PointArray r; 32 r.length = n; 33 r.x = (float*)malloc(n*sizeof(float)); 34 r.y = (float*)malloc(n*sizeof(float)); 35 r.z = (float*)malloc(n*sizeof(float)); 36 for (int i = 0; i < n; i++) { 37 r.x[i] = a[3*i]; 38 r.y[i] = a[3*i + 1]; 39 r.z[i] = a[3*i + 2]; 40 } 41 return r; 42 } 43 //输出三维点数组的数据 44 void print(PointArray r) 45 { 46 for (int i = 0; i < r.length; i++) { 47 printf("%f %f %f\n", r.x[i], r.y[i], r.z[i]); 48 } 49 } 50 // 51 void fileprint(FILE* fp,PointArray r) 52 { 53 for (int i = 0; i < r.length; i++) { 54 fprintf(fp,"%f %f ", r.x[i], r.y[i], r.z[i]); 55 } 56 fprintf(fp," quad\n"); 57 } 58 // 59 //对三维点进行旋转 60 void Rotate(PointArray pa, vector3 vec,float t) 61 { 62 float x,y,z,a,b,c; 63 float base=sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]); 64 a=vec[0]/base,b=vec[1]/base,c=vec[2]/base; 65 for (int i = 0; i < pa.length; i++) { 66 x=pa.x[i],y=pa.y[i],z=pa.z[i]; 67 // 68 pa.x[i]=(cos(t)+(1-cos(t))*a*a)*x+ 69 ((1-cos(t))*a*b-sin(t)*c)*y+ 70 ((1-cos(t))*a*c+sin(t)*b)*z; 71 // 72 pa.y[i]=((1-cos(t))*b*a+sin(t)*c)*x+ 73 (cos(t)+(1-cos(t))*b*b)*y+ 74 ((1-cos(t))*b*c-sin(t)*a)*z; 75 // 76 pa.z[i]=((1-cos(t))*c*a-sin(t)*b)*x+ 77 ((1-cos(t))*c*b+sin(t)*a)*y+ 78 (cos(t)+(1-cos(t))*c*c)*z; 79 } 80 } 81 // 82 void RotateX(PointArray pa,float t) 83 { 84 float x,y,z; 85 86 for (int i = 0; i < pa.length; i++) { 87 x=pa.x[i],y=pa.y[i],z=pa.z[i]; 88 89 pa.x[i]=x; 90 // 91 pa.y[i]=y*cos(t)-z*sin(t); 92 // 93 pa.z[i]=y*sin(t)+z*cos(t); 94 } 95 } 96 // 97 void RotateY(PointArray pa,float t) 98 { 99 float x,y,z; 100 for (int i = 0; i < pa.length; i++) { 101 x=pa.x[i],y=pa.y[i],z=pa.z[i]; 102 // 103 pa.x[i]=z*sin(t)+x*cos(t); 104 pa.y[i]=y; 105 pa.z[i]=z*cos(t)-x*sin(t); 106 } 107 } 108 // 109 void RotateZ(PointArray pa,float t) 110 { 111 float x,y,z; 112 for (int i = 0; i < pa.length; i++) { 113 x=pa.x[i],y=pa.y[i],z=pa.z[i]; 114 pa.x[i]=x*cos(t)-y*sin(t); 115 pa.y[i]=x*sin(t)+y*cos(t); 116 pa.z[i]=z; 117 } 118 } 119 //透视投影 120 void Perspective(PointArray pa,float ez,float n,float f) 121 { 122 float x,y,z; 123 for (int i = 0; i < pa.length; i++) { 124 x=pa.x[i],y=pa.y[i],z=pa.z[i]; 125 pa.x[i]=(ez-z)/(n-f)*x; 126 pa.y[i]=(ez-z)/(n-f)*y; 127 pa.z[i]=z; 128 } 129 } 130 // 131 static float yrot=45; 132 // 133 inline float fun(float x,float y) 134 { 135 //Matlab Peaks Function 136 float z=3*(1-x)*(1-x)*exp(-x*x - (y+1)*(y+1)) 137 - 10*(x/5 - x*x*x - y*y*y*y*y)*exp(-x*x-y*y) 138 - 1.0/3*exp(-(x+1)*(x+1) - y*y); 139 return z*10; 140 } 141 // 142 void display() 143 { 144 float a[12]; 145 PointArray ptsa,ptsb; 146 float d=0.1,factor=50; 147 float x,y,z1,z2,z3,z4; 148 FILE* fp=fopen("main.ps","wb"); 149 fprintf(fp,"%%!PS-Adobe-3.0 EPSF-3.0\n%%%%BoundingBox: -250 -250 250 250\n/rgb {setrgbcolor} def\n/np {newpath} def\n/cp {closepath} def\n/mt {moveto} def\n/rmt {rmoveto} def\n/ll {lineto} def\n/rl {rlineto} def\n/st {stroke} def\n/lw {setlinewidth} def\n/line {np mt ll st} def\n/quad {np mt ll ll ll cp st} def\n0 0 1 rgb\n0.2 lw\n"); 150 for(x=-3; x<3; x+=d) { 151 for(y=-3; y<3; y+=d) { 152 z1=fun(x,y); 153 a[0]=x*factor,a[1]=z1,a[2]=y*factor; 154 z2=fun(x+d,y); 155 a[3]=(x+d)*factor,a[4]=z2,a[5]=y*factor; 156 z3=fun(x+d,y+d); 157 a[6]=(x+d)*factor,a[7]=z3,a[8]=(y+d)*factor; 158 z4=fun(x,y+d); 159 a[9]=x*factor,a[10]=z4,a[11]=(y+d)*factor; 160 // 161 ptsa=eval(a,Length(a)); 162 RotateY(ptsa,yrot); 163 RotateX(ptsa,15*2*PI/360); 164 //ptsb=RotateZ(ptsb,6); 165 fileprint(fp,ptsa); 166 } 167 } 168 fprintf(fp,"\nshowpage\n%%%%EOF\n"); 169 fclose(fp); 170 yrot+=0.1; 171 } 172 int main() 173 { 174 display(); 175 }
4.结束语
虽然越来越多的新技术涌现出来,但是它们都不是凭空产生的。PostScript作为一项古老的技术,对于文档的排版和印刷而言,依然是值得学习的。