|
PS I Love YouPostScript isn't just for printers anymore |
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 |
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.
|
|
|
|
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:
pageview
,
or a DPS application and it will fail because it expects to talk to
printer-specific dictionaries and operators. You can emulate the
printer environment with a "dummy" printer, contained in the file
dpsdummyprinter.ps.
Add it to the head of your PS stream and you'll provide a happy
environment for PostScript that wants to be printed.
a2ps
Have a user who is fretting over printing something 100 characters
wide in portrait mode? Feed the file through a2ps
, and then change the font from 10 points down to 8 or 6 points to compress the text on each
line.
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.
|
Resources
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:
*** 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 ----
%! % /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