
- •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.3: Thinking through a Conditional
% if the variable is greater than three, myvariable 3 gt { %ifelse
% then set it back to 0, /myvariable 0 def
}{ %else
%otherwise, add one to it and tell me its current value: /myvariable myvariable 1 add store
(my variable is now ) print myvariable == flush
} ifelse
As long as you think about your algorithm as a straightforward sequence of steps and can figure out how to write the expression and each of the clauses, the ifelse statement itself should be easy to master. You can lay it out in a template in seconds. Then you are left with the task of the carefully implementing the algorithm, not the details of the conditional statement.
The major advantage to the indentation style and position of the { } braces seen in these examples is that program lines can be easily added to or deleted from the clauses of the ifelse statement without disrupting the syntactic balance of the braces. This becomes especially important when braces are nested several deep.
SETTING UP THE CONDITION
All conditional statements depend on something being true or false. There are many PostScript operators that return a boolean value (remember, this just means that it can only be true or false). The purpose of this boolean returned on the operand stack is so that you can use it with an ifelse statement. The simplest of these is the eq operator (see Example 7.4).
Example 7.4: Setting Up the Conditional
dup type /stringtype eq { %ifelse
%topmost element on stack is a string
}{ %else
%it is not a string
}ifelse
Chapter 7: BUILDING CONDITIONAL STATEMENTS |
81 |

Remember that comparing an object to another object with eq pops both objects from the stack before pushing the result of the comparison. If you need the object again, you should either use dup to make a copy before you compare, or give the object a name in a dictionary (see Declaring and Using Variables in Chapter 3).
There are many other operators that return booleans. For example, the readhexstring operator returns a boolean, even though most of the examples of its use (with the image operator, especially) simply throw the boolean away. For a more careful use of this boolean, see Example 7.5.
Example 7.5: Using a Conditional with readhexstring
currentfile 128 string readhexstring { %ifelse
%readhexstring filled up the buffer
}{ %else
%it encountered EOF or other problems
}ifelse
Table 7.1 shows the most useful PostScript operators that return booleans. An ifelse or if statement can follow any of these operators. In fact, the operators were designed to be followed by a conditional.
Table 7.1: PostScript Operators that Return Boolean Values
Arguments Operator |
Action |
|
any1 any2 |
eq bool |
test equal to |
any1 any2 |
ne bool |
test not equal to |
obj1 obj2 |
ge bool |
test greater than or equal to |
obj1 obj2 |
gt bool |
test greater than |
obj1 obj2 |
le bool |
test less than or equal to |
obj1 obj2 |
lt bool |
test less than |
bool1 bool2 |
and bool3 |
logical and |
bool1 |
not bool2 |
logical not |
bool1 bool2 |
or bool3 |
logical inclusive or |
bool1 bool2 |
xor bool3 |
logical exclusive or |
± |
true true |
push boolean value true |
± |
false false |
push boolean value false |
dict key |
known bool |
test whether key is in dict |
key |
where dict true |
find dict in which key is defined |
|
false |
|
82 |
Chapter 7: BUILDING CONDITIONAL STATEMENTS |

string seek |
anchorsearch post |
determine if seek is initial substring |
||
|
|
|
match |
of string |
|
|
|
true |
|
|
or |
|
string |
or return string if not |
|
|
|
false |
|
string seek |
search post match pre |
search for seek in string |
||
|
|
true |
|
|
|
or |
string false |
|
|
string |
token post token true |
read a token from start of string |
||
|
|
false |
|
|
any stopped bool |
execute and catch any call to stop |
|||
any xcheck bool |
test executable attribute |
|||
string |
rcheck bool |
test read access |
||
string |
wcheck bool |
test write access |
||
file_object |
read int true |
read one character from file |
||
|
|
false |
|
|
file_object string |
readhexstring |
read string in hexadecimal from |
||
|
|
substring |
file_object into buffer string |
|
|
|
boolean |
|
|
file_object string |
readline |
string bool |
read through newline |
|
file_object |
token token true |
read PostScript token from file |
||
|
|
false |
|
|
file_object |
status† |
boolean |
is file_object still valid? |
NOTE: Operators marked by a dagger (†) are not available in all interpreters; check before executing.
It is also useful to combine a few simple operations into powerful tests. In Example 7.6 are some techniques for type checking and other tasks, in the context of other simple procedures.
The program in Example 7.7 will open up a file from the disk and print (or display) it one line at a time, starting a new page as necessary. There are several interesting conditionals here, based on the current point and the results of the readline operator.
Chapter 7: BUILDING CONDITIONAL STATEMENTS |
83 |

Example 7.6: Procedure with Typechecking
/typecheck_moveto { %def
% if either argument is not a number, use 0 dup type /realtype ne
1 index type /integertype ne or { %if pop 0
}if exch
dup type /realtype ne
1 index type /integertype ne or { %if pop 0
}if
exch moveto } bind def
Example 7.7: Conditionals at Work
%note: this program will only work on a UNIX workstation, since it explicitly
%opens the file /etc/passwd and uses the non-standard “selectfont” operator /bottom 72 def
/lineshow |
% (string) lineshow - |
{ %def |
|
%works like show, but checks for bottom
%of page and also moves current point
%down one line after each call currentpoint exch pop% just the Y coord bottom lt { %if
showpage
72 750 moveto% top of new page
} if
gsave show grestore
0 -12 rmoveto |
% down one line |
} bind def |
|
%list the /etc/passwd file /Times-Roman 10 selectfont 72 750 moveto
/datafile (/etc/passwd) (r) file def /buffer 256 string def
84 |
Chapter 7: BUILDING CONDITIONAL STATEMENTS |

{ %loop
datafile buffer readline { %ifelse lineshow
}{ %else
fd closefile showpage exit
} ifelse
|
} bind loop |
|
|
A very common source of program bugs is to have one else clause in a |
|
|
conditional that leaves something on the operand stack (or uses too many |
|
|
elements from it). If you change the conditional expression so that the |
|
|
stack holds different elements, be careful to debug each of the individual |
|
|
clauses fully. If the else clauses handle obscure possibilities or error |
|
|
conditions, they may not be executed (and hence debugged) under normal |
|
|
testing, but may fail under heavy use. Watch for this source of errors and |
|
|
bugs, and try to develop a sound methodology that will help to eliminate |
|
|
the cause of them. |
|
|
|
|
|
|
|
TIP |
It is important to test both the true and false clauses of your conditional |
|
|
statements, especially as you gradually change your code and evolve the |
|
|
contents or the type of items on the stack before going into the condition- |
|
|
al. You can “rig” this testing by redefining the ifelse operator to reverse |
|
|
the sense of all your conditionals (see Example 7.8), just to give a dry run |
|
|
through them, or you can test each one by hand. |
|
|
|
|
|
|
|
|
Example 7.8: Redefining ifelse for Testing |
|
|
% flip the sense of “ifelse” so you can test both sides of the operation |
|
|
/orig_ifelse /ifelse load def |
|
|
/ifelse |
% boolean { true_proc } { false_proc } ifelse |
|
{ |
|
|
exch |
% execute procs in other order |
|
orig_ifelse |
|
|
} bind def |
|
As you gain more experience with the language, you will become expert in the use of the ifelse statement, which is a fundamental and useful operator in any PostScript language program.
Chapter 7: BUILDING CONDITIONAL STATEMENTS |
85 |