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

Capture One - RAW workflow sof...
Capture One is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 400 high-end cameras -- straight out of the box. It offers... Read more
Capture One - RAW workflow sof...
Capture One is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 400 high-end cameras -- straight out of the box. It offers... Read more
GraphicConverter 10.5.4 - $39.95
GraphicConverter is an all-purpose image-editing program that can import 200 different graphic-based formats, edit the image, and export it to any of 80 available file formats. The high-end editing... Read more
Dash 4.1.3 - Instant search and offline...
Dash is an API documentation browser and code snippet manager. Dash helps you store snippets of code, as well as instantly search and browse documentation for almost any API you might use (for a full... Read more
Microsoft OneNote 16.9 - Free digital no...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more
DEVONthink Pro 2.9.17 - Knowledge base,...
Save 10% with our exclusive coupon code: MACUPDATE10 DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research... Read more
OmniGraffle 7.6 - Create diagrams, flow...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
iFinance 4.3.7 - Comprehensively manage...
iFinance allows you to keep track of your income and spending -- from your lunchbreak coffee to your new car -- in the most convenient and fastest way. Clearly arranged transaction lists of all your... Read more
Opera 50.0.2762.58 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Microsoft Office 2016 16.9 - Popular pro...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more

Latest Forum Discussions

See All

Around the Empire: What have you missed...
Around this time every week we're going to have a look at the comings and goings on the other sites in Steel Media's pocket-gaming empire. We'll round up the very best content you might have missed, so you're always going to be up to date with the... | Read more »
Everything about Hero Academy 2: Part 4...
In this part of our Hero Academy 2 guide, we're going to have a look at some of the tactics you're going to need to learn if you want to rise up the ranks. We're going to start off slow, then get more advanced in the next section. [Read more] | Read more »
All the best games on sale for iPhone an...
Another week has flown by. Sometimes it feels like the only truly unstoppable thing is time. Time will make dust of us all. But before it does, we should probably play as many awesome mobile videogames as we can. Am I right, or am I right? [Read... | Read more »
The 7 best games that came out for iPhon...
Well, it's that time of the week. You know what I mean. You know exactly what I mean. It's the time of the week when we take a look at the best games that have landed on the App Store over the past seven days. And there are some real doozies here... | Read more »
Popular MMO Strategy game Lords Mobile i...
Delve into the crowded halls of the Play Store and you’ll find mobile fantasy strategy MMOs-a-plenty. One that’s kicking off the new year in style however is IGG’s Lords Mobile, which has beaten out the fierce competition to receive Google Play’s... | Read more »
Blocky Racing is a funky and fresh new k...
Blocky Racing has zoomed onto the App Store and Google Play this week, bringing with it plenty of classic kart racing shenanigans that will take you straight back to your childhood. If you’ve found yourself hooked on games like Mario Kart or Crash... | Read more »
Cytus II (Games)
Cytus II 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: "Cytus II" is a music rhythm game created by Rayark Games. It's our fourth rhythm game title, following the footsteps of three... | Read more »
JYDGE (Games)
JYDGE 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: Build your JYDGE. Enter Edenbyrg. Get out alive. JYDGE is a lawful but awful roguehate top-down shooter where you get to build your... | Read more »
Tako Bubble guide - Tips and Tricks to S...
Tako Bubble is a pretty simple and fun puzzler, but the game can get downright devious with its puzzle design. If you insist on not paying for the game and want to manage your lives appropriately, check out these tips so you can avoid getting... | Read more »
Everything about Hero Academy 2 - The co...
It's fair to say we've spent a good deal of time on Hero Academy 2. So much so, that we think we're probably in a really good place to give you some advice about how to get the most out of the game. And in this guide, that's exactly what you're... | Read more »

Price Scanner via

Deals on clearance 15″ Apple MacBook Pros wit...
B&H Photo has clearance 2016 15″ MacBook Pros available for up to $800 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.7GHz Touch Bar MacBook Pro... Read more
Apple restocked Certified Refurbished 13″ Mac...
Apple has restocked a full line of Certified Refurbished 2017 13″ MacBook Airs starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: – 13″ 1.8GHz/8GB/128GB... Read more
How to find the lowest prices on 2017 Apple M...
Apple has Certified Refurbished 13″ and 15″ 2017 MacBook Pros available for $200 to $420 off the cost of new models. Apple’s refurbished prices are the lowest available for each model from any... Read more
The lowest prices anywhere on Apple 12″ MacBo...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more
Apple now offering a full line of Certified R...
Apple is now offering Certified Refurbished 2017 10″ and 12″ iPad Pros for $100-$190 off MSRP, depending on the model. An Apple one-year warranty is included with each model, and shipping is free: –... Read more
27″ iMacs on sale for $100-$130 off MSRP, pay...
B&H Photo has 27″ iMacs on sale for $100-$130 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 27″ 3.8GHz iMac (MNED2LL/A): $2199 $100 off MSRP – 27″ 3.... Read more
2.8GHz Mac mini on sale for $899, $100 off MS...
B&H Photo has the 2.8GHz Mac mini (model number MGEQ2LL/A) on sale for $899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP. Read more
Apple offers Certified Refurbished iPad minis...
Apple has Certified Refurbished 128GB iPad minis available today for $339 including free shipping. Apple’s standard one-year warranty is included. Their price is $60 off MSRP. Read more
Amazon offers 13″ 256GB MacBook Air for $1049...
Amazon has the 13″ 1.8GHz/256B #Apple #MacBook Air on sale today for $150 off MSRP including free shipping: – 13″ 1.8GHz/256GB MacBook Air (MQD42LL/A): $1049.99, $150 off MSRP Read more
9.7-inch 2017 WiFi iPads on sale starting at...
B&H Photo has 9.7″ 2017 WiFi #Apple #iPads on sale for $30 off MSRP for a limited time. Shipping is free, and pay sales tax in NY & NJ only: – 32GB iPad WiFi: $299, $30 off – 128GB iPad WiFi... Read more

Jobs Board

*Apple* Data Center Site Selection and Strat...
# Apple Data Center Site Selection and Strategy Research Analyst Job Number: 83708609 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: Read more
Security Engineering Coordinator, *Apple* R...
# Security Engineering Coordinator, Apple Retail Job Number: 113237456 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: 40.00 **Job Read more
Firmware Engineer - *Apple* Accessories - A...
# Firmware Engineer - Apple Accessories Job Number: 113422485 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: 40.00 **Job Summary** Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Store Leader - Retail District Manag...
Job Description:Job SummaryAs more and more people discover Apple , they visit our retail stores seeking ways to incorporate our products into their lives. It's your Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.