PostScript

What PostScript Is

PostScript is a page-description language and programming language developed by Adobe and released in 1984. Instead of storing a page as a finished grid of pixels, a PostScript program contains instructions for constructing it: create paths, draw and fill shapes, position text and images, change colors, transform the coordinate system, and so on. A PostScript interpreter executes those instructions and turns the resulting page into pixels for a screen, printer, or imagesetter.

One of PostScript's important ideas was device independence. The program describes the page in an abstract coordinate system rather than for one particular printer resolution. The same page description could therefore be rendered on a desktop laser printer or on the much higher-resolution equipment used by a commercial printer. This separation between creating a document and rasterizing it for the final output device became one of the foundations of digital publishing.

PostScript was a major part of the desktop-publishing revolution of the 1980s. Apple's PostScript-based LaserWriter, introduced in 1985, together with the Macintosh and Aldus PageMaker, made it practical to design complete pages on a personal computer and send them directly to high-quality printers. What had previously required specialized typesetting and prepress equipment could increasingly be done on the desktop.

EPS, or Encapsulated PostScript, adapted the same language for graphics intended to be placed inside other documents. An EPS file is essentially a self-contained, normally single-page PostScript program describing an illustration, logo, chart, or other graphic. For many years it was one of the standard ways of exchanging vector artwork between publishing applications.

Today, PostScript is no longer the normal format for exchanging finished documents. PDF has largely taken over that role, including in modern print production, and many current RIPs process PDF directly. PDF inherited the PostScript imaging model, but stores the resulting page description in a structured form rather than being a general-purpose program that must be executed.

PostScript has not disappeared, however. It is still encountered in professional and legacy printing workflows, printer output, prepress systems, old document archives, and EPS artwork, and applications such as Illustrator and InDesign still support PostScript-related workflows. It is also interesting in its own right: a .ps file is not merely a graphics file but an executable program, with variables, procedures, dictionaries, loops, conditionals, files, and stacks.

The Three Language Levels

PostScript was extended twice, and Adobe called the results language levels rather than versions. A level is a capability set: an interpreter reports the highest one it supports through the languagelevel operator, and a program can branch on it — Adobe's own files do, and can draw a page quite differently depending on the answer.

LevelYearWhat it added
1 1984 The original language: a stack machine with variables, procedures and control flow; the imaging model of paths that can be filled, stroked or used to clip; a coordinate system that can be moved, scaled and rotated; scalable outline fonts (Type 1) and programmable ones (Type 3); images; and a stack of saved graphics states to draw inside and step back out of. Color is device color only — gray, RGB and CMYK.
2 1991 Filters — encoded and compressed data streams (ASCII-safe encodings, LZW, run-length, JPEG), which is what lets a program carry images and other binary payloads inline. Named resources and reusable forms. Patterns as a kind of color. Device-independent color, so a color can mean the same thing on two different devices, plus indexed palettes and separation (spot) colors. Composite fonts, for scripts needing more than 256 characters. Configuring the output device from inside the program. Better memory management, including garbage collection. And graphics states as objects that can be saved and reinstated in any order, rather than only unwound.
3 1997 Smooth shading — a real gradient for the first time, rather than one painted by hand. Zip compression, and embedded data a program can rewind and read again rather than only once. DeviceN color for multi-ink printing, and masked images. CID-keyed fonts for large character sets such as Chinese, Japanese and Korean. Idiom recognition, letting an interpreter substitute a faster implementation for a procedure it recognizes.

PostScript, PDF and Illustrator

The three formats share one imaging model but differ in what they are, and that decides how much of a document survives being opened.

PostScript is a program. The page does not exist in the file; it is what happens when the file runs. A .ps file can compute its own artwork in a loop, so the only general way to learn what is on the page is to execute it.

PDF (1993) is the same imaging model without the programming language. Adobe kept the paths, the color spaces, the font machinery and the filters, and threw away execution: a PDF is a structured collection of objects, and a page's marks are listed with no loops, procedures or variables. Everything is stated rather than computed, so a reader can find the fifth page without running the first four.

An Illustrator file is one of three containers, and which one decides how much of the document survives:

VariantWhat the file really is
Pre-v9 .ai A PostScript program, written in Illustrator's own set of drawing procedures, so sending the file straight to a PostScript printer prints it.
v9+ .ai A PDF file. It opens in any PDF reader because the PDF part is a complete rendering of the artwork; the editable Illustrator document rides alongside it in the same file.
Illustrator .eps Both at once. The printable PostScript comes first, and the complete Illustrator document follows it, compressed and encoded. The artwork is in the file twice.

Hence the difference that matters when you open one of these files: running the PostScript gives you a picture; reading the Illustrator syntax gives you a document. Layers, groups, object names, editable paths, gradients and spot colors survive only the second way — printing flattens every one of them into anonymous marks. So wherever Illustrator syntax is available, this application reads it rather than running it, and falls back to the interpreter only for a file that has none.

Each container has its own page with the full story of what this application reads and where the limits are: Adobe Illustrator, EPS, and PDF. This page documents the shared foundation they all draw on — the PostScript language and the interpreter behind it.

This application includes a built-in PostScript editor and debugger for writing, running, and stepping through .ps and .eps programs directly, with breakpoints and live inspection of the operand, call, and dictionary stacks. See the PostScript Debugger page for how to open it and how it works.

Language Support

The interpreter reports languagelevel 3. It implements 233 operators, which covers the arithmetic, stack, dictionary, control, string, array, type, file and matrix machinery in full, and the graphics operators partly; the rest are not supported.

Implemented

CategoryOperators
Stack pop dup exch index roll clear count counttomark cleartomark mark
Arithmetic add sub neg abs div idiv mod mul exp sqrt ln log sin cos atan floor ceiling round truncate bitshift rand srand
Relational, boolean, bitwise eq ne ge le lt gt not and or xor
Control exec stopped stop if ifelse for repeat loop exit quit execstack countexecstack
Arrays array packedarray aload astore forall
Dictionaries dict begin end def undef known where load store currentdict systemdict userdict dictstack countdictstack cleardictstack maxlength internaldict
Polymorphic length get put copy getinterval putinterval
Strings, types, conversion string type cvn cvs cvrs cvi cvr cvx cvlit xcheck rcheck wcheck status readonly executeonly noaccess search anchorsearch token
Files file run currentfile closefile flushfile resetfile filter read readstring readline readhexstring bytesavailable fileposition setfileposition status deletefile renamefile filenameforall echo print flush write writestring writehexstring
Path construction newpath moveto lineto curveto rmoveto rlineto rcurveto arc arcn arcto arct closepath currentpoint pathforall pathbbox flattenpath reversepath strokepath setbbox
User paths upath uappend ufill ueofill ustroke ustrokepath ucache ucachestatus setucacheparams — both the plain-array and encoded forms
Painting fill eofill stroke rectfill rectstroke image colorimage imagemask
Smooth shading shfill — axial and radial shadings (types 2–3) become real gradients in the document, and mesh shadings (types 4–7) become real meshes, in any color space and with every function type. Not yet painted: function-based shadings (type 1), which are read and reported rather than drawn
Clipping clip eoclip rectclip clippath initclip clipsave cliprestore — the clip is tracked, not applied to marks; see the caveat below
Graphics state gsave grestore grestoreall save restore gstate setgstate currentgstate initgraphics setlinewidth setlinecap setlinejoin setmiterlimit setdash setflat setstrokeadjust setoverprint — dashes are real: setdash takes a pattern of any length with a phase, and both stroke and strokepath honor it and every matching current…. Caps, joins and the miter limit reach the mark too
Color setgray setrgbcolor setcmykcolor sethsbcolor setcolorspace setcolor findcmykcustomcolor findrgbcustomcolor setcustomcolor setseparationgray and every matching current… — see the caveat below
Device-independent color CIEBasedA CIEBasedABC CIEBasedDEF CIEBasedDEFG Lab ICCBased Separation DeviceN — all converted through the real color pipeline, including an embedded /ICCBased profile and a /Separation or /DeviceN tint transform. Rendering intents are only remembered — the intent a profile's own header asks for is the one used.
Coordinate system and matrices matrix initmatrix defaultmatrix identmatrix currentmatrix setmatrix invertmatrix concat concatmatrix translate scale rotate transform dtransform itransform idtransform
Resources defineresource undefineresource findresource resourcestatus resourceforall
Fonts findfont selectfont scalefont makefont setfont currentfont rootfont definefont undefinefont findencoding show ashow widthshow awidthshow xshow yshow xyshow glyphshow cshow kshow stringwidth charpath, plus StandardEncoding and ISOLatin1Encoding — see Fonts
Pages showpage copypage erasepage
Output and diagnostics = == pstack
VM and interpreter parameters bind null version languagelevel usertime realtime setglobal currentglobal setshared currentshared gcheck startjob setpacking currentpacking setpagedevice currentpagedevice setsystemparams currentsystemparams getsystemparam setuserparams currentuserparams getuserparam setvmthreshold setcachelimit cachestatus defineuserobject execuserobject undefineuserobject flushpage
Standard dictionaries systemdict userdict globaldict statusdict shareddict errordict handleerror, and $error

Not Supported

Using any of these raises an error, and the error says which operator it was.

GroupOperators
Imagescustomcolorimage
Insideness testinginfill ineofill instroke inueofill inufill inustroke
Patternsmakepattern setpattern
Font machinerycomposefont eexec setcachedevice setcachedevice2 setcharwidth
Formsexecform
Binary object sequencesprintobject writeobject setobjectformat currentobjectformat
Devicesnulldevice banddevice framedevice renderbands
A program can manage files, but only inside its own folder, and only on this page. (name) (w) file — and (w+) and (a) — write into user/postscript, the same folder (name) run reads from, so a program can write a file and a later run can read it back. deletefile and renamefile work in that folder too, and filenameforall lists what is in it — including files in subfolders, named the way run names them (examples/lib.ps). Names must stay inside the folder: no ../, no absolute paths, nothing outside it. Every change is announced in the Output pane. A program can replace or remove a file that is already there, but a rename onto a name that is already taken is refused rather than allowed to overwrite. If you have the file open in a tab, the tab stays: a rename carries it to the new name, and a delete leaves the tab holding the only remaining copy of the text, marked unsaved.

A .ps you merely open as a document gets none of this and raises invalidfileaccess instead — for run and (r) file as well as for writing, and before it checks whether the file exists, so a refused program cannot use the error to map what is on your disk.

status is the one exception, and it matches Ghostscript: it never refuses. A file it may not look at answers false, the same as a file that is not there.

Anything a program writes to %stdout or %stderr appears in the Output pane, and (%stdout) (w) file opens a file object on the same stream.

image, colorimage and imagemask handle the common forms — the Level 1 operand form and the Level 2 dictionary form alike — at 1, 2, 4 or 8 bits per sample. 12 and 16 bits per sample are not supported, and neither is colorimage's one-source-per-component form.

Images in a Device-Independent Space

An image drawn while a /CIEBased, /ICCBased, /Lab, /Indexed, /Separation or /DeviceN space is current is converted through the real color pipeline, with the image's /Decode array honored.

A space with four color components — a four-ink /ICCBased, a /DeviceN with four inks, any /CIEBasedDEFG — reads as ink: the four samples are kept as they were written, in a CMYK image, and the space converts them at draw time. colorimage with four components is ink too; having no space to state, it is interpreted in the working CMYK profile, the same rule the rest of the application applies to untagged ink.

Only the << … >> image form reads the current color space. The five-operand image is always DeviceGray and colorimage is always a device space, whatever setcolorspace was last given — that is Ghostscript's behavior too, and it is what PLRM describes. The spaces that cannot be carried into an image are listed under Accepted, but with No Visible Effect below.

Accepted, but with No Visible Effect

OperatorsWhat actually happens
clip eoclip rectclip The clip is recorded on the graphics state — clippath reports it, and gsave/grestore carry it — but marks outside it are still painted, and a second clip replaces the first instead of intersecting with it.
rectstroke with a matrix The rectangle is stroked; the optional matrix operand, which in PLRM skews the pen, is accepted and ignored.
clipsave cliprestore They save and restore the recorded clip, so a program that brackets its drawing with them runs normally; the clip itself is still not applied to marks.
<< … >> image in a space with two, or more than four, color components A /DeviceN with two inks, or with five. An image is four bytes per pixel and the meanings those bytes can carry are gray, RGB and CMYK, so a two- or five-ink image has no shape to sit in: its first three samples are read as though they were RGB device values, and it says so on %stderr. One, three and four components are all carried — see Images in a device-independent space above. An /Indexed image is not affected however wide its base is — its samples are one-component indices and the palette is a table rather than an image, so /Indexed over a five-ink /DeviceN works where a direct one does not. A fill in any of them is converted normally.
setcolorspace with /Pattern It is remembered, so operand counts stay right and currentcolorspace answers properly — but it paints black, and says so on %stderr. Every other family — the CIE spaces, /Lab, /ICCBased, /Indexed, /Separation and /DeviceN — is converted for real; see Implemented above.
setscreen setcolorscreen sethalftone settransfer setcolortransfer setundercolorremoval setblackgeneration and every matching current… Halftone screens and transfer functions describe a marking engine, and drawing into a layer graph there is none. So each one is remembered and handed straight back: what currentscreen answers is what setscreen was last given, settransfer sets all four functions and currenttransfer reads the gray one, and the whole set is carried by gsave/grestore. That is what lets Illustrator's own prolog — which composes onto currenttransfer and puts currentundercolorremoval back afterwards — run instead of dying at its first line. Nothing about the marks changes. With nothing set, currentscreen reports 60 lpi.
setcolorrendering findcolorrendering setoverprintmode setsmoothness setcmykoverprint setcacheparams and every matching current… The same bargain: stored, answered back, and with no effect on the page. findcolorrendering always reports /DefaultColorRendering false, which is Ghostscript's answer too when no rendering dictionaries are installed. There is no glyph bitmap cache here, so the cache parameters are purely a round trip.
setsystemparams setuserparams setdevparams setvmthreshold and the matching current… / get… Parameters are kept in a dictionary per family and read back from it, so a program can test for a parameter by catching the undefined that an unknown key raises. The devices setdevparams knows are the ones this interpreter really has. setdistillerparams and currentdistillerparams are deliberately absent: they exist only in Acrobat Distiller, and every Adobe prolog asks /currentdistillerparams where to find out whether it is running inside one. Answering yes led those files straight into a branch that reads real Distiller settings — and killed them there.
copypage Ends the page and warns. Level 1's behavior of carrying the retained marks onto the next page is not reproduced.
flushfile, and flush Nothing is held back — a completed line has already reached the Output pane, and an output file's bytes are handed over by closefile. Note flush does not end a line, so (total: ) print flush 42 = stays on one.
vmreclaim vmstatus Accepted; JavaScript's collector is not a PostScript program's to schedule, and vmstatus answers placeholders. A program that calls vmreclaim in a loop — which a real interpreter needs — runs unchanged.

Fonts

A PostScript program does not just draw with its fonts, it computes with them — asking a font dictionary for its /FontMatrix and /FontBBox, positioning text by measuring it with stringwidth — so every name it asks for has to resolve to a real font dictionary in the middle of a run.

Each name is looked up when the program reaches findfont, in this order:

  1. A font already loaded here — anything in the font library, including the faces this app ships with and any you have added yourself.
  2. A font installed on your machine. If you have granted this browser access to your local fonts, a name that matches an installed face is fetched at that moment and used with its real metrics and its real outlines. Opening or running a file never asks for that permission: if it has not already been granted, this step is skipped silently. You grant it, if you want to, from the font picker.
  3. A substitute, the way Ghostscript substitutes: you get a complete font dictionary rather than an error, so the program runs and the page comes out. The stand-ins are chosen for metric compatibility rather than for looks — Tinos for Times and Cousine for Courier match the widths of the fonts they stand in for, so line breaks and column positions survive. Style matters too: Courier-Oblique lands on an italic face, not on the roman one. The dictionary keeps the name the program asked for, which is what you will see in the debugger. Ghostscript renames it to the substitute's; nothing in the language depends on either choice.

charpath appends the resolved face's real glyph outlines to the current path, so outlined text and text used as a clipping path both work. The one narrow exception depends on the font file rather than on anything the program did: PostScript reaches a glyph by name, and a font that carries no glyph names of its own — which is what recent system fonts on Windows ship — has its names reconstructed from its character map. That reaches every glyph a character code selects, but not a ligature, a small capital or a stylistic alternate, which no code selects and which charpath therefore has no name to ask for. In the rarer case of a face with neither names nor a usable character map, charpath reports it once on %stderr and the program carries on; text drawn with show is unaffected either way.

Numbers

PostScript has two numeric types: integer and real. The specification does not mandate their size; it requires only that an integer which overflows the implementation's range be converted to a real. Modern Ghostscript uses 64-bit integers, while this implementation is limited to 53 bits of integer precision, so it is at 253 that a result turns real. Reals are float32, as in Ghostscript.

Filters

The filter operator supports ASCIIHexDecode, ASCII85Decode, RunLengthDecode, SubFileDecode, LZWDecode (honoring /EarlyChange), FlateDecode and DCTDecode, plus the one encoding filter, NullEncode.

Any other filter name is undefined: filter recognizes the names above and raises undefined for anything else, naming the filter it did not know. That covers ReusableStreamDecode and the encoding filters ASCIIHexEncode, ASCII85Encode, RunLengthEncode, LZWEncode and FlateEncode, which real interpreters do have. Note that a filter name is not an operator and is not looked up in a dictionary — it is an argument to filter, so /ASCIIHexEncode load is undefined even in Ghostscript, where the filter itself works.

Decoding filters are not positionable: fileposition and setfileposition on one raise ioerror, as PLRM specifies.

FlateDecode cannot read straight from currentfile and raises an error if asked to: the end of a compressed stream cannot be found, so the rest of the program would be read as part of it. Wrap it the way every real file does — currentfile /ASCII85Decode filter /FlateDecode filter — and the outer filter finds the end.

DCTDecode is JPEG, and unlike FlateDecode it can read straight from currentfile, because a JPEG marks its own end. The width, height and component count in the JPEG's own header are ignored in favor of the operands you give image, and CMYK arrives as Adobe stores it, which is inverted. A /ColorTransform 0 parameter on a three-component JPEG is refused: it asks for the untransformed YCbCr samples, and the decoder's output space comes from the file's own markers.

Character Set

PostScript source must be ASCII or Latin-1. Anything above 0xFF — smart quotes, box-drawing characters pasted from a terminal, em dashes — is replaced with ?.

Errors

When a program fails, the message goes to the Output pane prefixed with error:, the status line reads Stopped on an error, and the preview keeps whatever had been drawn up to that point. The stacks stay as they were, so you can look at what the program was holding when it went wrong. errordict, $error and handleerror exist, so a program that installs its own error handler gets to run it.

PostScript is old enough that most of what was written about it is now scattered across archives rather than sold in bookshops, but almost all of it survives. These are the places worth knowing: the specifications the language is defined by, and the programs — real hand-written PostScript, not printer output — that are the interesting thing to open in the editor.

Specifications

The PostScript Language Reference, universally called the PLRM or the Red Book, is the definition of the language and settles every question about operator behavior. The Blue Book is the tutorial that came with Level 1 and is still the friendliest introduction. DSC is the comment convention that turns a program into a structured document, and is what tells a reader where the pages and the bounding box are.

Programs to Run

Ghostscript's examples directory is the classic small corpus: a page of interlocking Escher butterflies, nested color circles, a Vasarely grid, an alphabet specimen. They are short, they are executable, and they are a good first test of any interpreter.

Don Lancaster's Guru's Lair is the other kind of resource entirely — decades of PostScript written by someone who treated it as a general-purpose programming language rather than a printer format. Fractal ferns, Sierpinski triangles, bubble sorts, typography experiments and his own gonzo.ps utility library, all as downloadable source.

CTAN is enormous and unsorted for this purpose, but the TeX world produced PostScript for decades and plenty of package manuals and examples are still shipped as .ps files.

PostScript as an Application Language

Sun's NeWS window system, in the late 1980s, ran PostScript as the language applications themselves were written in: windows, menus, input events, lightweight processes and networking were all reached from PostScript code. People really did write applications in it — a HyperCard-like authoring environment (GoodNeWS, later HyperNeWS, then HyperLook), the PSIBER Space Deck, a version of SimCity, and the famously networked PizzaTool.

NeWS PostScript is not printer PostScript. Its extensions for windowing, events, processes and object-oriented class definitions are not part of the language this editor implements, so most NeWS source will not run here. It is still worth opening and reading: it is the best surviving evidence of what the language was capable of being used for.

History

In 2022 the Computer History Museum released the source code of Adobe's original PostScript interpreter — a version from February 1984, before the LaserWriter shipped — with Adobe's permission. It is the implementation rather than anything you can run here, but it is the other half of the story this page tells.

See also: PostScript Debugger for the built-in editor and debugger; Adobe Illustrator, EPS and PDF for the formats built on this language; and Debugger for the document-embedded VMs (Flash and DVD).