TweetFollow Us on Twitter

Meta Postscript
Volume Number:9
Issue Number:4
Column Tag:Cover Story

Metaprogramming Postscript

A system for arbitrary diagnostic analysis

By Gregor Koomey, Albany, New York

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Metaprogramming is the programming of programming; specifically, in this case, involving the manipulation of a language environment to alter the effect of a previously defined block of functionality (a program). The techniques described herein define an aspect of the PostScript (PS) programming language that should be both interesting and useful to anyone who needs to examine/troubleshoot PS programs beyond a superficial level and, hopefully, to anyone interested in the general appreciation of programming language order and heuristic. The substance involves the arbitrary manipulation of dictionary data structures in order to enhance the diagnostic capabilities of any given PostScript language environment.

Definition of Framework

This section is a brief description, for those who don’t know about, or are only vaguely familiar with, the PostScript programming language. Some of the following is later described in more detail.

I will use several abbreviations such as the following: PostScript <-> PS, procedure <-> proc, dictionary <-> dict, etc... Synonyms are employed to keep the text shorter and more readable than it might have been otherwise.

PostScript is an interpreted page description language (developed, marketed and trademarked by Adobe Systems, Inc.) whose inception initiated the series of events now referred to as the “desktop publishing revolution”, and is the defacto standard format (in the U.S., anyway) for complex DTP graphics. Initially it was implemented in laser printers and high-end imagesetters, allowing for a resolution-independent, graphic description format; PostScript text and graphics are easily proofed in house on a low resolution laser printer for later output at a service bureau on high resolution imagesetters. These days PS can be had in the form of low cost third party clone interpreters, available on all major computing platforms, as well as various forms of Display PostScript implemented on high- and low-end graphic workstations; while the focus of this article is on the embedded printer driving model, the techniques described herein should be adaptable to any PostScript language environment.

The language itself is an interpreted, free-binding, postfix, derivative/enhancement of the LISP concept. Interpretation is optimised through use of postfix notation (run-time syntactic analysis is minimized). Since the format is interpreted, most generated PS is in the form of ASCII text and is thus easy to examine for analysis/alteration (An aside: this may change if/when the PS level 2 binary formats are fully accepted by the marketplace; in any event, the text based format should always be available).

The “dictionary” data structure is central to the flexibility of the binding system. Like dictionary data structures in other environments, a PS dict is basically a grouping of key/value pairs; when a dict is accessed using a valid key, the appropriate value is returned for processing. A value can be anything recognised by the interpreter, from a number to an executable procedure body.

Dictionaries are represented to the system through a high-level stack hierarchy; during normal language operation the dict stack is searched from top to bottom (the most recently defined dict laying on the top) for every PS token identified as an executable name.

A procedure body is an array of executable objects with an executable attribute. The atomic functional objects are called “operators”; they are, in essence, pointers to machine code appropriate to carry out the function of the invoked operator.

Upon initialization, two dictionaries are available on the dict stack, systemdict and userdict. Systemdict contains all the pointers to the operator objects, as well as entries for a number of other internal PS objects (including an entry for itself). Userdict is storage supplied for user defined procedures and variables.

The PostScript language has not been widely marketed as a general purpose programming environment, though within the limitations of the standard job-oriented printer-embedded execution model, it is quite capable. Applications (app) that support PS generally define an app specific dictionary, to be laid over systemdict and userdict, so as to create an optimal PS printing environment for the logical model of the given application; this is further complicated by the popularity of comprehensive computing environments, such as Macintosh and Windows, which include their own custom dictionary as a channel into each respective high-level print mechanism. The intended area of focus of this article is the PS programming activity of analysis/design of a custom application printing environment through the creation/manipulation of an application dictionary.

The Problem

Assume that there is some previously defined PostScript code, in the format of an in house app-dictionary designed long ago by someone who left the company before you were hired; assume further that you have no documentation describing the various procedure definitions and, since there are hundreds of them, you don’t have time to decode and memorize each one. A glitch has arisen in translating your application’s graphics into PostScript (every other print format works fine), but you need coherent understanding of the questionable code in order to figure out what’s going wrong. Your boss is having fits because the latest program update is two weeks late and yours is the only section not finished...

The general problem addressed by the techniques presented here is the analysis, by an arbitrary designer of PostScript output, of PostScript structures defined by complex or encrypted code for which no documentation is available. While straight PS is fairly obvious in function, including commands such as moveto, lineto and curveto, most application generated PostScript looks as if it might have been created through typing randomly.

The reason for this is simple; one of the difficulties in dealing with an interpreted language is the manipulation of copious amounts of ASCII text. If your system setup involves a computer hooked through a cable to an embedded interpreter on a laser printer (the generally accepted paradigm of PS printing), code transmission alone probably accounts for most of your print time.

To minimize transmission, PS allows for simple, efficient definition of composite functional structures, the previously mentioned “procedure”. A procedure body in PostScript is simply an array (or packedarray) of executable objects with an executable attribute. The app designer can easily build an optimised, frequently used, arbitrarily complex proc, send the definition down to a PS dictionary structure at the beginning of the job and later invoke it with one or more letters, significantly reducing the transmitted code (assuming of course that the proc is used multiple times - there is always give and take in the PS system design process).

While this helps cut down transmitted code, it does nothing to improve readability, damning a PostScript analyst to a fairly nightmarish experience of printing out dictionary definition code and looking up, by hand, the definitions and redefinitions of various executable objects.

A similar problem is the result of encryption; some PS dictionary definitions are scrambled by their creators in order to discourage people from looking at them. The Adobe Type 1 Font format is an example of a PS dict definition whose substance was initially encrypted for purposes of security; since the specification has been published, security is no longer a problem, but the encryption format remains since the software base recognises it as the valid data format; this includes such widely used software utilities as ATM, in addition to the internal PS type 1 character building procedure.

With the problem of redefined code, the option of spending time with printouts looking up definitions by hand, while unpleasant, is at least an option; unless access to the appropriate key and decryptor is available, no such option exists for encrypted PostScript code. Hopefully, the techniques herein can solve any problems that might arise.

General Solution

The solution lies in using the flexibility of binding and procedure definition of the PostScript environment to redefine arbitrary dictionary entities, in this case all functional entries in systemdict, to include, in each entry, new code, in addition to the previously existing value, which tracks the functional activity of the PS engine.

This flexibility is largely due to the treatment of most internally representable data, especially procedure bodies, as first class objects, a strategy shared by one of the currently popular LISPs, a language called Scheme.

One of the most interesting facets of the various forms of LISP is the ability to treat procedure definitions, basically lists of operations, as data, so that the program environment can engage in on-the-fly procedure definition/alteration.

While PostScript does not share the “list” data structure with LISP (and so cannot really be considered part of the LISP family), the PS “procedure” data structure and the “typeless” nature of data objects allows for a similar enough ontology that it too offers functional code altering capability; in fact, within the limitations of the PostScript environment, the self alterability of arbitrary PS procedures is more pronounced than most LISP environments because the internals of procedures can be manipulated even after parsing, without reference to any form of the defining source code. (This is obviously an ambitious claim which overtly ignores a significant discussion of the differences between the LISP interpreter/compiler and the internal PS source-code-to-machine-action facility; it’s still, however, quite true...)

The technique is based on simple theory. Since all key and value entries in a dictionary are first class objects, they can be extracted, after definition but before functional application to the program code, adding instructions to the value to write the dictionary operator name (the key) and a newline, upon invocation, either to a disk file or to the standard output stream. In this case, the code is added only to operators and procedures, defining a simple PS compiler which allows for the arbitrary analysis of the use of the functionality built into the primitive PostScript environment. Building on this foundation, any given redefinition of the PS environment can be tracked and observed.

By the way, the term “compiler” is used in its loosest sense (“a program that translates data of one format to another”) and does not suggest that linkable machine language object code is herein created. A possible source of confusion is that the names of the primary functions in the example code are “compile1” and “compile2”; three levels of compilation are herein defined: altering the systemdict to write the names of the functions (compile1), altering the altered systemdict to also write the operands of the functions (compile2), and finally (the either simple or complex virtual compiler) to transform the functional activity of the PS interpreter into a readable format. The source data, to be acted upon by our virtual compiler, does not yet exist when the compiler is defined, since the compiler acts upon the internal PS dict activity, instead of a text file of source code; the text source to be analysed is parsed by the PS RIP and transformed into the source format for our compiler; the PS source code should be thought of as the potentiality of the activity which serves as the data input format for our compiler.

The standard PS execution model relies upon a number of internal data structures, including the standard input file (%stdin), the operand stack, the dictionary stack, the dictionary and the execution stack. An ASCII stream is received through %stdin from which PostScript tokens are extracted, of the various PS data types, including numbers, other literal objects, executable names, and a number of notational operators (such as “{“ and “}”). Numbers and other literal objects are pushed directly on the operand stack to be used as data. Upon encounter of an executable name, the interpreter searches the dictionary stack, from top to bottom, to find a key that matches the name. If a key is found, the appropriate dict is queried and the corresponding value is returned; if executable, the action is immediately invoked by pushing the value on the execution stack; if the value is literal, it is merely pushed on the operand stack, again to serve as data.

A number of problems arise. /systemdict, in most clones and certainly in all Adobe implementations of PostScript, is a read-only dictionary structure; it cannot itself be arbitrarily changed. In addition, many applications build their dictionaries by circumventing the standard PostScript execution model in the definition of their procedures using code of the following form:

 ... systemdict -name- get ...

where -name- is some key in systemdict; this code directly references systemdict, extracting the value corresponding to the supplied key, accessing even executable code as data, to be manipulated by dictionary and procedure construction operators. The advantage of this approach, to a PS system designer, is speed, since procedures can be built using the value in systemdict, a pointer to the machine code, which directly executes instead of going through the usual (time-consuming) dictionary lookup. The problem is that the read-only nature of systemdict added to non-standard procedure definition creates obstacles for our intended strategy.

The first step is to create a dictionary the same size as systemdict, then copy all the key/value pairs into it, placing it on top of the dict stack (userdict cannot be removed); the value for the /systemdict entry is then replaced with a pointer to the new dictionary; a call to systemdict will be intercepted, by the new systemdict entry. A copy of (pointer to) userdict is then placed on top of the dict stack so as to completely emulate the initial environment.

When systemdict is invoked under the standard execution model, the new dictionary, with whatever redefinitions we have defined, will be invoked in place of systemdict; in addition, the above mentioned non-standard use of systemdict will also result in placement of our redefined code. (Note that the technique described herein doesn’t require the explicit copying of a dict unless it is read-only, as are systemdict and the built-in font dictionaries in a printer; generally, a data structure defined in ROM on an embedded system is read-only)

Two problems exist regarding this particular solution.

First, PostScript memory, the size of the new dictionary structure, is lost through explicit creation of the new systemdict; this shouldn’t be a significant problem unless the code to be analysed is particularly memory inefficient. It’s mentioned here in the name of completeness.

Second, this strategy doesn’t entirely solve the problem of code circumventing the standard execution model; the operators get and load are sometimes used to extract objects of operatortype for procedure construction, sometimes not; it’s an unfortunately gray area. If procedure bodies are being replaced by larger procedure bodies, as would be the case in redefining an application dictionary, the technique works without a hitch. If, however, operator objects are replaced by procedure bodies, as occurs in the new systemdict, there is a problem. If a procedure body is positioned in the construction of another procedure, in the place of an operator object. the result, upon execution of the new proc, will be the original proc, left on the stack without execution, possibly invoking a typecheck error and certainly destroying the intended functionality of the constructed procedure. To get around this, two extra definitions, for load and get are included, but commented out. If the problem arises, try uncommenting the appropriate proc. If that doesn’t work, access thesysdict, the utilitydict entry for the original sysdict, as necessary to obtain the original operator objects, tracking the specific problem procedures in some other way.

Specific Solution

The technique is defined in two stages, the first of which involves a simple name writing operation, which redefines each functional (either procedure or operator) entry in a given dict into a procedure body including code to write the appropriate key to the output file. The second stage adds the ability to include the operands as well, so that the subsequent compiler can produce more useful information; since a newline is appended to each name in the dict, this defines the format of our output. Using this technique, every operator in any dictionary, even those involving encrypted definition, is open for examination.

A note with caution: the second stage is flexible enough to produce directly runnable code for a functional subset of PS commands, such as the graphics commands; this technique has been used to “unwrap” PS code that uses complex graphical calculation or extensive looping, so that a final print job, free of calculation, can be fed to disk either for transport to a high-res imagesetter, or for transformation into the Adobe Illustrator 88 file format (or something similar) as an EPS file.

Unfortunately, the code presented here is not quite robust enough to produce a runnable representation of all objects encountered in the interpreter. The cvs, or convert to string, operator is limited in function to simple objects; if it gets even mildly confused, it responds with “-nostringval-” or something equally unpalatable. The /writeobj procedure is designed to write out an object, if directly accessible through cvs, or a representation of the object type, for more complex things; further, it serves as a central location for modification of the representational code. The /dictdump procedure is an example of one way to modify the code in order to write out a more complex data object. Redefine /writeobj as necessary.

A further problem involves the fairly common situation where the results of a computation are offered to an operator as operand:

 ...variable variable add 15 moveto...

The output, if both add and moveto are being tracked, would be:

 ...num num add
  (num+num) 15 moveto...

If an output file including this code were run as a PS file, the result of the add operation would be left on the stack, without being consumed; since many such cases are likely to exist, in any given print job, the result would be either duplicate data passed to operators or, eventually, an operand stack overflow. The strategy must, therefore, be to think of the output of this technique as data for analysis.

The Code

The simple compiler model is defined in listing one for systemdict. In order to overcome the read-only nature of systemdict, additional, clearly marked code to copy the dict is included; this should be removed if these techniques are to be applied to any other dictionary that can be directly modified (ie. is not read-only).

A utility dictionary is defined to hold all technique specific bindings. It is represented internally with the first binding, /utility.

The /outfile is a general convention in my PS designs to define a generic disk output file name; %stdout or any possible disk file can be set up as /outfile. In this case the output file is on a Macintosh hard drive, accessed from within the Freedom of Press PS emulation software; the main advantage, beside cost, to using a software PS emulator is direct access to a hard disk, which is otherwise available only with the more expensive printers and of course on Display PostScript equipped workstations.

Following the initialization of outfile is code to alter the default errorhandler to close outfile if anything goes wrong. Uncomment this if the relevent disk i/o system is not robust enough to do it for you. This is an area of limitation of the clones, such as the above mentioned Freedom of the Press which closes the file but does not then execute the appropriate error code (%stdout is partially emulated through Macintosh specific dialog boxes, and the implementation is not robust enough to absorb this minor fiddling and continue to work properly; fortunately, the implementation of the FOP file object seems to be strong enough to close the file on its own, without reference to the errorhandler fix).

Workstring is a string variable that should be long enough to accommodate any object representations likely to come along, yet not long enough to take up significant memory resources. The only limitations on the function of this variable are described below in the description of the writeobj procedure.

The comment procedure is included to allow for the binding of writestring before redefinition so it won’t be repeated in the output everytime a comment is placed. Comments are primary to the utility of the technique, serving the role of transparent breakpoints in the printout.

Dostack is a destructive stack dump procedure, so that the stack can be transmitted to disk at the appropriate place in the output. Remember that it is destructive; the stack is cleared by this proc.

Makestring and writeobj are defined here for easy customization of the code.

Makestring minimizes VM consumption by creating string constants of the appropriate size (and no larger) for each key to be written to disk. The cvs operator takes an object and a string returning the substring taken up by the representation of the object. If a constant string size is used, the VM corresponding to the total difference between the sum of the strings used and the sum of the substrings is lost. By using the global variable workstring in this way, no extra memory is lost.

Writeobj is the generic “write object to disk” function. It is separate from the main forall loop in order to be easily modified. The structure of this procedure uses the dictionary data structure to perform the role that in a more static language would be implemented with a “” structure; so long as the switch can be made to revolve around name objects, returned from some PS function, the dictionary data structure is very useful for this purpose. Note that the get operator is used for name lookup instead of the standard execution model; given the generality of the subject matter of this particular program (it’s designed to work on the environment itself), the ‘typename’ based switch mechanism might be useful for some additional purpose, so it seems best to leave that option open.

As previously mentioned, the program defined herein directly writes only simple objects, returning either a string describing the object type, if known, or “-nostringval-” for anything more complex. One simple object type that will not directly work with the workstring variable is a string of a size larger than the variable; the solution is to recognise strings directly, writing them to disk without conversion. Another string problem is that the character definitions for Truetype and type 1 fonts use data objects that are inaccessible to direct reading, for security purposes; in order to avoid errors, the example program checks for strings with the read-lock attribute, by using the rcheck operator, indicating them in the output stream with a “string object that does not allow read access” string. Both of these modifications, again, are good examples of where and how to insert code to enhance flexibility of the general technique.

/writename is a chunk of code manipulated by /compile1 to add the appropriate operations to write the arbitrary name to disk. It is never to be directly invoked and is included here as a data object that happens to be an executable procedure.

The /combineprocs procedure is similar in utility to the /concatprocs procedure defined in the green book (see bibliography); it takes two procedure bodies, returning a single synthesis including the functionality of the first proc, then the second. The main difference is that this procedure does not use the putinterval operator, allowing for a more efficient procedure definition resulting from use of the packedarray data structure, instead of the simple array; ‘packedarray’ uses less memory.

The two compile procedures, /compile1 and /compile2, are the procs for use in the forall loops at the heart of the technique.

Listing 1

To create the simple virtual compiler, the forall operator, which performs some procedure for each key/value pair in a dictionary, is passed the dictionary and the /compile1 procedure which tests whether the value is an array and then whether it is executable; if both of these are the case, or if the object turns out to be of type operatortype, the object is passed to a procedure that constructs a new procedure including code to write a string of the key and a newline to /outfile, either the original procedure body or just the constituent parts and, if necessary, the /exec operator, which is required to execute a procedure embedded within another procedure. This new procedure is then stored in the new dictionary, under the old key.

The new dictionary is then stored in the utility dictionary under the name dict2. Upon completion of the forall loop, dict2 is fully capable of writing all the keys to disk, according to the outfile specification. If more information is needed, the next forall loop must be included.

Listing 2

For the second stage, to build the complex virtual compiler, code is included that takes into account the n-ary operand property of each entry in the given dictionary. For our purposes, this information is encoded in yet another dictionary structure, dict3, which is a listing of almost all the PostScript level one systemdict keys that take operands, with the corresponding number of operands defined as name objects in the corresponding value slot. This particular step required a bit of drudgery, as a listing had to be made of how many operands each dict entry takes; a number of problematic operators were left off this list, particularly those whose operand list is variable in length, since these would require custom coding for each such operator. This list can be contracted or expanded, according to need.

The utility dictionary includes the code corresponding to each of the possible operand number values, listed using the numbers themselves as keys.

In normal operation, the PS interpreter immediately checks the type of all tokens before looking anything up; if the token is a number, it is merely left on the operand stack without any reference whatsoever to the dict stack; numbers can be used as keys only if the normal execution context is circumvented using explicit dictionary manipulation operators. In this case the get operator allows numbers to be used as keys into the dictionary; note that the procedures stored as values for the number keys are not meant to be executed directly, but again are data, intended to be attached to other procedure bodies.

The second forall loop loads whatever utility dict procedure corresponds to the dict3 number name, copies the key, saves it at the bottom of the stack, then checks to see if the key is available in dict2. If so, a new array is created into which both procedure bodies are combined, so that each will be executed upon invocation. The packedarray is made executable, then stored in dict2 using the key previously saved. If the key is not known in dict2 (a circumstance which should not occur, since the list was defined by hand) an error message is sent to outfile.

One thing to note in the complex compiler building loop is that no check is made for procedure-hood, allowing this code to avoid the nested conditionals of the first forall loop. The reason for this is simple; this code depends on the data dictionary, which only contains entries for objects which, after compile1, have all been transformed into the appropriate procedure bodies.

Application of Technique

The result of all this is a redefined PS execution environment through which interpreter activity can be easily tracked. Using the flexibility designed into the language allows for analysis of arbitrary PostScript at the level of the logical engine, from which few things can hide.

First, insert the listing 1 code. Create a PostScript file by funnelling the application output intended for the printer to a disk file. Open that file with a text editor (a word processor will do, but the file must be saved as plain text or ASCII) and copy the code from listing 1 to the beginning of the PostScript file (somewhere before the definition of the application dictionary; I usually put everything at the very beginning of the file which shouldn’t be a problem unless a print spooler is involved, in which case the code should be inserted after the initial round of %%comments); choose an outfile, according to your platform file protocol (if the “\” character is used (as in DOS, or on the Atari ST), it must be doubled in the file naming string, “\\”, because the backslash is used to denote octal character definition in PS strings), filling the appropriate information into the file invoking string. If the simple compiler is all that is desired (ie. a printout of systemdict operators, without any reference to the operands) this is all the dictionary manipulation that is required; in order to create the more complex compiler model, the code from listing two, which further alters the dictionary, must also be included.

Once the appropriate compiler model is defined in the file, the appropriate commands must be included to begin dict2 and userdict (as indicated).

Finally, insert comments in the body of the PS file, in order to define reference points in the output file, and finally include the line outfile closefile in order to properly close the file. When run, this will create an outfile which includes a history of all systemdict activity in the interpreter. The appropriate comment strategy should allow anyone to analyse the innermost structure of any previously defined PostScript application dictionary.

%Metaprogramming PostScript: 
%A System for Arbitrary Diagnostic Analysis 
%This text is Copyright 1992 Gregory Koomey.  
%All rights are reserved;  
%nothing herein shall be used without 
%written consent from the author.

%Listing 1 

%%begin utility dict definition
20 dict dup begin 
/utility exch def %internal identity of dict
/outfile (Horatio:Programming:Qued/M:quark outfile) (w) 
file def

%in case of error, the following code closes outfile
%/*handleerror errordict /handleerror get def
%errordict begin 
%/handleerror { 
%outfile closefile
%} bind def
%end %%errordict
%%end of error handling code

%to keep original systemdict available
/thesysdict systemdict def 
/workstring 100 string def

%some useful procedures
 {outfile (Comment: ) writestring 
 outfile exch writestring
 outfile (\r) writestring} bind def
 {workstring cvs dup length string copy} bind def
/dostack %inverted destructive stack print
 {outfile (dostack: ) writestring
 {writeobj outfile ( ) writestring}
 outfile (\r) writestring} bind def

%%The following are included for the complex compiler model
/writeobj % depends on the following dictionary definition
 {dup type dup writeobjdict exch known
 {writeobjdict exch get exec} %
 {workstring cvs outfile exch writestring pop} ifelse
 } bind def
14 dict dup /writeobjdict exch def
/arraytype{xcheck{ outfile (-executable-arraytype- ) writestring}
 { outfile (-arraytype- ) writestring}ifelse
 } bind def
/booleantype{workstring cvs outfile exch writestring
 outfile ( ) writestring} bind def
/dicttype {pop outfile (-dicttype- ) writestring} bind def
/filetype {pop outfile (-filetype- ) writestring} bind def
/fonttype {pop outfile (-fonttype- ) writestring} bind def
/integertype{workstring cvs outfile exch writestring
 outfile ( ) writestring} bind def
/marktype {pop outfile (-marktype- ) writestring} bind def
/nametype {outfile (/) writestring
 workstring cvs outfile exch writestring
 outfile ( ) writestring} bind def
/nulltype {pop outfile (-nulltype- ) writestring} bind def
/operatortype  {pop outfile (-operatortype- )  writestring} bind def
/packedarraytype {xcheck  {outfile (-executable-packedarraytype- ) writestring}
 {outfile (-packedarraytype- ) writestring} ifelse
 } bind def
/realtype {workstring cvs outfile exch writestring
 outfile ( ) writestring} bind def
/savetype {pop outfile (-savetype- ) writestring} bind def
/stringtype {dup rcheck not
 {pop outfile (\() writestring
 outfile (-string-with-no-read-access- ) writestring
 outfile (\)) writestring
 outfile ( ) writestring}
 {outfile (\() writestring
 outfile exch writestring
 outfile (\)) writestring
 outfile ( ) writestring } ifelse 
 } bind def

 exch writeobj writeobj
 outfile (\r) writestring
 } bind def

 outfile /dummy writestring
 outfile (\r) writestring
 } bind def
/combineprocs %takes two procs,  returns combined proc
 counttomark  2 add index
 aload pop
 counttomark 1 add index
 aload pop counttomark packedarray cvx
 4 1 roll pop pop pop
 } bind def

 type dup  /packedarraytype ne
 {/arraytype eq}
 {pop true} ifelse
 dup xcheck
 dup rcheck
 exch dup 3 1 roll makestring
 /writename load 1 
 3 2 roll put
 /writename load exch combineprocs
 2 index 4 1 roll put
 /outfile load
 3 -1 roll dup 4 1 roll makestring
 /writestring load
 /outfile load
 /writestring load
 7 -1 roll 
 thesysdict /exec get 
 8 packedarray 
 3 -1 roll dup 4 2 roll
 } ifelse

 {pop pop} ifelse
 dup type /operatortype eq
 /outfile load
 3 -1 roll dup 4 1 roll makestring
 /writestring load
 /outfile load
 /writestring load
 7 -1 roll 
 7 packedarray %
 3 -1 roll dup 4 2 roll
 {pop pop}
 } bind def

/compile2 { 
 numberdict exch get  exch 
 dup 3 1 roll %copy of key at bottom of stack
 dup dict2 exch known
 dict2 exch get
 dict2 3 1 roll put
 workstring cvs outfile exch writestring
 outfile ( ... is not known in dict\r) writestring

} bind def

%%end of utility dict definition

 (end of systemdict listing) comment
%%beginning of code specifically for duplication of systemdict
systemdict length dict dup /dict2 exch def%the arbitrary name used here 
for our dict is /dict2
systemdict {3 -1 roll dup 4 2 roll put} forall %
dup /systemdict exch put %redefine systemdict entry

%two special operation definitions which may or may not be necessary
%/load %offered a key as operand
%{dup thesysdict exch known 
%{thesysdict exch dup 3 1 roll get
%type /operatortype ne
%{load} if
%{load} ifelse
%} bind put

%/get %offered a dict and key as operand
%{ exch dup 3 1 roll
%systemdict eq
%{thesysdict exch dup 3 1 roll get
%type /operatortype eq
%{exch pop}
%{get} ifelse
%{get} ifelse
%} bind put

%%end of code specifically for duplication of systemdict



% code to list dict2 in outfile as /name object/type
(the following is a listing of dict2) comment
 (end of listing of dict2) comment

6 dict dup /numberdict exch def begin
/1 { 
 dup writeobj outfile ( ) writestring
 } bind def
/2 { 
 2 copy exch writeobj outfile ( ) writestring
  writeobj outfile ( ) writestring
 } bind def
/3 {
 3 copy 3 -1 roll writeobj outfile ( ) writestring
 exch writeobj outfile ( ) writestring
 writeobj outfile ( ) writestring
 } bind def
/4 { 
 4 copy 4 -1 roll writeobj outfile ( ) writestring
 3 -1 roll writeobj outfile ( ) writestring
 exch writeobj outfile ( ) writestring
  writeobj outfile ( ) writestring
 } bind def
/5 { 
 5 copy 5 -1 roll writeobj outfile ( ) writestring
 4 -1 roll writeobj outfile ( ) writestring
 3 -1 roll writeobj outfile ( ) writestring
 exch writeobj outfile ( ) writestring
  writeobj outfile ( ) writestring
 } bind def
/6 { 
 6 copy 6 -1 roll writeobj outfile ( ) writestring
 5 -1 roll writeobj outfile ( ) writestring
 4 -1 roll writeobj outfile ( ) writestring
 3 -1 roll writeobj outfile ( ) writestring
 exch writeobj outfile ( ) writestring
 writeobj outfile ( ) writestring
 } bind def


%Listing 2
165 dict  dup
/dict3 exch def 
%math ops
/add    /2def
/div    /2def
/idiv   /2def
/mod    /2def
/mul    /2def
/sub    /2def
/abs    /1def
/neg    /1def
/floor  /1def
/round  /1def
/truncate /1def
/sqrt   /1def
/atan   /2def
/cos    /1def
/sin    /1def
/exp    /2def
/ln/1 def
/log    /1def
/srand  /1def
%array ops
/array  /1def
/length /1def 
/get    /2def
/put    /3def
/getinterval/3 def
/putinterval/3 def
/astore /1def
/aload  /1def
/copy   /2def
/forall /2def
%packedarray ops
/packedarray/1 def
/setpacking /1 def
%Dict ops
/dict   /1def
/begin  /1def
/load   /1def
/known  /2def
/where  /1def
/copy   /2def
%string ops
/string /1def
/anchorsearch  /2def
/search /2def
/token  /1def
%boolean ops
/eq/2 def
/ne/2 def
/ge/2 def
/gt/2 def
/le/2 def
/lt/2 def
/and    /2def
/not    /1def
/or/2 def
/xor    /2def
/bitshift /2def
%control ops
/exec   /1def
/if/2 def
/ifelse /3def
/for    /4def
/repeat /2def
/loop   /1def
%type ops
/type   /1def
/cvlit  /1def
/cvx    /1def
/xcheck /1def
/executeonly/1 def
/noaccess /1def
/readonly /1def
/rcheck /1def
/wcheck /1def
/cvi    /1def
/cvn    /1def
/cvr    /1def
/cvrs   /3def
/cvs    /2def
%file ops
/file   /2def
/read   /1def
/write  /2def
/readhexstring /2def
/writehexstring  /2def
/readstring /2 def
%/writestring  /2def %might duplicate output
/readline /2def
/token  /1def
/bytesavailable  /1def
/status /1def
/run    /1def
/print  /1def %might duplicate output, depending on setup...
%VM ops
%gstate ops
/setlinewidth  /1def
/setlinecap /1 def
/setlinejoin/1 def
/setmiterlimit /1def
/sethsbcolor/3 def
/setrgbcolor/3 def
/settransfer/1 def
%coord system and matrix ops
/identmatrix/1 def
/defaultmatrix /1def
/currentmatrix /1def
/translate/2def %sometimes /3
/scale  /2def %sometimes /3
/rotate /1def
/concat /1def
/concatmatrix  /3def
/transform/2def%sometimes /3
/dtransform /2 def%sometimes /3
/itransform /2 def%sometimes /3
/idtransform/2 def%sometimes /3
/invertmatrix  /2def
%path construction
/moveto /2def
/lineto /2def
/arc    /5def
/arcn   /5def
/arcto  /5def
/rcurveto /6def
/charpath /2def
/pathforall /4 def
/image  /5def
%device setup and output
/banddevice /4 def
/framedevice/4 def
/renderbands/1 def
%character and font
/definefont /2 def
/findfont /1def
/makefont /2def
/show   /1def
/ashow  /3def
/awidthshow /6 def
/kshow  /2def
/stringwidth/1 def
%font cache ops
/setcachedevice  /6def
/setcharwidth  /2def
/setcachelimit /1def
/setcacheparams  /3def
%stack manipulation ops
/pop    /1def
/exch   /2def
/dup    /1def
/copy   /2def
/index  /1def
/roll   /2def

%needs to be at end of definition of dict

/bind   /1def
/def    /2def
end %dict is in utility dict under dict3
(end of dict3 definition\r) comment

dict3 %procedure requires that dict to be modified 
 %is in utility as dict2

(this is the end of the dict redefinition...\r) comment

%Systemdict Finish
dict2 begin
userdict begin

(the following is a listing of dict2, after n-ary stuff added) comment
 (end of listing of dict2, after n-ary stuff added) comment

(this is the beginning of redefined environment...\r) comment

Book list:

PostScript Language Reference Manual, second edition Adobe Systems Inc., Addison-Wesley, 1985. “The Red Book” is the standard reference material for all implementations up to and including Level 2 and Display PostScript.

PostScript Language Tutorial and CookBook, Adobe Systems Inc., Addison-Wesley, 1985. “The Blue Book” is the official Adobe tutorial.

PostScript Language Program Design, Adobe Systems Inc., Addison-Wesley, 1988. “The Green Book” is an advanced technique manual geared toward the design of PS printer drivers.

Real World PostScript, ed. Stephen F. Roth, Addison-Wesley, 1988. This is the most interesting of the lot, consisting of essays and code by various (non-Adobe) PS professionals; particularly interesting is the essay “PostScript as a Programming Language”, by Bill Woodruff.

Programming the LaserWriter, David Holzgang, Addison-Wesley, 1991. Custom program the Macintosh LaserWriter driver with Think C object system.

Inside PostScript, Frank Braswell, Systems of Merritt & Peachpit Press, 1989. In depth reference describing QMS-PS 800 true Adobe PostScript environment; Nuts and bolts.

Encapsulated PostScript - Application Guide for Macintosh and PCs, Peter Vollenweider, Prentice-Hall UK, 1990. Fairly useful PS/EPS interchange information regarding specific applications.

Online resources:

Adobe forum on Compuserve (go Adobe) - If you have complex questions, ask them here.

PostScript roundtable on Genie (psrt) - This is somewhat hobbyist oriented, but includes an extensive file library of programming examples.


Apple Computer - if you don’t know what this is you shouldn’t be reading this magazine

Adobe, Inc. - Company that created and markets the PostScript language and related software products

Display PostScript - screen display oriented dialect of PostScript found in NeXT and Sun workstations

Epson - printer manufacturer

DTP - desktop publishing

ASCII text - plain text without any control codes that comes out of programmer’s editors

postfix notation - operands precede operator: “5 10 mul” results in “50”

stack - last in, first out data structure

operand stack - primary stack of PostScript environment, upon which operators recieve and return data

dictionary - collection of key/value pairs

dictionary stack - dictionary objects are activated by placing them on this stack

array - random access data structure

procedure body - array of objects with executable attribute corresponding to stream of code

font - dictionary of procedures specifically oriented toward the setting of text

type 1 font - font using efficient low level details of Adobe PS environment

type 3 font - font built around standard PostScript model

truetype font - font designed for use in Mac and Windows environments for both screen and printer

systemdict - dictionary in PS environment from which all built in operators are invoked; read-only and irremovable

userdict - writeable dictionary in default PS environment

EPS file - encapsulated PostScript code that is written within certain limitations, sometimes with an embedded preview graphic


Community Search:
MacTech Search:

Software Updates via MacUpdate

Ableton Live 10.1.1 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
BetterTouchTool 3.202 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
Fission 2.4.6 - Streamlined audio editor...
Fission can crop and trim audio, paste in or join files, or just rapidly split one long file into many. It's streamlined for fast editing. Plus, it works without the quality loss caused by other... Read more
Drama 1.0.27 - Prototyping, animation...
Drama's handy 3-in-1 functionality uniquely integrates design, animation and prototyping into a single familiar tool. No more frustrating switching between apps or learning new stuff. And by... Read more
Adobe Lightroom Classic CC 8.4.1 - Impor...
Adobe Lightroom Classic is available as part of Adobe Creative Cloud for as little as $9.99/month bundled with Photoshop CC as part of the photography package. Adobe Lightroom Classic CC (was Adobe... Read more
iExplorer 4.3.2 - View and transfer file...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
Adobe After Effects CC 2018 16.1.3 - Cre...
After Effects CC 2018 is available as part of Adobe Creative Cloud for $52.99/month (or $20.99/month for a single app license). The new, more connected After Effects CC 2018 can make the impossible... Read more
Adobe Audition CC 2019 12.1.4 - Professi...
Audition CC 2019 is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition CC 2019 empowers you to create and... Read more
Adobe Premiere Pro CC 2019 13.1.5 - Digi...
Premiere Pro CC 2019 is available as part of Adobe Creative Cloud for as little as $52.99/month. The price on display is a price for annual by-monthly plan for Adobe Premiere Pro only Adobe Premiere... Read more
Navicat Premium Essentials 12.1.25 - Pro...
Navicat Premium Essentials is a compact version of Navicat which provides basic and necessary features you will need to perform simple administration on a database. It supports the latest features... Read more

Latest Forum Discussions

See All

Marvel Strike Force is adding Agent Coul...
Marvel Strike Force, the popular squad-based RPG, is set to receive a bunch of new content over the next few weeks. [Read more] | Read more »
Lots of premium games are going free (so...
You may have seen over the past couple weeks a that a bunch of premium games have suddenly become free. This isn’t a mistake, nor is it some last hurrah before Apple Arcade hits, and it’s important to know that these games aren’t actually becoming... | Read more »
Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »

Price Scanner via

4-core and 6-core 2018 Mac minis available at...
Apple has Certified Refurbished 2018 Mac minis available on their online store for $120-$170 off the cost of new models. Each mini comes with a new outer case plus a standard Apple one-year warranty... Read more
$250 prepaid Visa card with any Apple iPhone,...
Xfinity Mobile will include a free $250 prepaid Visa card with the purchase of any new iPhone, new line activation, and transfer of phone number to Xfinity Mobile. Offer is valid through October 27,... Read more
Sprint is offering the 64GB Apple iPhone 11 P...
Sprint has the new 64GB iPhone 11 Pro available for $12.50 per month for new customers with an eligible trade-in in of iPhone 7 or newer. That’s down from their standard monthly lease of $41.67. The... Read more
Final week: Apple’s 2019 Back to School Promo...
Purchase a new Mac using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address qualify for the discount... Read more
Save $30 on Apple’s AirPods at these reseller...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP as part of their Labor Day sale. Shipping is free: – AirPods with Charging Case: $144.95 $15 off MSRP – AirPods with... Read more
Preorder your Apple Watch Series 5 today at A...
Amazon has Apple Watch Series 5 GPS models available for preorder and on sale today for $15 off Apple’s MSRP. Shipping is free and starts on September 20th: – 40mm Apple Watch Series 5 GPS: $384.99 $... Read more
21″ iMacs on sale for $100 off Apple’s MSRP,...
B&H Photo has new 21″ Apple iMacs on sale for $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Overnight shipping... Read more
2018 4 and 6-Core Mac minis on sale today for...
Apple resellers are offering new 2018 4-Core and 6-Core Mac minis for $100-$150 off MSRP for a limited time. B&H Photo has the new 2018 4-Core and 6-Core Mac minis on sale for up to $150 off... Read more
Save $150-$250 on 10.2″ WiFi + Cellular iPads...
Verizon is offering $150-$250 discounts on Apple’s new 10.2″ WiFi + Cellular iPad with service. Buy the iPad itself and save $150. Save $250 on the purchase of an iPad along with an iPhone. The fine... Read more
Apple continues to offer 13″ 2.3GHz Dual-Core...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases are new... Read more

Jobs Board

*Apple* Mobility Pro - Best Buy (United Stat...
**719499BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 001266-Charleston-Store **Job Description:** At Best Buy, our Read more
Best Buy *Apple* Computing Master - Best Bu...
**733266BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000144-Union City-Store **Job Description:** **What does a Read more
Best Buy *Apple* Computing Master - Best Bu...
**730765BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000565-St Petersburg-Store **Job Description:** **What does Read more
*Apple* Mobile Master - Best Buy (United Sta...
**725617BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 001095-Chesterfield-Store **Job Description:** **What does a Read more
Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.