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 9.7.5 - 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
Maintenance 2.3.5 - System maintenance u...
Maintenance is a system maintenance and cleaning utility. It allows you to run miscellaneous tasks of system maintenance: Check the status of the hard disk Repair permissions Run periodic scripts... Read more
OnyX 3.3.8 - Maintenance and optimizatio...
OnyX is a multifunction utility that you can use to verify the startup disk and the structure of its system files, to run miscellaneous maintenance and cleaning tasks, to configure parameters in the... Read more
Merlin Project 4.3.1 - $289.00
Merlin Project is the leading professional project management software for OS X. If you plan complex projects on your Mac, you won’t get far with a simple list of tasks. Good planning raises... Read more
WhatsApp 0.2.6426 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
DiskCatalogMaker 7.2.5 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
BBEdit 12.0.1 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
Hazel 4.2.2 - Create rules for organizin...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a familiar... Read more
Hopper Disassembler 4.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
Adobe InCopy CC 2018 - Create...
InCopy CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous InCopy customer). Adobe InCopy CC 2018, ideal for large team projects... Read more

Guns Royale guide - beginner tips and tr...
If you've been itching to find a mobile battle royale game like Player Unknown's Battlegrounds, you're finally in luck. Guns Royale is a new survival shooter that takes all of the things you love about good ol' PUBG and puts it in a tidy mobile... | Read more »
What we know about Animal Crossing on mo...
At last, we'll be receiving some news about the mobile version of Animal Crossing in a special Nintendo Director at11 PM on October 24. There's been little word on the game since it was first announced, having been met with a series of delays.... | Read more »
Darts of Fury guide - how to rise in the...
Darts of Fury is a new, immensely absorbing darts game from indie studio Yakuto. It's darts in its purest form, but collectible darts and other upgrades give this game an addictive edge that's hard to shake. As your progress out of the beginner... | Read more »
ICEY (Games)
ICEY 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ICEY is a 2D side-scrolling action game. As you follow the narrator's omnipresent voice, you will see through ICEY's eyes and learn the... | Read more »
The best new games we played this week -...
We've made it, folks. Another weekend is upon us. It's time to sit back and relax with the best new releases of the week. Puzzles, strategy RPGs, and arcade games abound this week. There's a lot of quality stuff to unpack this week, so let's hop... | Read more »
Wheels of Aurelia (Games)
Wheels of Aurelia 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: | Read more »
Halcyon 6: Starbase Commander guide - ti...
Halcyon 6 is a well-loved indie RPG with stellar tactical combat and some pretty good writing, too. It's now landed on the App Store, so mobile fans, if you're itching for a good intergalactic adventure, here's your game. Being a strategy RPG, the... | Read more »
Game of Thrones: Conquest guide - how to...
Fans of base building games might be excited to know that yet another entry in the genre has materialized - Game of Thrones: Conquest. Yes, you can now join the many kingdoms of the famed book series, or create your own, as you try to conquer... | Read more »
Halcyon 6: Starbase Commander (Games)
Halcyon 6: Starbase Commander Device: iOS Universal Category: Games Price: $6.99, Version: (iTunes) Description: An epic space strategy RPG with base building, deep tactical combat, crew management, alien diplomacy,... | Read more »
Legacy of Discord celebrates its 1 year...
It’s been a thrilling first year for fans of Legacy of Discord, the stunning PvP dungeon-crawling ARPG from YOOZOO Games, and now it’s time to celebrate the game’s first anniversary. The developers are amping up the festivities with some exciting... | Read more »

Price Scanner via

27″ 3.4GHz iMac on sale for $1699, save $100
Amazon has the 27″ 3.4GHz iMac (MNE92LL/A) on sale today for $1699 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model (except for Apple’s $1529... Read more
Clearance 2016 15″ MacBook Pros available for...
B&H Photo has leftover 2016 15″ MacBook Pros available for up to $700 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.7GHz Touch Bar MacBook Pro Space... Read more
Save $100 on 13″ MacBook Airs, prices start a...
Adorama has 2017 13″ MacBook Airs on sale today for $100 off MSRP including free shipping. Adorama charges NY & NJ sales tax only: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $899, $100 off MSRP... Read more
1.4GHz Mac mini available for $399, $100 off...
TigerDirect has the 1.4GHz Mac mini on sale today for $399 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model. Although currently out of stock,... Read more
21″ 2.3GHz iMac on sale for $999, save $100
MacMall has the 21″ 2.3GHz iMac (MMQA2LL/A) on sale today for $999 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model. Read more
12″ iPad Pros on sale for $50 off MSRP, no ta...
Adorama has 12″ iPad Pros on sale today for $50 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: – 12″ 64GB iPad Pro: $749, save $50 – 12″ 256GB iPad Pro: $899, save $50... Read more
9″ iPads on sale for $30 off, starting at $29...
MacMall has 9″ iPads on sale for $30 off including free shipping: – 9″ 32GB iPad: $299 – 9″ 128GB iPad: $399 Read more
Apple restocks full line of refurbished 13″ M...
Apple has restocked a full line of Apple Certified Refurbished 2017 13″ MacBook Pros for $200-$300 off MSRP. A standard Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
13″ 3.1GHz/256GB MacBook Pro on sale for $167...
Amazon has the 2017 13″ 3.1GHz/256GB Space Gray MacBook Pro on sale today for $121 off MSRP including free shipping: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (MPXV2LL/A): $1678 $121 off MSRP Keep an... Read more
13″ MacBook Pros on sale for up to $120 off M...
B&H Photo has 2017 13″ MacBook Pros in stock today and on sale for up to $120 off MSRP, each including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook... Read more

Jobs Board

*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
Project Engineer, *Apple* Education Profess...
Project Engineer, Apple Education Professional Services Job Number: 113143353New York City, New York, United StatesPosted: Oct. 17, 2017Weekly Hours: 40.00 Job Read more
Commerce Software Engineer, *Apple* Media P...
Commerce Software Engineer, Apple Media Products Job Number: 113092072New York City, New York, United StatesPosted: Oct. 19, 2017Weekly Hours: 40.00 Job Summary With Read more
Engineering Manager, *Apple* Retail Enginee...
# Engineering Manager, Apple Retail Engineering Job Number: 58139948 Santa Clara Valley, California, United States Posted: 20-Oct-2017 Weekly Hours: 40.00 **Job 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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.