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

Chapter 9
Procedures
In most programming languages, procedures are used to group together a set of instructions into a package that can be given a name and invoked by that name. The PostScript language provides a similar mechanism, although it is not as formal as many other languages. In particular, there are no local variables or specific parameter-passing conventions. It is up to you to decide what resources the procedure will use and how it will interact with its environment.
A PostScript procedure does not have to take its “traditional” form as a parcel of instructions to be called as a sort of subroutine. In fact, procedure bodies are used in many places without having names at all. For example, an ifelse statement requires two procedure bodies among its arguments, although only one of them is used each time ifelse is invoked.
In traditional procedural programming languages, a distinction is made between a procedure and a function. A function is simply a set of instructions that returns a value or values to the caller. For example, if you wanted to compute the average of three numbers, you might use a
105

function that had three operands passed to it and returned a real number (their average). In most traditional procedural programming languages, you need to specify what kind of value will be returned by the function while you are setting up the function itself.
PostScript procedures can act as functions quite simply by leaving something behind on the operand stack when they exit. In fact, since there is no compile-time checking of your program, a procedure might return a value inadvertently. Furthermore, a PostScript procedure acting as a function can return a value of any type, which is both good and bad. Although there are explicit data types in the PostScript language, the lack of a compile cycle forces run-time type checking, which, although it does a good job of checking types, often does so a bit too late.
WHAT EXACTLY IS A PROCEDURE?
In formal PostScript language terms, a procedure body is just an executable array, which is an array of PostScript objects that has its executable flag set. There are no further requirements of a procedure from the language’s point of view. In fact, the procedure does not even have to be composed of legal language elements for you to declare it. Since PostScript is an interpreted language, it is not until you try to run the program that the procedure will be interpreted (and that you will find out if it is written reasonably).
There are several places where procedure bodies are often found (or are required).
•Procedure bodies are used with operators like loop, for, forall, and filenameforall.
•Procedure bodies are used in ifelse statements to provide the true and false clauses of the conditional.
•Some operators—including image, kshow, forall, settransfer, and others related to these—require procedure bodies as operands.
•User-defined procedures can behave just like built-in operators, and are a useful way to extend the language.
Let’s look at a typical procedure definition and its use (Example 9.1).
106 |
Chapter 9: PROCEDURES |

Example 9.1: Typical Procedure Definition
/S { moveto show } def (some text) 100 200 S
The procedure’s name is S, and it takes three bits of data as parameters: two real numbers (the x and y locations for moveto) and a string.
There is no mention of parameters or data required on the operand stack in the typical procedure definition. It’s okay to define procedures that are dependent on the operand stack, because you have to use them, and only you must keep the contents of the stack straight. The language does not enforce it, other than by generating an error if something doesn’t work.
In order to gain a real understanding of this common procedure definition and invocation, let’s rearrange it in some interesting ways, all of which are legal and will work just fine. Example 9.2 sets forth some alternative ways to define the S procedure of Example 9.1.
Example 9.2: Alternative Ways to Define a Procedure
(S) cvn { moveto show } def { moveto show } /S exch def
currentdict /S { moveto show } put
/S [ (moveto) cvn (show) cvn ] cvx def
All of the definitions shown in Example 9.2 have exactly the same effect. They are not different procedure definitions, they are the same procedure definition, accomplished in various ways. This illustrates that procedures are not magic; they are simply a collection of instructions in an executable array.
You can associate a procedure body with a name if you like, by creating a definition in a dictionary. This can be done in various ways (including use of put as shown as the third alternative in Example 9.2), but the simplest of them is the method that just uses the def operator. The def operator is very simplistic. All it does is take two objects and make an association between them in the current dictionary. It does not help you write correct
Chapter 9: PROCEDURES |
107 |