Skip to content
Just van Rossum edited this page Jun 7, 2021 · 22 revisions

This is a collection of handy code snippets for doing various small but important tasks in fonttools.

# To get access to an OpenType/TrueType font
from fontTools.ttLib import TTFont
font = TTFont(filename)

cmap: Things to do with glyph names, IDs, and Unicode codepoints

# How to use the cmap table to map characters to glyphs
cmap = font.getBestCmap()
glyphs = [cmap[ord(x)] for x in characters ]

# How to determine what Unicode points are covered by the font
codepoints = list(font.getBestCmap().keys())

# To go from glyph ID to glyph name
name = font.getGlyphOrder()[glyphid]

# To add/change/set a Unicode value of a specific glyph,
# you have to set a glyph for a unicode and not a unicode for a glyph.
# This example sets the 'a' unicode to the 'a' glyph
for table in font["cmap"].tables:
    # chr(97) == "a" 
    table.cmap[97] = "a"
# Note: this is only correct if there are only Unicode cmap subtables

name: How to find and edit name strings

# To get the font family name in a default language:
if not font["name"].getDebugName(16):
    familyName = font["name"].getDebugName(1)
else:
    familyName = font["name"].getDebugName(16)

# Names are set with the name table ID, platformID, platEncID, langID, e.g. "Windows" and English
font["name"].setName("Acme Font", 16, 3, 1, 0x409)

metrics: How to measure various things

# Find the width (horizontal advance) of a glyph
advance, lsb = font["hmtx"][glyphname]
# Find the bounding box of a glyph
def boundingbox(font, glyphname):
    metrics = {}
    if "glyf" in font:
      glyf = font["glyf"][glyphname]
      metrics["xMin"], metrics["xMax"], metrics["yMin"], metrics["yMax"] = glyf.xMin, glyf.xMax, glyf.yMin, glyf.yMax
    else:
      bounds = font.getGlyphSet()[glyphname]._glyph.calcBounds(font.getGlyphSet())
      metrics["xMin"], metrics["yMin"], metrics["xMax"], metrics["yMax"] = bounds
    return bounds

contours: How to find out if the outlines of two glyphs are the same

from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import RecordingPen

font = TTFont(filename)
cmap = font.getBestCmap()
gs = font.getGlyphSet()

g1 = gs[cmap[ord("A")]]
g2 = gs[cmap[ord("B")]]

p1 = RecordingPen()
p2 = RecordingPen()

g1.draw(p1)
g2.draw(p2)

print(p1.value == p2.value)

GDEF: How to interpret data from the GDEF table

def categorize_glyph(font,glyphname):
    classdefs = font["GDEF"].table.GlyphClassDef.classDefs
    assert(glyphname in classdefs)
    if classdefs[glyphname] == 1: return ("base",None)
    if classdefs[glyphname] == 2: return ("ligature",None)
    if classdefs[glyphname] == 3:
        # Now find attachment class
        markAttachClassDef = font["GDEF"].table.MarkAttachClassDef.classDefs
        return ("mark",markAttachClassDef[glyphname])
    if classdefs[glyphname] == 4: return ("component",None)
    raise ValueError

GSUB/GPOS table lookups

#Get a list of all lookups:
font["GSUB"].table.LookupList.Lookup
font["GPOS"].table.LookupList.Lookup

# Generally speaking the "stuff" you want is within a subtable:
lookup = font["GPOS"].table.LookupList.Lookup[0]
for s in lookup.SubTable:
    # To get which glyphs this applies to, you often want to try
    appliesTo = s.Coverage.glyphs
    pass

# Find the GPOS CursiveAttachment lookups
cursives = filter(lambda x: x.LookupType==3, font["GPOS"].table.LookupList.Lookup)
anchors = {}
for c in cursives:
    for s in c.SubTable:
        for glyph, record in zip(s.Coverage.glyphs, s.EntryExitRecord):
            anchors[glyph] = []
            if record.EntryAnchor:
                anchors[glyph].append( (record.EntryAnchor.XCoordinate, record.EntryAnchor.YCoordinate) )
            if record.ExitAnchor:
                anchors[glyph].append( (record.ExitAnchor.XCoordinate, record.ExitAnchor.YCoordinate) )
return anchors

Paths

# How to access a glyph's path information by name (TT outlines):
g = font["glyf"][glyphname]

# How to access a glyph's path information by glyph ID (TT outlines):
glyf = font["glyf"]
g = glyf[glyf.getGlyphName(glyphid)]

feaLib

# How to parse a fea file into an abstract syntax tree
from fontTools.feaLib.parser import Parser
parsetree = Parser(file, font.getReverseGlyphMap()).parse()
# (Second argument can be empty if you're "purely" parsing FEA,
# but if you pass it then additional checking/disambiguation is done.)

Dump an OT Table structure to XML string

from fontTools.misc.testTools import getXML
"\n".join(getXML(thing.toXML))

Table manipulation

# Add a new table to a font, if it does not exist
from fontTools.ttLib import newTable
if not "STAT" in font:
   font["STAT"] = newTable("STAT")
# ... do stuff with font["STAT"] ...

Draw with Drawbot