PostScript

Created: 20 Jan 2025
Updated: 02 May 2026
postscript.png

language

%!PS
%!PS-Adobe-2.0 EPSF-2.0
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 5 5 105 105
%   BoundingBox is EPS Clip (Encapsulated Postscript),
%   the boundaries of the drawing area,
%   useful creators, not taking in account by convert
%%BeginProlog
%   Can contain ONLY _def_ of variables and procedures
%%EndProlog
20 20 moveto
%%EOF
  • single pass compiler
  • ~400 operators
  • turing complete
  • interpreted (aka not fast)
  • postfix notation (aka RPN)
  • types:
    • char, integers, real/floats
    • strings
      • between ()
      • assigned by reference, must be manually copied when needed
  • weakly typed, you can assign a number to a variable, but then it will show it's printable character when asked to print
  • comments https://hepunx.rl.ac.uk/~adye/psdocs/DSC2.html
  • A path is a collection of, possibly disconnected, lines and areas describing the image.
  • A clipping path
    • defines the effective drawing area.
    • things outside will be discarded
    • by default it matches the border of the paper.
  • Uses a coordinate system, device independent.
    • X=right Y=up
    • unit is in points (1/72 inch long)
    • 1 point = 28.34645 cm = 1/72 inch
    • 72pt = 1 inch
    • 0,0 is the bottom left coordinate
    • aka doesn't depend on the resolution or paper size

fonts

fonts.png
Figure 1: postscript available font types

commands / operators

boolean

  • eq, ne, ge, gt, le, lt
  • and, not, or, xor
  • bitshift

array

  • index numering starts at 0
  • fixed size arrays
- [ 0 1 20 ] a  
n array a creates array of n length, w/ null on each element
a length n len(a)
a i get v a[i]
a i v put - a[i] = v
a s l getinterval a a[s:s+l]
a s b putinterval - a[s] = b
a aload a ..a  
..e a astore a stores stack upto array capacity into array
a b copy a b[0] = a
a {f} forall ..f(a) a.foreach(f)

control flow

  • repeat
  • for
  • loop
  • if/ifelse
  • there is no "else if" construct
    • alternatives
      • nested if
      • an exit at the end of a loop

        /b exch def
        /a exch def
        {
            a b gt {  1 exit } if
            a b lt { -1 exit } if
            0 exit
        } loop
        

files

  • =,, pop one prints string representation
  • (p)stack

stack manipulation

  • copy, copies Nth element(s) from top
  • index, copies Nth element from top, starting at 0
  • roll
  • count
  • clear
  • exch (aka swap)
  • pop
  • dup

string

  8#092 1 puts char "a" on stack
1 string 1 takes a number, push a string of that length
1 length 1 takes a string, push length
2 get 1 takes a index and string, returns int at idx
3 put 1 assigns int at idx to string
2 cvs 1 converts to string, substring if smaller
1 cvi 1 converts real to integer

graphics

  • gsave/grestore - state manage (path,color…)
  • [set¦current]rgbcolor - 3 components 0-1
  • [set¦current]hsbcolor - color given hue/saturation/brightness
  • [set¦current]gray - gray level 0-1(white)
  • [set¦current]miterlimit
  • [set¦current]linewidth
  • [set¦current]linecap - end - 0=butt, 1=round, 2=proj.square
  • [set¦current]linejoin - corners - 0=mitter, 1=round, 2=bevel
  • [set¦current]dash - separation and pattern for it
mitter.png
Figure 2: miter

matrix

  • A matrix is an array of 6 numbers.
  • CTM = Current Transformation Matrix
  • identity() = identity matrix
  • m, a, b are matrices
- matrix m identity()
- initmatrix - CTM = default matrix
m setmatrix - CTM = m
m concat - CTM = CTM * m
m currentmatrix m m = CTM
m identmatrix m m = identity()
a b m concatmatrix m m = a * b
a m invertmatrix m m = invert(a)
x y translate - CTM = translate(CTM, x, y)
x y m translate - translates x y by m
x y scale - CTM = scale(CTM, x, y)
x y m scale - scales x y by m
angle rotate - CTM = rotate(CTM, angle)
angle m rotate - rotates x y by m
x y transform    
x y m transform    

arithmetic / rand

1 srand - sets random seed
- rrand 1 gets random seed
- rand 1 random number (0 - 2^31-1)
  • add, sub, mul, div, idiv, mod (2)
  • abs, neg (1)
  • ceiling, floor, round, truncate (1)
  • sqrt, cos, sin, ln, log (1)
  • atan, exp (2)

paths

  • there is always a path
  • can be visually disconnected
Table 1: T=Take P=Put
T command P  
- newpath - clears current path
- closepath - closes current path, with a line to the last moveto
- stroke - draws current path + RESET PATH
- fill - fills current path + RESET PATH
- clip - clipping path = current path
- currentpoint x y returns xy of current point
x y [r]moveto - [relative] move current point
x y [r]lineto - line from current point to given point
x y w h rectstroke -  
x y w h rectfill -  
x y r a1 a2 arc[n] - counter/clockwise arc
x1 y1 x2 y2 r arct[o] - draws arc from current point x0,y0
x1 y1 x2 y2 x3 y3 curveto - (cubic) bezier curve from current point
str t/f charpath - adds chars outlines to path
  pathforall    
arc.png
Figure 3: arc: (x y r a1 a2)
arct.png
Figure 4: arct: (x1 y1 x2 y2 r)
curveto.png
Figure 5: curveto: (x1 y1 x2 y2 x3 y3)

fonts

  • no font is set by default (not a visible one)
  • requires a position being set first (aka moveto)
  • font size given in "points"
  • 1 p = 1/72 inch
/k findfont f pushes the fontdict of given key, size 1p
f n scalefont 1 scales fontdict
f setfont - sets current fontdict
/k s selectfont - sets current fontdict, at given scale
- currentfont f returns fontdict
s stringwidth x 0 distance from the current point
f [6] makefont f transforms font matrix by [6]
  • font matrix only affects fonts
  • makefont [6] matrix
    • [m 0 a n 0 0]
      • scale in m,n in x,y
      • angle of rotation
        • eg: y*tan(30)
        • to rotate 30 degrees
Table 2: show functions
s show - prints string of text
p s kshow - prints and executes p(c1,c2) between each 2 chars
x y s ashow - prints adding space x,y after each char
x y c s widthshow - prints adding space x,y after each char "c"
      (eg: 8#040 for space)
x y c ax ay s awidthshow   ashow + widthshow

local variables

/f {
  1 dict begin % makes x a local binding
    \x exch def
    x x lineto
  end
} def

gotchas

  • trigonometric functions take degrees as argument
    • not radians like most common languages now do
    • see snippets for \degrees function

tools

snippets

  • units

    /degrees { 180 mul 3.141592653589793 div } def
    /cm { 72 mul 2.54 div } def
    
  • safediv

    % a b -- c
    /safediv { dup 0 eq { 0 } { div } ifelse } def
    
  • color 255

    /setcolor { { 255 div } forall setrgbcolor } def
    
  • quicksort https://gist.github.com/kuroneko/e896a466dc4a1af55c9c
  • vector

    /vectoradd { exch 3 1 roll add 3 1 roll add exch } def
    /vectorsub { exch 3 1 roll sub 3 1 roll sub exch } def
    
  • position helpers

    /currentx { currentpoint pop } def
    /currenty { currentpoint exch pop } def
    
  • Factor-like stack shuffling operators

    /drop { pop } def
    /swap { exch } def
    /over { 1 index } def
    /pick { 2 index } def
    /dupd { exch dup 3 2 roll } def
    /swapd { 3 1 roll exch 3 2 roll } def
    /rot { 3 2 roll } def
    /-rot { 3 -2 roll } def
    /nip  { exch pop } def
    /2dup { 2 copy } def
    /bi { % x p q --
       rot % p q x
       dup % p q x x
      -rot % p x q x
      exch % p x x q
      exec % p x x'
      -rot % x' p x
      exch % x' x p
      exec % x' y'
      exch % y' x'
    } def
    /2keep { % ..a x y q:(..a x y -- ..b) -- ..b x y
      3 1 roll  % q x y
      2 copy    % q x y x y
      5 -1 roll % x y x y q
      exec      % x y
    } def
    
  • print stack count (need moveto)

    /printcount { count 20 string cvs show } def
    
  • filled circle

    currentpoint 6 0 360 arc fill stroke
    
  • a font initialization

    /Courier 20 selectfont 300 300 moveto
    
  • get page dimensions

    /width  currentpagedevice /PageSize get 0 get def
    /height currentpagedevice /PageSize get 1 get def
    
  • shapes.ps

    /boxfill {
      2 dict begin
        /height exch def
        /width  exch def
        gsave
          currentpoint
          height 2 div sub exch
          width  2 div sub exch
          width height rectfill
        grestore
      end } def
    /box {
      2 dict begin
        /height exch def
        /width  exch def
        gsave
          setlinewidth
          currentpoint
          height 2 div sub exch
          width  2 div sub exch
          width height rectstroke
        grestore
      end } def
    /boxrounded {
      5 dict begin
        /hheight exch 2 div def
        /hwidth  exch 2 div def
        /R exch def
        currentpoint /centery exch def /centerx exch def
        gsave
          setlinewidth
          hwidth neg 0 rmoveto
          centerx hwidth sub centery hheight add centerx centery hheight add R arct % U
          centerx hwidth add centery hheight add centerx hwidth add centery R arct % R
          centerx hwidth add centery hheight sub centerx centery hheight sub R arct % D
          centerx hwidth sub centery hheight sub centerx hwidth sub centery R arct % L
          closepath
          stroke
        grestore
      end } def
    /pentagon {
      1 dict begin /size exch def
        gsave
          0 0 moveto
          size 2 div neg
          size 36 sin 36 cos div 2 mul div neg
          moveto
          4 { size 0 rlineto 72 rotate } repeat
          closepath
          stroke
        grestore
      end } def
    /star {
      2 dict begin
        /side exch def
        /offset side 54 sin 54 cos div 2 mul div def
        gsave
          offset offset neg rmoveto
          5 {
            side 0 rlineto
            180 36 sub rotate side 0 rlineto
            180 108 add rotate
          } repeat
          stroke
        grestore
      end } def
    

3d shapes

/project {
  /z exch def /y exch def /x exch def
  x z safediv
  y z safediv
} def

/rotate_x {
  /angle exch def /z exch def /y exch def /x exch def
  x
  y angle cos mul z angle sin mul sub
  y angle sin mul z angle cos mul add
} def

/rotate_y {
  /angle exch def /z exch def /y exch def /x exch def
  z angle sin mul x angle cos mul add
  y
  z angle cos mul x angle sin mul sub
} def

/rotate_z {
  /angle exch def /z exch def /y exch def /x exch def
  x angle cos mul y angle sin mul sub
  x angle sin mul y angle cos mul add
  z
} def

codebases