What is Postscript?

Postscript is a page-layout language for printers and display devices. It uses a simple model of placing marks on a page, whether they are lines, textures, characters form a font or bit maps. It is platform independent and human-readable and writeable in its ASCII form. It is pretty well universal on all computer platforms, and many laser printers use it (HP uses a similar language called PCL, but even HP printers can use Postscript.)

There are primitive operations for all the common graphic objects and operators, for handling fonts, and for placing these anywhere on an unbounded surface. Advanced Postscript does color, color separations for commercial printing, and has the immediate advantage of being infinitely scalable in all its forms.

The language

The model of computation is that of a stack machine. Operands are pushed onto a stack and popped off by operators when needed. Values can be almost anything, including numbers, strings, blocks, called procedures, and names.

Syntax

The syntax is strictly postfix, in accordance with the stack architecture (like the HP calculators). There are the following syntactic categories, with examples:

  1. comments: %this is a comment
  2. numbers 123 -98 0 +17
    -.002 34.5 -3.62 123.6e10 -1.
  3. strings (This is a string
    on many lines (with embedded
    parentheses) and other characters!?"
    and escaped characters, as in C \n\n \(\))
  4. names abc Offset $$ a.b @pattern
  5. literals /abc /Offset /$$ /a.b /@pattern
  6. arrays [123 /abc (xyz)]
  7. procedures {add 2 div}

Semantics

There are in fact several stacks that control Postscript execution, but only the operand stack and the graphics stack are important here. Objects are copied onto a stack if simple, and placed by reference if compound. Basic operations are numeric, either integer or floating point. e.g.

3 5 add

pushes 3 and 5 onto the stack which are then popped by the addition operation. The result is pushed back on the stack.

3 5 add 4 mul 2 div

produces the result 16 on the stack.

Graphics operations are, in general, side-effect operations that leave the stack unchanged. e.g.

(a little string) show

places the characters "a little string" on the page at the current position, using the current font.

12 20 lineto

places a line from the current position to the coordinate position (12, 20) in the current path.

Values can be bound to names with def:

/a 3 def

names the value 3 as a, and

/a { ... } def

names the procedure between the braces as a.

Conditional execution is obtianed with if or ifelse:

a b gt { p } if

will only execute the procedure p if a is greater then b, and

a b gt { q } { q } ifelse

will execute p if a is greater than b and q if not.

Loops are either definite:

1 1 10 { p } for

will execute p 10 times, and

{ b} loop

will execute b until an exit operation is executed, in which case the loop is exited.

Graphics

The Postscript page is an unbounded plain stretching in the positive x and y directions. The origin is in the bottom left-hand corner (like ordinary graph paper), and all measurements are relative to this. The default unit of measurement is 1 point, or 1/72 inch. This is a traditional typesetters unit, but the units can be changed by scaling to anything you like. For instance,

72 72 scale

will change the units to inches, since now everything is now 72 times as large.

The axes can by moved and rotated using translate and rotate:

2 2 translate

will move the origin to a point 2 units up and 2 units across from the bottom left-hand corner;

90 rotate 

will rotate the axes 90( counterclockwise.

Drawing lines is done by placing them in a path first, and then "stroking" the path onto the page:

newpath
100 100 moveto 100 200 lineto 200 200 lineto
closepath stroke

draws a right-angled triangle.

Text can be drawn by using show:

/Helvetica findfont 12 scalefont setfont
100 100 moveto (Some Text) show

Draws the text "Some Text" in 12 pt. Helvetica at the postion (100, 100)

The parameters of the graphics state can be saved with gsave and restored with grestore. The state includes the current font, current color, current linewidth, current position and current path.

Postscript files

Every Postscript file must start with a comment which tells the device that the file contains Postscript code:

%!Postscript

Nothing will actually show unless the file contains at least one

showpage

which transmits the page to the devices renderer (Postscript printers convert their pages into raster images for tramsission to the drum).

If a Postscript file is simply sent directly to a Postscript printer, it will execute the program, porduce the page or pages, and print them.

An Example

Here is the NMSU logo in Postscript:

%!Postscript
%% definitions
/outsidecircletext
	/radius exch def	% give the first stack item a name
     /centerangle exch def	% and the second
     /ptsize exch def	% and the third
     /str exch def		% and the fourth
     /xradius radius ptsize 4 div add def
   gsave	% preserve the current graphics state
    centerangle str findhalfangle add rotate

   str
    {/charcode exch def
     ( ) dup 0 charcode put outsideplacechar
    } forall

   grestore	% restore the original graphics state
} def

/insidecircletext
	/radius exch def
     /centerangle exch def
     /ptsize exch def
     /str exch def
     /xradius radius ptsize 3 div sub def

   gsave
    centerangle str findhalfangle sub rotate

   str
    {/charcode exch def
     ( ) dup 0 charcode put insideplacechar
    } forall

   grestore
} def

/findhalfangle
  {stringwidth pop 2 div
    2 xradius mul pi mul div 360 mul
  } def

/outsideplacechar
 {/char exch def
  /halfangle char findhalfangle def
  gsave
   halfangle neg rotate
   radius 0  translate
   -90 rotate
   char stringwidth pop 2 div neg 0 moveto
   char show
  grestore
  halfangle 2 mul neg rotate
 } def

/insideplacechar
 {/char exch def
  /halfangle char findhalfangle def
  gsave
   halfangle rotate
   radius 0 translate
   90 rotate
   char stringwidth pop 2 div neg 0 moveto
   char show
  grestore
  halfangle 2 mul rotate
 } def


/pi 3.1415923 def

/tri
{
 newpath
 2 29 moveto
 26 -12 lineto
 2 2 lineto
 closepath 
 } def
%% end of definitions
gsave
500 700    680 sub currentpoint exch pop add translate
0.8 0.8 scale
% first the text around a circular path
/Helvetica findfont [14 0 0 12 0 0] makefont setfont
% this goes outside the circle
(NEW MEXICO STATE) 12 90 38 outsidecircletext
% this goes inside
/Helvetica findfont [17 0 0 12 0 0] makefont setfont
(UNIVERSITY) 12 -90 48 insidecircletext

% now the triangles
0 setlinejoin	% make sure the lines join neatly
3 setlinewidth	% set their width to 3/72 inch

tri stroke
120 rotate
tri stroke
120 rotate
tri stroke
grestore

showpage

Operator Summary

anypop--discard top element
any1 any2exchany2 any1exchange top tow elements
anydupany anyduplicate top element
an-1 ... a0 n jrolla(j-1) mod n ... a0 an-1 ... aj mod nroll n elements up j times
( any1 ... anynclear( discard all elements
num1 num2addsumnum1 plus num2
num1 num2divquotientnum1 divided by num2
int1 int2modremainderint1 mod int2
num1 num2mulproductnum1 times num2
num1 num2subdifferencenum1 minus num2
num1absnum2absolute value of num1
num1negnum2negative of num1
any1 any2eqbooltest equaL
any1 any2nebooltest not equal
num1 num2gebooltest greater than or equal
num1 num2gtbooltest greater than
num1 num2lebooltest less than or equal
num1 num2ltbooltest less than or equal
bool1 bool2andboollogical and
bool1notbool2logical not
bool1 bool2orboollogical or
--truetruepush value true
--falsefalsepush value false
anyexec--execute object
boo procif--execute proc if bool is true
bool proc1 proc2ifelse--execute proc1 if bool is true, execute proc2 if bool is false
init incr limit procfor--execute proc with values from init by steps of incr to limit
procloop--execute proc indefinitely
--exit--exit innermost active loop
any=--write text representation of any to standard output
( any1 ... anynstack( any1 ... anynprint stack non-destructively using =
--gsave--push graphics state
--grestore--pop graphics state
numsetlinewidth--set line width
tx tytranslate--translate user space by
(tx, ty)
anglerotate--rotate user space by angle
--newpath--initialize current path to empty
x ymoveto--set current point to (x, y)
dx dyrmoveto--relative to moveto
x ylineto--append straight line to
(x, y)
dx dyrlineto--relative to lineto
x y r ang1 ang2arc--append counterclockwise arc with radius r between ang1 and ang2
--closepath--connect subpath back to starting point
--fill--fill current path with current color
--stroke--draw line along current path
keyfindfontfontreturns the font called key
font scalescalefontfont'scales a 1 pt. font by scale
fontsetfont--sets font as current in the graphics state
stringshow--paints string on the page
--showpage--tramsit the current page to the printer and reset the page

The little quilts

Here is the code for the two little quilts, a and b, and a test that draw them:

%!Postscript

/bands
{newpath
0 0 moveto
0 144 rlineto
144 0 rlineto
0 -144 rlineto
closepath
0 144 moveto
144 -144 rlineto
48 144 moveto
96 -96 rlineto
96 144 moveto
48 -48 rlineto
0 0 moveto
stroke
} def

/arcs
{newpath
0 0 moveto
0 144 rlineto
144 0 rlineto
0 -144 rlineto
closepath
144 0 moveto
0 0 144 0 90 arc
144 48 moveto
48 48 96 0 90 arc
144 96 moveto
96 96 48 0 90 arc
0 0 moveto
stroke
} def

gsave
72 576 translate
1 1 3 {bands 0 -180 translate} for

180 180 translate
1 1 3 {arcs 0 180 translate} for
grestore

180 120 translate
0.2 0.2 scale
1 1 4 {-90 rotate bands 0 288 translate} for
1 1 4 {1 1 3 {arcs 90 rotate} for 0 288 translate} for

showpage