
- •Preface
- •DESIGN FEATURES
- •STRUCTURED PROGRAMMING TECHNIQUES
- •PROGRAMMING TASKS
- •WINDOW SYSTEMS, COMMUNICATIONS, AND DISPLAYS
- •DATA STRUCTURES AND ALGORITHMS
- •CONCLUDING THOUGHTS
- •PostScript is Not Like C
- •COMPARISON OF LANGUAGE MECHANISMS
- •EXPRESSING AN ALGORITHM AS A PROGRAM
- •THE UNIX SHELL AND OPERATING SYSTEM
- •INPUT, OUTPUT, AND THROUGHPUT
- •CONCLUDING THOUGHTS
- •Foundations
- •POSTSCRIPT LANGUAGE SYNTAX
- •SIMPLE PROGRAM STRUCTURE
- •Make Definitions First
- •Indentation Style
- •SETTING UP TEMPLATES
- •DECLARING AND USING VARIABLES
- •Arithmetic with Numeric Variables
- •Using the // Notation for Constants
- •ALLOCATING MEMORY
- •GETTING MEMORY BACK
- •OPENING AND CLOSING FILES
- •COMPARISONS AND EQUALITY OF OBJECTS
- •CONCLUDING THOUGHTS
- •Some Typical Programs
- •A TYPICAL PAGE DESCRIPTION PROGRAM
- •FONT PROGRAMS
- •PROGRAMS THAT READ DATA
- •QUERY PROGRAMS
- •ENCAPSULATED POSTSCRIPT PROGRAMS
- •PERSISTENTLY RESIDENT PROGRAMS
- •CONCLUDING THOUGHTS
- •Understanding the Stack
- •A QUICK OVERVIEW OF DATA TYPES
- •NAME LOOKUP
- •HOW OPERATORS USE THE STACK
- •GROUPING AND VISUAL CHUNKING
- •THINKING BACKWARD AND SIDEWAYS
- •COMPOSITE OBJECTS
- •THE OTHER STACKS
- •The Dictionary Stack
- •The Execution Stack
- •The Graphics State Stack
- •CONCLUDING THOUGHTS
- •Trusting the Stack
- •SAFETY OF DATA ON THE STACK
- •WHERE ARE THE DATA GOING?
- •REARRANGING THE STACK
- •Using the dup and index Operators
- •Using the roll Operator
- •CONDITIONALS AND LOOPS
- •RECURSION AND LOCAL VARIABLES
- •CONCLUDING THOUGHTS
- •Building Conditional Statements
- •SIMPLE CONDITIONALS
- •SETTING UP THE CONDITION
- •CONDITIONALS ARE NOT MAGIC
- •NESTED CONDITIONALS AND ELSE CLAUSES
- •COMPOUND CONDITIONALS
- •CONCLUDING THOUGHTS
- •Using Looping Constructs
- •LOOP BASICS
- •USING THE LOOP INDEX
- •LOOPS ARE PROCEDURE BODIES
- •LOOPS OF INSTRUCTIONS
- •EXITING LOOPS PREMATURELY
- •CONCLUDING THOUGHTS
- •Procedures
- •WHAT EXACTLY IS A PROCEDURE?
- •PARAMETER PASSING
- •CONSTRUCTING GOOD PROCEDURES
- •What to Name Your Procedure
- •A Useful Naming Convention
- •SELF-MODIFYING PROCEDURES
- •CONCLUDING THOUGHTS
- •Using Dictionaries
- •DICTIONARIES FOR NAME SCOPING
- •LOCAL DICTIONARIES
- •GLOBAL DICTIONARIES OF PROCEDURES
- •MAINTAINING THE DICTIONARY STACK
- •INTO AND OUT OF DICTIONARIES
- •LOOKING INTO DICTIONARIES
- •Using the forall Operator
- •Using the where and known Operators
- •REDEFINING OPERATORS
- •Changing the Behavior of Operators
- •Debugging with Redefined Names
- •Proper Nesting of Redefinitions
- •CONCLUDING THOUGHTS
- •Creating and Manipulating Data
- •CONSTRUCTING AN ARRAY
- •CONSTRUCTING A STRING
- •MANIPULATING DATA WITH PUT AND GET
- •CONCATENATING ARRAYS AND STRINGS
- •INPUT AND OUTPUT OF STRING DATA
- •ARRAYS VERSUS DICTIONARIES
- •ADVANCED TECHNIQUES
- •CONCLUDING THOUGHTS
- •Storing and Using Data
- •Data and the Operand Stack
- •Data and Algorithms for Underlining
- •CLASSICAL DATA STRUCTURES
- •Linked Lists
- •Using Arrays to Form Lists
- •Using Dictionaries to Form Lists
- •Queues, Trees, and Other Data Structures
- •CONCLUDING THOUGHTS
- •Program Data and Instructions
- •TURNING DATA INTO INSTRUCTIONS
- •TURNING INSTRUCTIONS INTO DATA
- •DATA CONVERSIONS
- •CONCLUDING THOUGHTS
- •File Objects
- •Streams and Files
- •PostScript File Operators
- •OPENING AND CLOSING FILES
- •READING AND WRITING FILES
- •Reading from a File
- •Writing to a File
- •Copying and Renaming Files
- •WRITING FORMATTED DATA TO FILES
- •Writing Out Various Data Types
- •Spaces, Tabs, Returns, and Special Characters
- •FILE STATUS INFORMATION
- •RANDOM VERSUS SEQUENTIAL ACCESS
- •CONCLUDING THOUGHTS
- •Appendix
- •Answers to Exercises

Figure 6.1: Anatomy of the roll Operator
miscellaneous data already on the stack
/A /B /C /D /E /F /G 4 -1 roll
roll amount
roll group
One helpful way to conceptualize the roll operator is to verbalize it. For the example shown in Figure 6.1, one might say “Take the top four elements on the stack and roll it negative one times.” The top four elements are easy enough to understand, but what does it mean to roll something “negative one times?” The sign of the number (negative or positive) indicates the direction to roll, and the magnitude of the number indicates the number of elements to be rolled. The easiest way to think of the direction of the roll is like this:
•If the roll amount is negative, that means you take the elements from the bottom of the roll group and bring them to the top.
•If the roll amount is positive, you take the elements from the top of the stack and roll them to the bottom of the roll group.
Figure 6.2 and Figure 6.3 show the effects of rolling with a positive and a negative roll amount.
CONDITIONALS AND LOOPS
One of the most difficult things to do in PostScript programming is to make sure you don’t inadvertently leave something on the stack or consume something from the stack that you weren’t supposed to. Keeping track is easy enough when there is only a single path through the code, but if there are lots of ifelse statements and loop constructs, it gets more difficult to trace through all the possibilities to make sure you’ve not forgotten anything.
One of the most common instances of this problem is caused by operators that return different numbers of arguments depending on whether they succeed or fail. Example 6.3 shows a conditional that accidentally leaves
Chapter 6: TRUSTING THE STACK |
73 |

a dictionary behind on the operand stack when the where operator succeeds (returns true), but works fine when the operator returns false.
Figure 6.2: Positive Rolling
roll group of 6
/A /B /C /D /E /F /G 6 2 roll
roll amount 2
roll direction “to bottom of roll group”
/A /B /C /D /E /F /G
6 2 roll
/A /F /G /B /C /D /E
result of 6 2 roll
Figure 6.3: Negative Rolling
roll group of 6
/A /B /C /D /E /F /G 6 -2 roll
roll amount -2
roll direction “to top of roll group”
/A /B /C /D /E /F /G
6 -2 roll
/A /D /E /F /G /B /C result of 6 -2 roll
74 |
Chapter 6: TRUSTING THE STACK |

Example 6.3: Accidentally Leaving Dictionary on Stack
/selectfont where not { %if /selectfont { %def
exch findfont exch scalefont setfont
} def
} if
Unfortunately, the where operator returns the dictionary in which the key is found in the event that it does find the key, but returns nothing other than the boolean if it does not find the key. Example 6.3 shows a simple fix to correct this problem. The if statement is changed to ifelse, and the extra dictionary is popped from the stack if the where operator returns true.
Example 6.4: Correctly Maintaining Dictionary Stack
/selectfont where not { %if /selectfont { %def
exch findfont exch scalefont setfont
} def |
|
}{ %else |
|
pop |
% throw away dictionary returned by “where” |
} ifelse |
|
Table 6.2 provides a list of operators that can cause you some trouble by returning sometimes unexpected results on the operand stack. If you get a typecheck error that you have trouble finding, this might be the cause.
Table 6.2: Operators with Unexpected Stack Results
Operator |
Results Returned |
anchorsearch |
different results on success and failure |
for |
unexpected loop index |
forall |
different results with different data types |
scalefont |
returns scaled dictionary onto stack |
search |
different results on success and failure |
stringwidth |
unexpected extra y value |
where |
different results on success and failure |
Chapter 6: TRUSTING THE STACK |
75 |

RECURSION AND LOCAL VARIABLES
If you create a procedure that calls itself recursively, you have to be careful about the names you use. Recursion is inherently stack-based. To make recursion work correctly in PostScript, you need either to use the dictionary stack or the operand stack to store intermediate results until your recursion is unwound back to the original invocation level.
Let’s look at a recursive function that has an integer passed to it as an argument, and that will ultimately return an integer as its result (Example 6.5). If the argument is even, the function calls itself recursively and adds one to the argument. If the argument is odd, the function returns. The result of this function is always an odd number.
Example 6.5: Recursion Using the Dictionary Stack
/recurse_proc |
% int recurse_proc int |
{ %def |
|
save
2 dict begin
/save_obj exch def /arg exch def
arg 2 div truncate 2 mul cvi
arg eq { %ifelse
% even number
arg 1 add recurse_proc }{ %else
arg
} ifelse |
|
save_obj |
% leave on stack |
end |
|
restore |
% to save_obj on stack |
} bind def |
|
2 recurse_proc |
|
If you want to store the function’s argument in a dictionary, you have to create a new dictionary and push it onto the dictionary stack each time the function is called, to maintain the name local to that instance of the function. In this example, the memory allocated by the dictionary is reclaimed by save and restore, putting each save object into the recursion dictionary until it is needed. If the function is called recursively, more
76 |
Chapter 6: TRUSTING THE STACK |