Figure 1. Output of the script from example 1, below.
The PSFile Python module helps you to create PostScript files from Python scripts. PSFile can creating the required wrappers to set up the page geometry; this allows you to only provide the PostScript code to draw the figure or page you want to create.
first public release
Installation instructions are in the file
README of the source code archive. The source code
archive also contains the example scripts from the documentation below (in
the sub-directory examples).
This manual explains the use of PSFile. In addition it includes a short
PostScript primer, mainly by use of examples. For a more in-depth
introduction to PostScript, I recommend Adobe's PostScript Language
Tutorial and Cookbook
, which can be downloaded from the Adobe web page
(as a
ZIP file,
containing the book and PostScript example code).
Before we focus on how to create PostScript files using the PSFile module, here is a short summary about how PostScript files can be used.
lpr).
graphicx package as
follows:
\documentclass{article}
\usepackage{graphicx}
\begin{document}
\includegraphics{fig1}
\end{document}
If you use latex and dvips to process this input file, the output will
include the figure from fig1.eps. If you use
pdflatex instead, you need to convert the figure to PDF first
(using epstopdf, see above); pdflatex will then include the figure from
fig1.pdf into the output.
The PSFile Python module allows to generate two different types of PostScript files: stand-alone files describing a complete one-page document and Encapsulated PostScript files describing a figure for inclusion into other documents.
An Encapsulated PostScript file describing an individual figure can be created as follows.
#! /usr/bin/env python
from psfile import EPSFile
fd = EPSFile("example.eps", 300, 300)
# ... write PostScript commands to `fd` ...
fd.close()
The EPSFile constructor has three required arguments: the
name of the file to create (typically using the file extension
.eps
), followed by the width and height of the figure in units of
1/72th of an inch. The following optional keyword arguments are available.
A PostScript file describing a full page can be created as follows. Files created this way can be printed directly.
#! /usr/bin/env python
from psfile import PSFile
fd = PSFile("example.ps", paper="letter")
# ... write PostScript commands to `fd` ...
fd.close()
The only required argument to the PSFILE constructor is
the output file name (typically using the file extension .ps
). The
following optional keyword arguments are available.
A4,
A3or
letterto choose the corresponding pre-defined paper size (an appended
*indicates landscape mode), or a pair `(width,height)` of integers to specify a custom paper size in units of 1/72th of an inch. The default is to use A4 paper.
The psfile module constructs the required headers for the output file to set up page dimensions, margins, etc. The coordinate system is set up so that coordinates (0,0) correspond to the lower-left corner of the drawing area.
Instances of the PSFile and EPSFile classes,
described above, are file-like objects which can be used to write the body
of the PostScript file. The following methods are provided:
In addition, there are two attributes which you can read to get the size of the drawing area:
The following example script creates the image from figure 1 above.
#! /usr/bin/env python
from random import uniform
from psfile import EPSFile
fd = EPSFile("ex1.eps", 600, 100)
# dark gray background
fd.append("0.1 setgray")
fd.append("0 0 %d %d rectfill"%(fd.width, fd.height))
# a grid of dark orange lines
fd.append("1 .596 .118 setrgbcolor")
fd.append("1 setlinewidth")
for i in range(1,5):
y = 100*i/5.0
fd.append("5 %.1f moveto 595 %.1f lineto"%(y,y))
for i in range(1,30):
x = 100*i/5.0
fd.append("%.1f 5 moveto %.1f 95 lineto"%(x,x))
fd.append("stroke")
# randomly colored, filled squares
for i in range(0,30):
x = i*20+3
for j in range(0,5):
y = j*20+3
col = uniform(0,1)
if 31*uniform(0,1) > i+1:
fd.append("0 %.3f 0 setrgbcolor"%col)
else:
fd.append("%.3f 0 0 setrgbcolor"%col)
fd.append("%.1f %.1f 14 14 rectfill"%(x,y))
fd.close()
To draw a polygonal line in PostScript you first have to move the
PostScript point to the starting point of the line using the
moveto PostScript operator, and then to move the pen
along the segments of the polygon using the lineto operator.
Finally, you have to use the stroke command to actually draw
the line.
stroke command.
Then clear the current path.
If you have to draw many line segments, it may be a good idea to use
commands like fd.define("l", "lineto") to
define one-letter abbreviations for moveto and
lineto.
The following code generates an Encapsulated PostScript file, containing two squares (one has a missing edge).
#! /usr/bin/env python
from psfile import EPSFile
fd = EPSFile("ex2.eps", 100, 100)
fd.append("""
% outer square
0 0 moveto
100 0 lineto
100 100 lineto
0 100 lineto
closepath
% inner square, open to the left
10 10 moveto
90 10 lineto
90 90 lineto
10 90 lineto
% draw the constructed path
stroke
""")
fd.close()
The output looks as follows:
You can use the setlinewidth command to set the width of a
line. This command can be given any time before the corresponding
stroke command.
The following example illustrates different choices of line widths.
#! /usr/bin/env python
from __future__ import division
from math import pow
from psfile import EPSFile
min_lw = .5
max_lw = 9
steps = 18
fd = EPSFile("ex3.eps", 350, 38,
margin_left=10+.5*min_lw, margin_right=10+.5*max_lw)
fd.append("/Times-Roman 10 selectfont")
for i in range(0, steps+1):
lw = min_lw * pow(max_lw/min_lw, i/steps)
x = fd.width*i/steps
fd.append("%.1f setlinewidth"%lw)
fd.append("%f 0 moveto 0 27 rlineto"%x)
fd.append("stroke")
fd.append("%f 30 moveto"%(x-6))
fd.append("(%.1f) show"%lw)
fd.close()
The output of this script looks as follows:
There are special commands to draw rectangles and circles.
stroke command.
stroke command after this to actually draw the
circle.
The following code creates a grid of 28 randomly coloured rectangles and labels them with the corresponding RGB intensities.
#! /usr/bin/env python
from __future__ import division
from random import uniform
from psfile import EPSFile
cols = 7
rows = 4
gap = 3
fd = EPSFile("ex4.eps", 350, 80)
fd.append("/Times-Roman 8 selectfont")
dx = (fd.width+gap)/cols
dy = (fd.height+gap)/rows
w = dx - gap
h = dy - gap
for j in range(0, rows):
for i in range(0, cols):
r = g = b = 0
while r + g + b < 1:
r, g, b = [ uniform(0,1), uniform(0,1), uniform(0,1) ]
fd.append("%.1f %.1f %.1f setrgbcolor"%(r,g,b))
fd.append("%f %f %f %f rectfill"%(i*dx, j*dy, w, h))
fd.append("0 setgray")
fd.append("%f %f moveto"%(i*dx+4, j*dy+3))
fd.append("(%.1f, %.1f, %.1f) show"%(r,g,b))
fd.close()
The output of one run of the code above looks as follows. Since the choice of colours is random, you'll most likely get a different picture when you run the script yourself.
stroke to draw
the outline). This operation clears the current path.
Remark. The operators described in this section can also be used, to set the background colour of a figure: just start the figure by drawing a coloured, filled rectangle which covers all of the drawing area, and then draw everything else on top of this.
Since fill clears the current path, some care needs to be
taken when filling a region and drawing the outline of the same region in
a different colour. The solution is to save the current graphics state
(which includes the current path) with gsave before issuing
the fill command and then to restore the state using
grestore before stroking the outline of the region. This
technique is illustrated in the following example.
The following code draws seven filled circles on a yellow background.
#! /usr/bin/env python
from psfile import EPSFile
fd = EPSFile("ex5.eps", 350, 50)
# draw the yellow background
fd.append("1 1 0 setrgbcolor")
fd.append("0 0 %d %d rectfill"%(fd.width, fd.height))
# draw black circles filled with red
fd.append("0 setgray")
for x in range(25, 375, 50):
fd.append("""%f 25 20 0 360 arc
gsave
1 0 0 setrgbcolor
fill
grestore
stroke"""%x)
fd.close()
The output looks as follows:
PostScript provides a set of 13 standard fonts which can be scaled to arbitrary sizes. These are used with the following commands:
Times-Roman,
Times-Italic, Times-Bold,
Times-BoldItalic, Helvetica,
Helvetica-Oblique, Helvetica-Bold,
Helvetica-BoldOblique, Courier,
Courier-Oblique, Courier-Bold,
Courier-BoldOblique or Symbol. See
figure 6 for the shape of the different fonts.
PostScript Language Tutorial and Cookbookor google for
ISOLatin1Encoding).
The following code illustrates the 13 PostScript standard fonts.
#! /usr/bin/env python
from psfile import EPSFile
fonts = [
"Times-Roman", "Times-Italic", "Times-Bold", "Times-BoldItalic",
"Helvetica", "Helvetica-Oblique", "Helvetica-Bold",
"Helvetica-BoldOblique", "Courier", "Courier-Oblique",
"Courier-Bold", "Courier-BoldOblique", "Symbol"
]
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
fd = EPSFile("ex6.eps", 420, len(fonts)*12)
for i, name in enumerate(reversed(fonts)):
fd.append("/Times-Roman 10 selectfont")
fd.append("0 %d moveto"%(12*i+3))
fd.append("(%s:) show"%name)
fd.append("/%s 10 selectfont"%name)
fd.append("100 %d moveto"%(12*i+3))
fd.append("(%s) show"%str)
fd.close()
The output looks as follows:
Figure 6. The PostScript standard fonts. This is the output of the script from example 6.
The following two PostScript commands allow to shift and rotate the picture. These functions can, for example, be used to create rotated text.
You can use gsave to save the current coordinate system
before using translate or rotate. This allows to
restore the old coordinate system using grestore.
#! /usr/bin/env python
from __future__ import division
from math import pi, sqrt
from random import vonmisesvariate
from psfile import EPSFile
# a sample from a von Mises distribution
sample = [ vonmisesvariate(pi/4, 1.0) for i in range(0,200) ]
# generate a histogram of the data
nhist = 4 * int(sqrt(len(sample))/4 + 0.5);
count = [ 0 ] * nhist
for x in sample:
y = x/(2*pi) % 1
count[int(y*nhist+.5)%nhist] += 1
# turn into a plot
radius = 72
fd = EPSFile("ex7.eps", 4*radius, 4*radius)
fd.append("/Times-Roman 10 selectfont")
fd.append("%f %f translate"%(2*radius, 2*radius))
fd.append("%f setlinewidth"%((2*pi*radius) / nhist - 1))
for k in count:
r = radius * (1 + .9*k/max(count))
fd.append("0 0 moveto %f 0 lineto"%r)
fd.append("%f -3 moveto (%d) show"%(r+3, k))
fd.append("%f rotate"%(360/nhist))
fd.append("stroke")
fd.append("""1 setlinewidth
0 0 %f 0 360 arc
gsave 1 setgray fill grestore stroke"""%radius)
fd.append("/Symbol 24 selectfont")
fd.append("-35 -10 moveto (k = 1.0) show")
fd.close()
Figure 7. A circular histogram for the von Mises distribution with μ=π/4 and κ=1.
PostScript Language Tutorial and Cookbook(as a ZIP file, containing the book and the example code), the PostScript Language Reference, and the
PostScript Language Program Designguide (again as a ZIP file).
Copyright © 2009, Jochen Voss. All content on this website (including text, pictures, and any other original works), unless otherwise noted, is licensed under a Creative Commons Attribution-Share Alike 3.0 License. Last update: 2009-12-13.