Add Anchor Points with FontForge Python

This article explains how to programmatically define and assign OpenType anchor points to glyphs using the FontForge Python module (fontforge). You will learn the required prerequisites, including setting up GPOS lookups and anchor classes, followed by the exact syntax and code required to place anchor points on base and mark glyphs.


Prerequisites: GPOS Lookup and Anchor Classes

In FontForge, an anchor point cannot exist in isolation; it must belong to an Anchor Class, which in turn belongs to an OpenType GPOS subtable (such as Mark-to-Base, Mark-to-Mark, or Cursive attachment).

Before attaching points to glyphs, you must ensure the corresponding GPOS lookup and anchor class exist in the font.

Step 1: Create the Lookup and Subtable

If your font does not already have an appropriate GPOS lookup, create one using font.addLookup() and font.addLookupSubtable():

import fontforge

font = fontforge.open("MyFont.sfd")

lookup_name = "mark_to_base_lookup"
subtable_name = "mark_to_base_subtable"

# Create a Mark-to-Base GPOS lookup
font.addLookup(lookup_name, "gpos_mark2base", (), (("latn", ("dflt",)),))

# Add a subtable to the lookup
font.addLookupSubtable(lookup_name, subtable_name)

Step 2: Define the Anchor Class

Once the subtable is defined, bind an anchor class (e.g., "top", "bottom") to that subtable using font.addAnchorClass():

anchor_class_name = "top"

# Associate the anchor class with the subtable
font.addAnchorClass(subtable_name, anchor_class_name)

Step 3: Add Anchor Points to Glyphs

Anchor points are placed using the glyph.addAnchorPoint() method.

Method signature:

glyph.addAnchorPoint(anchor_class_name, anchor_type, x, y, [lig_index])
  • anchor_class_name (str): The exact name of the anchor class defined in Step 2.
  • anchor_type (str): The point's role. Common values include:
    • "base": Used for standard base glyphs.
    • "mark": Used for combining diacritics.
    • "ligature": Used for composite glyphs requiring multiple attachment sites.
    • "basemark": Used when a mark acts as a base for another mark.
    • "entry" / "exit": Used for cursive script connections.
  • x (int/float): The horizontal coordinate of the point.
  • y (int/float): The vertical coordinate of the point.
  • lig_index (int, optional): Specifies which component of a ligature the anchor belongs to (0-indexed).

Complete Code Example

The following script sets up a lookup, defines an anchor class named "top", and positions coordinates on a base glyph (a) and a combining mark (acutecomb):

import fontforge

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

# 1. Create Lookup and Subtable
font.addLookup("m2b_lookup", "gpos_mark2base", (), (("latn", ("dflt",)),))
font.addLookupSubtable("m2b_lookup", "m2b_subtable")

# 2. Create Anchor Class
font.addAnchorClass("m2b_subtable", "top")

# 3. Add Base Anchor to glyph 'a'
base_glyph = font["a"]
base_glyph.addAnchorPoint("top", "base", 260, 480)

# 4. Add Mark Anchor to combining acute
mark_glyph = font["acutecomb"]
mark_glyph.addAnchorPoint("top", "mark", 0, 480)

# Save changes
font.save("MyFont.sfd")
font.close()

Managing Existing Anchor Points

To inspect or remove existing points programmatically:

  • Inspect points: Access glyph.anchorPoints, which returns a tuple of tuples containing point data:

    for pt in glyph.anchorPoints:
        name, pt_type, x, y, lig_index = pt
        print(name, pt_type, x, y)
  • Clear points: Assign an empty tuple to remove all anchor points from a glyph:

    glyph.anchorPoints = ()