
- •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

LOOKING INTO DICTIONARIES
Dictionaries contain key–value pairs. In addition to being able to retrieve specific entries from a dictionary with the get or load operator, you can see what entries already exist in the dictionary with the forall operator. This can be useful for various purposes, such as finding out what fonts are currently defined in the interpreter (looking in the FontDirectory or SharedFontDirectory dictionaries) or in debugging, to figure out which dictionary is currently on top of the dictionary stack.
Using the forall Operator
Looking through existing dictionaries can be instructive, even if it isn’t part of any production code you are writing. One of the most useful operators for this purpose is the forall operator. This operator loops through the contents of a dictionary and executes your procedure for each key–value pair found in the dictionary. Example 10.8 shows a simple way to enumerate a dictionary with the forall operator.
Example 10.8: Browsing a Dictionary with forall
/browse-dict |
% dictionary browse-dict – |
{ %def |
|
{ %forall
exch % name first (key: ) print == (val: ) print ==
}forall
}bind def
userdict browse-dict
The entries will not come out in a particularly sensible order. Instead, they are in hash table order, in which the location in the dictionary is determined by deriving an index directly from the key you used. (To digress slightly, an example hash function might be to add up the byte values of all the characters in a name object and use that number as an index into an array.) To make the forall operator a little more useful, you can use the type operator to take a look at each value in the dictionary, and take different measures depending on the data type. This will help to look into other dictionaries and arrays that might be lurking inside the
Chapter 10: USING DICTIONARIES |
127 |

dictionary you are perusing. But be careful if you use a recursive approach, since a dictionary may contain a reference to itself.
Example 10.9 shows another implementation of the browse-dict procedure; this implementation also browses subdictionaries and arrays. It uses the type operator to recognize these composite data types. This is much more complicated-looking code, mostly because of the inelegance of the ifelse constructs in the middle that are needed to check on the types of the data.
Example 10.9: Using type on each Element in a Dictionary
/browse-dict |
% dictionary browse-dict - |
|
{ %def |
|
|
{ %forall |
|
|
|
exch (key: ) print == |
|
|
dup type |
|
|
dup /dicttype eq { %ifelse |
|
|
(subdictionary: ) print |
|
|
browse-dict |
% recursively browse dictionary |
|
}{ |
|
|
(value: ) print == |
|
|
} ifelse |
|
}forall
}bind def
/browse-array |
% array browse-array - |
{ %def |
|
{ %forall
dup type
dup /dicttype eq { %ifelse (subdictionary: ) print
browse-dict % recursively browse dictionary
}{
dup /arraytype eq { %ifelse (array: ) print browse-array
}{ %else
(value: ) print == } ifelse
} ifelse
}forall
}bind def
128 |
Chapter 10: USING DICTIONARIES |

Using the where and known Operators
A very good way to make sure your code is portable if you are using features that may or may not be present on all implementations is to use either the known or the where operators. These operators allow you to check for the existence of names in dictionaries, and execute some conditional code based on whether or not you find them.
For example, let’s say you want to use four-color operations in the CMYK color space (cyan, magenta, yellow, and black). As a point of interest, the reason K is used by convention—rather than B—to represent black is that B is already used to represent the color blue in the RGB color model (red, green, blue).
You want to use the setcmykcolor operator, but if it isn’t available, you can simulate it using setrgbcolor. Using the where operator, you can define a simulation only if the name setcmykcolor is not defined already in the interpreter (see Example 10.10).
Example 10.10: Conditionally Defining a Procedure
% /C points to “setcmykcolor” if it exists, else it emulates it with “setrgbcolor” /C
/setcmykcolor where { %ifelse /setcmykcolor get
}{ %ifelse
{ %def
1 sub 3 { %repeat
3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat
setrgbcolor
} bind
} ifelse
def
In this case the /C is left on the operand stack while where is executed to determine whether or not the simulation is needed for setcmykcolor. If the name setcmykcolor is found, the current definition of it is loaded onto the operand stack. In either case, the name /C will have equivalent functionality to the setcmykcolor operator, and can be used throughout the program with the same arguments that setcmykcolor requires, without having to adjust the rest of the program according to the color model or the interpreter used.
Chapter 10: USING DICTIONARIES |
129 |