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

Notice that all of the variables for margins and leading are used with the double slash notation inside the procedure body. The names are there for you to look at when you are editing the program, but when it is executed, they are replaced by their numeric values inside the body of the procedure before the procedure is ever encountered. In fact, if you look at the procedure body on the stack right before bind is executed, you will see a procedure that looks quite a bit different than the one in your source program. (For instance, compare Example 3.11 and Example 3.12).
Example 3.12: After Immediate Name Lookup with //
/LeftMargin 108 def % constant definitions /TopMargin 792 72 sub def
/RightMargin 612 72 sub def
/BottomMargin 72 def |
|
/Leading 12 def |
|
/space |
% - space - |
{ %def |
|
currentpoint exch 540 gt { %ifelse 12 sub dup 72 lt { %ifelse
pop showpage 108 720 moveto
}{ %else
108 exch moveto
} ifelse }{ pop } ifelse
} bind def
NOTE: The // syntax for immediate name lookup is not available in the very first implementations of a PostScript interpreter, which are those with version numbers below 25.0 from Adobe Systems Incorporated.
ALLOCATING MEMORY
There are two ways that memory gets allocated in a PostScript program. It is either allocated implicitly when an object is created or it is allocated explicitly by using one of a handful of operators that create empty data structures of a particular size. See Table 3.1 for a list of operators that explicitly allocate memory for data storage.
Chapter 3: FOUNDATIONS |
29 |

Table 3.1: Operators that Explicitly Allocate Memory
Arguments Operator |
Action |
|
int |
array array |
create array of length int |
int |
dict dict |
create empty dictionary with |
± matrix matrix |
capacity for int elements |
|
create identity matrix |
||
any0 ... anyn-1 int |
packedarray packarr |
create packed array consisting |
|
|
|
|
string string |
of specified int elements |
int |
create empty string of length int |
The operators in Table 3.1, with the exception of matrix, all take an integer argument specifying exactly how large a data structure to allocate. The matrix operator always creates an array with six elements, and is equivalent to the code sequence [ 1 0 0 1 0 0 ].
There are also a number of ways to allocate memory implicitly, sometimes without even realizing it. In general, whenever a composite object is created, memory must be allocated to store its contents. There are a few operators that create composite objects implicitly, and a few bits of PostScript language syntax that will cause a composite object to be created. These operations are summarized in Table 3.2.
Table 3.2: Implicit Memory Allocation
Arguments Operator |
Action |
|
[ obj0 ... objn-1 |
] array |
create a literal array |
{ any0 ... anyn |
} proc |
create an executable array |
|
(any bytes) |
create a string body |
|
<hex bytes> |
create hexadecimal string body |
|
gstate gstate_obj |
create a new gstate object |
|
save save_obj |
create a new save object |
The most common of these methods for implicitly allocating memory are procedure bodies and strings. The latter would literal strings, if they are created by the PostScript interpreter when it recognizes a string represented with the ( ) notation.
A composite object, whether created explicitly or implicitly, uses memory in proportion to its size. For example, when you create a dictionary object, a certain amount of memory is allocated for each entry in the dictionary, and the total amount allocated depends on how many empty slots are in the dictionary to be created. Similarly, a string object will require more
30 |
Chapter 3: FOUNDATIONS |