Click on our Sponsors to help Support SunWorld
Sysadmin by Hal Stern

PS I Love You

PostScript isn't just for printers anymore

SunWorld
August  1995
[Next story]
[Table of Contents]
[Search]
Subscribe to SunWorld, it's free!

Abstract
You may think PostScript is for printers. However, it is a language that is beautiful in its simplicity and increasingly useful to know. PostScript files are a convenient exchange medium, because they are device-independent, ASCII, and relatively compact. What's the downside? PostScript is difficult to understand and more difficult to write, and nearly impossible to debug without a lot of trial-and-error experimentation. Thankfully, a few tricks can make your life with PostScript more pleasant.


Mail this
article to
a friend
All of my literary friends extol the virtues of learning a second or third language. C++ and Java don't count, they tell me, for computer languages are not in the same class as the "true" Romance languages. If I have to name a language that was beautiful in its simplicity and an increasingly useful skill, my nomination goes to Adobe's PostScript. Wait, you say. PostScript is for printers and reminds you of ancient LaserWriters attached to yellowing Macintoshes.

It's time to take out the language arts books, then, because PostScript is a generalized output facility in the same vein as XDraw() library routines. True, you'll see PostScript most frequently when dealing with printers, but today most commercial X11 servers also include an Adobe Display PostScript (DPS) interpreter. Freely available interpreters like ghostscript give anyone with a bitmap screen access to rich-text documents. PostScript files are a convenient exchange medium, because they are device-independent, ASCII, and relatively compact. What's the downside? PostScript is difficult to understand and more difficult to write, and nearly impossible to debug without a lot of trial-and-error experimentation. While its output is device-independent, some PostScript code assumes it is talking to a printer so it can make full use of its local facilities.

This month, we'll look at the practical ins and outs of PostScript, giving you enough information to help out the user who brings in PostScript output from a Windows application like SideKick, assuming (wrongly) that "it should just print here in the office." After covering the basic syntax of PostScript programs, we'll look at some conventional viewing mechanisms. We'll walk through the SideKick example and then discuss how to create and manipulate fonts to generate bar codes. Tips, tricks, and URLs are de rigeur, pointing you to a small fraction of the freely available PostScript goodies to be found on the Internet.


Advertisements

Max stacks facts

Why go to all of the trouble of learning PostScript? If you work for a company that develops software that generates hard copy, you'll probably be called on when the print-handling folks run into roadblocks. If you have PostScript printers, it's useful to be able to decipher output errors to decide if the PostScript you have is damaged or bug-ridden, and to help your users feed output from a variety of non-Unix sources into your printers. Finally, you can do some clever output management, including bar-code generation, thumbnail versions of presentations, and image clean up with the right tools. You don't have to be a language scholar, but familiarity with the basics will get you pointed in the direction of a solution when PostScript crises strike.

PostScript is a page description language. It's output-oriented, interpreted, and stack-based -- not all bad things. PS files generally are the output of word processing or formatting applications, and get sent to a printer (that speaks PostScript) or a viewing application. PostScript has some primitives for opening files, but it's not equipped to deal with input from pointing devices or sockets. Loyal Sun fans will remember arcane solutions to this problem from NeWS, Sun's PostScript-based window system that was later made obsolete by X11 and Adobe Display PostScript.

When we say a device speaks PostScript, we mean that it has a PostScript interpreter available. Being an interpreted language is a multi-faceted boon for PS: it lets decisions about output dot density be made at execution time, giving PostScript much of its device independence, and it's conducive to downloadable code segments such as fonts and frequently used routines. The PS interpreter also lets you overload or redefine primitive operations or previously defined routines, which is amazingly useful for reformatting output that's already cast into PostScript. We'll delve into an example of post-PostScripting shortly.

Stack-oriented languages give most people the most headaches. Anyone who loves the PostScript syntax probably also shelters an HP 12C calculator and sings its ballads in Reverse Polish Notation. Using a stack keeps the interpreters simple and relatively fast but makes PostScript code difficult to read and parse. For example, 5+7 is written in PostScript as 5 7 add and the equivalent of moveto(x+5, y+7) is x 5 add y 7 add moveto. If you want to become literate, get copies of the Blue and Red Books, formally known as PostScript Language Tutorial and Cookbook and PostScript Language Reference Manual. Neither book retains its original cover color in its latest edition, so be sure to scan the information on all of the books published by Adobe Press (including those mentioned in this month's installment) on Adobe's WWW server.

Great, you say, we can move and add, but what about drawing, which should feature prominently in a page description language? PostScript uses a "paint and stencil" model to construct an image on the output device. Think of the PostScript operators as first constructing some outline, or shape cutout, and then spraying paint through the outline onto the page. The paint can be white, black, any shade of gray or even color described by RGB (red, green, blue) values. The real-world rules of painting apply in PostScript as well: whatever you paint goes "on top of" what is already there. To round out the jumpstart, here's a thumbnail of the PostScript coordinate system: it's 72 "points" to the inch, with (0, 0) in the lower left corner of the page. A printer's point is really 1/72.27 of an inch, so a PostScript point isn't exactly a printer's point, but few people make the distinction.

When drawing, the interpreter keeps track of the current point (where you'd have your pen resting if you were really drawing), the current path (the outline or shape you're drawing), and a current shade of gray (defaulting to black). Drawing a triangle is as simple as moving to a good starting point and adding three lines :

%! triangle.ps 
newpath		% start a new shape
100 100 moveto 	% move about an inch and a half in
200 100 lineto 150 150 lineto 100 100 lineto 
stroke		% draw it
showpage	% render the entire page

Like most languages, white space adds to readability but is of no consequence to the interpreter. Any text following a % is taken to be a comment. PostScript files begin with %!, similar to the #! magic number for shell scripts.

The paint and stencil model is ideal if you want to draw a crescent:

150 150 50 0 360 arc stroke fill
0 setgrey 
140 140 40 0 360 arc stroke fill

The first arc statement draws a 50-point arc for 360 degrees (full circle), then we switch to a grey level of 0 (white), move slightly down and to the left of center, and draw a smaller circle. Do the operations in the reverse order and the larger black circle obscures the small, white one. The blue and red books are chock-full of examples that will raise your comfort level with the language and its syntax and impress you with its ability to describe elaborate pages in simple terms.

Ghost in the machine

There are three ways to get PostScript code turned into a visual image: print the page, use an interpreter like Sun's pageview or the freely available ghostscript, or render the code's output inside of an X11 window using Adobe's Display PostScript. We'll look at the two screen-output methods in more detail.

pageview takes a PostScript file and displays its output on your workstation. It's useful for debugging PostScript code, because you can load and reload a file until you get the desired effect. Errors are reported in the window in which you started pageview, giving you instant data on what went wrong procedurally in addition to the visual feedback you get from the on-screen image. Ghostscript is a freely available tool that performs the same function, with some added functionality. It runs on nearly everything, including Macs and PCs (and even the stray Amiga ST), and it supports Adobe's Portable Document Format (PDF) in addition to PostScript. Visit the ghostscript web page for the latest source availability and features.

But what if you have PostScript that you want included as part of a window's output? Drawing splines (text that follows an arbitrary curved baseline) or custom fonts is a job for PostScript. Tailoring 100 lines of PostScript to put a custom heading on a form is less of an ordeal than wading through XDraw() functions, especially when you can use a standalone tool like pageview to help you perfect the custom drawing code.

Marry the X Window System and pageview and you get Display PostScript (DPS) extensions to the X server that accept, interpret, and render PostScript code. Why use Display PostScript when X provides robust drawing functions? The primary motivation is the same behind describing pages using PostScript: you get output device (screen) independence without having to explicitly determine the resolution, dot pitch, or color capabilities of your display. In some cases, PostScript is your only avenue for delivering advanced graphics such as outline fonts or text that follows a non-linear baseline path.

Below, we've taken our triangle-drawing PS code and turned it into a DPS "wrap" segment, parameterized with the (x, y) corner of the triangle and its height:

defineps PSDrawTriangle (int x, y, size)
	x y moveto 	
	2 size mul x add y lineto
	size x add size y add lineto
	x y lineto
	stroke
endps

Every window system that includes DPS also has the pswrap utility to convert PostScript wraps into C-callable code. Feed this example into pswrap and out comes code you can link into your X11 application:

duey% pswrap -o triangle.c triangle.ps

The generated C code is not for the faint of heart, but once compiled the PSDrawTriangle() routine can be called like any X11 drawing primitive. You'll need to include several header paths in your Makefile, and link the supporting DPS libraries.

Nearly all of the PostScript primitives work, and DPS adds some new primitives for handling input and window-system events. If you want to use PostScript's file-manipulation features, you'll need to tell the X server that DPS has file-access permissions by starting your X server (openwinwdows) with the -dpsfileops option. Read through Programming the Display PostScript System with X, the bible of DPS usage, to ramp-up the learning curve. Examples from the book, along with a multitude of other files, are available from Adobe's mail and ftp servers. Send mail to ps-file-server@adobe.com with a body of "send Documents long.help" to get started.

Some document-preparation packages, such as FrameMaker, allow you to include arbitrary PostScript code on a page. In Frame 4.0 and later versions, create a text rectangle, and then mark it as "PostScript code" in the "Flow" dialog box. Import your PS file right into the rectangle, and it will be included in the bounding box you drew. The size of the box is usually passed to your code as a set of arguments, so you can scale the output to fit within the text rectangle borders (see the Frame documentation for more details). Embedding PostScript code is often the only way to draw high-density logos without suffering the jaggies from scanned images or screen snapshots taken at low density.

Turning screen-viewable PostScript into hardcopy should be trivial, but variations in language extensions, such as printer-specific features or DPS-event handling, create the opportunity for creative output problems. We'll provide a short tutorial in PostScript printer woes to help you remove roadblocks on the path to hardcopy.

Stack of trouble

Printers are difficult beasts because they combine moving parts, high temperatures, and tangled cables in an area that is inviting to user's hands and coffee cups. If you're struggling to get a PostScript printer to work, and aren't sure if it's the System V print spooler, your output filter, or the printer hardware that's at fault, try giving the printer a gentle shove by sending the above example directly to the printer device:

duey# cat triangle.ps > /dev/tty/a

Reach network-connected printers using telnet, specifying a port number if needed to reach the PostScript interpreter. If you use tip or telnet to reach the printer with an interactive session, type executive to be put into conversational mode with the interpreter. You can then rattle through some examples to be sure your cabling and hardware are without fault before dissecting the print spooling subsystem.

PostScript interpretation errors show up either on the banner page (with Sun's NeWSprint software) or in the printer log (if you are using custom filters with the BSD print subsystem or Adobe's TranScript print drivers). Errors regarding stack overflows, underflows, or type checking problems generally represent PostScript programming errors -- sometimes they are genuine bugs in output from document preparation packages, but more frequently it's bad coding on the part of one of your PS developers.

Stack overflows are the result of "leaky" programming that leaves items on the stack, perhaps pushing too many arguments to a function or calling duplication functions (for string handling) with a bit too much haste. Underflows are caused by the reverse -- the current operation is trying to consume more items than are available on the stack, possibly because a previous operation pushed fewer items that you expected back onto the stack. A common mistake, for example, is to calculate the width of a string without first making a copy of the string -- the stringwidth operator destroys its input, leaving you with an integer width but no string to draw. Review the stack-manipulation diagrams in the PS tutorial and reference manuals to see how each operation removes and replaces items.

Exitserver, stage right

There's an additional output message that you may see, particularly if you are sending Windows-prepared files to a PostScript printer: "exitserver: permanent state may be changed" The printer's PS interpreter provides a context for the execution of each job submitted, but the job can try to break out of the context and change the printer's state or configuration. Consider the following chain of events: A PC user prepares PS output from a Windows application, sending it to a file instead of a locally-attached printer (easily done using the "Print Setup..." dialog box in most applications). The PS file is then sent to a printer using PC/NFS, or by hand-carrying it to a Unix system on a floppy. The goal: high-quality output from a shared printer. The problem: the generated PS expects to find a "Windows Header" loaded into the printer, deposited there by a PS header file that updated the printer's internal data dictionary with some generic Windows items.

You can ask to send the header as part of your print job, or you can save it into a file as well, but you have to work around the anti-social assumption that the printer is the personal device of the user with the PS file. Assumptions about exclusive use are dangerous in the Unix world, so you'll need to hand-tool these PostScript headers to keep their hands to themselves. The easiest thing to do is to remove the check for the WinDict35 dictionary, and to remove the "stop" or "quit" directives from the end of the header file. Context diffs for removing these offending lines are available in the sidebar.

Here are some other PostScript tips and tricks to keep the users impressed:

What comes next? To learn more about PostScript and products built on it, check out Adobe's WWW server. You'll find it chock-full of information on Portable Document Format (PDF), a follow-on to PostScript that describes entire documents in device- and OS-independent fashion. You can use the PDF "exchange" application, part of Adobe's Acrobat family, to convert PostScript documents back into something you can edit -- PostScript is no longer a terminal state.

Dealing with a greater degree of heterogeneity in desktop systems means you'll have to focus more on device-independent description systems like PostScript, PDF, and Display PostScript. Take the time to explore the nuances of PostScript, especially its font-handling and image-manipulation capabilities. You may not fall in love, but it's not bad for a summer romance language.


Click on our Sponsors to help Support SunWorld


Resources


What did you think of this article?
-Very worth reading
-Worth reading
-Not worth reading
-Too long
-Just right
-Too short
-Too technical
-Just right
-Not technical enough
 
 
 
    

SunWorld
[Table of Contents]
Subscribe to SunWorld, it's free!
[Search]
Feedback
[Next story]
Sun's Site

[(c) Copyright  Web Publishing Inc., and IDG Communication company]

If you have technical problems with this magazine, contact webmaster@sunworld.com

URL: http://www.sunworld.com/swol-08-1995/swol-08-sysadmin.html
Last modified:

SidebarBack to story

Context diffs for removing
offending PC PostScript lines

*** fixed-windict.ps	Wed Jun 28 13:03:44 1995
--- windict.ps	Wed Jun 28 13:06:44 1995
***************
*** 1,12 ****
  %!PS-Adobe-3.0
  %%Creator: Windows PSCRIPT
  %%Title: PSHeader
- statusdict begin statusdict /jobname (PSHeader) put end
- /Win35Dict where{pop(Windows PS Header in place - not loaded again\n)print
- flush 100 100 moveto/Helvetica findfont 20 scalefont setfont(Windows PS Header in place - not reloaded.)show
- showpage stop}{serverdict begin statusdict begin 0000 checkpassword{(Windows PS Header downloaded.\n)print
- flush 0000 exitserver}{(Bad Password loading Header.\n)print flush
- stop}ifelse}ifelse
  /Win35Dict 290 dict def Win35Dict begin/bd{bind def}bind def/in{72
  mul}bd/ed{exch def}bd/ld{load def}bd/tr/translate ld/gs/gsave ld/gr
  /grestore ld/M/moveto ld/L/lineto ld/rmt/rmoveto ld/rlt/rlineto ld
--- 1,6 ----
***************
*** 146,150 ****
  /y y .4 sub def x y moveto(Stack =)show ostack{/y y .2 sub def x 1
  add y moveto dup type/stringtype ne{( max err string )cvs}if show}forall
  showpage}if end}def end}bd end
- /Helvetica findfont 20 scalefont setfont 100 100 moveto(Windows PostScript header loaded.)show
- showpage quit
--- 140,142 ----

SidebarBack to story


SidebarBack to story

PostScript code to fit addresses onto labels

%!
%

/inch { 72 mul } def

/boxwidth 2.5 inch def
/boxheight 1.0 inch def
/fontsize 18 def

/startpt 10.0 inch def
0.25 inch 0.45 inch translate
/Helvetica findfont fontsize scalefont setfont

/boxit3 {
	/botstr exch def
	/midstr exch def
	/topstr exch def
	6 startpt boxheight 7 div 2 mul sub moveto
	topstr show
	6 startpt boxheight 7 div 4 mul sub moveto
	midstr show
	6 startpt boxheight 7 div 6 mul sub moveto
	botstr show
	/startpt startpt boxheight sub def
	} def

% first column
(name) (street address) (city, state, zip) boxit3

% second column
2.8 inch 0 translate
/startpt 10.0 inch def
(name) (street address) (city, state, zip) boxit3

% third column
2.8 inch 0 translate
/startpt 10.0 inch def
(name) (street address) (city, state, zip) boxit3
showpage

SidebarBack to story