Modify Outline Contours with FontForge Python

Yes, you can modify outline contours programmatically using FontForge's built-in Python module. The scripting interface provides granular access to font structures, allowing you to traverse glyph layers, manipulate existing contour points, generate new vector paths from scratch, and execute geometric transformations. This capability is essential for automated font mastering, batch path corrections, algorithmic glyph generation, and variable font manipulation.

Accessing Glyph Layers and Contours

FontForge stores vector outlines within layers. By default, the visible glyph outline is located on the foreground layer. To inspect or modify contours, you load the font, select the desired glyph, and extract its foreground layer:

import fontforge

# Open the font file
font = fontforge.open("MyFont.sfd")

# Access a specific glyph
glyph = font["A"]

# Extract the foreground layer containing contours
layer = glyph.foreground

A layer acts as an iterable collection of fontforge.contour objects, and each contour acts as an iterable collection of fontforge.point objects.

Inspecting and Modifying Points

Each point object on a contour contains spatial coordinates and curve metadata:

  • x and y: The Cartesian coordinates of the node.
  • on_curve: A boolean value indicating whether the point is an on-curve anchor point (True) or an off-curve Bézier control handle (False).
  • selected: A boolean flag for selection state in the GUI.

To modify existing coordinates, you can iterate through the contours and update point positions directly:

for contour in layer:
    for point in contour:
        # Move all points 10 units right and 5 units up
        point.x += 10
        point.y += 5

# Crucial step: Reassign the modified layer back to the glyph
glyph.foreground = layer

In FontForge's Python API, changes made to a layer's contours are not applied to the glyph automatically. You must explicitly reassign the layer variable back to glyph.foreground to commit the modifications.

Creating and Appending New Contours

You can construct contours programmatically and add them to existing glyphs using the fontforge.contour class:

# Create an empty contour
new_contour = fontforge.contour()

# Add points: moveTo, lineTo, or curveTo
new_contour.moveTo(100, 100)
new_contour.lineTo(200, 100)
new_contour.lineTo(200, 200)
new_contour.lineTo(100, 200)
new_contour.closed = True

# Append to the layer and save to the glyph
layer += new_contour
glyph.foreground = layer

For cubic Bézier curves, use new_contour.cubicTo((cp1_x, cp1_y), (cp2_x, cp2_y), (end_x, end_y)).

Transformations and Contour Utilities

FontForge includes utility methods that apply directly to contour objects:

  • Transformations: Apply affine transformations using the psMat module.

    import psMat
    matrix = psMat.scale(1.2)
    for contour in layer:
        contour.transform(matrix)
    glyph.foreground = layer
  • Direction and Winding: Correct path orientation (clockwise vs. counter-clockwise) using contour.direction = "clockwise" or glyph.correctDirection().

  • Simplification: Merge redundant points along paths using contour.simplify() to optimize file size and clean up generated vectors.