Skip to content

Instantly share code, notes, and snippets.

@justvanrossum
Last active March 21, 2024 00:59
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justvanrossum/1f816294c55abd1e762976702b01e6b7 to your computer and use it in GitHub Desktop.
Save justvanrossum/1f816294c55abd1e762976702b01e6b7 to your computer and use it in GitHub Desktop.
Rename glyph names in OpenType feature files
from functools import singledispatch
from io import StringIO
from fontTools.feaLib import ast
from fontTools.feaLib.error import FeatureLibError
from fontTools.feaLib.parser import Parser
def renameGlyphs(feaSource, renameFunc, glyphNames=()):
features = Parser(StringIO(feaSource), glyphNames=glyphNames).parse()
renameStatement(features, renameFunc)
return features.asFea()
@singledispatch
def renameStatement(statement, renameFunc):
if not hasattr(statement, "__dict__"):
return
for value in statement.__dict__.values():
renameStatement(value, renameFunc)
@renameStatement.register
def _(statement: list | tuple, renameFunc):
for st in statement:
renameStatement(st, renameFunc)
@renameStatement.register
def _(
statement: ast.LanguageSystemStatement | ast.Comment | ast.FeatureLibLocation,
renameFunc,
):
pass
@renameStatement.register
def _(statement: ast.GlyphClass, renameFunc):
if hasattr(statement, "_has_been_renamed"):
return
statement._has_been_renamed = True
statement.glyphs = [renameFunc(g) for g in statement.glyphs]
statement.original = [
(renameFunc(start), renameFunc(end)) for start, end in statement.original
]
@renameStatement.register
def _(statement: ast.GlyphName, renameFunc):
if hasattr(statement, "_has_been_renamed"):
return
statement._has_been_renamed = True
statement.glyph = renameFunc(statement.glyph)
@renameStatement.register
def _(statement: ast.LigatureSubstStatement, renameFunc):
if hasattr(statement, "_has_been_renamed"):
return
statement._has_been_renamed = True
renameStatement(statement.prefix, renameFunc)
renameStatement(statement.glyphs, renameFunc)
renameStatement(statement.suffix, renameFunc)
if isinstance(statement.replacement, str):
statement.replacement = renameFunc(statement.replacement)
else:
renameStatement(statement.replacement, renameFunc)
@renameStatement.register
def _(statement: ast.MarkClass, renameFunc):
# Skip, MarkClassDefinition deals with this?
# Else we'll get RecursionError: maximum recursion depth exceeded
pass
if __name__ == "__main__":
import sys
def renameFunc(gn):
assert not gn.endswith(".XXX"), gn
return gn + ".XXX"
for p in sys.argv[1:]:
print("#", p)
with open(p) as f:
feaSource = f.read()
try:
newFea = renameGlyphs(feaSource, renameFunc)
except FeatureLibError as e:
print("**************** FeatureLibError", e)
print(newFea)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment