From a79285113181d020e196e4bdb12a1577ac51e9dd Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 27 Jun 2023 12:02:00 +0100 Subject: [PATCH] add removeUnusedNames staticmethod to name table --- Lib/fontTools/ttLib/tables/_n_a_m_e.py | 72 +++++++++++++++++++++++ Snippets/clean-nametable.py | 79 +------------------------- 2 files changed, 73 insertions(+), 78 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py index 320cfa820..8afb7ab2d 100644 --- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py +++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py @@ -11,6 +11,10 @@ from fontTools.misc.textTools import ( ) from fontTools.misc.encodingTools import getEncoding from fontTools.ttLib import newTable +from fontTools.ttLib.ttVisitor import TTVisitor +from fontTools import ttLib +import fontTools.ttLib.tables.otTables as otTables +from fontTools.ttLib.tables import C_P_A_L_ from . import DefaultTable import struct import logging @@ -223,6 +227,28 @@ class table__n_a_m_e(DefaultTable.DefaultTable): ) ] + @staticmethod + def removeUnusedNames(ttFont): + """Remove any name records which are not in NameID range 0-255 and not utilized + within the font itself.""" + visitor = NameRecordVisitor() + visitor.visit(ttFont) + toDelete = set() + for record in ttFont["name"].names: + # Name IDs 26 to 255, inclusive, are reserved for future standard names. + # https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids + if record.nameID < 256: + continue + if record.nameID not in visitor.seen: + toDelete.add(record.nameID) + + if not toDelete: + log.info("Name table has no redundant records, skipping") + return + log.info(f"Deleting name records with NameIDs {toDelete}") + for nameID in toDelete: + ttFont["name"].removeNames(nameID) + def _findUnusedNameID(self, minNameID=256): """Finds an unused name id. @@ -1138,3 +1164,49 @@ _MAC_LANGUAGE_TO_SCRIPT = { 150: 0, # langAzerbaijanRoman → smRoman 151: 0, # langNynorsk → smRoman } + + +class NameRecordVisitor(TTVisitor): + def __init__(self): + self.seen = set() + + +@NameRecordVisitor.register_attrs( + ( + (otTables.FeatureParamsSize, ("SubfamilyID", "SubfamilyNameID")), + (otTables.FeatureParamsStylisticSet, ("UINameID",)), + ( + otTables.FeatureParamsCharacterVariants, + ( + "FeatUILabelNameID", + "FeatUITooltipTextNameID", + "SampleTextNameID", + "FirstParamUILabelNameID", + ), + ), + (otTables.STAT, ("ElidedFallbackNameID",)), + (otTables.AxisRecord, ("AxisNameID",)), + (otTables.AxisValue, ("ValueNameID",)), + (otTables.FeatureName, ("FeatureNameID",)), + (otTables.Setting, ("SettingNameID",)), + ) +) +def visit(visitor, obj, attr, value): + visitor.seen.add(value) + + +@NameRecordVisitor.register(ttLib.getTableClass("fvar")) +def visit(visitor, obj): + for inst in obj.instances: + visitor.seen.add(inst.postscriptNameID) + visitor.seen.add(inst.subfamilyNameID) + + for axis in obj.axes: + visitor.seen.add(axis.axisNameID) + + +@NameRecordVisitor.register(ttLib.getTableClass("CPAL")) +def visit(visitor, obj): + for nameID in obj.paletteLabels: + if nameID != C_P_A_L_.table_C_P_A_L_.NO_NAME_ID: + visitor.seen.add(nameID) diff --git a/Snippets/clean-nametable.py b/Snippets/clean-nametable.py index e1b6136a0..2c7b42c32 100644 --- a/Snippets/clean-nametable.py +++ b/Snippets/clean-nametable.py @@ -1,84 +1,7 @@ #!/usr/bin/env python3 """Script to remove redundant font name table records.""" -from fontTools.ttLib.ttVisitor import TTVisitor -from fontTools import ttLib from fontTools.ttLib import TTFont -import fontTools.ttLib.tables.otTables as otTables -from fontTools.ttLib.tables import C_P_A_L_ import sys -import logging - - -logger = logging.getLogger() - - -class NameRecordVisitor(TTVisitor): - def __init__(self): - self.seen = set() - - def removeUnusedNameRecords(self, font): - self.visit(font) - toDelete = set() - for record in font["name"].names: - # Name IDs 26 to 255, inclusive, are reserved for future standard names. - # https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids - if record.nameID < 256: - continue - if record.nameID not in self.seen: - toDelete.add(record.nameID) - - if not toDelete: - logger.info("Name table has no redundant records, skipping") - return - logger.info(f"Deleting name records with NameIDs {toDelete}") - for nameID in toDelete: - font["name"].removeNames(nameID) - - -@NameRecordVisitor.register_attrs( - ( - (otTables.FeatureParamsSize, ("SubfamilyID", "SubfamilyNameID")), - (otTables.FeatureParamsStylisticSet, ("UINameID",)), - ( - otTables.FeatureParamsCharacterVariants, - ( - "FeatUILabelNameID", - "FeatUITooltipTextNameID", - "SampleTextNameID", - "FirstParamUILabelNameID", - ), - ), - (otTables.STAT, ("ElidedFallbackNameID",)), - (otTables.AxisRecord, ("AxisNameID",)), - (otTables.AxisValue, ("ValueNameID",)), - (otTables.FeatureName, ("FeatureNameID",)), - (otTables.Setting, ("SettingNameID",)), - ) -) -def visit(visitor, obj, attr, value): - visitor.seen.add(value) - - -@NameRecordVisitor.register(ttLib.getTableClass("fvar")) -def visit(visitor, obj): - for inst in obj.instances: - visitor.seen.add(inst.postscriptNameID) - visitor.seen.add(inst.subfamilyNameID) - - for axis in obj.axes: - visitor.seen.add(axis.axisNameID) - - -@NameRecordVisitor.register(ttLib.getTableClass("CPAL")) -def visit(visitor, obj): - for nameID in obj.paletteLabels: - if nameID != C_P_A_L_.table_C_P_A_L_.NO_NAME_ID: - visitor.seen.add(nameID) - - -def removeUnusedNameRecords(ttFont): - visitor = NameRecordVisitor() - visitor.removeUnusedNameRecords(ttFont) def main(): @@ -86,7 +9,7 @@ def main(): print("Usage: python nameClean.py font.ttf out.ttf") sys.exit() font = TTFont(sys.argv[1]) - removeUnusedNameRecords(font) + font["name"].removeUnusedNames(font) font.save(sys.argv[2])