PostScript

From Wikipedia

HomePage | Recent changes | View source | Discuss this page | Page history | Log in |

Printable version | Disclaimers | Privacy policy

PostScript is a page description language based on work originally done by John Gaffney at Evans and Sutherland in 1976, evolving through "JaM" ("John and Martin", Martin Newell) at Xerox PARC, and finally implemented in its current form by John Warnock et al. after he and Chuck Geschke founded Adobe Systems, Inc. in 1982.

The Apple LaserWriter used PostScript as a page description language, and an interpreter for this language was an ubiquitous components for laser printers in the 1990s. This was a departure from the practise of previous printer generations, where every company or even model used its own control language. PostScript was also novel in that it went beyond the simple capabilities of previous printer languages, which consisted mainly of series of escape sequences. In contrast, PostScript is a full-fledged programming language. In this way it parallels Emacs, which exploited a similar insight about editing tasks. It is also noteworthy for implementing on-the fly rasterisation, from Bezier curve descriptions, of high-quality fonts at low resolution (early laser printers rendered at 300 dpi for example); it was formerly believed that hand-tuned bitmap fonts were required for this task.

Many applications can transform a document into a PostScript program whose execution will result in the original document. This program can be sent to a interpreter in a printer (resulting in a printed document), or to one inside another application, which will display the document on-screen. Since the document-program is the same, regardless of its destination, it is called device-independent.

The combination of technical merits and widespread availability made PostScript a language of choice for graphical output for printing applications. Once the de-facto standard for electronic distribution of final documents, PDF has effectively succeeded PostScript in this area. By 2001 there are also fewer and fewer printer models which come with support for PostScript.

Usage as a Windowing System

In the early 1990s, Sun MicroSystems developed an entire windowing system based on PostScript, NeWS stands for Network Windowing Systems. Sun also developed TNT (the NeWS toolkit) for OpenLook programming. There were only a few companies committed to this unproven technology. The OpenLook version of FrameMaker by Frame Technology Inc. at the time was one of the few products successfully run on NeWS. After it was clear that OpenLook was had lost out to Motif in popularity, and after Adobe acquired FrameMaker, products on NeWS simply vanished. Most UNIX workstations, including Sun's run NeWS's competitor, the X Window System.

Around the same time, NeXT Computer designed Display PostScript as a windowing system which was later adopted by other companies such as IBM for their RS6000 workstations and SGI for their graphic workstations.

An attractive feature of PostScript as windowing system was to allow infinitely, smoothly scalable graphics and fonts on highend computers back in those days when bitmap were the norm. Nowaday such capability is taken for granted by computer users. One of the major competititors in the font technology was BitStream. The main font technologies used today are the PostScript Adobe Type 1 technology, and TrueType (developed jointly by Apple and Microsoft.)

Another interesting point is that the windowing servers in these systems execute user interface elements (widgets), because PostScript is a programming language. Part of the behaviour of a widget can be specified in its code. For example, a toggle button's display routine can query the button's state (pressed or not) and select the according visualization. The button can also react to mouse clicks by changing its state from pressed to not pressed and vice versa. All this can happen in the windowing server without interaction with the client program. Compare this to a "dumb" X windows server, which can only report "mouse clicked on button" events to a client, which then has to switch the state, and finally instruct the server to display the new state. If client and server are not on the same machine, these interactions will have to travel over the network, slowing the feedback loop down unnecessarily.

The Language

PostScript is an interpreted, stack-based language similar to Forth. The language syntax uses Reverse Polish notation which makes parantheses unnecessary, but reading a program requires some practice, because one has to keep the layout of the stack in mind. Most operators (what other languages term functions) take their arguments from the stack, and place their results onto the stack. Literals (for example numbers) have the effect of placing a copy of themselves on the stack.

Example:

 3 4 add 5 1 sub mul

will compute (3 + 4) * (5 - 1). Let's take a look at what happens in detail: 3 and 4 are both literals, so will push themselves onto the stack. So after these two instructions, the stack will look like this:

4
3

add is an operand, taking the two top-most elements from the stack (3 and 4 in our example), adds them together, and pushes the result onto the stack:

7

Next come two literals again, which will make the stack look like this (note that action is usually constrained to the top of the stack, leaving lower elements unchanged):

1
5
7

Another operand, sub, takes two elements from the top, subtracts the first (higher one) from the second, and pushes the result onto the stack:

4
7

It should be obvious that mul works like the other operators, taking its two arguments from the stack, and pushing their product:

28

But this did nothing more than an old RPN calculator. Of course, PostScript has variables. In detail, it has a dictionary where everything that is not a literal is looked up; on a match, the current value stored under the name is pushed; mismatches will result in an error. To place something in the dictionary we need the def operator, which takes a name and a value as its arguments. Names are constructed by prefixing (or quoting) with a slash. So

 /x1 15 def

will first push the name "x1" on the stack, then the value 15, then execute def which will take both from the stack, and write 15 into the dictionary under the name "x1". Later occurances of "x1" (not to be confused with "/x1") will push 15 onto the stack as long as the variable is unchanged. This code will increment the content of x1 by 2:

/x1 x1 2 add def

Some real programming language power is offered by { and }. The opening brace puts the interpreter in deferred execution mode, so that everything is just placed on the stack, even operators and other executable objects. The one exception is the closing brace, which takes everything put on the stack since the opening brace, bundles it up into an (anonymous) procedure, and places that on the stack.

This construct is used in various ways, for subroutine definition (the anonymous procedure is assigned to a variable), loops, conditionals, etc. Example:

x1 0 eq { 0 } { 1 x1 div } ifelse

This code first uses the eq operator to test whether the value of x1 is equal to 0, depending on the outcome eq will push true or false onto the stack. After that, two procedures are pushed onto the stack. Then ifelse is executed, which takes three arguments from the stack, and will execute either the second (if the third is true) or first (if the third is false). In summary, 0 results if x1 is 0, 1/x1 is the result for all other cases.

/inc3 { 3 add } def

Here def is used to place something in the dictionary, only this time it is a procedure instead of a simple integer. This works because the values coming from the dictionary are executed not just pushed (as simplistically stated above). Since executing a literal amounts to pushing it, that did not make a difference before. Now executing "inc3" will first look it up in the dictionary, find the procedure object representing "{ 3 add }" and execute that. One value must reside on the stack for this to work, since add needs to arguments only one of which is given in the procedure itself. Naturally, one passes arguments to procedures by placing them on the stack, so we can simply view "inc3" as a procedure that takes one argument. Example call:

71 inc3

will put 71 on the stack, which inc3 will increment by three, for a final result of 74.

Further Reading

"The PostScript Language Reference Manual" is the de facto defining work, known as "The Red Book" on account of its covers. The first edition covered PostScript Level 1, the second edition covered a greatly expanded language known as PostScript Level 2. The third edition covers PostScript 3 (with this version, Adobe dropped "level" from the name).

"PostScript Language Tutoral and Cookbook" is the corresponding introductory text, known as "The Blue Book" on account of its covers. It's an unusally good tutorial book, with lots of excellent examples.


See also:


This article (or an earlier version of it) contains material from FOLDOC, used with permission.