- 1. Coordinate Systems
- 1.1. Overview
- 1.2. User Space Basics
- 1.3. Coordinate Transformations
- 1.4. The Current Transformation Matrix (CTM)
- 1.5. Saving and Restoring Coordinate Systems
- 1.6. Device Space
- 1.7. Practical Coordinate Patterns
- 1.8. Common Coordinate System Patterns
- 1.9. Coordinate System Best Practices
- 1.10. Debugging Coordinates
- 1.11. Common Pitfalls
- 1.12. See Also
1. Coordinate Systems
PostScript uses a flexible coordinate system that separates logical drawing coordinates from physical device coordinates. Understanding this system is crucial for creating device-independent documents.
1.1. Overview
PostScript employs multiple coordinate spaces that transform between user-defined coordinates and device pixels:
-
User Space - The coordinate system you work in (default: points, 1/72 inch)
-
Device Space - The actual pixel coordinates of the output device
-
Current Transformation Matrix (CTM) - Converts user space to device space
This separation allows the same PostScript code to work correctly on different devices (screens, printers, plotters) without modification.
1.2. User Space Basics
1.2.1. Default User Space
By default, PostScript user space has these properties:
-
Origin (0,0) at the bottom-left corner
-
X-axis increases to the right
-
Y-axis increases upward
-
Units are in points (1 point = 1/72 inch)
-
Positive angles rotate counter-clockwise
% Draw a simple line from origin
0 0 moveto % Start at bottom-left (0,0)
100 100 lineto % Draw to (100, 100) - up and right
stroke
1.3. Coordinate Transformations
1.3.1. The translate Operator
Moves the origin to a new location:
% Move origin to center of US Letter page
306 396 translate
% Now (0,0) is at page center
0 0 moveto
100 0 rlineto % Draw 100 points to the right from center
stroke
Practical example - drawing multiple shapes:
% Draw three circles at different positions
gsave
100 100 translate
0 0 50 0 360 arc stroke
grestore
gsave
300 100 translate
0 0 50 0 360 arc stroke
grestore
gsave
200 300 translate
0 0 50 0 360 arc stroke
grestore
1.3.2. The scale Operator
Changes the size of the coordinate system:
% Scale by 2 in both directions
2 2 scale
% Now 1 unit = 2 points
0 0 moveto
50 0 lineto % Actually draws 100 points
stroke
Non-uniform scaling:
% Scale x by 2, y by 3
2 3 scale
0 0 moveto
50 50 lineto % Actually draws 100 points right, 150 points up
stroke
Converting units with scale:
% Work in millimeters instead of points
% 1 mm = 72/25.4 points ≈ 2.835 points
72 25.4 div dup scale
% Now coordinates are in millimeters
0 0 moveto
100 0 lineto % 100 mm line
stroke
1.3.3. The rotate Operator
Rotates the coordinate system (angle in degrees, counter-clockwise):
% Rotate 45 degrees
45 rotate
0 0 moveto
100 0 lineto % Line at 45-degree angle
stroke
Rotating around a point other than origin:
% Rotate around point (200, 300)
200 300 translate % Move origin to rotation center
45 rotate % Rotate
-200 -300 translate % Move origin back
% Or more simply:
% Draw at (200, 300), rotated 45 degrees
gsave
200 300 translate
45 rotate
% Draw here with (0,0) at the rotation point
0 0 50 0 360 arc
stroke
grestore
1.3.4. Combining Transformations
Transformations are cumulative and applied in order:
% Order matters!
gsave
100 100 translate % Move first
45 rotate % Then rotate
2 2 scale % Then scale
% Draw a square (will be rotated and scaled)
0 0 moveto
50 0 lineto
50 50 lineto
0 50 lineto
closepath
stroke
grestore
Different order gives different results:
% Compare: rotate then translate
gsave
45 rotate % Rotate first
100 100 translate % Then translate (in rotated space!)
0 0 50 0 360 arc
stroke
grestore
% Circle appears in different location!
1.4. The Current Transformation Matrix (CTM)
1.4.1. Understanding the CTM
The CTM is a 6-element matrix that transforms user space to device space:
[ a b c d tx ty ] Where: a, d = scaling factors b, c = rotation/skew factors tx, ty = translation values
Transformation formula:
x' = a*x + c*y + tx y' = b*x + d*y + ty
1.4.2. Accessing the CTM
% Get current matrix
matrix currentmatrix % Returns: [a b c d tx ty]
% Example: print the default matrix
matrix currentmatrix ==
% Outputs: [1 0 0 1 0 0] (identity matrix)
1.5. Saving and Restoring Coordinate Systems
1.5.1. Using gsave and grestore
The graphics state stack preserves the CTM:
% Save current state
gsave
% Modify coordinate system
100 100 translate
2 2 scale
45 rotate
% Draw something
0 0 50 0 360 arc stroke
% Restore original state
grestore
% Back to original coordinates
0 0 moveto
100 0 lineto
stroke
1.5.2. Nested Transformations
% Main scene transformation
gsave
300 400 translate
% Draw multiple objects with individual transforms
gsave
0 0 translate
0 0 30 0 360 arc stroke
grestore
gsave
100 0 translate
0 0 30 0 360 arc stroke
grestore
gsave
50 100 translate
0 0 30 0 360 arc stroke
grestore
grestore
1.6. Device Space
1.6.1. Understanding Device Space
Device space represents the actual output device:
-
Origin typically at bottom-left of page/screen
-
Units are device pixels or dots
-
Resolution varies by device (72 dpi, 300 dpi, 600 dpi, etc.)
1.6.2. Transforming Between Spaces
% Transform user coordinates to device coordinates
/userX 100 def
/userY 200 def
matrix currentmatrix
userX userY
dtransform % Device transform
% Stack now has: deviceX deviceY
Inverse transformation (device to user):
% Transform device coordinates to user coordinates
/deviceX 500 def
/deviceY 600 def
matrix currentmatrix
deviceX deviceY
idtransform % Inverse device transform
% Stack now has: userX userY
1.6.3. Device-Independent Drawing
Key principle: Always work in user space coordinates:
% Good: device-independent
/drawCircle { % x y radius -> -
0 360 arc stroke
} def
100 100 50 drawCircle % Works on any device
% Bad: device-dependent
/drawCircle {
% Assumes specific device resolution
500 500 moveto % Hardcoded device coordinates
% ...
} def
1.7. Practical Coordinate Patterns
1.7.1. Centering Content
% Center a drawing on the page
% For US Letter (612 × 792)
306 396 translate % Move to center
% Draw centered content
-50 -50 moveto
100 0 rlineto
0 100 rlineto
-100 0 rlineto
closepath
stroke
1.7.2. Creating a Grid System
% Define a 12-column grid (like Bootstrap)
/pageWidth 612 def
/columns 12 def
/gutterWidth 20 def
/columnWidth {
pageWidth gutterWidth columns 1 sub mul sub
columns div
} def
% Position element in column 3
/col3X {
columnWidth 2 mul % 2 full columns
gutterWidth 2 mul add % 2 gutters
} def
col3X 100 translate
% Draw content here
1.7.3. Aspect Ratio Preservation
% Scale to fit while preserving aspect ratio
/srcWidth 800 def
/srcHeight 600 def
/dstWidth 400 def
/dstHeight 400 def
% Calculate scale factor (use minimum to fit)
/scaleX dstWidth srcWidth div def
/scaleY dstHeight srcHeight div def
/scale scaleX scaleY lt { scaleX } { scaleY } ifelse def
% Center and scale
dstWidth 2 div dstHeight 2 div translate
scale dup scale
srcWidth -2 div srcHeight -2 div translate
% Draw content (will be scaled and centered)
0 0 srcWidth srcHeight rectfill
1.7.4. Margin System
% Define page margins (in points)
/marginLeft 72 def % 1 inch
/marginRight 72 def
/marginTop 72 def
/marginBottom 72 def
% Calculate content area
/pageWidth 612 def
/pageHeight 792 def
/contentWidth pageWidth marginLeft sub marginRight sub def
/contentHeight pageHeight marginTop sub marginBottom sub def
% Set up coordinate system for content
marginLeft marginBottom translate
% Now (0, 0) is at content area origin
% And (contentWidth, contentHeight) is top-right of content area
1.8. Common Coordinate System Patterns
1.8.1. Pattern 1: Local Coordinate System
Define objects in their own coordinate system:
% Define a star in its own coordinate system (centered at origin)
/star {
newpath
0 50 moveto
14.7 15.4 lineto
47.6 15.4 lineto
18.1 -5.9 lineto
29.4 -40.5 lineto
0 -18.2 lineto
-29.4 -40.5 lineto
-18.1 -5.9 lineto
-47.6 15.4 lineto
-14.7 15.4 lineto
closepath
} def
% Use it anywhere
gsave
100 100 translate
star fill
grestore
gsave
300 300 translate
2 2 scale
star fill
grestore
1.8.2. Pattern 2: Percentage-Based Layout
Work in percentages instead of absolute coordinates:
% Set up percentage-based coordinate system
/pageWidth 612 def
/pageHeight 792 def
% Scale so 100 = 100% of page
100 pageWidth div 100 pageHeight div scale
% Now work in percentages
50 50 translate % Center (50% of page width and height)
10 10 moveto % 10% from left, 10% from bottom
1.8.3. Pattern 3: Viewport System
Create multiple viewports on one page:
% Define viewport (x, y, width, height as percentages)
/viewport { % x y w h -> -
4 dict begin
/h exch 792 mul 100 div def
/w exch 612 mul 100 div def
/y exch 792 mul 100 div def
/x exch 612 mul 100 div def
gsave
x y translate
% Clip to viewport
newpath
0 0 moveto
w 0 rlineto
0 h rlineto
w neg 0 rlineto
closepath
clip
% Set up coordinate system within viewport
% (will be restored by grestore)
end
} def
/endviewport {
grestore
} def
% Use viewports
0 0 50 50 viewport
% Draw in bottom-left quadrant
endviewport
50 0 50 50 viewport
% Draw in bottom-right quadrant
endviewport
1.9. Coordinate System Best Practices
1.9.1. Always Use Graphics State Stack
% Good: isolated transformations
gsave
100 100 translate
% ...
grestore
% Bad: permanent transformations
100 100 translate
% ... transformations accumulate!
1.9.2. Document Your Coordinate System
% At the start of your document
% This file uses:
% - User space units: points (1/72 inch)
% - Origin: bottom-left
% - Page size: US Letter (612 × 792 points)
% - Margin: 1 inch (72 points) on all sides
1.9.3. Use Relative Coordinates for Shapes
% Good: relative positioning
/drawSquare { % size -> -
dup 0 rlineto
dup 0 exch rlineto
neg 0 rlineto
closepath
} def
% Use absolute positioning only for initial moveto
100 100 moveto
50 drawSquare
% Bad: absolute coordinates in shape
/drawSquare {
100 100 lineto % Hardcoded - not reusable
150 100 lineto
% ...
} def
1.10. Debugging Coordinates
1.10.1. Visualize Coordinate System
Draw a coordinate grid to understand current space:
/drawGrid {
gsave
0.1 setlinewidth
0.8 setgray
% Draw vertical lines
-500 10 500 {
dup -500 moveto 500 lineto stroke
} for
% Draw horizontal lines
-500 10 500 {
dup -500 exch moveto 500 exch lineto stroke
} for
% Draw axes
0 setgray
1 setlinewidth
-500 0 moveto 500 0 lineto stroke
0 -500 moveto 0 500 lineto stroke
grestore
} def
% Use it to see your coordinate system
gsave
100 100 translate
2 2 scale
45 rotate
drawGrid
grestore
1.10.2. Show Current Transformation
/showCTM {
(Current Transformation Matrix:) print
matrix currentmatrix {
( ) print
=string cvs print
} forall
() print
} def
% Usage
100 100 translate
showCTM
% Prints: Current Transformation Matrix: 1 0 0 1 100 100
1.10.3. Coordinate Debugging Function
/debugPoint { % x y label -> -
gsave
% Draw point
0 setgray
1 setlinewidth
newpath
2 copy 3 0 360 arc
fill
% Show label
/Helvetica findfont 10 scalefont setfont
moveto
5 5 rmoveto
show
grestore
} def
% Usage
100 200 (Point A) debugPoint
150 250 (Point B) debugPoint
1.11. Common Pitfalls
1.11.1. Forgetting to Save/Restore State
% Wrong: transformations accumulate
100 100 translate
% Draw
100 100 translate % Now at (200, 200) total!
% Draw
% Correct: use gsave/grestore
gsave
100 100 translate
% Draw
grestore
gsave
100 100 translate % Back to base, then +100, +100
% Draw
grestore
1.11.2. Incorrect Transformation Order
% Want to rotate around point (100, 100)
% Wrong order:
100 100 translate
45 rotate
% Rotates around new origin, not (100,100) in original space
% Correct order:
45 rotate
100 100 rotate 45 neg mul rotate % Complex!
% Better approach:
100 100 translate % Move origin to rotation center
45 rotate % Rotate
% Draw (0,0) is now at (100,100) in original space
1.12. See Also
-
Graphics State - Saving and restoring state
-
Transformation Commands - Complete transformation reference
-
translate - Translation details
-
scale - Scaling details
-
rotate - Rotation details
-
Path Construction - Building paths in user space