def parse_fonts(content): """ :param filepath: 请求ttf地址的响应 :return: 字体字典 """ font = TTFont(BytesIO(content)) glyphnames = font.getGlyphNames() unknown_list, fonts = [], {} for glyphname in glyphnames[1:]: item = {} glyph = font['glyf'][glyphname] item["cp"] = glyph.endPtsOfContours item["glyphname"] = glyphname if item['cp'] == [11]: item['xy'] = glyph.coordinates[0] unknown_list.append(item) for font in fonts_list: for dom in unknown_list: if dom.get("cp") == font.get("cp") and dom.get("cp") != [12]: fonts[dom['glyphname'][3:]] = font.get("value") else: if dom.get("cp") == [12]: if int(dom.get("xy")[0][1]) > 200: fonts[dom['glyphname'][3:]] = "十" else: fonts[dom['glyphname'][3:]] = "上" return fonts
def main(): font = TTFont('0805.woff') glyf = font.get('glyf') result = font['cmap'] cmap_dict = result.getBestCmap() for key, value in cmap_dict.items(): k_tmp = str(hex(eval(str(key)))) b = k_tmp.replace("0x", '') glyf = font.get('glyf') c = glyf[value] sql = "insert into woff20190806(name,ten_key,sixteen_key,position) values (\'%s\',%s,\'%s\',\'%s\')" % ( value, key, b, c.coordinates) print(sql)
def save_word(filename): tablename = filename.replace('.woff', '') + "_dic_tmp" font = TTFont(filename) glyf = font.get('glyf') result = font['cmap'] sql_delete = "truncate table %s" % (tablename) db.inset_data(sql_delete) cmap_dict = result.getBestCmap() for key, value in cmap_dict.items(): k_tmp = str(hex(eval(str(key)))) b = k_tmp.replace("0x", '') glyf = font.get('glyf') c = glyf[value] sql_insert = "insert into %s (name,ten_key,sixteen_key,position) values (\'%s\',%s,\'%s\',\'%s\')" % ( tablename, value, key, b, c.coordinates) db.inset_data(sql_insert)
def getAddrMapping(url): res = {} json_data = getDict() woff = getAddrWoff(url) rs = requests.get(woff) with open('../dzdp_woff/addrTest.woff', "wb") as f: f.write(rs.content) f.close() font = TTFont('addrTest.woff') # 读取woff文件 font.saveXML('addrTest.xml') # 转成xml result = font['cmap'] cmap_dict = result.getBestCmap() for key, value in cmap_dict.items(): k_tmp = str(hex(eval(str(key)))) b = k_tmp.replace("0x", '') glyf = font.get('glyf') c = glyf[value] coordinates = str(c.coordinates) for data in json_data: position = data["position"] if position == coordinates and data["word"] not in [ '', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]: res[value.replace("uni", "&#x")] = data["word"] print("getAddrMapping", res) return res
def save_word(filename): tablename = filename.replace('.woff', '') + "_dic" font = TTFont(filename) glyf = font.get('glyf') result = font['cmap'] sql_delete = "truncate table %s" % (tablename) db.execute(sql_delete) cmap_dict = result.getBestCmap() insert_data = [] for key, value in cmap_dict.items(): k_tmp = str(hex(eval(str(key)))) b = k_tmp.replace("0x", '') glyf = font.get('glyf') c = glyf[value] temp = (str(value), str(key), str(b), str(c.coordinates)) insert_data.append(temp) #print(insert_data) sql_insert = "insert into {0} (name, ten_key, sixteen_key, position) values (%s, %s, %s, %s)".format( tablename) db.insertmany(sql_insert, insert_data)
def getSortInfoOTF(fontPath: PathLike, fontNum: int): from fontTools.ttLib import TTFont suffix = fontPath.suffix.lower().lstrip(".") ttf = TTFont(fontPath, fontNumber=fontNum, lazy=True) sortInfo = dict(suffix=suffix) name = ttf.get("name") os2 = ttf.get("OS/2") post = ttf.get("post") if name is not None: for key, nameIDs in [("familyName", [16, 1]), ("styleName", [17, 2])]: for nameID in nameIDs: nameRec = name.getName(nameID, 3, 1) if nameRec is not None: sortInfo[key] = str(nameRec) break if os2 is not None: sortInfo["weight"] = os2.usWeightClass sortInfo["width"] = os2.usWidthClass if post is not None: sortInfo[ "italicAngle"] = -post.italicAngle # negative for intuitive sort order return sortInfo
def get_real_word(fake_word, html): font_ttf = re.findall("charset=utf-8;base64,(.*?)'\)", html)[0] #print(font_ttf) font = TTFont(BytesIO(base64.decodebytes(font_ttf.encode()))) numbering = font.get('cmap').tables[0].ttFont.get('cmap').tables[0].cmap word_list = [] for char in fake_word: decode_num = ord(char) if decode_num in numbering: num = numbering[decode_num] num = int(num[-2:]) - 1 word_list.append(num) else: word_list.append(char) real_word = '' for num in word_list: real_word += str(num) return real_word
def create_mapping(font_file): """ 打开字体文件并创建字符和数字之间的映射. """ # 打开字体文件,加载glyf font = TTFont(font_file) glyf = font.get('glyf') current_map = {} # 创建当前字体文件的数字映射 for i in glyf.keys(): # 忽略不是uni开头的字符 if not i.startswith('uni'): continue c = glyf[i] number = get_number_offset(c) # 发现有字符不在已有的集合中, 抛出异常. if number is None: print((c.yMax, c.xMax, c.yMin, c.xMin)) raise Exception current_map[i.strip('uni')] = number # print(json.dumps(current_map, indent=4)) return current_map
def double_encode_uc(input_path, output_dir, subfolder): t = time.time() print("AllCapist") font_count = 0 # walk the i,put directory input_dir, font_path = walk_input_path(input_path, target_extentions=TARGETS, recursive=subfolder) for p in font_path: if output_dir: dir_, font_file = os.path.split(p) out_path = os.path.join(output_dir, font_file) else: out_path = p f = TTFont(p) f_cmap = f.get("cmap") for table in f_cmap.tables: # if table.format in [4, 12, 13, 14]: cmap = {} for u, n in list(table.cmap.items()): cmap[u] = n if u not in IGNORE_UNICODES: lc = get_lowercase_unicode(u) if lc and lc != u and lc not in list(table.cmap.keys()): cmap[lc] = n table.cmap = cmap f.save(out_path) f.close() print(f"{p} -> done") font_count += 1 print("") print(f"All done: {font_count} font processed in {time.time() - t} secs")
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)
def instantiateVariableFont(varfont, location, inplace=False, overlap=True): """ Generate a static instance from a variable TTFont and a dictionary defining the desired location along the variable font's axes. The location values must be specified as user-space coordinates, e.g.: {'wght': 400, 'wdth': 100} By default, a new TTFont object is returned. If ``inplace`` is True, the input varfont is modified and reduced to a static font. When the overlap parameter is defined as True, OVERLAP_SIMPLE and OVERLAP_COMPOUND bits are set to 1. See https://docs.microsoft.com/en-us/typography/opentype/spec/glyf """ if not inplace: # make a copy to leave input varfont unmodified stream = BytesIO() varfont.save(stream) stream.seek(0) varfont = TTFont(stream) fvar = varfont['fvar'] axes = { a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in fvar.axes } loc = normalizeLocation(location, axes) if 'avar' in varfont: maps = varfont['avar'].segments loc = {k: piecewiseLinearMap(v, maps[k]) for k, v in loc.items()} # Quantize to F2Dot14, to avoid surprise interpolations. loc = {k: floatToFixedToFloat(v, 14) for k, v in loc.items()} # Location is normalized now log.info("Normalized location: %s", loc) if 'gvar' in varfont: log.info("Mutating glyf/gvar tables") gvar = varfont['gvar'] glyf = varfont['glyf'] hMetrics = varfont['hmtx'].metrics vMetrics = getattr(varfont.get('vmtx'), 'metrics', None) # get list of glyph names in gvar sorted by component depth glyphnames = sorted( gvar.variations.keys(), key=lambda name: (glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth if glyf[name].isComposite() else 0, name)) for glyphname in glyphnames: variations = gvar.variations[glyphname] coordinates, _ = glyf._getCoordinatesAndControls( glyphname, hMetrics, vMetrics) origCoords, endPts = None, None for var in variations: scalar = supportScalar(loc, var.axes) if not scalar: continue delta = var.coordinates if None in delta: if origCoords is None: origCoords, g = glyf._getCoordinatesAndControls( glyphname, hMetrics, vMetrics) delta = iup_delta(delta, origCoords, g.endPts) coordinates += GlyphCoordinates(delta) * scalar glyf._setCoordinates(glyphname, coordinates, hMetrics, vMetrics) else: glyf = None if 'cvar' in varfont: log.info("Mutating cvt/cvar tables") cvar = varfont['cvar'] cvt = varfont['cvt '] deltas = {} for var in cvar.variations: scalar = supportScalar(loc, var.axes) if not scalar: continue for i, c in enumerate(var.coordinates): if c is not None: deltas[i] = deltas.get(i, 0) + scalar * c for i, delta in deltas.items(): cvt[i] += otRound(delta) if 'CFF2' in varfont: log.info("Mutating CFF2 table") glyphOrder = varfont.getGlyphOrder() CFF2 = varfont['CFF2'] topDict = CFF2.cff.topDictIndex[0] vsInstancer = VarStoreInstancer(topDict.VarStore.otVarStore, fvar.axes, loc) interpolateFromDeltas = vsInstancer.interpolateFromDeltas interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas) CFF2.desubroutinize() interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder) interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc) del topDict.rawDict['VarStore'] del topDict.VarStore if 'MVAR' in varfont: log.info("Mutating MVAR table") mvar = varfont['MVAR'].table varStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc) records = mvar.ValueRecord for rec in records: mvarTag = rec.ValueTag if mvarTag not in MVAR_ENTRIES: continue tableTag, itemName = MVAR_ENTRIES[mvarTag] delta = otRound(varStoreInstancer[rec.VarIdx]) if not delta: continue setattr(varfont[tableTag], itemName, getattr(varfont[tableTag], itemName) + delta) log.info("Mutating FeatureVariations") for tableTag in 'GSUB', 'GPOS': if not tableTag in varfont: continue table = varfont[tableTag].table if not getattr(table, 'FeatureVariations', None): continue variations = table.FeatureVariations for record in variations.FeatureVariationRecord: applies = True for condition in record.ConditionSet.ConditionTable: if condition.Format == 1: axisIdx = condition.AxisIndex axisTag = fvar.axes[axisIdx].axisTag Min = condition.FilterRangeMinValue Max = condition.FilterRangeMaxValue v = loc[axisTag] if not (Min <= v <= Max): applies = False else: applies = False if not applies: break if applies: assert record.FeatureTableSubstitution.Version == 0x00010000 for rec in record.FeatureTableSubstitution.SubstitutionRecord: table.FeatureList.FeatureRecord[ rec.FeatureIndex].Feature = rec.Feature break del table.FeatureVariations if 'GDEF' in varfont and varfont['GDEF'].table.Version >= 0x00010003: log.info("Mutating GDEF/GPOS/GSUB tables") gdef = varfont['GDEF'].table instancer = VarStoreInstancer(gdef.VarStore, fvar.axes, loc) merger = MutatorMerger(varfont, instancer) merger.mergeTables(varfont, [varfont], ['GDEF', 'GPOS']) # Downgrade GDEF. del gdef.VarStore gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef is None: del gdef.MarkGlyphSetsDef gdef.Version = 0x00010000 if not (gdef.LigCaretList or gdef.MarkAttachClassDef or gdef.GlyphClassDef or gdef.AttachList or (gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)): del varfont['GDEF'] addidef = False if glyf: for glyph in glyf.glyphs.values(): if hasattr(glyph, "program"): instructions = glyph.program.getAssembly() # If GETVARIATION opcode is used in bytecode of any glyph add IDEF addidef = any( op.startswith("GETVARIATION") for op in instructions) if addidef: break if overlap: for glyph_name in glyf.keys(): glyph = glyf[glyph_name] # Set OVERLAP_COMPOUND bit for compound glyphs if glyph.isComposite(): glyph.components[0].flags |= OVERLAP_COMPOUND # Set OVERLAP_SIMPLE bit for simple glyphs elif glyph.numberOfContours > 0: glyph.flags[0] |= flagOverlapSimple if addidef: log.info("Adding IDEF to fpgm table for GETVARIATION opcode") asm = [] if 'fpgm' in varfont: fpgm = varfont['fpgm'] asm = fpgm.program.getAssembly() else: fpgm = newTable('fpgm') fpgm.program = ttProgram.Program() varfont['fpgm'] = fpgm asm.append("PUSHB[000] 145") asm.append("IDEF[ ]") args = [str(len(loc))] for a in fvar.axes: args.append(str(floatToFixed(loc[a.axisTag], 14))) asm.append("NPUSHW[ ] " + ' '.join(args)) asm.append("ENDF[ ]") fpgm.program.fromAssembly(asm) # Change maxp attributes as IDEF is added if 'maxp' in varfont: maxp = varfont['maxp'] setattr(maxp, "maxInstructionDefs", 1 + getattr(maxp, "maxInstructionDefs", 0)) setattr(maxp, "maxStackElements", max(len(loc), getattr(maxp, "maxStackElements", 0))) if 'name' in varfont: log.info("Pruning name table") exclude = {a.axisNameID for a in fvar.axes} for i in fvar.instances: exclude.add(i.subfamilyNameID) exclude.add(i.postscriptNameID) if 'ltag' in varfont: # Drop the whole 'ltag' table if all its language tags are referenced by # name records to be pruned. # TODO: prune unused ltag tags and re-enumerate langIDs accordingly excludedUnicodeLangIDs = [ n.langID for n in varfont['name'].names if n.nameID in exclude and n.platformID == 0 and n.langID != 0xFFFF ] if set(excludedUnicodeLangIDs) == set( range(len((varfont['ltag'].tags)))): del varfont['ltag'] varfont['name'].names[:] = [ n for n in varfont['name'].names if n.nameID not in exclude ] if "wght" in location and "OS/2" in varfont: varfont["OS/2"].usWeightClass = otRound( max(1, min(location["wght"], 1000))) if "wdth" in location: wdth = location["wdth"] for percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()): if wdth < percent: varfont["OS/2"].usWidthClass = widthClass break else: varfont["OS/2"].usWidthClass = 9 if "slnt" in location and "post" in varfont: varfont["post"].italicAngle = max(-90, min(location["slnt"], 90)) log.info("Removing variable tables") for tag in ('avar', 'cvar', 'fvar', 'gvar', 'HVAR', 'MVAR', 'VVAR', 'STAT'): if tag in varfont: del varfont[tag] return varfont
def parse(self, response, **kwargs): # 58.com uses custom fonts to hide price and room count # To extract these data, we need to load the embedded font from the source code first font_ttf = re.findall("charset=utf-8;base64,(.*?)'\)", response.text)[0] font = TTFont(BytesIO(base64.decodebytes(font_ttf.encode()))) numbering = font.get('cmap').tables[0].ttFont.get( 'cmap').tables[0].cmap lis = response.xpath( '//ul[@class="house-list"]/li[@class="house-cell"]') for li in lis: infor = li.xpath( 'div[@class="des"]/p[@class="infor"]//text()').extract() infor = [x.strip() for x in infor if x.strip()] if len(infor) < 2: print(infor) continue item = A58SpiderItem() item['url'] = li.xpath('div[@class="des"]/h2/a/@href').extract()[0] item['area'] = infor[0] item['community'] = infor[1] sent_time = li.xpath( 'div[@class="list-li-right"]/div[@class="send-time"]/text()' ).extract() sent_time = [x.strip() for x in sent_time if x.strip()] item['date'] = self.convert_time( sent_time[0].strip()) if sent_time else 'Unknown' if len(infor) >= 3: metro_data = re.match(r'距(.*)号线(.*?)地铁站步行(.*?)m', infor[2]) if metro_data: item['metro'] = metro_data.group(1) item['subway_station'] = metro_data.group(2) item['distance'] = int(metro_data.group(3)) else: print(infor[2]) continue else: # item['metro'] = 'Unknown' # item['subway_station'] = 'Unknown' # item['distance'] = 'Unknown' print('No subway info, drop', item['community']) continue price = li.xpath( 'div[@class="list-li-right"]/div[@class="money"]/b/text()' ).extract()[0] item['price'] = self.convert_real_word(numbering, price) rooms = li.xpath('div[@class="des"]/h2' '/a/text()').extract()[0].replace( item['community'], '').strip() item['rooms'] = self.convert_real_word(numbering, rooms) yield item next_page = response.xpath( '//div[@class="pager"]/a[@class="next"]/@href').extract() if next_page: yield scrapy.Request(next_page[0])
class BMFontTTF: def __init__(self, ttf, chars="", size=32, stroke_color=None, stroke_width=0): self.size = size self.chars = None self.stroke_color = stroke_color self.stroke_width = stroke_width self.ttf = None self.glyf = None self.font = None if self.__check_ttf(ttf): self.__check_chars(chars) def __check_ttf(self, ttf): if os.path.isfile(ttf) and ( os.path.splitext(ttf)[-1]).lower() == ".ttf": self.ttf = ttf self.glyf = TTFont(ttf).getBestCmap() self.font = ImageFont.truetype(ttf, self.size) else: print("找不到字体: %s" % ttf) return False if not self.glyf or not self.font: print("无效的字体: %s" % ttf) return False return True def __check_chars(self, text): chars = [] arr = list(set(list(text))) for char in arr: if self.has_char(char): chars.append(char) if len(chars) == 0: print("找不到有效的输入字符") return False self.chars = sorted(chars) self.chars.append(u"\u0020") return True def has_char(self, char): try: return self.glyf.get(ord(char)) is not None except Exception: print("无效的字符: %s" % char) return False def save(self, path, save_file): if path is None: print("无效的保存位置: %s" % str(path)) return os.makedirs(path, exist_ok=True) if not os.path.isdir(path): print("无效的保存位置: %s" % str(path)) return else: # rmtree(path, ignore_errors=True) os.makedirs(path, exist_ok=True) if not self.chars or len(self.chars) == 0: return channel = (0, 0, 0, 0) files = [] from_path = os.path.join(get_app_cache_dir(), datetime.now().strftime("%Y%m%d%H%M%S")) os.makedirs(from_path, exist_ok=True) for char in self.chars: size = self.font.getsize(char, stroke_width=self.stroke_width) size2 = self.font.getsize(char) offset = ((size[0] - size2[0]), (size[1] - size2[1])) # size = (size[0] + offset[0], size[1] + offset[1]) image = Image.new("RGBA", size, channel) draw = ImageDraw.Draw(image) draw.text(offset, char, font=self.font, spacing=0, stroke_fill=self.stroke_color, stroke_width=self.stroke_width) repl = ESCAPE_SWAP_CHARS.get(char) repl = repl or char print("TTF正在添加字符: %s => %s" % (char, repl)) filename = os.path.join(from_path, "%s.png" % repl) image.save(filename) files.append(filename) Atlas({ "image": from_path, "output": path, "atlas": files, "max_width": Globals.get_max_width(), "save_file": save_file }).generate()
]): 2, str([1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1]): 7, } # decrypt = decrypt_collection.find_one({"_id": 1})["match"] file_name = ttfUrl.split("/")[-1] match = {} # 获取ttf 文件 res = requests.get(ttfUrl) with open(file_name, "wb") as file: file.write(res.content) # ttf 文件解析 font = TTFont(file_name) font.get("glyf") font.getTableData("glyf") for key in font["glyf"].glyphs: f = font["glyf"].glyphs[key] if not hasattr(f, "flags"): continue if str(f.flags.tolist()) in ttfMatch: match[key.lower().replace("uni", "&#x") + ";"] = str(ttfMatch[str( f.flags.tolist())]) # 粘贴到system_config 表 print("'{}': {}".format(file_name.split(".")[0], match))
class ChangeFontColor: """ Tool that changes color in SVG/COLR font. It tries to simplify the process a bit. """ def __init__(self, path: Path, colors, inputType: str) -> None: self.font = TTFont(path) self.hexColors = (colors if inputType == "hex" else list( map(self.rgbToHex, colors))) self.rgbColors = (colors if inputType == "rgb" else list( map(self.hexToRgb, colors))) svg = self.font.get("SVG ") cpal = self.font.get("CPAL") if svg: self.changeSVGColor(svg) if cpal: self.changeCPALColor(cpal) self.font.save(path.absolute().with_name(f"colored_{path.name}")) def changeSVGColor(self, table: S_V_G_) -> None: """ Regexes colors in svg data in given font and changes their color. """ colors = self.hexColors[::-1] for i, entry in enumerate(table.docList): matches = re.finditer('fill="#([0-9]*)"', entry[0]) spans = [match.span(1) for match in matches][::-1] if i == 0: assert len(spans) == len( colors ), "have same number of new colors as in your input file" for color, (start, end) in zip(colors, spans): entry[0] = f"{entry[0][:start]}{color}{entry[0][end:]}" def changeCPALColor(self, table: C_P_A_L_) -> None: """ Changes CPAL table in given font, this table contains palettes of colors. Currently, it works only with fonts that have only one color in their palette. """ colors = self.rgbColors for i, color in enumerate(colors): if len(color) == 3: colors[i] += (255, ) table.palettes = [ list(map(lambda x: Color(*x[:3][::-1], x[3]), colors)) ] def hexToRgb(self, val: str) -> Tuple[int, ...]: """ Converts hex value to rgb """ splits = tuple(val[i:i + 2] for i in range(0, len(val), 2)) rgbSpace = tuple(map(lambda x: int(x, 16), splits)) return rgbSpace def rgbToHex(self, val: Tuple[int, ...]) -> str: """ Converts rgb value to hex """ return "".join(tuple(format(i, "x").zfill(2) for i in val))