def __init__(self, path_old: Union[Path, str], path_new: Union[Path, str], log: bool = True) -> None: self.font_old = TTFont(str(path_old)) self.font_old.cmap = { v: k for k, v in self.path_old.getBestCmap().items() } self.font_old.go = self.path_old.getGlyphOrder() self.font_old.path = path_old self.font_new = TTFont(str(path_new)) self.font_new.cmap = self.path_new.getBestCmap() self.font_new.go = self.path_new.getGlyphOrder() self.font_new.path = path_new self.log = True self.name_map = { k: self.font_new.cmap.get(v) for k, v in self.font_old.cmap.items() } self.name_map.update({ i: i for i in filter(lambda x: x in self.font_new.go, self.font_old.go) }) self.incompatible_glyphs = self.get_incompatible_glyphs()
def __init__( self, path_old: Union[Path, str], path_new: Union[Path, str], path_xml: Union[Path, str], ): self.font_old = TTFont(str(path_old)) self.font_new = TTFont(str(path_new)) self.tree = et.parse(str(path_xml))
def removeOverlaps( font: ttFont.TTFont, glyphNames: Optional[Iterable[str]] = None, removeHinting: bool = True, ) -> None: """Simplify glyphs in TTFont by merging overlapping contours. Overlapping components are first decomposed to simple contours, then merged. Currently this only works with TrueType fonts with 'glyf' table. Raises NotImplementedError if 'glyf' table is absent. Note that removing overlaps invalidates the hinting. By default we drop hinting from all glyphs whether or not overlaps are removed from a given one, as it would look weird if only some glyphs are left (un)hinted. Args: font: input TTFont object, modified in place. glyphNames: optional iterable of glyph names (str) to remove overlaps from. By default, all glyphs in the font are processed. removeHinting (bool): set to False to keep hinting for unmodified glyphs. """ try: glyfTable = font["glyf"] except KeyError: raise NotImplementedError( "removeOverlaps currently only works with TTFs") hmtxTable = font["hmtx"] # wraps the underlying glyf Glyphs, takes care of interfacing with drawing pens glyphSet = font.getGlyphSet() if glyphNames is None: glyphNames = font.getGlyphOrder() # process all simple glyphs first, then composites with increasing component depth, # so that by the time we test for component intersections the respective base glyphs # have already been simplified glyphNames = sorted( glyphNames, key=lambda name: ( glyfTable[name].getCompositeMaxpValues(glyfTable).maxComponentDepth if glyfTable[name].isComposite() else 0, name, ), ) modified = set() for glyphName in glyphNames: if removeTTGlyphOverlaps(glyphName, glyphSet, glyfTable, hmtxTable, removeHinting): modified.add(glyphName) log.debug("Removed overlaps for %s glyphs:\n%s", len(modified), " ".join(modified))
def update(self): glyphs_dict = {} font = TTFont(font_VTT_source) glyphs = font.getGlyphOrder() for glyph in glyphs: glyphs_dict[glyph] = [font.getGlyphID(glyph)] font = TTFont(font_new) glyphs = font.getGlyphOrder() for glyph in glyphs: if glyph in glyphs_dict: glyphs_dict[glyph].append(font.getGlyphID(glyph)) id_dict = {v[0]: v[1] for v in glyphs_dict.values() if len(v) == 2} root = self.tree.find("glyf") for child in root: talk = child.find("instructions//talk").text if talk != None: glyph_id = child.attrib["ID"] new_id = id_dict[int(glyph_id)] child.set("ID", str(id_dict[int(glyph_id)])) assembly = child.find("instructions//assembly").text assembly_content = [] if assembly: for line in assembly.split("\n"): if line.startswith("OFFSET[R]"): line = line.split(",") line[1] = " %s" % (id_dict[int(line[1])]) line = ",".join(line) print(line) assembly_content.append(line) child.find("instructions//assembly").text = "\n".join( assembly_content)
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 flattenGlyphs(input, fontNumber, output): font = TTFont(input, fontNumber=fontNumber) font.recalcBBoxes = False if "glyf" in font: for glyphName in font.getGlyphOrder(): glyph = font["glyf"][glyphName] coordinates, endPtsOfContours, flags = glyph.getCoordinates( font["glyf"]) glyph.numberOfContours = len(endPtsOfContours) glyph.coordinates = coordinates glyph.endPtsOfContours = endPtsOfContours glyph.flags = flags glyph.program = ttProgram.Program() font["glyf"][glyphName] = glyph elif "CFF " in font: cff = font["CFF "] fontName = cff.cff.fontNames[0] topDict = cff.cff[fontName] for glyphID in range(len(font.getGlyphOrder())): charString = topDict.CharStrings.charStringsIndex[glyphID] charString.decompile() localSubrs = getattr(charString.private, "Subrs", []) globalSubrs = charString.globalSubrs inlinedProgram = inlineProgram(localSubrs, globalSubrs, charString.program) charString.program = inlinedProgram if "Private" in topDict.rawDict and "Subrs" in topDict.Private.rawDict: topDict.Private.Subrs del topDict.Private.rawDict["Subrs"] del topDict.Private.Subrs topDict.GlobalSubrs.items = [] else: raise FlattenError("Could not flatten glyphs.") font.save(output)
def run(self): try: # Debugging in stdout print(f"\n\n{datetime.datetime.now()}") self.ttfont = TTFont(self.font_model.fontpath) # instantiate self.instantiate_variable_font() # edit name table records self.edit_name_table() # edit bit flags self.edit_bit_flags() # write to disk self.ttfont.save(self.outpath) except Exception as e: self.signals.error.emit(f"{e}") sys.stderr.write(f"{traceback.format_exc()}\n") else: # returns the file out file path on success self.signals.result.emit(self.outpath) self.signals.finished.emit()
def _convert(filename): out_file = tmp_path / "converted_file.ttf" process = subprocess.run( f"python -m bdf2ttf.convert {filename} -o {out_file}", shell=True, stderr=subprocess.STDOUT) __tracebackhide__ = True if process.returncode: pytest.fail(f"failed to convert:\n{capfd.readouterr().out}") assert out_file.exists() return TTFont(out_file)
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 reorderFont(input, fontNumber, desiredGlyphOrder, output): font = TTFont(input, fontNumber=fontNumber, lazy=False) font = fullyLoadFont(font) if "CFF " in font: cff = font["CFF "] fontName = cff.cff.fontNames[0] topDict = cff.cff[fontName] topDict.compilerClass = ReorderedTopDictCompiler glyphOrder = font.getGlyphOrder() reorderedGlyphs = reorderGlyphs(glyphOrder, desiredGlyphOrder) if reorderedGlyphs is None: return False font.setGlyphOrder(reorderedGlyphs) # Glyph order is cached in a few places, clear those out. if "glyf" in font: del font["glyf"].glyphOrder if hasattr(font, "_reverseGlyphOrderDict"): del font._reverseGlyphOrderDict # CFF stores glyph order internally, set it: if "CFF " in font: cff = font["CFF "] fontName = cff.cff.fontNames[0] topDict = cff.cff[fontName] topDict.charset = reorderedGlyphs fixLayoutCoverage(font) tmp = BytesIO() font.save(tmp) tableOrder = font.reader.keys() success = computeTableOrder(tableOrder) if not success: tmp.close() return False outputStream = open(output, "wb") reorderFontTables(tmp, outputStream, tableOrder) tmp.close() outputStream.close() return True
def ttfont_glyph_to_skia_path(glyph_name: str, tt_font: ttFont.TTFont) -> pathops.Path: """ Converts fontTools.ttLib.TTFont glyph to a pathops.Path object by glyph name. During this conversion, all composite paths are decomposed. """ glyf_table = tt_font["glyf"] glyph_set: ttFont._TTGlyphSet = tt_font.getGlyphSet() tt_glyph = glyf_table[glyph_name] skia_path = pathops.Path() skia_path_pen = skia_path.getPen() if tt_glyph.isComposite(): decompose_pen = DecomposingRecordingPen(glyph_set) glyph_set[glyph_name].draw(decompose_pen) decompose_pen.replay(skia_path_pen) return skia_path else: glyph_set[glyph_name].draw(skia_path_pen) return skia_path
def font_desecret(self, font_url, string_list): bin_data = base64.decodebytes(font_url.encode()) font = TTFont(BytesIO(bin_data)) font.saveXML("text.xml") font = TTFont(BytesIO(bin_data)) c = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap res_ls = [] for string in string_list: res_str = '' for char in string: try: decode_num = ord(char) num = c[decode_num] num = int(num[-2:]) - 1 except: num = "." res_str += str(num) res_ls.append(res_str) return res_ls
from pprint import pprint # woff反爬 # 难点:爬下来的字是个特殊符号,并且其对应的字符编码每次请求都会改变, # 线索:虽然字符编码数不胜数轮番上阵,但表示某个数字的字符编码们有着唯一坐标,(坐标——多种编码——数字) # 过程:下载一个woff文件, # 通过fonttool找到编码与坐标的对应关系,通过手动python找到数字与编码的对应关系,从而找到数字与坐标的关系,这个关系是唯一的,无论字符编码怎么改变 # 在新的请求中爬取内容,得到特殊符号,解析字符编码,通过fonttool找到坐标,通过之前的对应关系找到数字 # 得到结果 # # 然而以上并不成立,对应的坐标只是大致相同 crawl_url = 'https://maoyan.com/films/344869' # 建立第坐标与数字对应关系 font = TTFont('maoyan7.woff') FONT = { "uniE723": "0", "uniF4EF": "3", "uniF259": "7", "uniED7A": "5", "uniF202": "2", "uniE99E": "4", "uniEA38": "9", "uniED43": '1', "uniE153": '8', "uniE1DC": '6' } dic = {
def update(self, filename: str, x_counts=40, y_counts=20, fontsize=27, show_img=False, strict=False): """更新字体映射表, 或者覆盖原来的映射表 :param fontsize: :param strict: :param filename: 字体文件 :param x_counts: pillow画出来的图片一行几个字 :param y_counts: pillow画出来的图片一列几个字 :param show_img: 展示pillow画出来的图片不? :return: """ # open font file self.font = TTFont(filename) self.pillow_font = ImageFont.truetype(filename, fontsize) font = self.font pillow_font = self.pillow_font # draw font cmap: dict = font.getBestCmap() # 删除没用的字体代码 del cmap[120] # get font info font_name = chr(list(cmap.keys())[0]) font_size = list(pillow_font.getsize(font_name)) font_offset = list(pillow_font.getoffset(font_name)) font_size[0] += font_offset[0] // 2 font_size[1] += font_offset[1] // 2 batch_size = x_counts * y_counts # 连续作画 for i in range(0, len(cmap), batch_size): cmap_batch = list(cmap.items())[i:i + batch_size] canvas_size = font_size[0] * (x_counts + 3), font_size[1] * y_counts # drawing text = '' for index, each in enumerate(cmap_batch): char = chr(each[0]) text += char if (index + 1) % x_counts == 0: text += '\n' text = text.strip() canvas = Image.new('RGB', canvas_size, (255, 255, 255)) draw = ImageDraw.Draw(canvas) draw.text((0, 0), text, fill=0, font=pillow_font) if show_img: canvas.show() canvas.save('temp.jpeg', format='jpeg') result = self._orc.recognize('temp.jpeg') t_text = text.split('\n') t_result = result.split('\n') has_error = False for j in zip(t_text, t_result): # print(list(j), len(j[0]), len(j[1])) if len(j[0]) != len(j[1]): # 识别失败 has_error = True # 打印错误信息 if strict: raise RuntimeError('识别失误 {}, {}, {}'.format( list(j), len(j[0]), len(j[1]))) self.real_font_mapping.update(dict(zip(*j))) if not has_error: logger.info('字体图标识别成功')
import fontTools from fontTools.ttLib.ttFont import TTFont # font = TTFont('maoyan7.woff') font.saveXML('local_fonts7.xml') # # 每一个数字虽然对应多个16进制,但是对应一个唯一坐标 # f = font.getGlyphOrder()[2] # print(f) # print(font['glyf'][f].coordinates) # print(font['glyf'][f].coordinates.array) # print(font['glyf'][f].coordinates.array.tobytes()) # print(font['glyf'][f].coordinates.array.tobytes().hex()) # '7e01f5ff1d01e2ff2401230206010402d900e601a400c7017200bb0170000f02b0003102f90064020f01760221018c023001af025201cc027e01d1027e01e6ff' # '7f01f1ff1e01e6ff2401230203011102c900e6019d00b8016900b70174001a02ac003102f40064020b017702210196022f01ae024401bd028201d1027801e6ff' # '7f01e6ff2401e6ff24011e020301fe01ce00db019000b9016700ba0170000b02b600310201015e0206017d02260196023801b8024a01c6027001c2027e01e5ff' # # # print(font.keys()) # ['GlyphOrder', 'head', 'hhea', 'maxp', 'OS/2', 'hmtx', 'cmap', 'loca', 'glyf', 'name', 'post', 'GSUB'] # # # # # 获取getGlyphOrder节点的name值,返回为列表 # # print(font.getGlyphOrder()) # ['glyph00000', 'x', 'uniF013', 'uniF4D4', 'uniEE40', 'uniF7E1', 'uniF34B', 'uniE1A0', 'uniF1BE', 'uniE91E', 'uniF16F', 'uniF724'] # # print(font.getGlyphNames()) # # print(font.getBestCmap()) # # # s ="""""" # print(s) # print(ord(s)) # '' # # #
def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("font") args = parser.parse_args() solve(TTFont(args.font))
"1EEAB..1EEBB", "1FBF0..1FBF9", "20000..2A6DD", "2A700..2B734", "2B740..2B81D", "2B820..2CEA1", "2CEB0..2EBE0", "2F800..2FA1D", "30000..3134A", "E0100..E01EF", ] if __name__ == "__main__": list_of_fonts = [ TTFont(file=Path("C:\\Windows\\fonts\\arial.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\calibri.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\cour.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\ebrima.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\LeelawUI.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\micross.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\taile.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\msyi.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\Nirmala.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\segoeui.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\seguihis.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\seguisym.ttf")), TTFont(file=Path("C:\\Windows\\fonts\\seguisym.ttf")), ] xid_start_glyphs = {}
def sortFonts(fontPaths): return sorted( fontPaths, key=lambda ff: (-TTFont(file=ff)["OS/2"].usWidthClass, -TTFont(file=ff)["post"]. italicAngle, TTFont(file=ff)["OS/2"].usWeightClass))
class VTT_updater: def __init__(self, path_old: Union[Path, str], path_new: Union[Path, str], log: bool = True) -> None: self.font_old = TTFont(str(path_old)) self.font_old.cmap = { v: k for k, v in self.path_old.getBestCmap().items() } self.font_old.go = self.path_old.getGlyphOrder() self.font_old.path = path_old self.font_new = TTFont(str(path_new)) self.font_new.cmap = self.path_new.getBestCmap() self.font_new.go = self.path_new.getGlyphOrder() self.font_new.path = path_new self.log = True self.name_map = { k: self.font_new.cmap.get(v) for k, v in self.font_old.cmap.items() } self.name_map.update({ i: i for i in filter(lambda x: x in self.font_new.go, self.font_old.go) }) self.incompatible_glyphs = self.get_incompatible_glyphs() def get_glyph_map(self) -> dict: glyph_map = {} for i, g_new in enumerate(self.font_new.go): if g_new in self.font_old.go: glyph_map[self.font_old.index(g_new)] = i return glyph_map def get_incompatible_glyphs(self) -> list: incompatible_glyphs = list( filter(lambda x: not self.name_map[x] in self.font_new.go, self.font_old.go)) for g_name in self.font_old.go: if g_name not in incompatible_glyphs: g_old = self.font_old["glyf"][g_name] g_new = self.font_new["glyf"][self.name_map[g_name]] pen_old = RecordingPen() g_old.draw(pen_old, self.font_old["glyf"]) pen_new = RecordingPen() g_new.draw(pen_new, self.font_new["glyf"]) for (pt_old, *_), (pt_new, *_) in zip_longest(pen_old.value, pen_new.value, fillvalue=[None, None]): if pt_old != pt_new: if self.log: print( f"{g_name}/{self.name_map[g_name]} has an incompatible contour" ) break else: if hasattr(g_old, "components") and hasattr( g_new, "components"): for comp_old, comp_new in zip_longest( g_old.components, g_new.components): if comp_old.glyphName != comp_new.glyphName: if self.log: print( f"{g_name}/{self.name_map[g_name]} has incompatible components" ) break continue incompatible_glyphs.append(g_name) return incompatible_glyphs def update_assembly(self) -> None: for g_name in self.font_old.go: if g_name not in self.incompatible_glyphs: g_old = self.font_old["glyf"][g_name] if hasattr(g_old, "program"): g_new = self.font_new["glyf"][self.name_map[g_name]] g_new.program = g_old.program return None def update_glyph_programs(self, font) -> None: pattern = r"(.*OFFSET\[[r,R]\]\ ?),.*" for key in font["TSI1"].glyphPrograms: glyph_program = font["TSI1"].glyphPrograms[key].replace("\r", "\n") matches = list(re.finditer(pattern, glyph_program)) if matches: components = self.font_new["glyf"][ self.name_map[key]].components for match, component in zip(matches[::-1], components[::-1]): left, right = match.span(0) command = match.group(1) g_name, (*transformations, x, y) = component.getComponentInfo() assembly = [x, y] if transformations != [1, 0, 0, 1]: assembly.extend(transformations) assembly = list(map(str, assembly)) gid = self.font_new.go.index(g_name) new_command = f"{command}, {gid}, {', '.join(assembly)}" glyph_program = (glyph_program[:left] + new_command + glyph_program[right:]) font["TSI1"].glyphPrograms[key] = glyph_program.replace( "\n", "\r") return None def _filter_glyphs(self, dict_data) -> dict: new_dict_data = {} keys = [i for i in dict_data.keys()] for key in keys: if key not in self.incompatible_glyphs: new_dict_data[self.name_map[key]] = dict_data[key] return new_dict_data def update_TSI_tables(self) -> None: self.font_new["TSI0"] = self.font_old["TSI0"] # empty... self.font_new["TSI1"] = self.font_old["TSI1"] # assembly self.font_new["TSI2"] = self.font_old["TSI2"] # empty... self.font_new["TSI3"] = self.font_old["TSI3"] # VTT talks self.font_new["TSI5"] = self.font_old["TSI5"] # glyph groups self.font_new["cvt "] = self.font_old["cvt "] # cvts self.font_new["prep"] = self.font_old["prep"] # prep self.font_new["fpgm"] = self.font_old["fpgm"] # fpgm self.font_new["TSI1"].glyphPrograms = self._filter_glyphs( self.font_new["TSI1"].glyphPrograms) self.font_new["TSI3"].glyphPrograms = self._filter_glyphs( self.font_new["TSI3"].glyphPrograms) return None def update_table_entries(self) -> None: update = dict( maxp=[ "maxSizeOfInstructions", "maxFunctionDefs", "maxStorage", "maxStackElements", "maxZones", "maxTwilightPoints", ], head=["checkSumAdjustment"], ) for table, attributes in update.items(): for attribute in attributes: setattr( self.font_new[table], attribute, getattr(self.font_old[table], attribute), ) self.font_new["head"].flags |= 1 << 3 return None def update(self) -> None: self.update_assembly() self.update_glyph_programs(self.font_old) self.update_TSI_tables() self.update_table_entries() return None def write(self, save_as: Union[Path, str, bool] = None) -> None: if save_as: self.font_new.save(str(save_as)) else: self.font_new.save(str(self.font_new.path)) return None
class InstanceWorker(QRunnable): def __init__( self, outpath=None, font_model=None, axis_model=None, name_model=None, bit_model=None, ): super().__init__() self.signals = InstanceWorkerSignals() self.outpath = outpath self.font_model = font_model self.axis_model = axis_model self.name_model = name_model self.bit_model = bit_model self.ttfont = None @pyqtSlot() def run(self): try: # Debugging in stdout print(f"\n\n{datetime.datetime.now()}") self.ttfont = TTFont(self.font_model.fontpath) # instantiate self.instantiate_variable_font() # edit name table records self.edit_name_table() # edit bit flags self.edit_bit_flags() # write to disk self.ttfont.save(self.outpath) except Exception as e: self.signals.error.emit(f"{e}") sys.stderr.write(f"{traceback.format_exc()}\n") else: # returns the file out file path on success self.signals.result.emit(self.outpath) self.signals.finished.emit() def instantiate_variable_font(self): axis_instance_data = self.axis_model.get_instance_data() instantiateVariableFont(self.ttfont, axis_instance_data, inplace=True) print("\nAXIS INSTANCE VALUES") print( f"Instantiated variable font with axis definitions:\n{axis_instance_data}" ) def edit_name_table(self): # string, nameID, platformID, platEncID, langID name_record_plat_enc_lang = (3, 1, 1033) name_instance_data = self.name_model.get_instance_data() name_table = self.ttfont["name"] # set 3, 1, 1033 name records (only!) # mandatory writes name_table.setName(name_instance_data["nameID1"], 1, *name_record_plat_enc_lang) name_table.setName(name_instance_data["nameID2"], 2, *name_record_plat_enc_lang) name_table.setName(name_instance_data["nameID3"], 3, *name_record_plat_enc_lang) name_table.setName(name_instance_data["nameID4"], 4, *name_record_plat_enc_lang) name_table.setName(name_instance_data["nameID6"], 6, *name_record_plat_enc_lang) # optional writes # Approach: # (1) if user text data exists, write it # (2) if user text data does not exist but record does, delete it # (3) otherwise do nothing if name_instance_data["nameID16"] != "": name_table.setName( name_instance_data["nameID16"], 16, *name_record_plat_enc_lang ) elif name_table.getName(16, *name_record_plat_enc_lang): name_table.removeNames(16, *name_record_plat_enc_lang) if name_instance_data["nameID17"] != "": name_table.setName( name_instance_data["nameID17"], 17, *name_record_plat_enc_lang ) elif name_table.getName(17, *name_record_plat_enc_lang): name_table.removeNames(17, *name_record_plat_enc_lang) if name_instance_data["nameID21"] != "": name_table.setName( name_instance_data["nameID21"], 21, *name_record_plat_enc_lang ) elif name_table.getName(21, *name_record_plat_enc_lang): name_table.removeNames(21, *name_record_plat_enc_lang) if name_instance_data["nameID22"] != "": name_table.setName( name_instance_data["nameID22"], 22, *name_record_plat_enc_lang ) elif name_table.getName(22, *name_record_plat_enc_lang): name_table.removeNames(22, *name_record_plat_enc_lang) # update name table data self.ttfont["name"] = name_table # print name table report print("\nNAME TABLE EDITS") print("Name records at write time:\n") print(f"nameID1: {self.ttfont['name'].getName(1, *name_record_plat_enc_lang)}") print(f"nameID2: {self.ttfont['name'].getName(2, *name_record_plat_enc_lang)}") print(f"nameID3: {self.ttfont['name'].getName(3, *name_record_plat_enc_lang)}") print(f"nameID4: {self.ttfont['name'].getName(4, *name_record_plat_enc_lang)}") print(f"nameID6: {self.ttfont['name'].getName(6, *name_record_plat_enc_lang)}") print( f"nameID16: {self.ttfont['name'].getName(16, *name_record_plat_enc_lang)}" ) print( f"nameID17: {self.ttfont['name'].getName(17, *name_record_plat_enc_lang)}" ) print( f"nameID21: {self.ttfont['name'].getName(21, *name_record_plat_enc_lang)}" ) print( f"nameID22: {self.ttfont['name'].getName(22, *name_record_plat_enc_lang)}" ) def edit_bit_flags(self): # edit the OS/2.fsSelection bit flag pre_os2_fsselection_int = self.ttfont["OS/2"].fsSelection edited_os2_fsselection_int = self.bit_model.edit_os2_fsselection_bits( pre_os2_fsselection_int ) # edit OS/2.fsSelection in the TTFont attribute self.ttfont["OS/2"].fsSelection = edited_os2_fsselection_int # edit head.macstyle bit flag pre_head_macstyle_int = self.ttfont["head"].macStyle edited_head_macstyle_int = self.bit_model.edit_head_macstyle_bits( pre_head_macstyle_int ) self.ttfont["head"].macStyle = edited_head_macstyle_int # bit flag debugging stdout report print("\nBIT FLAGS") print( f"\nOS/2.fsSelection updated with the following data:\n" f"{self.bit_model.get_os2_instance_data()}" ) print(f"Pre OS/2.fsSelection: {num2binary(pre_os2_fsselection_int, bits=16)}") print( f"Post OS/2.fsSelection: {num2binary(self.ttfont['OS/2'].fsSelection, bits=16)}" ) print( f"\nhead.macStyle bit flag updated with the following data:\n" f"{self.bit_model.get_head_instance_data()}" ) print(f"Pre head.macStyle: {num2binary(pre_head_macstyle_int, bits=16)}") print( f"Post head.macStyle: {num2binary(self.ttfont['head'].macStyle, bits=16)}" )
parse = argparse.ArgumentParser(description="将ttf字体文件转为图片") parse.add_argument('-f', required=True, help="输入字体文件") parse.add_argument('-o', help="图片输出目录,默认为当前目录下imgs", default="imgs") parse.add_argument('-s', '--size', type=int, help="输出图片的像素大小", default=512) args = parse.parse_args() os.makedirs(args.o, exist_ok=True) def uni_2_png(txt, font=args.f, img_size=args.size): img = Image.new('1', (img_size, img_size), 255) draw = ImageDraw.Draw(img) font = ImageFont.truetype(font, int(img_size * 0.7)) txt = chr(txt) x, y = draw.textsize(txt, font=font) draw.text(((img_size - x) // 2, (img_size - y) // 2), txt, font=font, fill=0) # draw.text((0,0), txt, font=font, fill=0) file_name = '%s/%s.png' % (args.o, i) img.save(file_name) if __name__ == '__main__': f = TTFont(args.f) for i in f.getBestCmap(): uni_2_png(i)
from pathlib import Path from fontTools.ttLib.ttFont import TTFont # some font types require an specified fontNumber parameter # This list will need to change depending on what fonts you have installed list_of_fonts = [ TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\gw2696936.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\gw2695709.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Behistun-J8dE.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\unifont_upper-13.0.03.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Everson Mono.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Everson Mono Oblique.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Everson Mono Bold Oblique.ttf" )), TTFont(file=Path( "C:\\Users\\Quinten\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Everson Mono Bold.ttf" )),