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

Example 7.12: Nested Conditionals
myfont /Optima eq { %ifelse ptsize 12 eq { %ifelse }{ %else
ptsize 100 gt { %ifelse }{ % else
} ifelse
} ifelse
|
}{ % else |
|
} ifelse |
|
The second important thing to keep straight in a complicated nested |
|
conditional statement is the contents of the operand stack. This is |
|
particularly important if you use dup or keep data on the operand stack |
|
that must survive into the middle of the conditional somewhere. |
|
Otherwise, nested conditionals are fairly straightforward to use. |
|
|
TIP |
If you use ifelse rather than if, make sure you supply both procedures, and |
|
that you think carefully about what is on the operand stack for each of |
|
them. |
|
|
COMPOUND CONDITIONALS
Compound conditionals are found wherever there is a single condition but many possible else clauses. In other languages, this might be handled by a case statement, but—as you’ve already learned—there is no case operator in the PostScript language.
First, let’s look at an example of a compound conditional implemented in the most straightforward manner, as a series of else clauses. Remember that PostScript is not a compiled language. Therefore, you should try to put the most likely conditions first, since they will be tested in order. Since there is no way to choose from a parallel set of procedure bodies, the only way to implement a compound conditional with ifelse is to nest the conditionals, adding a new test for each else clause. This can get very tricky to maintain, since it may nest very deep to supply all of the required clauses (see Example 7.13).
88 |
Chapter 7: BUILDING CONDITIONAL STATEMENTS |

You can imagine how complicated that would get if you had twenty or thirty different commands to look for. Example 7.14 uses a very slightly different structure for the same conditional that makes it a little flatter and easier to maintain.
Example 7.13: Compound Conditional with ifelse
%read a token from the input stream and look for a command
%like “PRINT”, “QUIT”, or “HELP”.
currentfile 256 string readline token { %ifelse % a token is a command
dup /PRINT eq { %ifelse do_print
}{ %else
dup /QUIT eq { %ifelse do_quit
}{ %else
dup /HELP eq { %ifelse do_help
}{ %else do_unknown
} ifelse
} ifelse
} ifelse }{ %else
(no more commands.) = stop
} ifelse
Example 7.14: Compound Conditional with loop
currentfile 256 string readline token { %ifelse % a token is a command
{ %dummy loop, always exited
dup /PRINT eq { do_print exit } if dup /QUIT eq { do_quit exit } if dup /HELP eq { do_help exit } if do_unknown
exit
} loop }{ %else
(no more commands.) = stop
} ifelse
Chapter 7: BUILDING CONDITIONAL STATEMENTS |
89 |

Example 7.15 presents one more way to set up a compound conditional, one that uses the power and flexibility of the PostScript language, particularly the name lookup and dictionary mechanisms. In this implementation, the multiple cases are stored as procedures in a dictionary (the current dictionary, for simplicity in this example), and the command string is looked up in the current dictionary and executed. Note that this requires only a single conditional (for the unknown case).
Example 7.15: Compound Conditional Using Name Lookup
/commands 4 dict def commands begin /PRINT { do_print } def /QUIT { do_quit } def /HELP { do_help } def
currentfile 256 string readline token { %ifelse % a token is a command
dup where { %ifelse exch get exec
}{ %else do_unknown
} ifelse }{ %else
(no more commands.) = stop end % commands dictionary
} ifelse
As you can see, there are several approaches to setting up a compound conditional. The language is very flexible, but it is a bit of trouble to do without a case statement.
CONCLUDING THOUGHTS
The most important thing to remember about conditionals and the ifelse operator is that the whole construction is interpreted each time it is encountered. The condition (true or false) is computed each time, the two procedure bodies are loaded onto the operand stack, and the ifelse operator pushes one or the other of them onto the operand stack. There’s nothing really magical about the way it works, and the procedure bodies it uses are interchangeable with all other procedures in the PostScript language.
90 |
Chapter 7: BUILDING CONDITIONAL STATEMENTS |

The next chapter presents a look at looping constructs. Loops share many properties with conditionals, since they require procedure bodies as arguments. Some of the concepts learned in this chapter will also apply in the next chapter.
EXERCISES
1.Design a case operator for the PostScript language, and design a procedure that will implement it. Think about the design trade-offs between ease of use (once your case operator exists) versus ease of implementation for you. Supply some simple documentation (comments in your program are good enough) explaining how to use your new case operator.
2.The following procedure emulates the setcmykcolor language extension by using setrgbcolor and doing a simple conversion. Design a conditional statement with the where operator that will define this procedure only if the setcmykcolor operator does not already exist.
/setcmykcolor { %def 1 sub 4 1 roll
3 { %repeat
3 index add neg dup 0 lt {pop 0} if 3 1 roll
}repeat setrgbcolor pop
}bind def
3.The “standard” procedure used with the image operator uses the readhexstring operator to get a line of data from the input file. The trouble is, it ignores the boolean returned by readhexstring, which is not really a good idea. Please rewrite this procedure body to check for the end-of-file condition reported by the readhexstring operator. (The second line of hex data is incomplete in this example, causing an error in the execution of the program as it stands. Fill out the line of data to see what the program is supposed to do.)
/picstr 16 string def
100 100 translate 100 900 scale 16 2 8 [ 16 0 0 16 0 0 ]
{ currentfile picstr readhexstring pop } image 00FF00FF00FF00FF00FF00FF00FF00FF 00FF00FF00FF00FF
Chapter 7: BUILDING CONDITIONAL STATEMENTS |
91 |

92 |
Chapter 7: BUILDING CONDITIONAL STATEMENTS |