postscript

Created: 20 Jan 2025
Updated: 19 Feb 2025

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
  • A path is a collection of, possibly disconnected, lines and areas describing the image.
  • A clipping path is a type of path that defines the effective drawing area. 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)
    • 1pt = 28.34645 cm
    • 72pt = 1 inch
    • 0,0 is the bottom left coordinate
    • aka doesn't depend on the resolution or paper size
  • Truetype: .ttf apple font
    • troff (typesetter roff)

commands / operators

array

index numering starts at 0

  [ 0 1 20 ]    
1 array 1 creates array of given length
1 length 1  
2 get 1 arr idx
3 put - arr idx val
3 getinterval 1 arr idx len
3 putinterval - arr idx arr
1 aload ? puts arr elements + arr into stack
? astore 1 stores stack upto array capacity into array
2 copy 1 copies arr1 to initial subarray of arr2 ?
2 forall 1 executes proc for every element in array

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

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=square
  • [set¦current]linejoin - corners - 0=mitter, 1=round, 2=bevel
mitter.png
Figure 2: miter

matrix

  • matrix, creates an identity matrix
  • 2/3 translate 0 origin
  • 2/3 scale 0 user space
  • 1/2 rotate 0 user space

arithmetic / rand

  • srand sets random seed
  • rrand gets random seed
  • rand push random number > 0 < ?
  • 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
T   P  
0 newpath 0 clears current path
0 closepath 0 closes current path, with a line to the last moveto
0 stroke 0 draws current path + RESET PATH
0 fill 0 fills current path + RESET PATH
0 currentpoint 2 returns current point
4 rectstroke - x y w h
4 rectfill - x y w h
2 [r]moveto 0 [relative] move current point
2 [r]lineto 0 puts a line from current point, and moves it there
5 arc[n] 0 counter/clockwise arc (x y r a1 a2)
5 arct[o] - draws arc from current point x0,y0 (x1 y1 x2 y2 r)
6 curveto - bezier curve from current point (x1 y2 x2 y2 x3 y3)
2 charpath 0 adds chars outlines to path
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

/Courier 20 selectfont
300 300 moveto
(foo) show
1 findfont 1 pushes the fontdict of given key
2 scalefont 1 set+scale fontdict from size 1 (default) to given
1 setfont 0 sets current fontdict
2 selectfont    
- currentfont 1 returns fontdict
1 show - prints string of text

local variables

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

snippets

  • escher style impossible triangle https://gist.github.com/jgamble/bbaff6f932adc21e804cdf43eaae308f
  • mandelbrot https://gist.github.com/dln/bc88911f6bf8e55856d9
  • L-Systems https://gist.github.com/usr-ein/47661f123f055fda3ed73e2e3bce20ad
  • quicksort https://gist.github.com/kuroneko/e896a466dc4a1af55c9c
  • aoc21 https://github.com/juntuu/advent_of_code_2021/
  • 3d donut https://personal.math.ubc.ca/~cass/graphics/manual/code/doughnut.txt
  • raytracer https://gist.github.com/grkvlt/2651230
  • 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 operators

    /drop { pop  } def
    /nip  { exch pop } def
    /swap { exch } def
    /over { 1 index } def
    /pick { 2 index } 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 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
    

codebases