Advanced PostScript Stacks, dictionaries, graphics state, transformations P.J. Drongowski 28 September 2004 (Updated: 4 October 2004) PostScript stacks * Operand stack: Holds operands for operations and results from operations (such as arithmetic.) * Dictionary stack: Provides a context for name searches (variable names, procedure names.) * Execution stack: Holds executable objects (procedures or files) that are in partial stages of execution. * Graphics state stack: Maintains current and nested graphics states (positional information, color, line width.) Operand stack * Holds operands for most PostScript operators * Arithmetic example + 3 4 add 7 mul + Answer: 49 + Reverse Polish Notation (RPN) + We will see this again + = operator pops and displays item from operand stack + stack operator displays stack contents without side-effect Dictionaries and dictionary stack * A dictionary is an associative table containing key-value pairs * A key is a symbolic name by which a value may be retrieved * The table is "associative" because it is not indexed in the conventional sense; it uses user-/pre-defined keys instead * Dictionary operators + n dict Create a new dictionary of at most n elements and return the dictionary on the operand stack + key value def Define a new dictionary entry + key load Get topmost value associated with key + key value store Replace topmost definition of key + begin Pushes a dictionary on the dictionary stack + end Pops a dictionary from the dictionary stack * Dictionaries usually store: + Variables + Procedure definitions + Font dictionary * Interpreter maintains a dictionary stack defining the current dynamic name space + Dictionaries can be pushed (begin operator) and popped (end operator) + Interpreter searches for definition of a name beginning with dictionary on top of the dictionary stack and works downward + Provides nested symbol scopes, e.g., local variables for nested calls - What is a "local variable?" Draw analogy to C/C++ local variables - See arrow procedure for another dictionary example Example: Create new dictionary once; add to local scope (Cookbook, pg. 130) /localdict 1 dict def % Define new dictionary named "localdict" /sampleproc { localdict begin % Push localdict on dictionary stack /localvariable 6 def end % Pop localdict from dictionary stack } def Example: Create new dictionary/context on every call /sampleproc { 1 dict begin % Create and push anonymous dictionary /localvariable 6 def end % Pop dictionary from dictionary stack } def Fonts * Fonts are represented as dictionaries! * Predefined fonts can be looked up in a system dictionary "FontDictionary" + The findfont operator obtains the font dictionary identified by the key and pushed the associated font dictionary on the operand stack + Example: /Times-Roman findfont * Fonts must be scaled before they can be used + The scalefont operator applies scale factor to font on the operand stack + Example: 15 scalefont + This produces a 12 *unit* font, not a 12 point font; if user space is later scaled where one unit is a centimeter, a 12 unit character will be 12 centimeters high! * A font must be made the current font in the graphics state before it can be used + The setfont operator makes the font on the operand stack the current font + Example: setfont * Common built-in fonts (keys) Times-Roman Helvetica Courier Symbol Times-Bold Helvetica-Bold Courier-Bold Times-Italic Helvetica-Oblique Courier-Oblique Times-BoldItalic Helvetica-BoldOblique Courier-BoldOblique Execution stack * Usually transparent to the PostScript program * Maintains intermediate execution state of functions, the interpreter, etc. Graphics state * Describes how PostScript operators affect the current page * Includes current path, gray value, ..., and user coordinate system * The initgraphics operator resets the graphics state to default values * Current graphics state can be saved and restored using the graphics state stack + gsave operator saves the current graphics state in order to return to it later; the graphics state is saved on the graphics state stack + grestore operator restores the most recently saved graphics state * An important part of the graphics state is the Current Transformation Matrix or CTM Coordinate systems * Graphics operators work within the user coordinate system * User coordinate space can be changed using transformations tx ty translate Moves the origin of user space to new position angle rotate Rotates the coordinate axes (degrees, counterclockwise) sx sy scale Changes the size of the coordinate space units * Goal: Define each graphical element in its own coordinate system, indpendent of any other element * Each graphical element may be positioned, oriented and scaled by temporarily modifying the user coordinate system * Transformations can be represented as 3x3 matrices: a b 0 c d 0 tx ty 1 * A point is represented as a vector, [x y 1] * Points are transformed by post-multiplying the vector by a transformation matrix: [x y 1] | a b 0 | a,b,c,d scale and rotate | c d 0 | tx,ty translate | tx ty 1 | * Alternatively, the transformation can be written as formulae: x' = ax + cy + tx y' = bx + dy + ty * Translation matrix: Scaling matrix: Rotation matrix: 1 0 0 sx 0 0 cos a sin a 0 0 1 0 0 sy 0 -sin a cos a 0 tx ty 1 0 0 1 0 0 1 * Negative scale factors can be used to reflect a picture around an axis * If a picture (graphical element) is not centered at the origin, then scale and rotate also translate the image * A sequence of matrix operations can be concatenated (through matrix multiplication) to form a single matrix which achieves the same effect * More efficient at runtime since only one vector X matrix mult is performed * Each of the translate, scale and rotate operators concatenate the desired transformation matrix to the Current Transformation Matrix (CTM) CTM' = T X CTM where T is a transformation matrix * We must apply transformations in the correct order -- matrix multiplication is *not* commutative * With multiple levels of graphical structure (e.g., a scene graph), we must save the CTM before calling instancing transformations * A "safe" sequence of operations is: 1. Save current (instancing) transformation (gsave) 2. Concatenate one or more instancing transformations (translate, ...) 3. Draw the subpart (call a function to draw something) 4. Restore the original (instancing) transformation (grestore)