Post Scriptum language

// Example 11

// This example heavily uses global variables.  
// The "glo" dictionary is a container for global variables.

glo = [ 
  charcount: 0,
  cpx: 0,
  cpy: 0,
  firstx: 0,
  firsty: 0,
  newx: 0, 
  newy: 0,
  ovr: 0,
  pathdist: 0,
  setdist: 0,
  str: ""
];

procedure pathtext( str, offset )
{
        glo.str = str;
        glo.pathdist = 0;
        glo.setdist = offset;
        glo.charcount = 0;

        gsave();
        flattenpath();
        pathforall( movetoproc, linetoproc, curvetoproc, 
                        closepathproc );
        grestore();

        newpath();
}

procedure movetoproc( newx, newy ) 
{
        glo.firstx = newx;
        glo.firsty = newy;
        glo.newx = newx;
        glo.newy = newy;
        glo.ovr = 0;
        cp = [ transform(newx, newy) ];
        glo.cpx = cp[ 0 ];
        glo.cpy = cp[ 1 ];
}

procedure linetoproc( newx, newy )
{
        oldx = glo.newx;
        oldy = glo.newy;
        glo.newx = newx;
        glo.newy = newy;

        dx = newx - oldx;
        dy = newy - oldy;
        dist = sqrt( dx*dx + dy*dy );

        if ( dist != 0 ) {
                dsx = glo.ovr * dx/dist;
                dsy = glo.ovr * dy/dist;
                cp = [ transform(oldx+dsx, oldy+dsy) ];
                glo.cpx = cp[ 0 ];
                glo.cpy = cp[ 1 ];

                glo.pathdist += dist;

                loop {    // this is an infinite loop in Post Scriptum
                        if ( glo.setdist <= glo.pathdist ) {
                                if ( glo.charcount < length(glo.str) ) {
                                        setchar(dx, dy);
                                }
                                else
                                        break;   // break the loop here
                        }
                        else {
                                glo.ovr = glo.setdist - glo.pathdist;
                                break;
                        }
                }
        }

}

procedure curvetoproc() {
        print( "ERROR: No curveto's after flattenpath!\n" );
}

procedure closepathproc() {
        linetoproc( glo.firstx, glo.firsty );
        movetoproc( glo.firstx, glo.firsty );
}

procedure setchar(dx, dy) {
        char = getinterval( glo.str, glo.charcount, 1 );
        glo.charcount += 1;
        sw = [ stringwidth( char ) ];
        charwidth = sw[ 0 ];
        gsave();

        // 'itransform' returns two values on the stack and
        // 'translate' accepts two arguments
        translate( itransform( glo.cpx, glo.cpy ));

        rotate( atan(dy, dx) );
        moveto(0, 0);
        show(char);
        cp = [ transform( currentpoint() ) ];
        glo.cpx = cp[ 0 ];
        glo.cpy = cp[ 1 ];
        grestore();
        glo.setdist += charwidth;
}

setfont( scalefont( findfont( 'Helvetica' ),  16 ));
newpath();
arc( 200, 500, 70, 0, 270 );
arc( 200 + 110, 500, 70, 270, 180 );

// NOTE:  literal strings are glued together, as in C/C++
pathtext( "If my film makes one more person feel miserable"
          " I'll feel I've done my job. -- WOODY ALLEN",
                55 );

newpath();
moveto( 150, 310 );
lineto( 360, 310 );
lineto( 360, 400 );
lineto( 150, 400 );
closepath();
moveto( 360, 347 );
lineto( 410, 330 );
lineto( 410, 380 );
lineto( 360, 363 );
setlinewidth( 2 );
stroke();

showpage();