def __init__(self, file=None, shareTables=False, **kwargs): fonts = self.fonts = [] if file is None: return assert 'fontNumber' not in kwargs, kwargs closeStream = False if not hasattr(file, "read"): file = open(file, "rb") closeStream = True tableCache = {} if shareTables else None header = readTTCHeader(file) for i in range(header.numFonts): font = TTFont(file, fontNumber=i, _tableCache=tableCache, **kwargs) fonts.append(font) # don't close file if lazy=True, as the TTFont hold a reference to the original # file; the file will be closed once the TTFonts are closed in the # TTCollection.close(). We still want to close the file if lazy is None or # False, because in that case the TTFont no longer need the original file # and we want to avoid 'ResourceWarning: unclosed file'. if not kwargs.get("lazy") and closeStream: file.close()
def __init__(self, file=None, shareTables=False, **kwargs): fonts = self.fonts = [] if file is None: return assert 'fontNumber' not in kwargs, kwargs if not hasattr(file, "read"): file = open(file, "rb") tableCache = {} if shareTables else None header = readTTCHeader(file) for i in range(header.numFonts): font = TTFont(file, fontNumber=i, _tableCache=tableCache, **kwargs) fonts.append(font)
def numFontsTTC(fontPath: PathLike): from fontTools.ttLib.sfnt import readTTCHeader with open(fontPath, "rb") as f: header = readTTCHeader(f) return header.numFonts
def dir_font_info( fonts_dir: str = "", info_file_dir: str = "font_info", info_filename: str = "font_info.json", ): available_font_extension_set: set = [ ".ttf", ".ttc", ".otf", ".woff", ".woff2", ] if not fonts_dir: if os.name != "nt": raise OSError("only available in Windows") windows_fonts_dir: str = os.path.join(os.environ["SystemRoot"], "Fonts") fonts_dir = windows_fonts_dir FONTTOOLS_NAME_ID_FAMILY = 1 FONTTOOLS_NAME_ID_NAME = 4 FONTTOOLS_PLATFORM_ID_WINDOWS = 3 LCID_EN = 1033 update_font_info_bool: bool = True info_file_dir = os.path.abspath(info_file_dir) if not os.path.isdir(info_file_dir): os.makedirs(info_file_dir) info_filepath: str = os.path.join(info_file_dir, info_filename) font_info_dict: dict = dict(font_info_list=[]) if os.path.isfile(info_filepath): font_info_dict = load_config(info_filepath) all_font_filename_list: list = [ filename for filename in os.listdir(fonts_dir) if any(filename.lower().endswith(ext) for ext in available_font_extension_set) ] all_font_filepath_list: list = [ os.path.join(fonts_dir, filename) for filename in all_font_filename_list ] info_existed_font_filepath_set: set = set( single_font_info_dict["filepath"] for single_font_info_dict in font_info_dict["font_info_list"]) if set(all_font_filepath_list) == info_existed_font_filepath_set: update_font_info_bool = False if update_font_info_bool: font_info_dict = dict(font_info_list=[]) for font_path in all_font_filepath_list: font_num: int = 1 with open(font_path, "rb") as file: font_sfnt_version: str = "" file.seek(0) font_sfnt_version = file.read(4) file.seek(0) if font_sfnt_version == b"ttcf": header = readTTCHeader(file) font_num = header.numFonts file.seek(header.offsetTable[0]) data = file.read(sfntDirectorySize) if len(data) != sfntDirectorySize: font_error_warning_str: str = ( f"{font_path} is Not a Font Collection " "(not enough data), skip.") g_logger.log(logging.WARNING, font_error_warning_str) warnings.warn(font_error_warning_str, RuntimeWarning) continue data_dict: dict = sstruct.unpack(sfntDirectoryFormat, data) font_sfnt_version = data_dict["sfntVersion"] elif font_sfnt_version == b"wOFF": font_num = 1 data = file.read(woffDirectorySize) if len(data) != woffDirectorySize: font_error_warning_str: str = ( f"{font_path} is Not a WOFF font " "(not enough data), skip.") g_logger.log(logging.WARNING, font_error_warning_str) warnings.warn(font_error_warning_str, RuntimeWarning) continue data_dict: dict = sstruct.unpack(woffDirectoryFormat, data) font_sfnt_version = data_dict["sfntVersion"] else: font_num = 1 data = file.read(sfntDirectorySize) if len(data) != sfntDirectorySize: font_error_warning_str: str = ( f"{font_path} is Not a TrueType or OpenType font " "(not enough data), skip.") g_logger.log(logging.WARNING, font_error_warning_str) warnings.warn(font_error_warning_str, RuntimeWarning) continue data_dict: dict = sstruct.unpack(sfntDirectoryFormat, data) font_sfnt_version = data_dict["sfntVersion"] font_sfnt_version_tag = Tag(font_sfnt_version) if font_sfnt_version_tag not in ( "\x00\x01\x00\x00", "OTTO", "true", ): print(font_sfnt_version) print(font_sfnt_version_tag) font_error_warning_str: str = ( f"{font_path} is Not a TrueType or OpenType font " "(bad sfntVersion), skip.") g_logger.log(logging.WARNING, font_error_warning_str) warnings.warn(font_error_warning_str, RuntimeWarning) continue for font_num_index in range(font_num): tt_font = ttLib.TTFont(font_path, fontNumber=font_num_index) family_list: list = [] for name_record in tt_font["name"].names: if (name_record.nameID == FONTTOOLS_NAME_ID_FAMILY and name_record.platformID == FONTTOOLS_PLATFORM_ID_WINDOWS): record_str: str = "" try: record_str = name_record.toStr() except UnicodeDecodeError: encoding: str = chardet.detect( name_record.string)["encoding"] if encoding: record_str = name_record.string.decode( encoding) else: continue family_list.append(record_str) family_list = list(set(family_list)) single_font_info_dict: dict = dict( filepath=font_path, family_list=family_list, index=font_num_index, file_font_num=font_num, ) font_info_dict["font_info_list"].append(single_font_info_dict) save_config(info_filepath, font_info_dict) return font_info_dict["font_info_list"]