How to Extract Vector Coordinates in FontForge Python
Extracting vector path coordinates from glyphs is a common task in font engineering, procedural geometry, and digital fabrication. FontForge provides a built-in Python module that offers full programmatic access to glyph outlines, contours, and individual nodes. This guide demonstrates how to open a font, target specific glyphs, iterate through their vector paths, and store the resulting \((x, y)\) coordinate points into standard Python lists.
Understanding the FontForge Geometry Hierarchy
Vector glyphs in FontForge are structured hierarchically:
- Glyph: The character container containing multiple layers.
- Layer: Typically the
foregroundlayer, which contains the visual artwork. - Contour: An individual path (or outline) made up of interconnected points.
- Point: An individual node containing coordinates
(
x,y) and attributes such as whether it is on-curve or off-curve (control point).
Code Example: Extracting Coordinates to a List
You can run the following script using the FontForge Python
interpreter (fontforge -script script.py) or via an
external Python environment where the fontforge package is
compiled and available.
import fontforge
def extract_glyph_coordinates(font_path, glyph_name):
"""
Extracts vector coordinates from a specified glyph into a list.
Returns a list of contours, where each contour is a list of coordinate tuples.
"""
# Open the font file
font = fontforge.open(font_path)
# Verify the glyph exists
if glyph_name not in font:
font.close()
raise ValueError(f"Glyph '{glyph_name}' not found in {font_path}")
glyph = font[glyph_name]
# Access the active outline layer (foreground)
layer = glyph.foreground
glyph_paths = []
# Iterate through each contour in the layer
for contour in layer:
contour_points = []
# Iterate through each point in the contour
for point in contour:
# Extract x, y coordinates as a tuple
# You can also capture point.on_curve (True/False) if needed
contour_points.append((point.x, point.y))
glyph_paths.append(contour_points)
font.close()
return glyph_paths
# Example usage:
if __name__ == "__main__":
font_file = "MyFont.ttf"
target_glyph = "A"
coordinates = extract_glyph_coordinates(font_file, target_glyph)
print(coordinates)Parsing the Extracted Data
The resulting data structure is a nested list:
[
[(100.0, 0.0), (250.0, 700.0), (350.0, 700.0), (500.0, 0.0)], # Outer contour
[(200.0, 200.0), (300.0, 500.0), (400.0, 200.0)] # Inner contour (counter)
]- Nested List (Contours): Glyphs with cutouts (such as the letter "O" or "A") contain multiple contours. Preserving each contour as a sublist ensures the path winding and individual vector loops remain separated.
- Flattened List: If you only require a flat list of all vertices regardless of contour boundaries, you can flatten the output using list comprehension:
flat_coordinates = [point for contour in coordinates for point in contour]Capturing Point Types (On-Curve vs. Off-Curve)
Bézier splines rely on both on-curve anchor points and off-curve
control points. If your target application needs to distinguish between
anchor points and handles, include the on_curve
attribute:
contour_points.append({
"x": point.x,
"y": point.y,
"on_curve": point.on_curve
})This ensures complete vector fidelity when exporting geometry to formats such as SVG, JSON, or custom CAD workflows.