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.

Koch curve

Mandelbrot set

Christmas card
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
fillorstrokethese 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.