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?

Actually, neither.

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 Command Line

The simplest way to use peps is by typing the program name (peps) followed by the name of the input file. For example:

	% peps

This will convert the file (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, that resolution would result in a very huge bitmap.

The -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 the -h and the -v switches.

The -p switch will make sure the output comes as a PNG bitmap. That is the default anyway. Thus,

	% peps -r 36 -p

will produce this image:

	% peps -v 36 -h 18 -p

results in:

While the -r, -v and -h 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. The -w and -q switches determine the width and height respectively.

	% peps -w 250 -q 100 -p


Use the -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


Use the -m switch instead of the -p switch to produce monochrome (grayscale) PNG bitmaps:

	% peps -Q 300 -m

The -x, -X, -y, and -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

results in:

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.

The -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


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 %%BoundingBox: 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

zooms in:

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 -s switch. If, on the other hand, you want to enable it explicitly, use the -S (capital letter) switch.

The -g, -t and -l 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 1 (no anti-aliasing), 2 (some anti-aliasing), and 4 (full anti-aliasing).

The default value is 4 for both.

The -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

will send the output to tiger.png.

The -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 the %!PS-Adobe- and has a %%BoundingBox: comment before any actual Postscript code appears, or you use the -b switch.

The -c switch tells peps it is used either from a CGI script or as a CGI script. The -C and -M switches may also be used with CGI scripts. More about that later.

The -n switch will result in producing a pnm bitmap, while the -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

Note: When using the -f switch, 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 -f switch 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 x11 device sends its output to a graphical window, not to a file.

The -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.png.gz

The -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:

	% man peps
All switches can be listed in any order. If a switch contradicts another switch, whichever switch is listed later will win.


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 treats the # 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 appropriate 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 Content-Encoding 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 /cgi-bin/ directory. Or you may have to end the script name with the .cgi 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 .htaccess file:

	AddHandler	cgi-script	png

This will work in the directory in which the .htaccess 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 created bitmaps.

Enough talk, let's see a simple example. Let's create a file, name it 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 contents:

	#!/usr/bin/peps -p -c -b 10 10 20 20 -r3600 
	1 0 1 setrgbcolor
	10 10 moveto
	20 20 lineto

We have started the file with #!/usr/bin/peps -p -c -b 10 10 20 20 -r3600. Remember, we have named the file line.png. 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 /usr/www/bobby/images/line.png 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 GS_FONTPATH 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 .htaccess file. Assuming the fonts are in /usr/home/bobby/myfonts, this is what we add to .htaccess:

	SetEnv	GS_FONTPATH	/usr/home/bobby/myfonts

And here is our script:


	# 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

	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

The Apache server sets the REMOTE_ADDR environmental variable to the caller's IP address. And the sh shell replaces $REMOTE_ADDR in our script with the value of REMOTE_ADDR. Note that we started the script with 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 it for.

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:

Advanced CGI

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 Content-Type line and exit. In that case, you can also use the -z switch to turn the gzip compression on, or the -Z (capital letter) to turn it off.

That way, you could rewrite our last example to something like this:


	# 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
		echo Content-Encoding: gzip

	# Print a blank line

	# 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

	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

The two scripts are functionally identical, though the latter requires the system to do much more work than the former.

Determination of Content-Type

In the scripts we have seen, we used peps to print the correct HTTP 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:

	Cotent-Type: image/x-device

The device part is the name of the Ghostscript output device specified with the -f switch.

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:

  1. The file specified by the -M file switch.

  2. The file peps.mime located in the home directory. Whose home directory? If the PEPSUSER environmental variable is defined, it will look in the home directory of the user specified by it. Or, in Unix speak: ~$PEPSUSER/peps.mime. If the PEPSUSER environmental variable is not defined, it will look in the home directory of the effective user that invoked peps. Or in Unix speak: ~/peps.mime.

  3. The file /etc/peps.mime, unless the system administrator compiled peps with a different directory to look in.

  4. 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 nobody, 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 PEPSUSER environmental 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 .htaccess file:

	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 with 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 vanish.

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 -E [time]. The time parameter tells xpeps how many seconds after sending its data to Ghostscript it should “evaporate.” Additionally, with the -E 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 and -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 to change /usr/X11R6/bin/xpeps to /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 by ftp, and by html.

The home page is


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 and devices.

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:

Xpeps under Cygwin/X

Or it runs directly under Ms Windows (still requiring Cygwin):

Xpeps under Cygwin without X


Version 2.0

Release date: 25 January 2005

Version 1.0

Initial release, 4 July 2001.

Copyright © 2001, 2005 G. Adam Stanislav
All rights reserved