- 1. Painting
1. Painting
Painting is the process of rendering paths and other graphics to the output device. Understanding painting operators is crucial for making your PostScript paths visible.
1.1. Overview
PostScript provides three main painting operations:
-
Stroking - Drawing the outline of a path
-
Filling - Filling the interior of a closed path
-
Clipping - Restricting where subsequent graphics can appear
Each operation consumes the current path, so you must reconstruct the path if you need to paint it multiple ways.
1.2. Stroke Operations
1.2.1. Basic Stroking: stroke
Draws the outline of the current path using current graphics state:
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath
2 setlinewidth
stroke % Path is consumed (cleared)
The appearance is controlled by:
-
Line width (
setlinewidth) -
Line cap (
setlinecap) -
Line join (
setlinejoin) -
Dash pattern (
setdash) -
Current color
1.2.2. Preserving Path After Stroke
The path is consumed by stroke, so use gsave/grestore:
newpath
100 100 moveto
200 200 lineto
% Stroke without losing path
gsave
stroke
grestore
% Path still exists
2 0 0 setrgbcolor % Change to red
stroke % Stroke again
Or rebuild the path:
% Better: define path as procedure
/myPath {
newpath
100 100 moveto
200 200 lineto
} def
myPath stroke
myPath 1 0 0 setrgbcolor stroke
1.2.3. Stroking Rectangles: rectstroke
Optimized operator for rectangular strokes:
% rectstroke: x y width height rectstroke
100 100 200 150 rectstroke
% Equivalent to:
% newpath
% 100 100 moveto
% 200 0 rlineto
% 0 150 rlineto
% -200 0 rlineto
% closepath
% stroke
1.3. Fill Operations
1.3.1. Basic Filling: fill
Fills the interior of the current path:
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath
0.8 0.2 0.2 setrgbcolor
fill % Path is consumed
1.3.2. Fill Rules
PostScript uses the non-zero winding number rule by default:
% Non-zero winding number (default)
newpath
% Outer rectangle (counterclockwise)
100 100 moveto
300 100 lineto
300 300 lineto
100 300 lineto
closepath
% Inner rectangle (same direction)
150 150 moveto
250 150 lineto
250 250 lineto
150 250 lineto
closepath
fill % Both filled (same winding direction)
1.3.3. Even-Odd Fill: eofill
Uses the even-odd rule for filling:
% Even-odd rule
newpath
% Outer rectangle
100 100 moveto
300 100 lineto
300 300 lineto
100 300 lineto
closepath
% Inner rectangle (creates hole)
150 150 moveto
250 150 lineto
250 250 lineto
150 250 lineto
closepath
eofill % Inner is a hole (odd/even crossings)
Visual comparison:
% Create star with crossing lines
/star {
newpath
200 300 moveto
250 150 lineto
100 220 lineto
300 220 lineto
150 150 lineto
closepath
} def
% Non-zero winding
gsave
star fill
grestore
% Even-odd (creates holes at intersections)
gsave
100 0 translate
star eofill
grestore
1.4. Combined Fill and Stroke
1.4.1. Fill Then Stroke
Common pattern for outlined shapes:
% Define path once
/myShape {
newpath
200 200 100 0 360 arc
} def
% Fill
myShape
0.8 0.8 1 setrgbcolor
fill
% Stroke
myShape
0 0 0.5 setrgbcolor
2 setlinewidth
stroke
Using gsave/grestore:
newpath
200 200 100 0 360 arc
% Fill preserving path
gsave
0.8 0.8 1 setrgbcolor
fill
grestore
% Stroke
0 0 0.5 setrgbcolor
2 setlinewidth
stroke
1.5. Clipping Operations
1.5.1. Basic Clipping: clip
Intersects current clipping path with current path:
gsave
% Set clipping path
newpath
200 200 100 0 360 arc
clip
newpath % Clear path after clip
% Only visible inside circle
100 100 200 200 rectfill
grestore
% Clipping restored
Important: Always use newpath after clip to avoid clipping to an empty path.
1.5.2. Even-Odd Clipping: eoclip
Clips using even-odd rule:
gsave
% Create clipping path with hole
newpath
100 100 moveto
300 100 lineto
300 300 lineto
100 300 lineto
closepath
150 150 moveto
250 150 lineto
250 250 lineto
150 250 lineto
closepath
eoclip
newpath
% Visible in outer, not inner
50 50 250 250 rectfill
grestore
1.5.3. Rectangular Clipping: rectclip
Optimized for rectangular clipping regions:
gsave
% rectclip: x y width height rectclip
100 100 200 150 rectclip
% Draw - only visible in rectangle
0 0 400 400 rectfill
grestore
1.6. Painting Strategies
1.6.1. Strategy 1: Path Reuse with Procedures
/triangle {
newpath
100 100 moveto
200 100 lineto
150 200 lineto
closepath
} def
% Use multiple times
triangle 1 0.8 0.6 setrgbcolor fill
triangle 0 0 0 setrgbcolor 2 setlinewidth stroke
1.6.2. Strategy 2: Layered Drawing
Draw from back to front:
% Background
0.9 0.9 0.9 setrgbcolor
0 0 612 792 rectfill
% Middle layer
0.8 0.8 1 setrgbcolor
100 100 200 200 rectfill
% Foreground
1 1 0.8 setrgbcolor
150 150 100 100 rectfill
% Outline on top
0 0 0 setrgbcolor
1 setlinewidth
100 100 200 200 rectstroke
1.7. Advanced Painting Techniques
1.7.1. Gradient Fills (Simulated)
% Simple horizontal gradient
/hgradient { % x y width height -> -
4 dict begin
/h exch def
/w exch def
/y exch def
/x exch def
0 1 w {
/i exch def
i w div setgray
x i add y 1 h rectfill
} for
end
} def
100 100 200 100 hgradient
Radial gradient (simplified):
/radialgradient { % x y maxRadius -> -
3 dict begin
/mr exch def
/cy exch def
/cx exch def
mr -1 0 {
/r exch def
r mr div setgray
newpath
cx cy r 0 360 arc
fill
} for
end
} def
200 200 100 radialgradient
1.7.2. Pattern Fills
Using clipping for pattern fills:
/dotPattern {
gsave
% Set up pattern
10 10 scale
0 1 10 {
/y exch def
0 1 10 {
/x exch def
x y 0.3 0 360 arc
fill
} for
} for
grestore
} def
% Apply pattern to shape
gsave
newpath
200 200 100 0 360 arc
clip
newpath
dotPattern
grestore
1.7.3. Multi-Pass Rendering
Render same path with different effects:
/myPath {
newpath
200 200 100 0 360 arc
} def
% Pass 1: Shadow
gsave
205 195 translate
myPath
0.7 setgray
fill
grestore
% Pass 2: Fill
myPath
1 0.8 0.6 setrgbcolor
fill
% Pass 3: Highlight
gsave
myPath
clip
newpath
190 220 30 0 360 arc
1 1 1 setrgbcolor
fill
grestore
% Pass 4: Outline
myPath
0 0 0 setrgbcolor
2 setlinewidth
stroke
1.7.4. Transparency Simulation
Use patterns or gray levels to simulate transparency:
% Crosshatch pattern for transparency
/transparent { % density (0-1)
1 dict begin
/d exch def
gsave
% Create fine crosshatch
0.5 setlinewidth
0 2 100 {
dup 0 moveto 100 lineto
} for
0 2 100 {
dup 0 exch moveto 100 exch lineto
} for
stroke
grestore
end
} def
1.8. Practical Painting Examples
1.8.1. Example 1: Button with Border
/button { % x y width height label
5 dict begin
/label exch def
/h exch def
/w exch def
/y exch def
/x exch def
% Background
0.9 0.9 0.9 setrgbcolor
x y w h rectfill
% Border
0 0 0 setrgbcolor
1 setlinewidth
x y w h rectstroke
% Text
/Helvetica findfont 12 scalefont setfont
x w 2 div add y h 2 div add moveto
label dup stringwidth pop 2 div neg 0 rmoveto
show
end
} def
100 100 100 40 (Click Me) button
1.8.2. Example 2: Pie Chart
/pieSlice { % cx cy r startAngle endAngle color
6 dict begin
/color exch def
/ea exch def
/sa exch def
/r exch def
/cy exch def
/cx exch def
newpath
cx cy moveto
cx cy r sa ea arc
closepath
% Fill
color aload pop setrgbcolor
gsave fill grestore
% Outline
0 0 0 setrgbcolor
1 setlinewidth
stroke
end
} def
% Draw pie chart
200 200 100 0 90 [1 0.8 0.8] pieSlice
200 200 100 90 180 [0.8 1 0.8] pieSlice
200 200 100 180 270 [0.8 0.8 1] pieSlice
200 200 100 270 360 [1 1 0.8] pieSlice
1.8.3. Example 3: Progress Bar
/progressBar { % x y width height percent
5 dict begin
/pct exch def
/h exch def
/w exch def
/y exch def
/x exch def
% Background
0.9 0.9 0.9 setrgbcolor
x y w h rectfill
% Progress
0.2 0.6 1 setrgbcolor
x y w pct mul h rectfill
% Border
0 0 0 setrgbcolor
1 setlinewidth
x y w h rectstroke
end
} def
100 100 200 30 0.75 progressBar % 75% complete
1.9. Best Practices
1.9.1. Separate Path and Paint
% Good: reusable path
/myShape {
newpath
100 100 moveto
200 200 lineto
} def
myShape stroke
myShape fill
% Bad: path and paint mixed
/myShape {
newpath
100 100 moveto
200 200 lineto
stroke % Can't reuse for fill
} def
1.9.2. Use Graphics State for Complex Painting
% Good: isolated state changes
gsave
1 0 0 setrgbcolor
3 setlinewidth
newpath
100 100 200 200 rlineto
stroke
grestore
% State restored
% Bad: permanent changes
1 0 0 setrgbcolor
3 setlinewidth
% ... affects everything after
1.10. Common Pitfalls
1.10.1. Path Consumed by Paint
% Wrong: path lost after stroke
newpath
100 100 moveto
200 200 lineto
stroke
fill % ERROR: nothing to fill
% Correct: save path or rebuild
/myPath {
newpath
100 100 moveto
200 200 lineto
} def
myPath stroke
myPath fill
1.11. Performance Considerations
1.11.1. Use Optimized Operators
% Faster: optimized operators
100 100 200 150 rectfill
100 100 200 150 rectstroke
% Slower: manual path construction
newpath
100 100 moveto
200 0 rlineto
0 150 rlineto
-200 0 rlineto
closepath
fill
newpath
100 100 moveto
200 0 rlineto
0 150 rlineto
-200 0 rlineto
closepath
stroke
1.12. See Also
-
Path Construction - Building paths
-
Graphics State - Controlling appearance
-
Painting Commands - Complete command reference
-
fill - Fill path
-
stroke - Stroke path
-
clip - Set clipping path
-
Patterns - Advanced pattern fills