Writing PostScript by hand

Lucio Chiappetti - IASF Milano (INAF) - february 2013

Dieses Buch wird vielleicht nur der verstehen, der die Gedanken, die darin
ausgedrückt sind oder doch ähnliche Gedanken schon selbst einmal gedacht hat.
Es ist also kein Lehrbuch. Sein Zweck wäre erreicht, wenn es einem, der es mit
Verständnis liest, Vergnügen bereitete.

PostScript is one of the most common (and in my opinion, successful) page description languages, used to produce and print documents and graphics.
Since the '90s, when I got a paper copy of the second edition of the Red Book (i.e. the PostScript® Language Reference, latest issue available online), I found it a great way of generating graphics, both programmatically as well as by hand editing, both for real work and for fun.

Although I've never done such an ambitious thing as writing a word processor in PostScript (like Graham's Freeman Quikscript, which I - and knowingly or unknowingly many people in my institute - use to print ASCII files, e-mail, publication lists, etc.), I played around with PostScript a bit, and I would like to share my fun so that ... its purpose will be reached, if it will bring pleasure to an understanding reader (L. Wittgenstein, Tractatus Logico-philosophicus, Foreword)


PostScript is essentially a glorified RPN pocket calculator (like the famous HP 35, this sort of thing, which I first saw as a high school 4th class student in the '70s ... but was unreachable to me until I graduated).
I always liked RPN too, perhaps this is one of the reasons I like PostScript.

Where to start

You start with a good old text editor: my own choice would be The Hessling Editor, but you can do it with vi, emacs, even Wordpad :-), open a file, e.g. test.ps and type

get the postscript code for display
 %!PS-Adobe-2.0
 %
 /Helvetica findfont 40 scalefont setfont 0 0 0 setrgbcolor 100 400 moveto (Hello World !) show
 showpage 


What you can do


define macros

PostScript has a capability of defining macros i.e. shortcuts to complex operations or lists. Actually the distinction between (executable) macros and (valued) variables is somewhat blurred (see the Red Book for details). For you it is enough to know that: Let us consider for instance the "Hello world" example given above. Let us now consider for instance that we want to write text of different sizes, in different colours, and at different places. We can do like this, defining the macros first in the prologue and using them in the "body".

get the postscript code for display
 %!PS-Adobe-2.0
 % Prologue
   /BD   {bind def}    bind def
   /SRGB {setrgbcolor} BD
   /M    {moveto}      BD
   /ED   {exch def}    BD
   /H    {/fs ED /Helvetica findfont fs scalefont setfont} BD
 % Body
   40 H   0 0 0 SRGB   100 400 M   (Hello World !) show
   10 H   0 0 1 SRGB   100 390 M   (a little blue world) show
 showpage  

You see that the body is more compact and better legible.

standard useful macros


the birthday cake example

This example shows some simple nice features of PostScript like loops and distortions.

Let us assume you want to draw a birthday cake for somebody's birthday, and make it using his or her name. This is an idea of what I mean although it was not drawn for a real person but for ... a data format !

First of all some additional basic macros (self-explanatory), which will be used along with those defined above.

get the postscript code for saving
/TC   {true charpath} BD
/F    {fill} BD
/S    {stroke} BD
/D    {def} BD
/T    {translate} BD
/Sk   {scale} BD
/CP   {closepath} BD
/Ori {0 0 M} BD
/OrR {Ori rotate} BD    

Of course any drawiing should be enclosed in a rectangular background of a predefined colour.
A decent way of doing it is to translate the origin at a given point of the page, define the lower left and upper right corners of the interesting area (and save them so that one can re-use them e.g. to locate the centre or draw a grid), give a rectangular path and fill it.

get entire postscript code for display or save this bit
3.5 cm 5 cm T 0.3 0.0 0.8 SRGB
/llx 0 D /lly 0 D /urx 16.5 cm D /ury 16.5 cm D
newpath llx lly M llx ury L urx ury L urx lly L CP F   

In the prologue define two macros with the name, filled or stroked in different colours, so one can draw the name in one colour bordered with another colour.
Also define a macro M1 which puts on the stack the coordinates of the centre of the rectangular area. The "logo" will be placed here.
One can then produce a decorative circle simply repeating the name in a loop at e.g. 15 degrees from each other.
In this particular case the last "word" is repainted once in red colour.

get entire postscript code for display or save this bit
% place this in the prologue
%
 /FITS1 {GS OrR pc SRGB (FITS) TC S GR} BD
 /FITS2 {GS OrR fc SRGB (FITS) TC F GR} BD
 /FITS3 {-15 15 360 {dup FITS2 FITS1 } for 1 0 0 SRGB Ori (FITS) TC F 0 0 0 SRGB 0 FITS1} BD
 /M1 {urx 2 div ury 2 div} BD
%
% place this in the body after the rectangle fill
%
 /fc {1 0.5 0} D /pc {0 0 0} D 
 GS M1 T Ori FTB FITS3 GR

Note that the code in the body above is wrapped in a pair of gsave and grestore which relocates temporarily the origin at the centre, moves there, and defines the font (macro FTB). Also colours fc and pc are defined.

The next step to "project it in perspective" is simply to apply a distortion, i.e. a simple 1 0.5 scale.
Since this would be a layer of the cake, we enclose the logo inside a (distorted). circle of colour cc with border colour sc.

get entire postscript code for display or save this bit
% append this in the prologue
%
/layer {GS M1  1 0.5 Sk                        % go to origin and distort x=1 y=0.5
           0 0 6.4 cm 0 360 arc cc SRGB CP     % define a circle
           GS F GR                             % fill it with colour cc (and restore graphics context for re-use)
           6 SL sc SRGB S                      % stroke its border with thickness 6
           1 SL FTB FITS3                      % draw the name logo (with thickness 1)
        GR} BD
%
% replace this in the body after the rectangle fill
%
 /fc {1 0.5 0} D /pc {0 0 0} D     /cc {1 1 0} D /sc {0 1 1} D
 GS M1 T layer GR

The layer is defined as a macro in view of future re-use. One can then decide to overlap various layers simply translating them in the vertical direction.
To do this we apply the modification in red, i.e. we execute layer in a for-loop starting 100 points below and going up by 20 points (for a total of 6 layers 100 80 60 40 20 0). We read the index of the for loop from the stack using the roll operator.

get entire postscript code for display or save this bit
% replace this in the prologue
%
/layer {GS M1  3 -1 roll sub T                 % shift down the origin by current for loop index
           1 0.5 Sk                            % go to new origin and distort x=1 y=0.5
           0 0 6.4 cm 0 360 arc cc SRGB CP     % define a circle
           GS F GR                             % fill it with colour cc (and restore graphics context for re-use)
           6 SL sc SRGB S                      % stroke its border with thickness 6
           1 SL FTB FITS3                      % draw the name logo (with thickness 1)
        GR} BD
%
% replace this in the body after the rectangle fill
%
 /fc {1 0.5 0} D /pc {0 0 0} D     /cc {1 1 0} D /sc {0 1 1} D
 100 -20 0 {layer} for

But since one wants the layers to alternate between two different colour patterns we define an if-then-else operator which toggles trivially between two families of colours. The flag set to true or false establishes the colour of the first layer (try to change it !).

get entire postscript code for display or save this bit
% append this in the prologue
%
 /toggle {flag {/fc {0.85 0.65 0.55} D /pc {0.3 1 0.3} D /cc {1 1 1} D /sc {1 0 0} D}
               {/fc {1 0.5 0}        D /pc {0 0 0}     D /cc {1 1 0} D /sc {0 1 1} D} ifelse
               flag not /flag E D } BD
%
% replace this in the body after the rectangle fill
%

 /fc {1 0.5 0} D /pc {0 0 0} D     /cc {1 1 0} D /sc {0 1 1} D
 /flag true D
 100 -20 0 {layer toggle} for

The candle is not drawn in an optimized way (ideally it should be defined in a 1-point size and made scalable and relocatable arbitrarily). While you can see the effect in the complete file I report its code here

get the postscript code for saving
% append this in the prologue
%
/disc {GS 1 0.5 Sk 20 0 360 arc F GR} BD
/candle {GS M1 T Ori -20 0 L -20 150 L 20 150 L 20 0 L CP 0.95 0.95 0.8 SRGB F 0 0 disc 0.9 0.9 0.75 SRGB 0 300 disc GR } BD 
/flame {GS M2 bc SRGB f f Sk 0 50 M 50 -10 -50 -10 0 50 curveto F GR} BD
%
% append this in the body after the cake stuff
%
candle 
/bc {1 0.1 0}   D /f 1.5  D flame
/bc {1 0.5 0}   D /f 1.25 D flame
/bc {1 1 0}     D /f 0.9  D flame
/bc {1 1 1}     D /f 0.8  D flame
/bc {0.9 0.9 1} D /f 0.5  D flame
GS M2 0 10 5 15 4 arct 5 SL 0 0 0 SRGB S GR 

The candle itself, is a simple cylinder, approximating drawing the bottom (a distorted filled circle), a rectangle in the same colour, and the top (another distorted circle) in a darker colour.
The flame uses the curveto Bezier operator, and is repeated with different colours and scaling factor to give the Faradayan effect of a candle flame with temperature getting higher to the inside. The last statement uses the arct operator to draw the wick.
It is left as an exercise to the reader to transform the candle in one of arbitrary size and length and position, so you can make cakes for any birthday !

more special effects

Some variations on the theme of a circular logo made with a name are the following.

polygons and stars

Taking advantage of the computing operators in PostScript it should be rather easy to draw regular polygons even when it is not obvious to do so via the classical ruler and compass construction.


more stars ... and tails


more geometrical effects

What follows is a collection of some nice effects using more or less simple geometry and exploiting PostScript operators and features.

using arbitrary fonts

This is strictly not something I do on my own, but a way to exploit resources available on the web.

If one wants to write in an arbitrary alphabet, one can look for True Type fonts on the network. One will typically obtain a file with extension .ttf.

Next one should procure an utility called ttf2pt1 (e.g. look on sourceforge). Compile it and use it like

ttf2pt1 -e file.ttf file
This will generate two files file.afm and file.pfa

The .pfa files (quite bulky) can be incorporated into your PostScript file (even at the very beginning, appending your code at the end).
You should take note of the value on the resource line named /FontName: the string between /FontName and def is the name of interest.
Then in your file you should define a macro e.g.

   /myfont {/fs exch def name findfont fs scalefont setfont} BD
Then, when you want to issue a string in that font, invoke the macro specifying the size, say you want a big, 55-point size: 55 myfont.
Then you simply issue show commands for your strings, e.g. (Hello world) show, to write your greeting in gothic (Fraktur) or uncial (irish) font.

But what if the font is in a foreign alphabet ? You can use the octal representation for the characters you need, e.g. (\060\061\364) show.

But how can you know the octal representation for your alphabet ? You can write a test program like this, after the font pfa file and the standard macros define

   /T {name         findfont 30 scalefont setfont} BD
   /F {/Times-Roman findfont 30 scalefont setfont} BD
   /C {{/myh exch def /mys exch def gsave currentpoint translate 
        T myh true charpath stroke  30 0 moveto 
        F mys show grestore         0 30 neg rmoveto} BD
In this example the macros T and F switch to the chosen font and the standard Times Roman font at size 30, while the macro C displays the octal code (in Times) and the character corresponding it in font T alongside to it.
In the example the outline of the character is shown, so you can have an idea of how it would look like when stroked or filled. You may get something like this

You will also get an idea of where the character is placed with respect to the baseline (some foreign fonts have diacritics which go above or below other characters).

In practice you invoke the macro in a sequence, e.g. this (when completed !!) will put the characters on 5 columns on two pages

get entire postscript code (subject to change without notice) for display or for saving
  20 800 moveto 0 setgray
 (000) (\000) C
 (001) (\001) C
 (002) (\002) C
 (003) (\003) C
 (004) (\004) C
 (005) (\005) C
 (006) (\006) C
 (007) (\007) C
 (010) (\010) C
 (011) (\011) C
 ...
 (031) (\031) C
 100 800 moveto
 (032) (\032) C
 ...
 (063) (\063) C
 180 800 moveto
 (064) (\064) C
 ...
 (115) (\115) C
 260 800 moveto
 (116) (\116) C
 ...
 (147) (\147) C
 340 800 moveto
 (150) (\150) C
 (177) (\177) C
 showpage
 20 800 moveto
 (200) (\200) C
 ...
 (377) (\377) C
 showpage

sax.iasf-milano.inaf.it/~lucio/WWW/Opinions/postscriptum.html :: original creation 2013 mar 04 19:09:53 CET :: last edit 2013 Mar 04 19:09:53 CET