peps-2.0 will preprocess encapsulated Postscript files and allow you to convert them into bitmaps. Note the word allow: peps does not do the conversion for you. Rather, it prepares the Postscript input for a bitmap conversion by Ghostscript, which must already be installed on your system.
peps reads its input either from a file or from stdin and sends its output either to a file or to stdout. You can use it as a stand-alone Postscript-to-bitmap conversion program, or as a Unix filter. And you can use it on line to create bitmaps on the fly and send them out to a web browser, even compressing them if the browser supports gzip compression (most browsers do, but peps will check first, unless you tell it otherwise).
Because peps uses Ghostscript to do the actual conversion, you can use it to convert Postscript into any type of bitmap supported by your particular installation of Ghostscript. While in version 1.0 the default was to create pnm bitmaps, in version 2.0 the default is to produce PNG bitmaps with 16 million colors. The reason why version 1.0 defaulted to pnm was that at the time version 1.0 was written, most Unix installations of Ghostscript could only anti-alias pnm bitmaps. Recent versions of Ghostscript do not have this limitation.
Please take a look at this picture:
It suffers from a serious case of aliasing. To see it, compare the connectors on the FreeBSD Box and on Windows 95 with those on the Router. They are different even though their original Postscript code is identical. Which one is correct?
Now, take a look at this picture:
It was produced from the same EPS original, but using 4-bit anti-aliasing. To produce them, version 1.0 had to use Ghostscript to create a pnm file, then use pnmtopng to convert it into a PNG file. With version 2.0 peps uses Ghostscript to create the PNG bitmap directly.
The simplest way to use peps is by typing the program name (peps) followed by the name of the input file. For example:
% peps tiger.ps
This will convert the file tiger.ps (which comes as part of the Ghostscript distribution) into a PNG bitmap, using peps defaults, which means vertical and horizontal resolution of 100, 4-bit anti-aliasing, using the safe mode, sending the output to stdout.
Usually, defaults are not good enough. For example, for the above images, the resolution of 100 was correct. But in the case of tiger.ps, that resolution would result in a very huge bitmap.
-r switch followed by a number defines the resolution of the
image. The larger the number, the larger the resultant bitmap.
You can also set the vertical and horizontal resolution separately, using
-h and the
-p switch will make sure the output comes as a
PNG bitmap. That is the default anyway. Thus,
% peps -r 36 -p tiger.ps
will produce this image:
% peps -v 36 -h 18 -p tiger.ps
switches tell peps what resolution in dots per inch to use, you have the option
to instead decide how many pixels wide and high, or wide or high the image should be.
-q switches determine the width
and height respectively.
% peps -w 250 -q 100 -p tiger.ps
-W or the
-Q switch to decide the width
or the height, while keeping the original width/height ratio. Note that
these switches are capital letters.
% peps -W 250 -p tiger.ps
-m switch instead of the
switch to produce monochrome (grayscale) PNG bitmaps:
% peps -Q 300 -m tiger.ps
-Y switches allow you to add extra space to the left
(-x), right (-X), bottom (-y), and the top (-Y) of the image. This is the same
as creating a frame for the image. You can also use negative numbers, which
will crop the image instead:
% peps -r 36 -x -100 -X -100 -Y 20 -p tiger.ps
Please note that these numbers are in Postscript points, not in the number of pixels to add (or cut). That means that when you change the resolution of the output, the frame/crop will keep its relative size: The result will look exactly the same, but will be resized as necessary.
-a switch defines the angle of rotation of the image in
degrees, about the center of the image. By default the angle is 0. A positive
angle rotates counter-clockwise, a negative one clockwise.
Rotation almost always results in parts of the image being cut off. This can be prevented by adding some extra borders as described above.
% peps -r 36 -p -x 60 -y 40 -Y 50 -a 30 tiger.ps
In all of the above examples, peps read the input file. It checked
that it started with
%!PS-Adobe- to make sure the file had a
valid Postscript header. It then looked for the standard
line to find the proper image bounding box. Without this information
peps would not know what to ask Ghostscript to do.
You can override the bounding box with the
-b switch followed by
the left, bottom, right, and top coordinates in Postscript points. When you
do that, peps does not look for the header, so your input does not
even need to have it. This allows you to use any Postscript file for
peps input, not just the encapsulated ones.
% peps -b 120 345 250 400 -Q 300 -p tiger.ps
PostScript is not just an image description language. It is a true programming language which allows you to read and write files on the system. By default, peps tells Ghostscript to ignore any commands to write to files. This is a safety feature. Without it, you might download an EPS file from the web, and be surprised that some of your data was mysteriously wiped out.
If, however, you want to disable this safeguard, use the
switch. If, on the other hand, you want to enable it explicitly,
-S (capital letter) switch.
switches control anti-aliasing. The
-g switch controls
the anti-aliasing of graphics, the
-t switch controls
the anti-aliasing of text, and the
-l switch controls
the anti-aliasing of both, graphics and text.
These two switches must be followed by the number of bits to use
for anti-aliasing. The valid values are
2 (some anti-aliasing), and
4 (full anti-aliasing).
The default value is
4 for both.
-o switch allows you to declare an output file.
By default, the output goes to stdout, but you can send it to a
file using this option. For example,
% peps -p -o tiger.png tiger.ps
will send the output to tiger.png.
-i switch tells peps to read the input from
stdin. This was added after much soul searching in version
2.0. Why much soul searching? Because stdin cannot be
rewound. And because the Postscript specification allows you to
create Postscript files, encapsulated or not, which defers the
%%BoundingBox: comment to the trailer. When that happens and
the input is coming from stdin, well, whatever Postscript
code appears before the
%%BoundingBox: comment, it is lost and
peps cannot send it to Ghostscript.
So, in version 1.0, I decided not to accept input from stdin
at all. But that limits the usefulness of peps. As of
version 2.0, peps will read its input from stdin
under the condition that either the input starts with
%!PS-Adobe- and has a
comment before any actual Postscript code appears, or you use
-c switch tells peps it is used either from
a CGI script or as a CGI script. The
switches may also be used with CGI scripts. More about
-n switch will result in producing a pnm bitmap,
-f switch followed by a Ghostscript device name
instructs peps that the bitmap should be produced by
that device. Of course, Ghostscript has to know about
such a device. For example, we could create a jpeg bitmap:
% peps -Q 250 -f jpeg tiger.ps
Note: When using the
-fswitch, please be mindful that the purpose of peps is to convert Postscript code into bitmaps. And while Ghostscript treats bitmaps as special-purpose devices, not all of Ghostscript devices are bitmaps. If you use the
-fswitch to select a non-bitmap device, chances are it will either not work at all or not work as you were hoping. For example, if you opt for
-f x11, one of two things will happen: If you are running X Window, Ghostscript will paint the output to a window but the window will then disappear. And if you are not running on X Window, you will just get an error message. This is because the
x11device sends its output to a graphical window, not to a file.
-z switch tells peps to pipe its output
through gzip which will compress it. peps will
ignore the switch if the output goes to a file. But if you want
the output in a .gz file, just redirect it to one, like this:
% peps -z -p tiger.ps > tiger.png.gz
-Z (capital letter) does the opposite: It tells
peps not to compress the output.
System administrators may choose to compile peps with a different set of defaults than discussed here. You can find out what defaults are on your system by entering:
% peps -d
You can find what version of peps is installed on your system by typing:
% peps -v
If it tells you the version, that is the version installed. If it shows you the list of options instead, it is version 1.0 (in that case you should upgrade—or ask your system administrator to upgrade—to the current version).
You can get a quick list of options by typing:
% peps -h
For a more detailed list, type:
All switches can be listed in any order. If a switch contradicts another switch, whichever switch is listed later will win.% man peps
Under Unix it is possible to run files as scripts by starting
the files with a
#! followed by the full path of the program
that can interpret the files. That only works if that program
# character as the start of a comment.
The Postscript language does not treat the
# character as
the start of a comment, but peps will ignore the first line
of its input if it starts with a
#. It must be the very first
letter of the very first line. Any other line that contains it,
will not be treated as a comment, only the first one.
This is particularly useful in CGI scripts discussed next.
CGI stands for Common Gateway Interface. It allows webmasters to create web content on the fly. If you are not familiar with how CGI works, you may want to read my CGI Programming Is Simple! tutorial.
It is quite easy and simple to create images on the fly with
peps and have a web server send them to a web browser.
For that, we use the
-c flag, and send the output to
stdout. When we do that, peps will first write the
Content-Type header to stdout. It will
then check to see if the browser accepts gzip compression.
If so, it will also write the correct
header. It will then print two new-line characters, indicating
that the header is finished and contents follow. Only then will
it send the bitmap (compressed by gzip if applicable)
to stdout. And, of course, as discussed above,
it will ignore the first input line if it starts with a
To execute your scripts, the web server needs to know that
they are CGI executables. Depending on your web server configuration,
you may have to place your scripts into a
directory. Or you may have to end the script name with the
extension. Or, you may configure your we server to recognize some
other extension as a script. For example, on the Apache server
you just need to add the following line the
AddHandler cgi-script png
This will work in the directory in which the
file is located and all of its subdirectories. And as you
probably have regular PNG files on your web site, you need
to create a separate directory just for the dynamically
Enough talk, let's see a simple example. Let's create a file,
line.png and store it in the directory with that
.htaccess file. Now assuming that your copy of peps
is installed in
/usr/bin, let's edit the file with these
#!/usr/bin/peps -p -c -b 10 10 20 20 -r3600 1 0 1 setrgbcolor 10 10 moveto 20 20 lineto stroke showpage
We have started the file with
#!/usr/bin/peps -p -c -b 10 10 20 20
-r3600. Remember, we have named the file
Now, assuming that your Unix user name is
bobby and the file
is located in
/usr/www/bobby/images, that first line will cause
the system to execute the following:
/usr/bin/peps -p -c -b 10 10 20 20 -r3600 /usr/www/bobby/images/line.png
That means peps will read its input from
and write its output to stdout. It will produce a PNG bitmap
with a resolution of 3600 dpi, both vertically and horizontally. It
will expect the Postscript code bounding box to be
10 10 20 20.
And it will produce all the necessary CGI headers, as well as check
whether the browser can accept gzip compression.
When reading the file, peps will discard the first line and pass everything else on to Ghostscript with all the necessary instructions on what kind of bitmap to produce.
The Postscript code is very simple in this example: Draw a magenta
colored line from
10 10 to
20 20. Now because the
bounding box also goes from
10 10 to
20 20, the line
will go from the lower left corner to the upper right coner. It will be a very
tiny line. But because we are using the resolution of 3600 dpi, while our computer
monitor is set up for maybe 100 dpi, the magenta line should seem as if
we were looking at it through a microscope.
And to see this script in live action, see it at
(use your browser back button to return here).
Naturally, on a web site you would use Postscript to do more than draw a magenta line.
There still is much more that you can do. Since peps can accept its input from stdin, you can use it to create images that are different for every person, or different on different occasions. You can use peps in a shell script that changes the Postscript code as necessary, or you can pipe the output of some program to peps.
In our next example, we are going to write a shell script
that produces Postscript code to display the IP address
of the visitor to your web site. We will use a font that
is not distributed with Ghostscript because it is a font
we paid for. We are allowed to use it in our creations, but not to
give the font itself away. So, we upload the font to some directory
that is not accessible from the web. We must let Ghostcript
know where the font is. We do so by setting the
environmental variable. We could do that from our shell script.
But then we'd need to do it from every shell script we use
with fonts. So, the better solution (at least with the
Apache web server) is to do it in the
file. Assuming the fonts are in
this is what we add to
SetEnv GS_FONTPATH /usr/home/bobby/myfonts
And here is our script:
#!/bin/sh # Since this is only valid for the recipient, # turn off any caching other than users's own echo Cache-Control: private # Display web site visitor's IP address. /usr/bin/peps -p -c -b -20 -7 90 35 -W468 << EOF 0 0.3 0.5 setrgbcolor -20 -7 moveto 90 -7 lineto 90 35 lineto -20 35 lineto closepath fill 0.8 0.9 1 setrgbcolor /ACaslon-BoldOsF findfont 15 scalefont setfont -17 -1 moveto ($REMOTE_ADDR) show 0.4 0.6 0.75 setrgbcolor -17 24 moveto /ACaslon-Regular findfont 10 scalefont setfont (Y) show -1.6 0 rmoveto % kerning (ou'll be happy to know) show -16.4 12 moveto (that your IP is:) show showpage EOF
The Apache server sets the
variable to the caller's IP address. And the
$REMOTE_ADDR in our script with the value
REMOTE_ADDR. Note that we started the script
echo Cache-Control: private. This
tells the web browser that it can cache the image, but it also
tells any gateways the image may be passing through not to cache
it because the image only makes sense to the one person we made
By the way, the
OsF in the name of the font used to
display the IP address stands for Old-style Figures.
That means that no, the numbers are not misaligned, as inevitably
some people will complain, but that they are perfectly
aligned according to classical typography which aligns
numbers with lower-case letters and gives them descenders (as in the p)
and ascenders (as in the h).
Here is what that script has produced specifically for you:
If you prefer, you can create your HTTP header and simply run
peps without the
-c switch. In that case,
you may want to use the
-C (capital letter)
switch, which will simply print the HTTP
line and exit. In that case, you can also use the
switch to turn the gzip compression on, or the
(capital letter) to turn it off.
That way, you could rewrite our last example to something like this:
#!/bin/sh # Since this is only valid for the recipient, # turn off any caching other than users's own echo Cache-Control: private # Print the Content-Type line /usr/bin/peps -p -C # Figure out if the browser supports gzip compression if echo $HTTP_ACCEPT_ENCODING | grep -q gzip; then ZETA=-z echo Content-Encoding: gzip else ZETA=-Z fi # Print a blank line echo # Display web site visitor's IP address. /usr/bin/peps -p $ZETA -b -20 -7 90 35 -W468 << EOF 0 0.3 0.5 setrgbcolor -20 -7 moveto 90 -7 lineto 90 35 lineto -20 35 lineto closepath fill 0.8 0.9 1 setrgbcolor /ACaslon-BoldOsF findfont 15 scalefont setfont -17 -1 moveto ($REMOTE_ADDR) show 0.4 0.6 0.75 setrgbcolor -17 24 moveto /ACaslon-Regular findfont 10 scalefont setfont (Y) show -1.6 0 rmoveto % kerning (ou'll be happy to know) show -16.4 12 moveto (that your IP is:) show showpage EOF
The two scripts are functionally identical, though the latter requires the system to do much more work than the former.
In the scripts we have seen, we used peps to print the correct
Content-Type. Let us see how peps
figures out what to print: It looks for the type in several places. If it finds
the correct type, it quits looking and prints it. If it does not find
it anywhere, it prints:
device part is the name of the Ghostscript
output device specified with the
So, what are the places peps searches for an answer? They are
several files, which may or may not exist (if they do not exist,
they just do not exist, peps will continue to work). If they do
exist, peps expects them to contain a list of Ghostscript devices
and their corresponding content types, one per line. Also, peps
will treat the
# character as the start of a comment
extending to the end of the line. An example of the list might be:
# gs device MIME bbox text/plain; charset=8859-1 bmp16 image/bmp bmp16m image/bmp bmp256 image/bmp bmp32b image/bmp bmpgray image/bmp bmpmono image/bmp cgm8 image/cgm cgm24 image/cgm cgmmono image/cgm epswrite application/postscript faxg3 image/g3fax jpeg image/jpeg jpeggray image/jpeg miff24 image/x-miff pbm image/x-portable-bitmap pbmraw image/x-portable-bitmap pcx16 image/x-pcx pcx24b image/x-pcx pcx256 image/x-pcx pcxcmyk image/x-pcx pcxgray image/x-pcx pcxmono image/x-pcx pdfwrite application/pdf pgm image/x-portable-graymap png16 image/png png16m image/png png256 image/png pngalpha image/png pnggray image/png pnm image/x-portable-anymap ppm image/x-portable-pixmap psgray application/postscript psmono application/postscript psrgb application/postscript pswrite application/postscript tiff12nc image/tiff tiff24nc image/tiff tiffcrle image/tiff tiffg3 image/tiff tiffg32d image/tiff tiffg4 image/tiff tifflzw image/tiff tiffpack image/tiff
peps searches through these files and in this order:
The file specified by the
peps.mime located in the home directory.
Whose home directory? If the
variable is defined, it will look in the home directory of the user
specified by it. Or, in Unix speak:
PEPSUSER environmental variable is not defined,
it will look in the home directory of the effective user that invoked
peps. Or in Unix speak:
/etc/peps.mime, unless the system
administrator compiled peps with a different directory to look
A list hard-coded inside peps. That means that any of the above files are only necessary if your system uses Ghostscript with devices that peps does not know about. These could be custom bitmap formats, or formats added to future versions of Ghostscript.
Since CGI scripts are normally invoked by a web server, under most
circumstances, the effective user will be
or a similar special user. If you need to define your own content
types, you can use the
-M switch. But a better
choice is to define the
variable (better because you only need to do it once). Assuming again
that your user name is
bobby and that
you are using the Apache web server, you just need to enter the
following into the
SetEnv PEPSUSER bobby
Developing CGI scripts would be rather tedious if you had to debug them by uploading them on your web server and seeing if they work as desired, then editing them at home or work and again upload them, and so on. To make their development much easier, a special version of peps, called xpeps will print the bitmap into a window instead of sending it to stdout or to a file.
Here is an example of what it looks like under X window:
Xpeps is not an X window program. It simply calls
Ghostscript and tells it to process the EPS source and display it
using its x11 device. We could actually accomplish the same
peps -f x11, but if we did, we would encounter
a problem: As soon as Ghostscript is done processing the EPS input,
it would exit. The bitmap would flash on the display and promptly
To overcome this problem, xpeps waits for you to press ENTER before quitting Ghostscript. This works well when its input comes from a file, but what is xpeps to do when the input comes from stdin? A default solution is to fork and wait until, well, until you kill it:
Doing that would allow you to test your CGI scripts, like this:
But, while it would work, it would leave a lot of zombies behind.
Therefore, xpeps introduces two new command line options,
-e [time] and
time parameter tells xpeps
how many seconds after sending its data to Ghostscript it should
evaporate. Additionally, with the
option, xpeps will print out the
pid of its background
process, so you can
kill it explicitly. If you do
not specify the
time parameter, xpeps
will wait for its evaporation delay, which is forever by default,
but you can (and probably should) compile it with a finite value.
You can use these switches whether the input comes from stdin or from a file. Here is an example of running xpeps three times in a row, the first two time specifying the evaporation delay of 600 seconds, the third time running it without a delay, so xpeps waits for us to press ENTER:
You can use the
-E switches with
peps as well. They will simply do nothing. But that allows you to debug
your scripts with xpeps and once they are debugged, you just need
/usr/bin/peps and install the script on your web server.
You can read an Acrobat (PDF) version of the Unix man page for peps.
peps-2.0 is distributed in source code from ftp.peps.redprince.net/unix/peps by ftp, and www.peps.redprince.net/peps/2.0/ by html.
The home page is www.peps.redprince.net.
To install peps you need a computer computer operating Unix. You also need Ghostscript on your path (its full path will be hardcoded into peps and xpeps at compilation time. Similarly, you will need gzip.
Additionally, if you want to install xpeps, you will also need
X Window, and your copy of Ghostscript will need to support the x11
You need all of the above before installing peps and xpeps. Please read the README file for further installation information. Assuming you already have the above requirements, installing peps alone on most systems is as simple as typing:
$ make install clean
Or, to install both, peps and xpeps:
$ make install xinstall clean
If you do not have Unix, the best thing to do is to get it. You can get FreeBSD and install it right off the Internet. If, however, you absolutely must use MS Windows, all is not lost. You will need to install Cygwin. Then you will either need to get the Cygwin version of Ghostscript and X Window, and install peps and xpeps like this:
$ env BINEXT=.exe make install xinstall clean
Or you will need to install Ghostscript for Windows (you will still need Cygwin), and note where its file gswin32c.exe is located, then convert its path into the Cygwin path, such as /cygdrive/c/gs/gs-8.15/bin, and then type all of the following:
$ export GSPATH=/cygdrive/c/gs/gs-8.15/bin/gswin32c.exe $ export XCOLOR=display $ export XGRAY=display $ export XBINDIR=/usr/bin $ export BINEXT=.exe $ make install xinstall clean
Depending on which way you install xpeps under Cygwin, it either runs using X window:
Or it runs directly under Ms Windows (still requiring Cygwin):
Release date: 25 January 2005
Initial release, 4 July 2001.
Copyright © 2001, 2005 G. Adam Stanislav
All rights reserved