Quick PostScript Programming Tutorial
9 minutes read | 1819 words by Ruben BerenguelFor those of you who don’t know, PostScript is a programming language (and also the name of the type of files you write in this language) used as a way to define what a page looks like. A PostScript file contains instructions which create text, or draw images, and these instructions can do quite a lot of work. PostScript is directly processed by almost all laser printers, and if you are a Linux or MacOs user you already have some mean to process and open it. In Windows you can download an install AFPL GhostScript, or visit this page to convert a PS to PDF.
As you may already know, PostScript is a Turing complete programming language. This means that any program you can write in any programming language, you can write in PostScript (albeit it will be slower). As geekish and curious as I am, a few days ago I decided to learn some of its core features, and as I tended to forget new languages almost as quick as I learnt them (Oh Python, where are thou?) I started writing some small reminders. If I forgot, I just read them and ready to go. In this quick tutorial/knowledge-refresher for PostScript programming, I’ll sketch some key points and show a pair of examples: a Mandelbrot set and a Koch’s snowflake. As a side note, the code for the snowflake is quite general for L-systems.“My” code for this system is a rewriting, rescaling and simplifying of the code from Michel Charpentier at this page. When you are through this tutorial, take a look at his code, he is much more clever than I am when PostScript-ing, and his results are awesome.
From now on, PS will mean, of course, PostScript.
PS is stack
oriented,
based on RPN (reverse polish
notation). This
means that you have a stack, like a stack of plates, and if you want to multiply
3 by 4, you should write 3 4 mu
to push 3 and 4 to the stack (4 being the
topmost element now). Mul is the command for multiplying, as you may guess. 3
and 4 get pushed to the stack and then multiplied. Now the instruction mul
leaves his result in the stack, ready to be used. Keep how the stack works in
mind. When writing some complex formulae or algorithms, the state of the stack
is important, if you start to lose track, write it down on the side. Also, it is
more efficient and quicker to just use what you have on the stack and don’t rely
on named variables.
To make a comment within a PS program, write %
, and it is customary to start
every PS file with the comment line %!PS
which allows all devices to recognize
it as PostScript.
There are two separate stacks: a operand stack and a dictionary stack. Well, in fact as xardox from Reddit points out, there are 3 stacks, these two and the function stack: thanks to it you don’t need to mess with function call and return addresses in the other two stacks. In the first one, operands (as 3 and 4 in the previous example) are kept. In the second one, names of operators or variables are kept. And as it is a stack, if you define a variable named A twice, when you call it only the most recent (nearer to the top of the stack) will be used.
In fact, defining a variable or defining an operator has the same syntax as
defining a variable. /name content def
for a variable, /name {operations} def
for an operator. I find this nice (very
Lisp-y).
Variable and function names are made of combinations of characters (a…z) and
numbers, as usual in other programming languages. Curiously enough, you can
name a variable 7U
if you want, but starting variables with a number is “bad
style”, usually. Keep it in mind. You may wonder why I wrote /name
when defining
a variable. Well preceding a name with a /
(slash) puts the name itself as
operand in the stack, without the slash, PS looks for it in the dictionary stack
and puts its corresponding value in the stack. (Thanks to
xardox from Reddit for the slash/backslash
distinction).
After so much talking, sure you are wondering How does one draw? Well,
- first we have to define a path with
newpath
, - then we issue some movement and tracing commands,
- then
fill
orstroke
these commands, - and finally finish the path.
As a starting point, a square 200 points per side in the bottom left of the page:
newpath
200 200 moveto % as the name says, move the cursor to x y
200 0 rlineto % line from actual point toward the right
% side
0 200 rlineto % the actual point is now in the right side,
% go up
\-200 0 rlineto % go left
0 -200 rlineto % ta daa A Square
closepath
stroke
showpage
newpath
and closepath
enclose a drawing path, and moveto
moves the cursor to an
starting position. rlineto
creates lines in relative coordinates (from the
actual point taken as (0,0)), a similar construct with lineto
uses global
coordinates (and there is also a rmoveto
). stroke
draws the previous paths, and
if we were to put fill
instead of stroke
, it would be a black square.
Changing colors is as easy as (before stroking or filling) issuing a num setgray
for a gray level, where num
is between 0 (black) and 1 (white). If you prefer
RGB, Red Green Blue setrgbcolor
would do the same with RGB values (also ranging
from 0 to 1 for each colour component).
Writing is a little trickier in PostScript. First, you have to find a font
with name findfont
, then numpt scalefont
to scale it to a
certain number of points, finally setfont
. And how do you write?
newpath
, move
to wherever you want and (Text) show
. Show acts as
both a stroke
and closepath
for text, so if you want to change colors or something, do so
before writing.
/Times-Roman findfont
20 scalefont
setfont
newpath
200 200 moveto % as the name says, move the cursor to x y
200 0 rlineto % line from actual point toward the right side
0 200 rlineto % the actual point is now in the right side, go up
\-200 0 rlineto % go left
0 -200 rlineto
235 295 moveto
(Hi, I'm A Square) show % ta daa A Square
closepath
stroke
showpage
You can rotate
, translate or scale your constructs (and if you want to keep
your previous transformation safe, issue a gsave
before doing anything and a
grestore
after doing it, this way the initial and final position states are the
same). To rotate
by 60 degrees our friend A Square, put a 60 rotate
command
after the first moveto
. Also, if you pretended to draw something in the usual
orientation, you should issue a gsave
before rotating, and a grestore
after
stroking. This is important!
/Times-Roman findfont
20 scalefont
setfont
gsave
newpath
200 200 moveto % as the name says, move the cursor to x y
60 rotate
200 0 rlineto % line from actual point toward the right side
0 200 rlineto % the actual point is now in the right side, go up
\-200 0 rlineto % go left
0 -200 rlineto
35 95 rmoveto
(Hi, I'm A Square) show % ta daa A Square
closepath
stroke
grestore
gsave
newpath
0.6 setgray
200 200 moveto
30 rotate
200 0 rlineto
0 200 rlineto
\-200 0 rlineto
0 -200 rlineto
35 95 rmoveto
(Hi, I'm A Square) show
closepath
stroke
grestore
newpath
0.8 setgray
200 200 moveto
200 0 rlineto
0 200 rlineto
\-200 0 rlineto
0 -200 rlineto
35 95 rmoveto
(Hi, I'm A Square) show
closepath
stroke
showpage
This starts to get annoying, so let’s define a square function, which assumes we are already at a point where we will draw a square of a certain side (the side value will be in the stack).
/box {
dup dup dup % this places 4 instances of the side in the stack
0 rlineto % just one argument, as below we have side x 4
0 exch rlineto % exch swaps the first and second argument
neg 0 rlineto % neg changes the sign
neg 0 exch rlineto % you guess?
} def
and this simplifies the gsave-grestore
blocks as
gsave
newpath
0.6 setgray
200 200 moveto
x rotate
200 box
35 95 rmoveto
(Hi, I'm A Square) show
closepath
stroke
grestore
dup
is a really useful command, which duplicates the topmost item in the stack,
you will be using it a lot. And the same goes for exch
, which swaps the first
and second items. The most useful one, when doing some “big” code is m n roll
…
but it is trickier. As an example, if you have n
elements in the stack, and want
to move the topmost to the bottom (shifting), you should n 1 roll
. If instead
you want to shift to the other side, n n-1 roll
. Have a look at how it works in
some references below if you need anything harder, but these two are
almost all there is to it. neg
, together with add
sub
mul
and div
form the core
arithmetic structure.
PS of course has a number of flow control structures. The simplest, is repeat
.
As a simple example, 3 {dup} repeat
would result in the first line in our box
operator. Enclosing something in braces lets PS know it is a set of
instructions, by the way.
The for
loop is by far the most interesting and the one you (well, at least me)
will be using more often. Its syntax is initial increment final {what to do} for
.
for
puts the counter at the topmost of the stack, so if you want to have a named
variable corresponding to the counter, the first line in the for
block should be /n exch def
. Later on in the final samples I will show some nested for
constructs
for more entertainment value.
For conditionals, you have if
and ifelse
. They are quite straightforward,
condition {true procedure} {false procedure} ifelse
and if
it were an if
, dont
write a false procedure.
I think that this is almost all needed to start with PostScript.
Now, the “hard” examples. Click to go to a gist with the code!
And some references
- Wikipedia article
- L-systems in PostScript
- A PostScript web-server
- A first guide to PostScript
- PostScript Language Reference (Red book) (if this link stops working, just Google for it, it’s freely available from Adobe but the location changes)
If you enjoyed this small tutorial, please share its link with your friends! It would made a difference, PostScript is a wonderful programming language which deserves to be more widely known.