Programming Digital Media

Mixing Python and PostScript

Mixing Python and PostScript

Though PostScript is a complete programming language, we can also use Python as a way to construct PostScript files. The PostScript code that we generate will usually not include any complex control structures (like loops); we'll do this in Python. Instead, the PostScript code will just be a set of drawing commands, more like a data file that has been generated by an application like Photoshop.

File output in Python

Since we'll be creating PostScript files, we need to know how to write out text files from Python. File input and output in Python uses a file object, created with the open function. The open function takes two arguments, the filename and the mode (whether we are reading or writing the file). Let's say that we are going to create a file called circles.ps, that creates a grid of circles. We'll define a variable called fp (for "file pointer") using the open function

        fp = open('circles.ps', 'w') 

Note that the filename ends in .ps, which is typical for a PostScript file. The 'w' argument (a string consisting solely of the letter "w") indicates that we are writing a file called "circles.ps".

Now that have a file object, we can write into it using the write method of file objects. Remember that methods of an object are specified by preceding the method name with the instance name and a period character. For our file object instance named fp, the write method is called fp.write. Let's write the two character identifiers required by PostScript files:

        fp.write('%!\n') 

Notice that we needed to add a "newline" character at the end (the \n) so that %! will be on a line by itself.

We'll draw a five by five grid of circles in the lower left corner of the page. We need two Python loops, one for each row, and one for each circle in each row. (This is a similar structure to our two loops for all the pixels in an image.)

        for x in range(5): 
          for y in range(5): 

We now need to specify where the circles will be located. We'll say that the circles are half an inch from the left and bottom sides of the paper, and all the circles are an inch apart. We'll define variables cx and cy to be the X and Y coordinates of the circle centers:

            cx = 36 + x * 72 
            cy = 36 + y * 72 

Now we're ready to draw the circle, that is, to add some more PostScript arguments and operators to our PostScript file. Remember from the previous tutorial that the arc operator requires five arguments:

     x y r ang1 ang2 arc

We'll make the radius of each of our circles a quarter of an inch in length. We can write the arguments and the arc operator like this:

            fp.write('newpath %g %g 18 0 360 arc closepath fill\n' % (cx, cy)) 

This is the only statement in the loop body. Once we've written all the arc operators and arguments for all the X and Y pairs, we need to use showpage to indicate that we're done defining this page:

        fp.write('showpage\n') 

We've finished with our PostScript file, but we need to also end our use of the file object, fp, by using the close method:

        fp.close() 

This completes our Python script to generate a simple PostScript file. Here's the entire script — click on it to download it:

fp = open('circles.ps', 'w') 
fp.write('%!\n') 
for x in range(5): 
    for y in range(5): 
        cx = 36 + x * 72 
        cy = 36 + y * 72 
        fp.write('newpath %g %g 18 0 360 arc closepath fill\n' % (cx, cy)) 
fp.write('showpage\n') 
fp.close() 

We can create Python scripts that take arguments from the command line like our other scripts. Here's the same idea as the simple circle drawing script we just wrote, but with the number of circles and the radius specified as command-line arguments. Download this script by clicking on it and try changing the way that it draws circles. For a description of how the script arguments are handled, see the tutorials "Making Python scripts" and "Simplifying script arguments".

import sys 
 
if len(sys.argv) != 5: 
    print """ 
Usage: python make_postscript_file_2.py radius nx ny ps-filename' 
 
Create a PostScript file called "ps-filename" that contains an evenly 
spaced grid of circles in a letter-sized page, with "nx" circles 
across, and "ny" circles up and down.  The radius of each circle is 
"radius".  """ 
    sys.exit(1) 
 
radius, nx, ny = map(int, sys.argv[1:4]) 
ps_filename = sys.argv[4] 
 
# Assuming we're using standard letter-sized paper: 
paper_width = 8.5 * 72 
paper_height = 11 * 72 
 
# What's the distance between circles? 
# (The "1.0" forces floating-point arithmetic.) 
xdist = paper_width / (nx + 1.0) 
ydist = paper_height / (ny + 1.0) 
 
# Open the PostScript file for writing ('w'): 
fp = open(ps_filename, 'w') 
 
# Write the "magic number" characters that specify that this is a 
# PostScript file: 
fp.write('%!\n') 
 
# By starting with 1, and ending with the total number in each 
# direction, the circles are evenly spaced across the page: 
for x in range(1, nx + 1): 
    for y in range(1, ny + 1): 
        cx = xdist * x 
        cy = ydist * y 
        fp.write('newpath %g %g %g 0 360 arc closepath fill\n' % \ 
                (cx, cy, radius)) 
 
# Finally, display the page: 
fp.write('showpage\n') 
 
# Close the PostScript file we've been writing: 
fp.close()