Postscript

First a side note

I have an old postscript printer (an HP Laserjet 4m) that does only postscript level 2. Some (many?) tools (such as evince) emit level 3, which the printer rejects, which gives me heartache. The answer is xpdf and pdftops (not (emphatically not) pdf2ps)). These either emit nice level 2 postscript and/or are not buggy like pdf2ps.
The good stuff seems to be in poppler-utils

Getting started

For a long time, I have had a copy of the 3 postscript books on my shelf, just gathering dust. Recently I got an interest in making my postscript printer do some special things and so it was time to try some postscript programming. Reading the tutorial in the first book got me started.

There are lots of tutorials and reference information online. They seem to come and go (mostly go), so I decided to write my own:

What I like to do is to type some postscript into a file and use ghostview to look at the output:

gv test.ps
This works pretty well, once you realize that ghostview may not be showing you the entire page. The thing to do is to fiddle with it's unique page scrolling dingus or change the viewing scale.

Next thing is to try sending something to a printer. I innocently try:

lpr test.ps
And get a nice listing of my program, but not what I wanted. Looking at some postscript files laying around on my system, I notice that they all begin with:
%!PS-Adobe-3.0
Indeed, this does the trick. Rumor has it that just starting the file with %! would be sufficient. Having this magic shebang line makes my printing system actually render the postscript rather than printing the program as ascii text on the printer.

Units and addressing

Here is the bottom line right up front. Postscript starts at the lower left corner of the page (which is 0,0). The top right corner of a 8.5 by 11 page will be the coordinate 612,792. There are 72 postscript units per inch. For more details, keep reading.

The next frontier is finding out what the addressible range is on my printer given an 8.5 by 11 inch sheet of paper. I suppose this is documented somewhere, but my bet is that I may as well just use trial and error and learn a bit of postscript in the process. A useful fact is that postscript works in its own system of units of which there are 72 per inch. This is what most of the world calls a "point", but the postscript literature seems very careful not to refer to them as such. For good old U.S. letter sized paper, 8.5 inches is 612 postscript units (0-611) and 11 inches is 792 units (0-791).

I wrote a program that generates graph paper (I didn't set out to generate graph paper, but that is what it amounts to). And graph paper in postscript units of all things! It has divisions of 5, 25, and 100 postscript units. Now it is fairly easy to print this out and see what part of the 8.5 by 11 inch sheet we can actually print on. On my HP Color Laserjet 4600dn, it looks like I can get to:

In other words, there are 12 units (or 12/72 or 1/6 of an inch), i.e. 0.1666 inches on all 4 sides that are not accessible. I suppose this is an aspect of the printer mechanism itself. This yields a printable area of 8 1/6 by 10 4/6 inches. Let's suppose we want to print a bunch of standard 2 by 3.5 inch business cards on 8.5 by 11 paper. We could get 12 of them if we stood them on end (this 3x4 block would be 8 by 10.5 inches). The other way would give us only 10 cards via a 2x5 block 7 by 10 inches.

Some postscript basics

Postscript like any programming language can manipulate values. Postscript also manipulates graphic state. Postscript works in reverse polish notation (which will drive most people nuts). To add 2 plus 2, you do:
2 2 add
To draw a line you do:
100 100 moveto
244 244 lineto
stroke
showpage
To draw a circle of radius 144 centered at 200,200 you do:
newpath 200 200 144 0 360 arc closepath stroke showpage
To set the line width, you do:
0.25 setlinewidth
To define something with a value you do:
/dingus 17 def
To define a procedure you do:
/mything { add mul } def
Here is a postscript idiom that some find confusing:
200 /var exch def
What this does is to create a variable named "var" and put the value on the stack (here 200) into it. What is going on at the time the exch gets executed is that the stack holds two things - "200" and "/var". We need to flip these around so that when the def executes, it does "/var 200 def" for us.

Postscript loops

Postscript has conditionals, but we are going to jump right over them and talk about loops. A "for loop" in postscript has 4 operands - start, increment, end, and block to repeat.
0 10 100 {xxxxxxx} for
Another way is to use loop and exit.
Another way to build loops is to use repeat.

As you might expect with a stack intensive language, postscript does support recursion.

Postscript Fonts

Now, what fonts do I have available? Apparently there are 35 "standard" postscript fonts, and most printers have them built in. Many printers have additional fonts and/or can have additional fonts loaded into them. Some printers have only 13 of the 35 listed below, these are the Courier, Times, Helvetica (but not the Helvetica-Narrow) and the Symbol.
Here is a list of the 35 standard fonts:

The standard fonts are called "Type 1 fonts". This is a deep subject that I have yet to probe into.

I wrote a program to demonstrate the 13 basic fonts.
I also wrote a program to demonstrate the other 22 standard fonts.

My browser renders the above links via some viewer, which is useful, but not exactly what I had in mind. If you actually want to look at the postscript, you will have to do something clever, or just save the file to your disk and use some editor to look at it.

Using ghostscript as an interpreter

I wanted to be able to run postscript as a subprocess and get a value back from it. Here is how to do it. A sample bit of postscript is:
%!PS-Adobe-3.0 EPSF-3.0
/Times-Roman findfont 10 scalefont setfont
(pickle) stringwidth pop ==
quit
And I feed it to ghostscript via:
gs -q -dNODISPLAY size.ps
This works nicely. Also note that it can be quite handy to just run "gs" instead of "gv". The "gv" command is just a front end to gs anyway, and gs will give you interactive postscript with a prompt.

Links

Here are some links that look useful and interesting:
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org