import sys import os import argparse from typing import Tuple, List from fontTools import ttLib from fontTools.ttLib import TTFont from fontTools.varLib.mutator import instantiateVariableFont from fontTools.ttLib.tables._f_v_a_r import NamedInstance from fontTools.ttLib.tables._n_a_m_e import NameRecord def camelString(text: str): return text.replace(' ', '') def sortNamingTable(names: List[NameRecord]) -> List[NameRecord]: """ Parameters: names (List[NameRecord]): Naming table Returns: The sorted naming table. Based on FontConfig: - https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/d863f6778915f7dd224c98c814247ec292904e30/src/fcfreetype.c#L1127-1140 """ def isEnglish(name: NameRecord) -> bool: # From: https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/d863f6778915f7dd224c98c814247ec292904e30/src/fcfreetype.c#L1111-1125 return (name.platformID, name.langID) in ((1, 0), (3, 0x409)) # From: https://github.com/freetype/freetype/blob/b98dd169a1823485e35b3007ce707a6712dcd525/include/freetype/ttnameid.h#L86-L91 PLATFORM_ID_APPLE_UNICODE = 0 PLATFORM_ID_MACINTOSH = 1 PLATFORM_ID_ISO = 2 PLATFORM_ID_MICROSOFT = 3 # From: https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/d863f6778915f7dd224c98c814247ec292904e30/src/fcfreetype.c#L1078 PLATFORM_ID_ORDER = [ PLATFORM_ID_MICROSOFT, PLATFORM_ID_APPLE_UNICODE, PLATFORM_ID_MACINTOSH, PLATFORM_ID_ISO, ] return sorted(names, key=lambda name: (PLATFORM_ID_ORDER.index(name.platformID), name.nameID, name.platEncID, -isEnglish(name), name.langID)) def getFirstDecodedName(nameID: int, names: List[NameRecord]) -> str: """ Parameters: names (List[NameRecord]): Naming table Returns: The first decoded name. """ names = sortNamingTable(names) for name in names: if name.nameID != nameID: continue try: unistr = name.toUnicode() except UnicodeDecodeError: continue return unistr def splitNamedInstance(font: TTFont, output_dir: str) -> List[Tuple[str, str]]: """ Parameters: font (TTFont): A font Returns: An dictionnary. The key is the font name and the value an NamedInstance The dictionnary support the R/B/I/BI family model: https://learn.microsoft.com/en-us/typography/opentype/spec/stat#alternate-font-family-models """ if "fvar" not in font: raise Exception("The font is not an Variable Font.") # Read the note about the R/B/I/BI model: https://learn.microsoft.com/en-us/typography/opentype/spec/stat#alternate-font-family-models # INVALID_STYLE = ("regular", "bold", "italic", "oblique") family_name = getFirstDecodedName(16, font['name'].names) # If nameID 16 is None, try nameID 1https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#terminology if family_name is None: family_name = getFirstDecodedName(1, font['name'].names) instances_info: List[Tuple[str, str]] = [] for instance in font["fvar"].instances: instance_name = getFirstDecodedName(instance.subfamilyNameID, font['name'].names) font_name = f"{family_name} {instance_name}" postscript_name = camelString(family_name) + '-' + camelString(instance_name) # instance_font = instantiateVariableFont(font, instance.coordinates) # name_table = instance_font['name'] # langID: int # platformID: int # platEncID: int # # Take "Arial Black" as reference. # for record in name_table.names: # if record.nameID == 1: # family name # name_table.setName(font_name, record.nameID, record.platformID, record.platEncID, record.langID) # platEncID = record.platEncID # platformID = record.platformID # langID = record.langID # if record.nameID == 2: # style name # name_table.setName('Regular', record.nameID, record.platformID, record.platEncID, record.langID) # if record.nameID == 4: # full name # name_table.setName(font_name, record.nameID, record.platformID, record.platEncID, record.langID) # if record.nameID == 6: # PostScript Name # name_table.setName(postscript_name, record.nameID, record.platformID, record.platEncID, record.langID) # name_table.setName(family_name, 16, platformID, platEncID, langID) # name_table.setName(instance_name, 17, platformID, platEncID, langID) # output_path = os.path.join(output_dir, f"{font_name}.ttf") # instance_font.save(output_path) # print(font_name + ' saved') instances_info.append((font_name, postscript_name)) return instances_info def generateSampleHtml(instances_info: List[Tuple[str, str]], output_dir: str): html_content = """
""" for font_name, postscript_name in instances_info: html_content += f'