Table of Contents

1. Path Construction

Paths are the fundamental building blocks of all vector graphics in PostScript. Understanding how to construct and manipulate paths is essential for creating any visual output.

1.1. Overview

A path is a mathematical description of shapes made up of straight lines and curves. Paths exist in user space and can be:

  • Stroked - Drawn as an outline

  • Filled - Filled with color

  • Clipped - Used to restrict drawing area

  • Converted - Used as character outlines

Paths are built step-by-step by adding elements, then rendered with painting operators.

1.2. Path Concepts

1.2.1. The Current Path

PostScript maintains a single current path that you build incrementally:

newpath           % Clear current path
100 100 moveto    % Start new subpath
200 100 lineto    % Add line segment
200 200 lineto    % Add another segment
% Path exists but is not visible yet
stroke            % Now render the path

1.2.2. The Current Point

The current point is the end of the last path segment:

newpath
100 100 moveto    % Current point: (100, 100)
200 100 lineto    % Current point: (200, 100)
200 200 lineto    % Current point: (200, 200)

currentpoint      % Returns: 200 200

1.2.3. Subpaths

A path can contain multiple disconnected subpaths:

newpath
% First subpath
100 100 moveto
200 100 lineto
200 200 lineto

% Second subpath
300 300 moveto
400 300 lineto
400 400 lineto

stroke  % Both subpaths are stroked

1.2.4. Open vs. Closed Paths

Paths can be open or closed:

% Open path
newpath
100 100 moveto
200 100 lineto
200 200 lineto
stroke  % Does not connect back to start

% Closed path
newpath
100 100 moveto
200 100 lineto
200 200 lineto
closepath  % Connects back to (100, 100)
stroke

1.3. Basic Path Construction

1.3.1. Starting a New Path: newpath

Clears the current path and resets the current point:

newpath  % Start with clean slate
% currentpoint would cause error - no current point yet

1.3.2. Moving: moveto

Begins a new subpath at specified coordinates:

newpath
100 100 moveto    % Absolute coordinates
% Current point is now (100, 100)

1.3.3. Relative Moving: rmoveto

Moves relative to current point:

newpath
100 100 moveto    % Start at (100, 100)
50 0 rmoveto      % Move to (150, 100)
0 50 rmoveto      % Move to (150, 150)

1.3.4. Drawing Lines: lineto

Adds a straight line from current point to specified coordinates:

newpath
100 100 moveto
200 100 lineto    % Horizontal line
200 200 lineto    % Vertical line
100 200 lineto    % Back horizontally
stroke

1.3.5. Relative Lines: rlineto

Adds line relative to current point:

newpath
100 100 moveto
100 0 rlineto     % 100 points right
0 100 rlineto     % 100 points up
-100 0 rlineto    % 100 points left
closepath
stroke

1.3.6. Closing Paths: closepath

Adds a line from current point back to subpath start:

newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath         % Closes back to (100, 100)
% Forms a complete rectangle
stroke

1.4. Curved Path Segments

1.4.1. Bézier Curves: curveto

Creates a cubic Bézier curve using two control points:

% curveto: x1 y1 x2 y2 x3 y3 curveto
% (x1,y1) = first control point
% (x2,y2) = second control point
% (x3,y3) = end point

newpath
100 100 moveto
150 50 200 50 250 100 curveto  % S-curve
stroke

Smooth curve example:

newpath
50 100 moveto
100 50 150 150 200 100 curveto   % Wave
200 100 moveto
250 50 300 150 350 100 curveto   % Another wave
stroke

1.4.2. Relative Bézier Curves: rcurveto

Bézier curve with relative coordinates:

newpath
100 100 moveto
50 -50 100 -50 150 0 rcurveto  % All relative to (100, 100)
stroke

1.4.3. Arc Segments: arc

Creates a circular arc (counter-clockwise):

% arc: x y radius startAngle endAngle arc
newpath
200 200 50 0 360 arc  % Complete circle
stroke

newpath
200 200 50 0 90 arc   % Quarter circle
stroke

Important: arc adds a line from current point to arc start if current point exists:

newpath
100 100 moveto
200 200 50 0 180 arc  % Line from (100,100) to arc start
stroke

1.4.4. Clockwise Arc: arcn

Creates arc in clockwise direction:

% arcn: x y radius startAngle endAngle arcn
newpath
200 200 50 0 90 arcn  % Clockwise from 0° to 90°
stroke

1.4.5. Arc Through Point: arct

Creates arc tangent to two lines:

% arct: x1 y1 x2 y2 radius arct
% Creates arc of given radius tangent to:
% - Line from current point to (x1,y1)
% - Line from (x1,y1) to (x2,y2)

newpath
100 100 moveto
200 100 300 200 20 arct  % Rounded corner
300 200 lineto
stroke

1.4.6. Arc To Point: arcto

Similar to arct but returns tangent points:

% arcto: x1 y1 x2 y2 radius arcto -> xt1 yt1 xt2 yt2
newpath
100 100 moveto
200 100 200 200 30 arcto  % Returns 4 values
pop pop pop pop            % Discard if not needed
stroke

1.5. Common Path Shapes

1.5.1. Rectangle

Building a rectangle manually:

% Rectangle: x y width height
/rectangle {
  4 dict begin
    /h exch def
    /w exch def
    /y exch def
    /x exch def

    newpath
    x y moveto
    w 0 rlineto
    0 h rlineto
    w neg 0 rlineto
    closepath
  end
} def

100 100 200 150 rectangle
stroke

1.5.2. Circle

% Circle: x y radius
/circle {
  0 360 arc
} def

200 200 50 circle
stroke

1.5.3. Rounded Rectangle

% Rounded rectangle: x y width height radius
/roundrect {
  5 dict begin
    /r exch def
    /h exch def
    /w exch def
    /y exch def
    /x exch def

    newpath
    x r add y moveto
    x w add y x w add y h add r arcto 4 {pop} repeat
    x w add y h add x y h add r arcto 4 {pop} repeat
    x y h add x y r arcto 4 {pop} repeat
    x y x w add y r arcto 4 {pop} repeat
    closepath
  end
} def

100 100 200 150 20 roundrect
stroke

1.5.4. Regular Polygon

% Regular polygon: x y radius sides startAngle
/polygon {
  5 dict begin
    /angle exch def
    /n exch def
    /r exch def
    /y exch def
    /x exch def

    newpath
    /angleStep 360 n div def
    x y moveto
    x r angle cos mul add y r angle sin mul add moveto
    1 1 n {
      pop
      /angle angle angleStep add def
      x r angle cos mul add y r angle sin mul add lineto
    } for
    closepath
  end
} def

% Pentagon
200 200 50 5 90 polygon
stroke

% Hexagon
300 300 50 6 0 polygon
stroke

1.5.5. Star

% 5-pointed star: x y outerRadius innerRadius
/star5 {
  4 dict begin
    /ir exch def
    /or exch def
    /y exch def
    /x exch def

    newpath
    /angle 90 def
    x or angle cos mul add y or angle sin mul add moveto

    0 1 4 {
      pop
      /angle angle 72 sub def
      x ir angle 36 sub cos mul add y ir angle 36 sub sin mul add lineto
      x or angle cos mul add y or angle sin mul add lineto
    } for
    closepath
  end
} def

200 200 80 30 star5
stroke

1.6. Path Manipulation

1.6.1. Reversing Path Direction: reversepath

Reverses the direction of the current path:

newpath
100 100 moveto
200 100 lineto
200 200 lineto
reversepath  % Now goes: (200,200)->(200,100)->(100,100)

1.6.2. Converting Stroke to Path: strokepath

Converts stroked outline to a fillable path:

newpath
100 100 moveto
200 200 lineto
10 setlinewidth
strokepath    % Path is now the outline of the stroke
fill          % Fill the stroke outline

1.6.3. Flattening Curves: flattenpath

Converts curves to straight line segments:

newpath
100 100 moveto
200 100 250 200 150 200 curveto
flattenpath   % Curve becomes multiple line segments

1.6.4. Getting Path Information: pathforall

Iterate through path segments:

% pathforall: moveto lineto curveto closepath pathforall
% Executes procedures for each path element

{  % moveto procedure
  (Move to: ) print exch =string cvs print ( ) print =string cvs print ()
}
{  % lineto procedure
  (Line to: ) print exch =string cvs print ( ) print =string cvs print ()
}
{  % curveto procedure
  (Curve) print
}
{  % closepath procedure
  (Close) print
}
pathforall

1.6.5. Path Bounding Box: pathbbox

Gets the bounding box of the current path:

newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath

pathbbox  % Returns: llx lly urx ury
% Stack: 100 100 200 200

1.7. Advanced Path Techniques

1.7.1. Compound Paths with Holes

Create shapes with holes using subpaths:

% Outer rectangle
newpath
100 100 moveto
300 100 lineto
300 300 lineto
100 300 lineto
closepath

% Inner rectangle (hole) - reverse direction
200 200 moveto
200 150 lineto
150 150 lineto
150 200 lineto
closepath

% Fill with even-odd rule
eofill

1.7.2. Path Clipping

Use paths to define clipping regions:

gsave
  % Create clipping path
  newpath
  200 200 100 0 360 arc
  clip
  newpath  % Start new path for drawing

  % Only visible inside circle
  100 100 200 200 rectfill
grestore

1.7.3. Text as Paths: charpath

Convert text to paths:

newpath
100 100 moveto
/Helvetica-Bold findfont 72 scalefont setfont
(HELLO) true charpath  % true = stroke outline
2 setlinewidth
stroke

% Or fill the character paths
newpath
100 200 moveto
(WORLD) false charpath  % false = fill outline
fill

1.7.4. Dashed Paths

Create dashed lines with custom patterns:

newpath
100 100 moveto
300 100 lineto

% Set dash pattern
[10 5 2 5] 0 setdash
2 setlinewidth
stroke

1.8. Path Construction Patterns

1.8.1. Pattern 1: Path Builder

Construct complex paths programmatically:

/pathBuilder {
  10 dict begin
    /segments exch def
    newpath
    segments {
      aload pop
      /type exch def
      type (M) eq { moveto } if
      type (L) eq { lineto } if
      type (C) eq { curveto } if
      type (Z) eq { closepath } if
    } forall
  end
} def

% Usage
[
  [(M) 100 100]
  [(L) 200 100]
  [(L) 200 200]
  [(L) 100 200]
  [(Z)]
] pathBuilder
stroke

1.8.2. Pattern 2: Parametric Curves

Generate curves from mathematical functions:

% Draw sine wave
/sineWave {
  newpath
  0 1 360 {
    /x exch def
    x 200 div dup
    100 mul sin 50 mul
    200 add exch 100 add exch
    x 0 eq { moveto } { lineto } ifelse
  } for
} def

sineWave
stroke

1.8.3. Pattern 3: Morphing Shapes

Interpolate between two paths:

% Morph from circle to square
/morphShape {  % t (0-1)
  1 dict begin
    /t exch def
    newpath

    0 15 345 {
      /angle exch def

      % Circle point
      /cx 100 angle cos mul def
      /cy 100 angle sin mul def

      % Square point (approximate)
      /sx 100 angle cos mul abs angle cos mul def
      /sy 100 angle sin mul abs angle sin mul def

      % Interpolate
      cx 1 t sub mul sx t mul add 200 add
      cy 1 t sub mul sy t mul add 200 add

      angle 0 eq { moveto } { lineto } ifelse
    } for
    closepath
  end
} def

% Draw morphing sequence
0 0.2 1 {
  gsave
    0 morphShape
    stroke
  grestore
} for

1.8.4. Pattern 4: Constrained Paths

Build paths with constraints:

% Path that stays within bounds
/constrainedLine {  % x1 y1 x2 y2 minX minY maxX maxY
  8 dict begin
    /maxY exch def /maxX exch def
    /minY exch def /minX exch def
    /y2 exch def /x2 exch def
    /y1 exch def /x1 exch def

    % Clamp endpoints
    /x1 x1 minX maxX clamp def
    /y1 y1 minY maxY clamp def
    /x2 x2 minX maxX clamp def
    /y2 y2 minY maxY clamp def

    newpath
    x1 y1 moveto
    x2 y2 lineto
  end
} def

/clamp {  % value min max -> clampedValue
  2 copy gt { exch } if pop
  2 copy lt { exch } if pop
} def

1.9. Practical Examples

1.9.1. Example 1: Grid Pattern

% Draw a grid
/drawGrid {  % spacing width height
  3 dict begin
    /h exch def
    /w exch def
    /s exch def

    0.5 setgray
    0.5 setlinewidth

    % Vertical lines
    0 s w {
      dup 0 moveto h lineto stroke
    } for

    % Horizontal lines
    0 s h {
      dup 0 exch moveto w exch lineto stroke
    } for
  end
} def

20 400 400 drawGrid

1.9.2. Example 2: Flow Chart Connector

% Connector with rounded corners
/connector {  % x1 y1 x2 y2 radius
  5 dict begin
    /r exch def
    /y2 exch def /x2 exch def
    /y1 exch def /x1 exch def

    newpath
    x1 y1 moveto

    % Horizontal then vertical with rounded corner
    x2 r sub y1 lineto
    x2 y1 x2 y2 r sub r arct 4 {pop} repeat
    x2 y2 lineto
  end
} def

100 100 300 200 15 connector
stroke

1.9.3. Example 3: Pie Chart Slice

% Pie slice: centerX centerY radius startAngle endAngle
/pieSlice {
  5 dict begin
    /endAngle exch def
    /startAngle exch def
    /r exch def
    /cy exch def
    /cx exch def

    newpath
    cx cy moveto
    cx cy r startAngle endAngle arc
    closepath
  end
} def

% Draw pie chart
200 200 100 0 60 pieSlice fill
200 200 100 60 180 pieSlice fill
200 200 100 180 360 pieSlice fill

1.9.4. Example 4: Arrow Path

% Arrow from (x1,y1) to (x2,y2) with given head size
/arrow {  % x1 y1 x2 y2 headSize
  5 dict begin
    /hs exch def
    /y2 exch def /x2 exch def
    /y1 exch def /x1 exch def

    % Calculate angle
    /dx x2 x1 sub def
    /dy y2 y1 sub def
    /angle dy dx atan def

    % Draw shaft
    newpath
    x1 y1 moveto
    x2 y2 lineto
    stroke

    % Draw head
    newpath
    x2 y2 moveto
    x2 hs angle 180 add 30 add cos mul add
    y2 hs angle 180 add 30 add sin mul add lineto
    x2 y2 lineto
    x2 hs angle 180 add 30 sub cos mul add
    y2 hs angle 180 add 30 sub sin mul add lineto
    closepath
    fill
  end
} def

100 100 300 200 15 arrow

1.10. Best Practices

1.10.1. Always Start with newpath

% Good
newpath
100 100 moveto
200 200 lineto
stroke

% Risky - might add to existing path
100 100 moveto
200 200 lineto
stroke

1.10.2. Close Paths When Appropriate

% Good: explicitly closed
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath
fill

% OK for strokes but not ideal for fills
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
fill  % PostScript closes automatically for fill

1.10.3. Use Relative Coordinates for Reusability

% Good: reusable shape
/square {  % size
  dup 0 rlineto
  dup 0 exch rlineto
  neg dup rlineto
  0 exch rlineto
  closepath
} def

% Use anywhere
newpath 100 100 moveto 50 square

% Bad: hardcoded positions
/square {
  100 100 moveto
  150 100 lineto
  % ... not reusable
} def

1.10.4. Separate Path Construction from Rendering

% Good: path construction separate from rendering
/buildCircle {
  newpath
  0 360 arc
} def

/renderFilled {
  gsave
    0.8 0.2 0.2 setrgbcolor
    fill
  grestore
} def

/renderStroked {
  gsave
    0 0 0 setrgbcolor
    2 setlinewidth
    stroke
  grestore
} def

% Use flexibly
100 100 50 buildCircle renderFilled
200 200 50 buildCircle renderStroked

1.11. Common Pitfalls

1.11.1. Forgetting Current Point

% Wrong: no current point
newpath
lineto  % ERROR: nocurrentpoint

% Correct
newpath
100 100 moveto
200 200 lineto

1.11.2. Not Clearing Path with newpath

% Wrong: paths accumulate
100 100 moveto
200 100 lineto
stroke

300 300 moveto  % Still part of previous path!
400 400 lineto
stroke  % Draws both paths

% Correct
newpath
100 100 moveto
200 100 lineto
stroke

newpath  % Clear previous path
300 300 moveto
400 400 lineto
stroke

1.11.3. Incorrect Arc Direction

% Creates unexpected results
newpath
200 200 100 90 0 arc  % Draws 270° arc (90° to 0° counterclockwise)

% Use arcn for clockwise
newpath
200 200 100 90 0 arcn  % Draws 90° arc clockwise

1.12. See Also


Back to top

Copyright © 2025 Ribose. PostScript is a trademark of Adobe. Distributed under the MIT License.