When I entered industry, it was common to maintain a small library of relevant books (as in physical books, bound paper) in one's cubicle.
Upon arriving at my very first cube, I discovered that the previous occupant had left me several books:
- Adobe Systems. PostScript Language Program Design. Addison-Wesley, 1988.
- Adobe Systems. PostScript Language Reference Manual, Second Edition. Addison-Wesley, 1990.
- Adobe Systems. PostScript Language Tutorial and Cookbook. Addison-Wesley, 1985.
The work at hand involved communicating with some high-end printers to produce nicely printed documents, so it's not surprising that they thought knowledge of PostScript to be relevant.
And so I began to read, and to write: small PostScript files by hand to understand the fundamentals of this printer language.
I hold that the most interesting aspect of PostScript is that a PostScript file doesn't represent a document so much as it describes a computer program to produce a document. Rather than file descriptors, sockets, and memory pages, the atoms of a PostScript program are text, paths, and graphics. When your printer puts toner to paper, it's executing a program.
PostScript is heavily oriented around stacks
- placing data on top of them, and then popping the top items off,
transforming them, and placing the transformed values back onto the
stack. To this end, the syntax of the language features reverse
Polish notation, or RPN. Compared to the more familiar in-fix style
of most popular languages (e.g. 1 + 2) or prefix style in
others (+ 1 2), in RPN, the operation appears last (e.g.
1 2 +). You can visualize the stack-iness with that
example:
- Place 1 onto the stack.
- Place 2 onto the stack.
- The
+pops the two items off the stack (1, 2), transforms them (resulting in 3), and places that 3 back onto the stack. - Our stack is now a solitary 3.
RPN makes the order of operations clear without need for parenthetical expressions, but requires one to track the execution stack when the code is read left-to-right. This style of notation is found on some famous calculators as it allows complicated expressions to be evaluated without the user needing to track balancing parenthesis.
Despite the interesting syntax, the familiar control structures are available. For example, consider this recursive procedure:
/Fract % Name our procedure "Fract"
{
45 rotate % Rotate the text
(descending) show % Write the word
gsave .6 .6 scale % Scale the next generation down
1 setlinewidth
down DoLine
depth maxdepth le % Until we hit maxdepth...
{
135 rotate Fract % ...rotate one way, recurse
-270 rotate Fract % ...rotate another way, recurse
} if
up grestore
} defWhile the operator order might take some mental adjustment, the familiar shape of classical recursion is clear. We write a word, apply some transformations, and then write the word again, on and on until a defined maximum depth is reached. The result is a text-based fractal, with the word "descending" curling towards infinity.
Let's first look at a rasterized version of that document. Click the thumbnail to view full-size, or if you feel like exercising your CPU a bit, view the PDF.
Sure enough, the word "descending" is rendered smaller and smaller in a self-similar recursive pattern.
The entire PostScript file is below, or download it for your recursing pleasure:
<< /PageSize [1728 2592] >> setpagedevice
/depth 0 def
/maxdepth 10 def
/down {/depth depth 1 add def} def
/up {/depth depth 1 sub def} def
/DoLine
{0 2080 rlineto currentpoint translate 0 0 moveto} def
/Fract
{
45 rotate % Skew the text
(descending) show % Write the word
gsave .6 .6 scale % Scale the next generation down
1 setlinewidth
down DoLine
depth maxdepth le % Until we hit maxdepth...
{
135 rotate Fract % ...rotate one way, recurse
-270 rotate Fract % ...rotate another way, recurse
} if
up grestore
} def
864 96 moveto
/Times-Roman findfont 231 scalefont setfont
Fract
stroke % Bring our fractal into existence!
showpage % Receive your applause.Including the comments, that file weighs in at 721 bytes - less than a kilobyte - rendering an arbitrarily intricate graphic. Compare that to 23,841 bytes for the PDF, or 166,948 bytes for the (imprecisely rendered) PNG above. The PostScript version only describes the means by which to create the design, while the PDF and PNG versions describe the final result.
While thousands of bytes is insignificant in modern computing, adjusting the maximum recursion depth from 10 to 20 would add no size to the PostScript file ("10" is exactly the same length as "20", two characters each), but the resulting figure would be stunningly more intricate. The resulting static PDF, however, would be quite heavy. Increasing it to 99 would result in a near-incomputable and un-storable static form, but again the PostScript copy would grow no larger. I doubt the ability of your typical laser printer to actually produce such a print from the PostScript file, though it will make a valiant attempt.
PostScript hacking has many parallels to - and perhaps is a niche of - Demoscene, where very small programs produce fine art on a screen. Here, though, the demos are printed nicely on quality paper.