def get_glyph_id(self, glyph): ttf = TTFont(self.file_path) ttf.saveXML('../fonts/01.xml') # gly_list = ttf.getGlyphOrder() # 获取 GlyphOrder 字段的值 index = ttf.getGlyphID(glyph) # os.remove(self.file_path) return index
def cmap_dump(self): font = TTFont(self.fontfile) #TODO(ahmetcelik) cmap in format 12 should be used if it exists cmapTable = font['cmap'].getcmap(3, 10) if not cmapTable: cmapTable = font['cmap'].getcmap(3, 1) assert cmapTable, 'Unicode cmap table required' cmap = cmapTable.cmap # unicode table codepoints = [] glyphs = [] for code, name in cmap.iteritems(): id = font.getGlyphID(name) glyphs.append(id) codepoints.append(code) if self.debug: print id, name, code font.close() cp_dumper = Dumper(self.folder + '/codepoints') cp_dumper.dump_array(codepoints, 'I', '>') cp_dumper.close() gid_dumper = Dumper(self.folder + '/gids') gid_dumper.dump_array(glyphs, 'H', '>') gid_dumper.close()
def cmap_dump(self): font = TTFont(self.fontfile) #TODO(ahmetcelik) cmap in format 12 should be used if it exists cmapTable = font['cmap'].getcmap(3, 10) if not cmapTable: cmapTable = font['cmap'].getcmap(3, 1) assert cmapTable,'Unicode cmap table required' cmap = cmapTable.cmap # unicode table codepoints = [] glyphs = [] for code, name in cmap.iteritems(): id = font.getGlyphID(name) glyphs.append(id) codepoints.append(code) if self.debug: print id,name,code font.close() cp_dumper = Dumper(self.folder + '/codepoints') cp_dumper.dump_array(codepoints, 'I', '>') cp_dumper.close() gid_dumper = Dumper(self.folder + '/gids') gid_dumper.dump_array(glyphs, 'H', '>') gid_dumper.close()
class TTXLEFont(LEFontInstance): def __init__(self, fname, size=12): super(TTXLEFont, self).__init__() self.ttx = TTFont(fname) self.size = size self.upem = self.ttx['head'].unitsPerEm self.cmap = self.ttx['cmap'].getcmap(3, 1).cmap def getFontTable(self, table): return self.ttx.getTableData(table) def getAscent(self): self.ttx['hhea'].ascent * self.size * 1. / self.upem def getDescent(self): self.ttx['hhea'].descent * self.size * 1. / self.upem def getLeading(self): self.ttx['hhea'].lineGap * self.size * 1. / self.upem def getUnitsPerEm(self): return self.upem def mapCharToGlyph(self, code): return self.ttx.getGlyphID(self.cmap[code]) def getGlyphAdvance(self, glyph): if glyph >= self.ttx['maxp'].numGlyphs: return (0., 0.) name = self.ttx.getGlyphName(glyph) x = self.ttx['hmtx'][name][0] * self.size * 1. / self.upem if 'vmtx' in self.ttx: y = self.ttx['vmtx'][name][0] * self.size * 1. / self.upem else: y = 0. return (x, y) def getGlyphPoint(self, glyph, point): return (0., 0.) def getXPixelsPerEm(self): return self.size def getYPixelsPerEm(self): return self.size def getScaleFactorX(self): return 1. def getScaleFactorY(self): return 1.
def tran(self, text, html): url = re.findall("url\('(.*?.woff)'", html)[0] with open('人人车01.ttf', 'wb') as f: f.write(requests.get(url=url).content) font1 = TTFont('人人车.ttf') obj_list1 = font1.getGlyphNames()[1:] # 获取所有字符的对象,去除第一个和最后一个 uni_list1 = font1.getGlyphOrder()[1:] font2 = TTFont('人人车01.ttf') obj_list2 = font2.getGlyphNames()[1:] # 获取所有字符的对象,去除第一个和最后一个 uni_list2 = font2.getGlyphOrder()[1:] dict = { 'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9' } dict1 = { 'zero': '0', 'one': '1', 'two': '2', 'four': '3', 'three': '4', 'five': '5', 'seven': '6', 'nine': '7', 'six': '8', 'eight': '9' } ''' 遍历加密的内容text,在新的ttf文件中查找每一个text的元素。如果找到,则替换''' for a in text: for uni2 in uni_list2: # print(uni2) try: id = dict[str(uni2)] # 找到unit2未加密对应的数字 except: continue id_1 = font2.getGlyphID(str(uni2)) # Z找到unit2在ttf文件中的id obj2 = font2['glyf'][uni2] # str(id) != str(id_1): # 若未加密的数字id和ttf中对应的id_1不相等,说明a加密了 if str(id) == str(a): for uni1 in uni_list1: obj1 = font1['glyf'][uni1] if obj1 == obj2: text = text.replace(a, dict1[uni1]) return text
def parse(self, response, **kwargs): page_num = response.meta['page_num'] num_li = [] font = self.find_b64_font.search(response.xpath('//style/text()').get()).group(1) font = TTFont(BytesIO(base64.b64decode(font))) best_cmap = font.getBestCmap() tmp_num_li = response.xpath('//div[@class="row"]/div[@class="col-md-1"]/text()').getall() for tmp_num in tmp_num_li: tmp_li = [] tmp_num = tmp_num.strip() for num in tmp_num: glyph_id = best_cmap.get(ast.literal_eval(num.encode('unicode_escape').decode().replace('\\u', '0x'))) real_num = str(font.getGlyphID(glyph_id) - 1) tmp_li.append(real_num) num_li.append(int(''.join(tmp_li))) self.num_count += sum(num_li) self.logger.info(f'[ page: {page_num} ] => {num_li}')
def parse(self, response, **kwargs): page_num = response.meta['page_num'] num_li = [] font = self.find_b64_font.search( response.xpath('//style/text()').get()).group(1) font = TTFont(BytesIO(base64.b64decode(font))) best_cmap = font.getBestCmap() current_num_map = dict() for i in best_cmap.values(): real_num = str(font.getGlyphID(i) - 1) current_num_map[i] = real_num tmp_num_li = response.xpath( '//div[@class="row"]/div[@class="col-md-1"]/text()').getall() for tmp_num in tmp_num_li: tmp_li = [] tmp_num = tmp_num.strip() for num in tmp_num: tmp_li.append(current_num_map[self.num_map[num]]) num_li.append(int(''.join(tmp_li))) self.num_count += sum(num_li) self.logger.info(f'[ page: {page_num} ] => {num_li}')
def fontchange(str1): font = TTFont('tyc-num5-22.woff') # 打开文件 # font.saveXML('./tyc-num.xml') mappings = {} for k, v in font.getBestCmap().items(): if v.startswith('uni'): # 形如 <map code="0xe040" name="uni45"/> 可直接转换得到结果 mappings['{:x}'.format(k)] = unichr(int(v[3:], 16)) else: mappings['{:x}'.format(k)] = v num_dict = { '5': '0', '9': '1', '7': '2', '4': '3', '1': '4', '6': '5', '2': '6', '0': '7', '8': '8', '3': '9' } list1 = [] for i in str1: try: if i.isdigit(): # new_str = new_str.join(num_dict[i]) list1.append(''.join(num_dict[i])) else: key = re.search('.*u([0-9a-f]{4}).*', str(i.encode('unicode_escape'))).group(1) value = mappings[key] # 得到另一个映射 id = font.getGlyphID(value) list1.append(''.join(ocr[id])) except Exception: list1.append(i) return ''.join(str(x) for x in list1)
def cmap_dump(self): font = TTFont(self.fontfile) cmap = font['cmap'].getcmap(3, 1).cmap # unicode table assert cmap, 'Unicode cmap table required' codepoints = [] glyphs = [] for code, name in cmap.iteritems(): id = font.getGlyphID(name) glyphs.append(id) codepoints.append(code) if self.debug: print id,name,code font.close() cp_dumper = Dumper(self.folder + '/codepoints') cp_dumper.dump_array(codepoints, 'H', '>') cp_dumper.close() gid_dumper = Dumper(self.folder + '/gids') gid_dumper.dump_array(glyphs, 'H', '>') gid_dumper.close()
class Builder(object): def __init__(self, conf=None): self.conf = conf self.uids_for_glyph_names = None if self.conf['verbose']: logging.getLogger().setLevel(logging.DEBUG) def run(self): logger.info("Creating a new font") ff_font = fforge.create_font(self.conf) # Find and add regular glyphs svg_filepaths = util.get_svg_filepaths(self.conf['glyph_svg_dir']) # TODO: Validate regular SVGs logger.info("Adding glyphs and ligatures") fforge.add_glyphs(ff_font, svg_filepaths, self.conf) tmp_dir = tempfile.mkdtemp() tmp_file = os.path.join(tmp_dir, "tmp.ttf") logger.debug("Using temp file: %s", tmp_file) # TODO: Validate ligature tables to avoid warning during generate # "Lookup subtable contains unused glyph NAME making the whole subtable invalid" logger.info("Generating intermediate font file") ff_font.generate(tmp_file) del ff_font logger.info("Reading intermediate font file") self.font = TTFont(tmp_file) logger.info("Adding SVGinOT SVG files") # TODO: Validate color SVGs self.add_color_svg() self.add_name_table() logger.info("Saving output file: %s", self.conf['output_file']) self.font.save(self.conf['output_file']) # Cleaning Up os.remove(tmp_file) os.rmdir(tmp_dir) logger.info("Done!") # 0 for success return 0 def add_color_svg(self): svg_files = util.get_svg_filepaths(self.conf['color_svg_dir']) svg_list = [] # Set default namespace (avoids "ns0:svg") ET.register_namespace("", "http://www.w3.org/2000/svg") for filepath in svg_files: glyph_id = self.get_glyph_id(filepath) svg_tree = ET.parse(filepath) svg_root = svg_tree.getroot() # Add Glyph ID as SVG root id, required by SVGinOT spec. svg_root.set('id', "glyph{}".format(glyph_id)) # Remove the viewBox/height/width attributes since they are # processed inconsistently by Gecko and Edge renderers. try: del svg_root.attrib['viewBox'] except KeyError: pass try: del svg_root.attrib['height'] except KeyError: pass try: del svg_root.attrib['width'] except KeyError: pass # Add the transform to size the SVG to the FONT_EM svg_transform = self.create_color_transform(filepath) logger.debug("Set SVG transform: {}".format(svg_transform)) svg_transform_attrib = {"transform": svg_transform} # Create a new group tag to apply the transform to new_svg_group = ET.Element('g', svg_transform_attrib) # Copy all SVG root children to the new group for child in svg_root: new_svg_group.append(child) # Backup the root attribs, clear the children, and apply attribs svg_root_attrib = svg_root.items() svg_root.clear() for name, value in svg_root_attrib: svg_root.set(name, value) # Append the new group. svg_root.append(new_svg_group) data = ET.tostring(svg_root, encoding='UTF-8') logger.debug("Glyph ID: %d Adding SVG: %s", glyph_id, filepath) svg_list.append([data, glyph_id, glyph_id]) svg_table = table_S_V_G_() # The SVG table must be sorted by glyph_id svg_table.docList = sorted(svg_list, key=lambda table: table[1]) svg_table.colorPalettes = None self.font['SVG '] = svg_table def get_glyph_id(self, filepath): """ Find a Glyph ID for the filename in filepath """ if self.uids_for_glyph_names is None: self.uids_for_glyph_names = self.get_uids_for_glyph_names() (codepoint, filename) = util.codepoint_from_filepath(filepath) # Check for a regular glyph first try: glyph_name = self.uids_for_glyph_names[codepoint] except KeyError: # If that doesn't work check for a Ligature Glyph glyph_id = self.font.getGlyphID(filename) if glyph_id is -1: logger.warning( "No Glyph ID found for: %s (Note: A regular " "glyph is required for each color glyph)", filepath) logger.debug("Found Ligature Glyph: %s", filename) return glyph_id logger.debug("Found regular Glyph: %s", glyph_name) return self.font.getGlyphID(glyph_name) def get_uids_for_glyph_names(self): """ Get a dict of glyph names in the font indexed by unicode IDs """ codepoints = {} for subtable in self.font['cmap'].tables: if subtable.isUnicode(): for codepoint, name in subtable.cmap.items(): # NOTE: May overwrite previous values codepoints[codepoint] = name if len(codepoints) is 0: raise NoCodePointsException( 'No Unicode IDs/CodePoints found in font.') return codepoints def create_color_transform(self, filepath): """ Generate the transform for the color SVG. """ svg_transform = "" if 'color_transform' in self.conf: svg_transform = "{} ".format(self.conf['color_transform']) svg_height, _ = util.get_dimensions(filepath) # Find the scale multiplier based on current height verses intended # height (aka font EM). Whatever the SVG is, it needs to be scaled to # fit within the FONT_EM. scale = FONT_EM / svg_height # No need to adjust, but called out for clarity. translate_x = 0 # SVG(y Down) vs Glyph/TTF(y Up) y-coordinate differences. # Simple answer is -FONT_EM but that does not account for descent. # Y=0 is on the baseline without a descent adjustment. # Default font descent: -2/10*2048 = -409.6 translate_y = -(FONT_EM - FONT_EM * .2) svg_transform += "translate({},{}) scale({})".format( translate_x, translate_y, scale) return svg_transform def add_name_table(self): # FontForge doesn't support all font fields, so we use FontTools. self.name_table = table__n_a_m_e() self.name_table.names = [] tn = self.conf['table_name'] # Set the values that will always exist. self.add_name_records(tn['family'], NR.FAMILY) self.add_name_records(tn['subfamily'], NR.SUBFAMILY) fullname = '' if 'full_name' in tn: fullname = tn['full_name'] else: fullname = "{} {}".format(tn['family'], tn['subfamily']) self.add_name_records(fullname, NR.FULL_NAME) # Add the build date to the version now = time.strftime('%Y%m%d') version = "{} {}".format(tn['version'], now) self.add_name_records(version, NR.VERSION) # Add the build date to the unique id unique_id = '' if 'unique_id' in tn: unique_id = "{} {}".format(tn['unique_id'], now) else: unique_id = now self.add_name_records(unique_id, NR.UNIQUE_ID) # Set the values that don't always exist for key, name_id in (('copyright', NR.COPYRIGHT), ('postscript_name', NR.PS_NAME), ('trademark', NR.TRADEMARK), ('manufacturer', NR.MANUFACTURER), ('designer', NR.DESIGNER), ('description', NR.DESCRIPTION), ('url_vendor', NR.URL_VENDOR), ('url_designer', NR.URL_DESIGNER), ('license', NR.LICENSE), ('url_license', NR.URL_LICENSE)): if key in tn: self.add_name_records(tn[key], name_id) self.font['name'] = self.name_table def add_name_records(self, text, name_id): # <namerecord nameID="0" platformID="0" platEncID="0" langID="0x0"> self._add_name_record(text, name_id, NR.PLATFORM_UNICODE, NR.UC_ENC_UNICODE1, NR.UC_LANG_NA) # <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True"> self._add_name_record(text, name_id, NR.PLATFORM_MAC, NR.MAC_ENC_ROMAN, NR.MAC_LANG_EN) # <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> self._add_name_record(text, name_id, NR.PLATFORM_WIN, NR.WIN_ENC_UNICODE, NR.WIN_LANG_EN) def _add_name_record(self, text, name_id, platform_id, plat_enc_id, lang_id): # TODO: The installed version of fontTools doesn't have # table__n_a_m_e.setName(). record = NameRecord() # PyYAML creates strings, force to Unicode record.string = unicode(text) record.nameID = name_id record.platformID = platform_id record.platEncID = plat_enc_id record.langID = lang_id self.name_table.names.append(record)
# Aligned by design. Curr offset will point to the start of the first font curr_offset = header_size + len(args.pt) * size_info_size # This will be filled with the font data font_data = bytearray() # Iterate though all sizes for pt in range(len(args.pt)): # Get the font size font_size = args.pt[pt] # Load the font font = ImageFont.truetype(args.ttf, font_size) font_info = TTFont(args.ttf) start_id = font_info.getGlyphID('space') scale = font_info['head'].unitsPerEm if args.prev: preview = "" for i in range(num_chars): preview += str(chr(i + start_char)) image = Image.new("RGB", (1000, 1000)) draw = ImageDraw.Draw(image) draw.text((0, 0), preview, font=font) image = image.crop(image.getbbox()) image.save(args.tf + "_prev" + str(args.pt[pt]) + ".png") pixels = image.load() # Find the max and min values ymax = 0
def _export_svg(self, otfpath, palette=0, parent_window=None): font = TTFont(otfpath) if font.has_key("SVG "): print(" WARNING: Replacing existing SVG table in %s" % otfpath) # font.getReverseGlyphMap(rebuild=1) svg = table_S_V_G_("SVG ") svg.version = 0 svg.docList = [] svg.colorPalettes = None if parent_window is not None: progress = ProgressWindow("Rendering SVG ...", tickCount=len(self.keys()), parentWindow=parent_window) _palette = self.palettes[palette] _svg_palette = [] _docList = [] reindex = {0xffff: 0xffff} count = 0 for i in sorted(_palette.keys(), key=lambda k: int(k)): red = int(_palette[i][1:3], 16) green = int(_palette[i][3:5], 16) blue = int(_palette[i][5:7], 16) if len(_palette[i]) >= 9: alpha = int(_palette[i][7:9], 16) else: alpha = 0xff reindex[int(i)] = count count += 1 _svg_palette.append((red, green, blue, alpha)) # print("Palette:", len(_svg_palette), _svg_palette) _pen = SVGpen(self.rfont, optimize_output=True) for glyphname in self.keys(): # ["A", "P"]: #self.keys(): # look up glyph id try: gid = font.getGlyphID(glyphname) except: assert 0, "SVG table contains a glyph name not in font.getGlyphNames(): " + str(glyphname) # update progress bar if parent_window is not None: progress.update("Rendering SVG for /%s ..." % glyphname) # build svg glyph _svg_transfrom_group = """<g transform="scale(1 -1)">%s</g>""" contents = u"" for i in range(len(self[glyphname].layers)): _color_index = reindex[self[glyphname].colors[i]] # print(" Layer %i, color %i" % (i, _color_index)) rglyph = self.rfont[self[glyphname].layers[i]] if _color_index == 0xffff: r, g, b, a = (0, 0, 0, 0xff) else: r, g, b, a = _svg_palette[_color_index] _pen.reset() rglyph.draw(_pen) if _pen.d: contents += u'<g fill="#%02x%02x%02x"><path d="%s"/></g>' % (r, g, b, _pen.d) if contents: contents = _svg_transfrom_group % contents _svg_doc = u"""<svg enable-background="new 0 0 64 64" id="glyph%i" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">%s</svg>""" % (gid, contents) _docList.append((_svg_doc, gid, gid)) svg.docList = sorted(_docList, key=itemgetter(1)) if parent_window is not None: progress.close() # save font["SVG "] = svg font.save(otfpath[:-4] + "_svg" + otfpath[-4:]) font.close()
class Builder(object): def __init__(self, conf=None): self.conf = conf self.uids_for_glyph_names = None if self.conf['verbose']: logging.getLogger().setLevel(logging.DEBUG) def run(self): logger.info("Creating a new font") ff_font = fforge.create_font(self.conf) # Find and add regular glyphs svg_filepaths = util.get_svg_filepaths(self.conf['glyph_svg_dir']) # TODO: Validate regular SVGs logger.info("Adding glyphs and ligatures") fforge.add_glyphs(ff_font, svg_filepaths, self.conf) tmp_dir = tempfile.mkdtemp() tmp_file = os.path.join(tmp_dir, "tmp.ttf") logger.debug("Using temp file: %s", tmp_file) # TODO: Validate ligature tables to avoid warning during generate # "Lookup subtable contains unused glyph NAME making the whole subtable invalid" logger.info("Generating intermediate font file") ff_font.generate(tmp_file) del ff_font logger.info("Reading intermediate font file") self.font = TTFont(tmp_file) logger.info("Adding SVGinOT SVG files") # TODO: Validate color SVGs self.add_color_svg() self.add_name_table() logger.info("Saving output file: %s", self.conf['output_file']) self.font.save(self.conf['output_file']) # Cleaning Up os.remove(tmp_file) os.rmdir(tmp_dir) logger.info("Done!") # 0 for success return 0 def add_color_svg(self): svg_files = util.get_svg_filepaths(self.conf['color_svg_dir']) svg_list = [] # Set default namespace (avoids "ns0:svg") ET.register_namespace("", "http://www.w3.org/2000/svg") for filepath in svg_files: glyph_id = self.get_glyph_id(filepath) svg_tree = ET.parse(filepath) svg_root = svg_tree.getroot() # Add Glyph ID as SVG root id, required by SVGinOT spec. svg_root.set('id', "glyph{}".format(glyph_id)) # Remove the viewBox/height/width attributes since they are # processed inconsistently by Gecko and Edge renderers. try: del svg_root.attrib['viewBox'] except KeyError: pass try: del svg_root.attrib['height'] except KeyError: pass try: del svg_root.attrib['width'] except KeyError: pass # Add the transform to size the SVG to the FONT_EM svg_transform = self.create_color_transform(filepath) logger.debug("Set SVG transform: {}".format(svg_transform)) svg_transform_attrib = {"transform": svg_transform} # Create a new group tag to apply the transform to new_svg_group = ET.Element('g', svg_transform_attrib) # Copy all SVG root children to the new group for child in svg_root: new_svg_group.append(child) # Backup the root attribs, clear the children, and apply attribs svg_root_attrib = svg_root.items() svg_root.clear() for name, value in svg_root_attrib: svg_root.set(name, value) # Append the new group. svg_root.append(new_svg_group) data = ET.tostring(svg_root, encoding='UTF-8') logger.debug("Glyph ID: %d Adding SVG: %s", glyph_id, filepath) svg_list.append([data, glyph_id, glyph_id]) svg_table = table_S_V_G_() # The SVG table must be sorted by glyph_id svg_table.docList = sorted(svg_list, key=lambda table: table[1]) svg_table.colorPalettes = None self.font['SVG '] = svg_table def get_glyph_id(self, filepath): """ Find a Glyph ID for the filename in filepath """ if self.uids_for_glyph_names is None: self.uids_for_glyph_names = self.get_uids_for_glyph_names() (codepoint, filename) = util.codepoint_from_filepath(filepath) # Check for a regular glyph first try: glyph_name = self.uids_for_glyph_names[codepoint] except KeyError: # If that doesn't work check for a Ligature Glyph glyph_id = self.font.getGlyphID(filename) if glyph_id is -1: logger.warning("No Glyph ID found for: %s (Note: A regular " "glyph is required for each color glyph)", filepath) logger.debug("Found Ligature Glyph: %s", filename) return glyph_id logger.debug("Found regular Glyph: %s", glyph_name) return self.font.getGlyphID(glyph_name) def get_uids_for_glyph_names(self): """ Get a dict of glyph names in the font indexed by unicode IDs """ codepoints = {} for subtable in self.font['cmap'].tables: if subtable.isUnicode(): for codepoint, name in subtable.cmap.items(): # NOTE: May overwrite previous values codepoints[codepoint] = name if len(codepoints) is 0: raise NoCodePointsException( 'No Unicode IDs/CodePoints found in font.') return codepoints def create_color_transform(self, filepath): """ Generate the transform for the color SVG. """ svg_transform = "" if 'color_transform' in self.conf: svg_transform = "{} ".format(self.conf['color_transform']) svg_height, _ = util.get_dimensions(filepath) # Find the scale multiplier based on current height verses intended # height (aka font EM). Whatever the SVG is, it needs to be scaled to # fit within the FONT_EM. scale = FONT_EM / svg_height # No need to adjust, but called out for clarity. translate_x = 0 # SVG(y Down) vs Glyph/TTF(y Up) y-coordinate differences. # Simple answer is -FONT_EM but that does not account for descent. # Y=0 is on the baseline without a descent adjustment. # Default font descent: -2/10*2048 = -409.6 translate_y = - (FONT_EM - FONT_EM * .2) svg_transform += "translate({},{}) scale({})".format(translate_x, translate_y, scale) return svg_transform def add_name_table(self): # FontForge doesn't support all font fields, so we use FontTools. self.name_table = table__n_a_m_e() self.name_table.names = [] tn = self.conf['table_name'] # Set the values that will always exist. self.add_name_records(tn['family'], NR.FAMILY) self.add_name_records(tn['subfamily'], NR.SUBFAMILY) fullname = '' if 'full_name' in tn: fullname = tn['full_name'] else: fullname = "{} {}".format(tn['family'], tn['subfamily']) self.add_name_records(fullname, NR.FULL_NAME) # Add the build date to the version now = time.strftime('%Y%m%d') version = "{} {}".format(tn['version'], now) self.add_name_records(version, NR.VERSION) # Add the build date to the unique id unique_id = '' if 'unique_id' in tn: unique_id = "{} {}".format(tn['unique_id'], now) else: unique_id = now self.add_name_records(unique_id, NR.UNIQUE_ID) # Set the values that don't always exist for key, name_id in ( ('copyright', NR.COPYRIGHT), ('postscript_name', NR.PS_NAME), ('trademark', NR.TRADEMARK), ('manufacturer', NR.MANUFACTURER), ('designer', NR.DESIGNER), ('description', NR.DESCRIPTION), ('url_vendor', NR.URL_VENDOR), ('url_designer', NR.URL_DESIGNER), ('license', NR.LICENSE), ('url_license', NR.URL_LICENSE)): if key in tn: self.add_name_records(tn[key], name_id) self.font['name'] = self.name_table def add_name_records(self, text, name_id): # <namerecord nameID="0" platformID="0" platEncID="0" langID="0x0"> self._add_name_record(text, name_id, NR.PLATFORM_UNICODE, NR.UC_ENC_UNICODE1, NR.UC_LANG_NA) # <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True"> self._add_name_record(text, name_id, NR.PLATFORM_MAC, NR.MAC_ENC_ROMAN, NR.MAC_LANG_EN) # <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> self._add_name_record(text, name_id, NR.PLATFORM_WIN, NR.WIN_ENC_UNICODE, NR.WIN_LANG_EN) def _add_name_record(self, text, name_id, platform_id, plat_enc_id, lang_id): # TODO: The installed version of fontTools doesn't have # table__n_a_m_e.setName(). record = NameRecord() # PyYAML creates strings, force to Unicode record.string = unicode(text) record.nameID = name_id record.platformID = platform_id record.platEncID = plat_enc_id record.langID = lang_id self.name_table.names.append(record)
def get_number(self, company_name): res = requests.get( "https://ss.cods.org.cn/latest/searchR?q={}¤tPage=1&t=common&searchToken=" .format(company_name), headers=self.headers) woff_name = re.findall(r"\/(\d+\.woff2)", res.text)[0] # print(woff_name) # 获得动态字体地址并请求获得字体对象 woff_url = "https://ss.cods.org.cn/css/woff/{}".format(woff_name) woff_res = requests.get(woff_url, headers=self.headers) font = TTFont(BytesIO(woff_res.content)) cmap = font.getBestCmap() fmap = { 0: None, 1: None, 2: None, 3: '0', 4: '1', 5: '2', 6: '3', 7: '4', 8: '5', 9: '6', 10: '7', 11: '8', 12: '9', 13: 'A', 14: 'B', 15: 'C', 16: 'D', 17: 'E', 18: 'F', 19: 'G', 20: 'H', 21: 'I', 22: 'J', 23: 'K', 24: 'L', 25: 'M', 26: 'N', 27: 'O', 28: 'P', 29: 'Q', 30: 'R', 31: 'S', 32: 'T', 33: 'U', 34: 'V', 35: 'W', 36: 'X', 37: 'Y', 38: 'Z', 39: '0', 40: '1', 41: '2', 42: '3', 43: '4', 44: '5', 45: '6', 46: '7', 47: '8', 48: '9', 49: 'A', 50: 'B', 51: 'C', 52: 'D', 53: 'E', 54: 'F', 55: 'G', 56: 'H', 57: 'I', 58: 'J', 59: 'K', 60: 'L', 61: 'M', 62: 'N', 63: 'O', 64: 'P', 65: 'Q', 66: 'R', 67: 'S', 68: 'T', 69: 'U', 70: 'V', 71: 'W', 72: 'X', 73: 'Y', 74: 'Z' } html = etree.HTML(res.text) number = html.xpath( "//div[@class='result result-2']//div[@class='info']/h6[text()='统一社会信用代码:']/following-sibling::p[1]/text()" )[0] # print(number) result = "" for n in number: n = ord(n) _id = font.getGlyphID(cmap[n]) # 根据索引id获得最终结果 r_n = fmap[_id] result += r_n return result
def _export_svg(self, otfpath, palette=0, parent_window=None): font = TTFont(otfpath) if font.has_key("SVG "): print(" WARNING: Replacing existing SVG table in %s" % otfpath) # font.getReverseGlyphMap(rebuild=1) svg = table_S_V_G_("SVG ") svg.version = 0 svg.docList = [] svg.colorPalettes = None if parent_window is not None: progress = ProgressWindow("Rendering SVG ...", tickCount=len(self.keys()), parentWindow=parent_window) _palette = self.palettes[palette] _svg_palette = [] _docList = [] reindex = {0xffff: 0xffff} count = 0 for i in sorted(_palette.keys(), key=lambda k: int(k)): red = int(_palette[i][1:3], 16) green = int(_palette[i][3:5], 16) blue = int(_palette[i][5:7], 16) if len(_palette[i]) >= 9: alpha = int(_palette[i][7:9], 16) else: alpha = 0xff reindex[int(i)] = count count += 1 _svg_palette.append((red, green, blue, alpha)) # print("Palette:", len(_svg_palette), _svg_palette) _pen = SVGpen(self.rfont, optimize_output=True) for glyphname in self.keys(): # ["A", "P"]: #self.keys(): # look up glyph id try: gid = font.getGlyphID(glyphname) except: assert 0, "SVG table contains a glyph name not in font.getGlyphNames(): " + str( glyphname) # update progress bar if parent_window is not None: progress.update("Rendering SVG for /%s ..." % glyphname) # build svg glyph _svg_transfrom_group = """<g transform="scale(1 -1)">%s</g>""" contents = u"" for i in range(len(self[glyphname].layers)): _color_index = reindex[self[glyphname].colors[i]] # print(" Layer %i, color %i" % (i, _color_index)) rglyph = self.rfont[self[glyphname].layers[i]] if _color_index == 0xffff: r, g, b, a = (0, 0, 0, 0xff) else: r, g, b, a = _svg_palette[_color_index] _pen.reset() rglyph.draw(_pen) if _pen.d: contents += u'<g fill="#%02x%02x%02x"><path d="%s"/></g>' % ( r, g, b, _pen.d) if contents: contents = _svg_transfrom_group % contents _svg_doc = u"""<svg enable-background="new 0 0 64 64" id="glyph%i" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">%s</svg>""" % ( gid, contents) _docList.append((_svg_doc, gid, gid)) svg.docList = sorted(_docList, key=itemgetter(1)) if parent_window is not None: progress.close() # save font["SVG "] = svg font.save(otfpath[:-4] + "_svg" + otfpath[-4:]) font.close()
def add_svg_table(font_path, file_paths, compress_table=False): gnames_dict = {} # key: glyph name; value: SVG file path for fpath in file_paths: gname = os.path.splitext(os.path.basename(fpath))[0] # trim extension # validate glyph name if not glyph_name_is_valid(gname, fpath): continue # skip any duplicates and 'space' if gname in gnames_dict or gname == 'space': log.warning("Skipped file '{}'. The glyph name derived from it " "is either a duplicate or 'space'".format(fpath)) continue # limit the length of glyph name to 31 chars if len(gname) > 31: num = 0 trimmed_gname = get_trimmed_glyph_name(gname, num) while trimmed_gname in gnames_dict: num += 1 trimmed_gname = get_trimmed_glyph_name(trimmed_gname, num) gnames_dict[trimmed_gname] = fpath log.warning("Glyph name '{}' was trimmed to 31 characters: " "'{}'".format(gname, trimmed_gname)) else: gnames_dict[gname] = fpath font = TTFont(font_path) svg_docs_dict = {} for gname, svg_file_path in gnames_dict.items(): try: gid = font.getGlyphID(gname) except KeyError: log.warning('Could not find a glyph named {} in the font' ''.format(gname)) continue with io.open(svg_file_path, encoding='utf-8') as fp: svg_item_data = fp.read() # Set id value svg_item_data = set_svg_id(svg_item_data, gid) # Scale and shift the artwork, by adjusting its viewBox svg_item_data = adjust_viewbox(svg_item_data, svg_file_path, VIEWBOX_SCALE) # Clean SVG document svg_item_data = clean_svg_doc(svg_item_data) svg_docs_dict[gid] = (svg_item_data.strip(), gid, gid) # Don't modify the input font if there's no SVG data if not svg_docs_dict: log.warning('None of the SVG files found could be added to the font') font.close() return # Make a list of the SVG documents sorted by GID svg_docs_list = sorted(svg_docs_dict.values(), key=lambda doc: doc[1]) svg_table = newTable('SVG ') svg_table.compressed = compress_table svg_table.docList = svg_docs_list svg_table.colorPalettes = None font['SVG '] = svg_table ext = '.ttf' if 'glyf' in font else '.otf' svg_font_filename = '{}{}'.format(PS_NAME, ext) svg_font_path = os.path.join(os.path.dirname(font_path), svg_font_filename) font.save(svg_font_path) font.close() log.info("Wrote '{}' containing {} SVG glyphs".format( os.path.basename(svg_font_path), len(svg_docs_list))) return svg_font_path
class TTVarCFont: def __init__(self, path, ttFont=None, hbFont=None): if ttFont is not None: assert hbFont is not None assert path is None self.ttFont = ttFont else: assert hbFont is None self.ttFont = TTFont(path, lazy=True) self.axes = { axis.axisTag: (axis.minValue, axis.defaultValue, axis.maxValue) for axis in self.ttFont["fvar"].axes } if hbFont is not None: self.hbFont = hbFont else: with open(path, "rb") as f: face = hb.Face(f.read()) self.hbFont = hb.Font(face) def keys(self): return self.ttFont.getGlyphNames() def __contains__(self, glyphName): return glyphName in self.ttFont.getReverseGlyphMap() def drawGlyph(self, pen, glyphName, location): normLocation = normalizeLocation(location, self.axes) fvarTable = self.ttFont["fvar"] glyfTable = self.ttFont["glyf"] varcTable = self.ttFont.get("VarC") if varcTable is not None: glyphData = varcTable.GlyphData else: glyphData = {} g = glyfTable[glyphName] varComponents = glyphData.get(glyphName) if g.isComposite(): componentOffsets = instantiateComponentOffsets( self.ttFont, glyphName, normLocation ) if varComponents is not None: assert len(g.components) == len(varComponents) varcInstancer = VarStoreInstancer( varcTable.VarStore, fvarTable.axes, normLocation ) for (x, y), gc, vc in zip( componentOffsets, g.components, varComponents ): componentLocation = unpackComponentLocation(vc.coord, varcInstancer) transform = unpackComponentTransform( vc.transform, varcInstancer, vc.numIntBitsForScale ) tPen = TransformPen(pen, _makeTransform(x, y, transform)) self.drawGlyph(tPen, gc.glyphName, componentLocation) else: for (x, y), gc in zip(componentOffsets, g.components): tPen = TransformPen(pen, (1, 0, 0, 1, x, y)) self.drawGlyph(tPen, gc.glyphName, {}) else: glyphID = self.ttFont.getGlyphID(glyphName) self.hbFont.set_variations(location) self.hbFont.draw_glyph_with_pen(glyphID, pen)
class Vharfbuzz: """A user-friendlier way to use Harfbuzz in Python. Args: filename (str): A path to a TrueType font file. """ def __init__(self, filename): self.filename = filename with open(self.filename, "rb") as fontfile: self.fontdata = fontfile.read() self.ttfont = TTFont(filename) self.glyphOrder = self.ttfont.getGlyphOrder() self.prepare_shaper() self.shapers = None self.drawfuncs = None def prepare_shaper(self): face = hb.Face(self.fontdata) font = hb.Font(face) upem = face.upem font.scale = (upem, upem) hb.ot_font_set_funcs(font) self.hbfont = font def make_message_handling_function(self, buf, onchange): self.history = {"GSUB": [], "GPOS": []} self.lastLookupID = None def handle_message(msg, buf2): m = re.match("start lookup (\\d+)", msg) if m: lookupid = int(m[1]) self.history[self.stage].append(self.serialize_buf(buf2)) m = re.match("end lookup (\\d+)", msg) if m: lookupid = int(m[1]) if self.serialize_buf(buf2) != self.history[self.stage][-1]: onchange(self, self.stage, lookupid, self._copy_buf(buf2)) self.history[self.stage].pop() if msg.startswith("start GPOS stage"): self.stage = "GPOS" return handle_message def shape(self, text, parameters=None, onchange=None): """Shapes a text This shapes a piece of text. Args: text (str): A string of text parameters: A dictionary containing parameters to pass to Harfbuzz. Relevant keys include ``script``, ``direction``, ``language`` (these three are normally guessed from the string contents), ``features``, ``variations`` and ``shaper``. onchange: An optional function with three parameters. See below. Additionally, if an `onchange` function is provided, this will be called every time the buffer changes *during* shaping, with the following arguments: - ``self``: the vharfbuzz object. - ``stage``: either "GSUB" or "GPOS" - ``lookupid``: the current lookup ID - ``buffer``: a copy of the buffer as a list of lists (glyphname, cluster, position) Returns: A uharfbuzz ``hb.Buffer`` object """ if not parameters: parameters = {} self.prepare_shaper() buf = hb.Buffer() buf.add_str(text) buf.guess_segment_properties() if "script" in parameters and parameters["script"]: buf.script = parameters["script"] if "direction" in parameters and parameters["direction"]: buf.direction = parameters["direction"] if "language" in parameters and parameters["language"]: buf.language = parameters["language"] shapers = self.shapers if "shaper" in parameters and parameters["shaper"]: shapers = [parameters["shaper"]] features = parameters.get("features") if "variations" in parameters: self.hbfont.set_variations(parameters["variations"]) self.stage = "GSUB" if onchange: f = self.make_message_handling_function(buf, onchange) buf.set_message_func(f) hb.shape(self.hbfont, buf, features, shapers=shapers) self.stage = "GPOS" return buf def _copy_buf(self, buf): # Or at least the bits we care about outs = [] for info, pos in zip(buf.glyph_infos, buf.glyph_positions): l = [self.glyphOrder[info.codepoint], info.cluster] if self.stage == "GPOS": l.append(pos.position) else: l.append(None) outs.append(l) return outs def serialize_buf(self, buf, glyphsonly=False): """Serializes a buffer to a string Returns the contents of the given buffer in a string format similar to that used by ``hb-shape``. Args: buf: The ``hb.Buffer`` object. Returns: A serialized string. """ outs = [] for info, pos in zip(buf.glyph_infos, buf.glyph_positions): glyphname = self.glyphOrder[info.codepoint] if glyphsonly: outs.append(glyphname) continue outs.append("%s=%i" % (glyphname, info.cluster)) if self.stage == "GPOS" and (pos.position[0] != 0 or pos.position[1] != 0): outs[-1] = outs[-1] + "@%i,%i" % (pos.position[0], pos.position[1]) if self.stage == "GPOS": outs[-1] = outs[-1] + "+%i" % (pos.position[2]) return "|".join(outs) def buf_from_string(self, s): """Deserializes a string. This attempts to perform the inverse operation to :py:meth:`serialize_buf`, turning a serialized buffer back into an object. The object is not a ``hb.Buffer``, but has a similar structure (``glyph_infos`` and ``glyph_positions``) so can be passed to code which expects a ``hb.Buffer``, such as :py:meth:`buf_to_svg` below. Args: s (str): A string produced by :py:meth:`serialize_buf` Returns a ``FakeBuffer`` object. """ buf = FakeBuffer() buf.glyph_infos = [] buf.glyph_positions = [] for item in s.split("|"): m = re.match(r"^(.*)=(\d+)(@(-?\d+),(-?\d+))?(\+(-?\d+))?$", item) if not m: raise ValueError("Couldn't parse glyph %s in %s" % (item, s)) groups = m.groups() info = FakeItem() info.codepoint = self.ttfont.getGlyphID(groups[0]) info.cluster = int(groups[1]) buf.glyph_infos.append(info) pos = FakeItem() pos.position = [ int(x or 0) for x in (groups[3], groups[4], groups[6], 0) ] # Sorry, vertical scripts buf.glyph_positions.append(pos) return buf def setup_svg_draw_funcs(self, container): if self.drawfuncs: return def move_to(x, y, c): c["output_string"] = c["output_string"] + f"M{x},{y}" def line_to(x, y, c): c["output_string"] = c["output_string"] + f"L{x},{y}" def cubic_to(c1x, c1y, c2x, c2y, x, y, c): c["output_string"] = (c["output_string"] + f"C{c1x},{c1y} {c2x},{c2y} {x},{y}") def quadratic_to(c1x, c1y, x, y, c): c["output_string"] = c["output_string"] + f"Q{c1x},{c1y} {x},{y}" def close_path(c): c["output_string"] = c["output_string"] + "Z" self.drawfuncs = hb.DrawFuncs() self.drawfuncs.set_move_to_func(move_to, container) self.drawfuncs.set_line_to_func(line_to, container) self.drawfuncs.set_cubic_to_func(cubic_to, container) self.drawfuncs.set_quadratic_to_func(quadratic_to, container) self.drawfuncs.set_close_path_func(close_path, container) def glyph_to_svg_path(self, gid): """Converts a glyph to SVG Args: gid (int): Glyph ID to render Returns: An SVG string containing a path to represent the glyph. """ if not hasattr(hb, "DrawFuncs"): raise ValueError( "glyph_to_svg_path requires uharfbuzz with draw function support" ) container = {"output_string": ""} self.setup_svg_draw_funcs(container) self.drawfuncs.get_glyph_shape(self.hbfont, gid) return container["output_string"] def buf_to_svg(self, buf): """Converts a buffer to SVG Args: buf (hb.Buffer): uharfbuzz ``hb.Buffer`` Returns: An SVG string containing a rendering of the buffer """ x_cursor = 0 paths = [] svg = "" if "hhea" in self.ttfont: ascender = self.ttfont["hhea"].ascender + 500 descender = self.ttfont["hhea"].descender - 500 fullheight = ascender - descender elif "OS/2": ascender = self.ttfont["OS/2"].sTypoAscender + 500 descender = self.ttfont["OS/2"].sTypoDescender - 500 fullheight = ascender - descender else: fullheight = 1500 descender = 500 y_cursor = -descender for info, pos in zip(buf.glyph_infos, buf.glyph_positions): glyph_path = self.glyph_to_svg_path(info.codepoint) dx, dy = pos.position[0], pos.position[1] p = (f'<path d="{glyph_path}" ' + f' transform="translate({x_cursor+dx}, {y_cursor+dy})"/>\n') svg += p x_cursor += pos.position[2] y_cursor += pos.position[3] svg = (( f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {x_cursor} {fullheight}"' + ' transform="matrix(1 0 0 -1 0 0)">\n') + svg + "</svg>\n") return svg