def main(): parser = argparse.ArgumentParser(description='something something webfonts') parser.add_argument('files', metavar='FILE', nargs='*') args = parser.parse_args() for file in args.files: font = TTFont(file) #for glyphName in font.getGlyphOrder(): # glyphTable = font["glyf"] # glyph = glyphTable.glyphs.get(glyphName) # glyph.expand(glyphTable) glyphs = font.getGlyphSet() for key in glyphs.keys(): value = glyphs[key] print("glyp width: " + key + ", " + str(value.width)) # glyph.recalcBounds(glyphTable) hmtxTable = font['hmtx'] correct_advance = (hmtxTable.metrics['A'][0], 0) for key, value in hmtxTable.metrics.items(): print("advance: " + key + ", " + str(value)) hmtxTable.metrics[key] = correct_advance name = os.path.splitext(file)[0] font.flavor = "woff" font.save(name + ".woff") font.flavor = "woff2" font.save(name + ".woff2")
def upload_to_ia(force=set()): s = get_session() item = s.get_item("NotoFonts") hashdict = {f["name"]: f["md5"] for f in item.files} fonts_modified = False for path in tqdm(sorted(pathset)): filename = path.name file = open(path, "rb").read() hash = md5(file).hexdigest() if "fonts" not in force: try: if hashdict[filename] == hash: print("SKIPPING: " + filename) continue except KeyError: pass fonts_modified = True print("WORKING: " + filename) upload_paths = [] ttf = TTFont(path) print(" CONVERTING TO woff2...") ttf.flavor = "woff2" woff2_path = "upload/" + path.with_suffix(".woff2").name try: ttf.save(open(woff2_path, "wb")) upload_paths.append(woff2_path) except TTLibError: print("could not convert to woff2") print(" CONVERTING TO woff...") ttf.flavor = "woff" woff_path = "upload/" + path.with_suffix(".woff").name ttf.save(open(woff_path, "wb")) upload_paths.append(woff_path) print(" UPLOADING...") r = item.upload(files=[*upload_paths, str(path)], retries=100) for upath in [woff2_path, woff_path]: remove(upath) if "css" in force or fonts_modified: from generate_css import build_all_css print(" GENERATING CSS...") build_all_css() css_files = glob("*.css") for path in [Path(p) for p in sorted(css_files)]: filename = path.name file = open(path, "rb").read() hash = md5(file).hexdigest() # if "css" not in force: try: if hashdict[filename] == hash: print("SKIPPING: " + filename) continue except KeyError: pass print(" UPLOADING " + filename) r = item.upload(files=css_files, retries=100)
def generateWoff2(self, verbosity=0): woff2_list = [] os.makedirs(self.assetdir, exist_ok=True) for subname, (subrange, unicodes) in self.urdict.items(): if verbosity == 2: print("Processing", subname) subs = Subsetter() font = TTFont(self.fontfile) subs.populate(unicodes=unicodes) subs.subset(font) cmap = font.getBestCmap() glyphcount = len(font.getGlyphOrder()) - 1 if cmap: basefile = self.basename + "." + subname outfile = os.path.join(self.assetdir, basefile + ".woff2") font.flavor = 'woff2' font.save(outfile) if verbosity == 1: print("Generated", outfile) elif verbosity == 2: print(" Generated", outfile) print(" Found", glyphcount, "glyphs for", len(cmap), "out of", len(unicodes), "unicodes") outfile = os.path.join(self.assetdir, basefile + ".woff") font.flavor = 'woff' font.save(outfile) if verbosity == 1: print("Generated", outfile) elif verbosity == 2: print(" Generated", outfile) print(" Found", glyphcount, "glyphs for", len(cmap), "out of", len(unicodes), "unicodes") outfile = os.path.join(self.assetdir, basefile + ".ttf") font.flavor = '' font.save(outfile) woff2_list.append((os.path.join(self.assetdir, basefile), subrange)) if verbosity == 1: print("Generated", outfile) elif verbosity == 2: print(" Generated", outfile) print(" Found", glyphcount, "glyphs for", len(cmap), "out of", len(unicodes), "unicodes") else: if verbosity == 2: print(" Found no glyphs for any of", len(unicodes), "unicodes") font.close() return woff2_list
def main(): parser = argparse.ArgumentParser(description='convert TTFs to webfonts') parser.add_argument('files', metavar='FILE', nargs='*') args = parser.parse_args() for file in args.files: font = TTFont(file) name = os.path.splitext(file)[0] font.flavor = "woff" font.save(name + ".woff") font.flavor = "woff2" font.save(name + ".woff2")
def convert_font(input_path, output_path, flavor): ''' フォントの形式を選択して出力する。 ''' font = TTFont(input_path) font.flavor = flavor font.save(output_path)
def makeWeb(args): font = TTFont(args.file) base, ext = os.path.splitext(args.file) font.flavor = "woff" font.save(os.path.join(args.dir, base + ".woff")) font.close()
def main(): parser = argparse.ArgumentParser() parser.add_argument("fonts", nargs="+", help="One or more TTF or OTF fonts") parser.add_argument("--output-dir", help="The output dir for the compressed fonts") args = parser.parse_args() if args.output_dir: outputDir = pathlib.Path(args.output_dir).resolve() outputDir.mkdir(parents=True, exist_ok=True) else: outputDir = None for p in args.fonts: p = pathlib.Path(p).resolve() parentFolder = outputDir if outputDir is not None else p.parent fileName = p.stem + ".woff2" outputPath = parentFolder / fileName print("source:", p) print("destination:", outputPath) f = TTFont(p) f.flavor = "woff2" f.save(outputPath)
def webfonts(infont, type): font = TTFont(infont, recalcBBoxes=0) woffFileName = makeOutputFileName( infont, outputDir=None, extension='.' + type) font.flavor = type font.save(woffFileName, reorderTables=False) font.close()
def post(self): fontdata = self.request.POST.get('font', None) # Need to use isinstance as cgi.FieldStorage always evaluates to False. # See http://bugs.python.org/issue19097 if not isinstance(fontdata, cgi.FieldStorage): self.redirect('/font_conversion.html?' + urllib.urlencode( {'err_msg': 'Please select a font'})) return #TODO(bstell) make this work correctly. font_type = 'woff' name = os.path.splitext(os.path.basename(fontdata.filename))[0] try: font = TTFont(fontdata.file) except: self.redirect('/font_conversion.html?' + urllib.urlencode( {'err_msg': 'failed to parse font'})) return self.response.headers['Content-Type'] = 'application/font-woff' self.response.headers['Content-Disposition'] = \ 'attachment; filename={0}.{1}'.format(name, font_type) font.flavor = font_type output = StringIO.StringIO() font.save(output) self.response.out.write(output.getvalue())
def makeWeb(args): """If we are building a web version then try to minimise file size""" font = TTFont(args.file) # removed compatibility glyphs that of little use on the web ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) cmap = font['cmap'].buildReversed() unicodes = set([min(cmap[c]) for c in cmap]) for r in ranges: unicodes -= set(range(r[0], r[1] + 1)) options = subset.Options() options.set(layout_features='*', name_IDs='*', drop_tables=['DSIG']) subsetter = subset.Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(font) base, ext = os.path.splitext(args.file) for flavor in ("ttf", "woff", "woff2"): if flavor is not "ttf": font.flavor = flavor font.save(args.dir + "/" + base + "." + flavor) font.close()
def makeWeb(args): """If we are building a web version then try to minimise file size""" font = TTFont(args.file, recalcTimestamp=False) # removed compatibility glyphs that of little use on the web ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) cmap = font['cmap'].buildReversed() unicodes = set([min(cmap[c]) for c in cmap]) for r in ranges: unicodes -= set(range(r[0], r[1] + 1)) options = subset.Options() options.set(layout_features='*', name_IDs='*', drop_tables=['DSIG']) subsetter = subset.Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(font) base, ext = os.path.splitext(args.file) for flavor in ("woff", "woff2"): font.flavor = flavor font.save(args.dir + "/" + base + "." + flavor) font.close()
def webfonts(infont, type): font = TTFont(infont, recalcBBoxes=0) # Generate WOFF2 woffFileName = makeOutputFileName(infont, outputDir=None, extension='.' + type) print("Processing %s => %s" % (infont, woffFileName)) font.flavor = type font.save(woffFileName, reorderTables=False) font.close()
def makeWeb(args): """If we are building a web version then try to minimise file size""" font = TTFont(args.file) base, ext = os.path.splitext(args.file) for flavor in ("woff", "woff2"): font.flavor = flavor font.save(args.dir + "/" + base + "." + flavor) font.close()
def main(args=None): parser = ArgumentParser(description="Pre-process UFO files") parser.add_argument("input") parser.add_argument("flavor") parser.add_argument("output") options = parser.parse_args(args) font = TTFont(options.input) font.flavor = options.flavor font.save(options.output)
def generateInstances(config, args): # Create the output path if it doesn't exist. if not os.path.exists(args.outputPath): os.makedirs(args.outputPath) for style in tqdm(config, ascii=True, leave=False): font = TTFont(args.source) # Instantiate the font and update the name table. instantiateFont(font, style["axes"], inplace=True, overlap=True) updateNames(font, style) family = style.get("prefFamily") if family == None: family = style.get("family") subfamily = style.get("subfamily") prefSubfamily = style.get("prefSubfamily") if prefSubfamily == None: prefSubfamily = subfamily prefSubfamily = prefSubfamily.replace(" ", "") # Perform additional table fixups. font["head"].macStyle = getMacStyle(subfamily) font["OS/2"].fsSelection = makeSelection(font["OS/2"].fsSelection, subfamily) # Override weight if requested. weightOverride = style.get("weightOverride") if weightOverride != None: font["OS/2"].usWeightClass = weightOverride # Override width if requested. widthOverride = style.get("widthOverride") if widthOverride != None: font["OS/2"].usWidthClass = widthOverride dropVariationTables(font) # Fix contour overlap issues on macOS. if args.fixContour == True: setOverlapFlags(font) ext = args.format if args.format is not None else "ttf" filename = getPostscriptName(style) + f".{ext}" outputPath = os.path.join(args.outputPath, filename) font.flavor = args.format font.save(outputPath)
def decompress(input_file, output_file): """Decompress WOFF2 font to OpenType font. Args: input_file: a file path, file or file-like object (open in binary mode) containing a compressed WOFF2 font. output_file: a file path, file or file-like object where to save the decompressed OpenType font. """ log.info("Processing %s => %s" % (input_file, output_file)) font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False) font.flavor = None font.flavorData = None font.save(output_file, reorderTables=True)
def main(args=None): if args is None: args = sys.argv[1:] if len(args) < 1: print("One argument, the input filename, must be provided.", file=sys.stderr) sys.exit(1) filename = args[0] outfilename = make_output_name(filename) print("Processing %s => %s" % (filename, outfilename)) font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False) font.flavor = None font.save(outfilename, reorderTables=True)
def main(args=None): if args is None: args = sys.argv[1:] if len(args) < 1: print("One argument, the input filename, must be provided.", file=sys.stderr) return 1 filename = args[0] outfilename = makeOutputFileName(filename, outputDir=None, extension='.woff2') print("Processing %s => %s" % (filename, outfilename)) font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False) font.flavor = "woff2" font.save(outfilename, reorderTables=False)
def buildCOLRv1(designspacePath, ttfPath, outTTFPath, saveWoff2, neutralOnly=False): import pathlib ttfPath = pathlib.Path(ttfPath) if outTTFPath is None: outTTFPath = ttfPath.parent / (ttfPath.stem + "-colrv1" + ttfPath.suffix) else: outTTFPath = pathlib.Path(outTTFPath) ttf = TTFont(ttfPath) axisTags = [axis.axisTag for axis in ttf["fvar"].axes] axisTagToIndex = {tag: index for index, tag in enumerate(axisTags)} globalAxisNames = { axis.axisTag for axis in ttf["fvar"].axes if not axis.flags & 0x0001 } assert globalAxisNames == { axisTag for axisTag in axisTags if axisTag[0] != "V" } vcFont = VarCoFont(designspacePath) # Update the glyf table to contain bounding boxes for color glyphs estimateCOLRv1BoundingBoxes(vcFont, ttf, neutralOnly) vcData, varStore = prepareVariableComponentData(vcFont, axisTags, globalAxisNames, neutralOnly) colrGlyphs = buildCOLRGlyphs(vcData, axisTagToIndex) ttf["COLR"] = buildCOLR(colrGlyphs, varStore=varStore) ttf.save(outTTFPath) ttf = TTFont(outTTFPath, lazy=True) # Load from scratch if saveWoff2: outWoff2Path = outTTFPath.parent / (outTTFPath.stem + ".woff2") ttf.flavor = "woff2" ttf.save(outWoff2Path)
def buildVarC( designspacePath, ttfPath, outTTFPath, doTTX, saveWoff2, neutralOnly=False ): import pathlib registerCustomTableClass("VarC", "rcjktools.table_VarC", "table_VarC") ttfPath = pathlib.Path(ttfPath) if outTTFPath is None: outTTFPath = ttfPath.parent / (ttfPath.stem + "-varc" + ttfPath.suffix) else: outTTFPath = pathlib.Path(outTTFPath) ttf = TTFont(ttfPath) axisTags = [axis.axisTag for axis in ttf["fvar"].axes] globalAxisNames = {axisTag for axisTag in axisTags if axisTag[0] != "V"} vcFont = VarCoFont(designspacePath) vcData, allLocations, neutralGlyphNames = vcFont.extractVarCoData( globalAxisNames, neutralOnly ) if neutralGlyphNames: gvarTable = ttf["gvar"] for glyphName in neutralGlyphNames: del gvarTable.variations[glyphName] buildVarCTable(ttf, vcData, allLocations) if doTTX: outTTXPath = outTTFPath.parent / (outTTFPath.stem + "-before.ttx") ttf.saveXML(outTTXPath, tables=["VarC"]) ttf.save(outTTFPath) ttf = TTFont(outTTFPath, lazy=True) # Load from scratch if doTTX: outTTXPath = outTTFPath.parent / (outTTFPath.stem + "-after.ttx") ttf.saveXML(outTTXPath, tables=["VarC"]) if saveWoff2: outWoff2Path = outTTFPath.parent / (outTTFPath.stem + ".woff2") ttf.flavor = "woff2" ttf.save(outWoff2Path)
def compress(input_file, output_file, transform_tables=None): """Compress OpenType font to WOFF2. Args: input_file: a file path, file or file-like object (open in binary mode) containing an OpenType font (either CFF- or TrueType-flavored). output_file: a file path, file or file-like object where to save the compressed WOFF2 font. transform_tables: Optional[Iterable[str]]: a set of table tags for which to enable preprocessing transformations. By default, only 'glyf' and 'loca' tables are transformed. An empty set means disable all transformations. """ log.info("Processing %s => %s" % (input_file, output_file)) font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False) font.flavor = "woff2" if transform_tables is not None: font.flavorData = WOFF2FlavorData( data=font.flavorData, transformedTables=transform_tables ) font.save(output_file, reorderTables=False)
text = 'OFL v1.1' record.string = text names.append(record) # keep every thing else except Descriptor, Sample Text elif nameID not in (10, 19): names.append(record) name.names = names # FFTM is FontForge specific, remove it del(font['FFTM']) # force compiling GPOS/GSUB tables by fontTools, saves few tens of KBs for tag in ('GPOS', 'GSUB'): font[tag].compile(font) font.save(outfont) # Generate WOFF woffFileName = makeOutputFileName(infont, outputDir=None, extension='.woff') print("Processing %s => %s" % (infont, woffFileName)) font.flavor = "woff" font.save(woffFileName, reorderTables=False) # Generate WOFF2 woff2FileName = makeOutputFileName(infont, outputDir=None, extension='.woff2') print("Processing %s => %s" % (infont, woff2FileName)) font.flavor = "woff2" font.save(woff2FileName, reorderTables=False) font.close() os.remove(tmpfont)
return [n for n in ttf['name'].names if n.nameID == nameID][0].toUnicode() os.chdir(fontsdir) for infont in os.listdir(os.getcwd()): if not fontExtension.search(infont): continue print(infont) outwoff = fontExtension.sub('.woff', infont) outwoff2 = fontExtension.sub('.woff2', infont) try: ttf = TTFont(infont) except: print("Error opening {}".format(infont)) continue ttf.flavor = 'woff' ttf.save(outwoff) ttf.flavor = 'woff2' ttf.save(outwoff2) axes = OrderedDict() if 'STAT' in ttf and hasattr(ttf['STAT'], 'table'): axes['order'] = [ a.AxisTag for a in sorted(ttf['STAT'].table.DesignAxisRecord.Axis, key=lambda a: a.AxisOrdering) ] if 'fvar' in ttf: for axis in ttf['fvar'].axes: axes[axis.axisTag] = { 'name':
ttf, instance.nameID if hasattr(instance, 'nameID') else instance.subfamilyNameID), }) return axes for fontfile in glob.glob(infiles): fontfilebase = os.path.basename(fontfile)[:-4] outbase = os.path.join(outdir, fontfilebase) ttf = TTFont(fontfile, recalcBBoxes=False) #list axes fileAxes[fontfilebase] = getVarAxes(ttf) if 'DSIG' in ttf: del (ttf['DSIG']) wofffile = outbase + ".woff" ttf.flavor = 'woff' ttf.save(wofffile) #using binary here because json.dumps returns raw bytes with io.open(os.path.join(outdir, 'axes.json'), 'wb') as axesfile: jsonbytes = json.dumps(fileAxes, indent=2, ensure_ascii=False) if not isinstance(jsonbytes, bytes): jsonbytes = jsonbytes.encode('utf-8') axesfile.write(jsonbytes) sys.exit(0)
def convert(self, source_path: Path, target_path: Path) -> None: f = TTFont(source_path) f.flavor = "woff2" f.save(target_path)
# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import print_function from fontTools.ttLib import TTFont, sfnt from os.path import splitext import sys if __name__ == '__main__': if len(sys.argv) < 2: print("usage: python %s filename" % sys.argv[0], file=sys.stderr) sys.exit(1) filename = sys.argv[1] basename = splitext(filename)[0] sfnt.USE_ZOPFLI = True for flavor in ["woff", "woff2"]: outfilename = "%s.%s" % (basename, flavor) print("Processing %s => %s" % (filename, outfilename)) font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False) for t in font.keys(): if hasattr(font[t], "compile"): font[t].compile(font) font.flavor = flavor font.save(outfilename, reorderTables=False)
def compile_fonts(flavors=['woff', 'woff2']): import json import hashlib from fontTools.ttLib import TTFont import fontTools.subset tim = sc.textdata.tim() font_face_decls = [] if not fonts_output_dir.exists(): fonts_output_dir.mkdir() font_data = get_fonts_data() fonts_seen = set() font_keys = set() def get_font_details(font_name): font_name = font_name.lower() result = {} result.update(font_data['defaults']) for key, value in sorted(font_data["families"].items(), key=lambda t: len(t[0]), reverse=True): if font_name.startswith(key.lower()): fonts_seen.add(key) result["key"] = key leftovers = font_name[len(key): ] if not value: return None if isinstance(value, str): result["family"] = value font_keys.add((key, value)) elif isinstance(value, list): result["subset"] = value elif isinstance(value, dict): result["subset"] = [value] elif not value: pass break else: logger.error('Font file %s does not have a matching entry in fonts.json', font_name) return None for key, weight in sorted(font_data["weights"].items(), key=lambda t: len(t[0]), reverse=True): if key in leftovers: result["weight"] = weight break else: result["weight"] = "normal" for key, style in sorted(font_data["styles"].items(), key=lambda t: len(t[0]), reverse=True): if key in leftovers: result["style"] = style break else: result["style"] = "normal" return result seen = {} compiled_fonts = set(fonts_output_dir.glob('**/*')) valid_compiled_fonts = set() extra_global_subset_glyphs = ''.join(sorted(set(get_glyphs_from_table_data() + get_glyphs_from_template_data()))) for file in sorted(fonts_dir.glob('**/*')): changed = False nonfree = "nonfree" in file.parts if file in compiled_fonts: continue if file.suffix not in {'.ttf', '.otf', '.woff', '.woff2'}: continue base_name = sanitize_font_name(file.stem) if base_name in seen: logger.error('Font file %s is too similiar to other file %s, skipping', file, seen[base_name]) seen[base_name] = file with file.open('rb') as f: font_binary_data = f.read() md5 = hashlib.md5(font_binary_data) font_details = get_font_details(file.stem) if not font_details: continue if font_details['subset']: extra_subset_commands = ['--layout-features+=liga,dlig,smcp,c2sc,onum', '--desubroutinize'] if font_details['weight'] in {'bold', 'semibold'}: weight = 'bold' elif font_details['style'] in {'italic'}: weight = 'italic' else: weight = 'normal' for subset_details in font_details['subset']: subset_languages = subset_details['subset_languages'] subset_unicodes = set() if subset_languages == '*': # global subset subset_unicodes.update(extra_global_subset_glyphs) subset_unicodes.update(tim.get_codepoints_used(lang_uid=None, weight_or_style=weight)) else: if isinstance(subset_languages, str): subset_languages = [subset_languages] for language in subset_languages: codepoints = tim.get_codepoints_used(lang_uid=language, weight_or_style=weight) if codepoints is None: logger.error('Error in fonts.json, language uid "{}" not found in TIM'.format(language)) else: subset_unicodes.update(codepoints) subset_text = ''.join(subset_unicodes) subset_text = ''.join(sorted(subset_text.lower() + subset_text.upper())) subset_md5 = md5.copy() subset_md5.update(subset_text.encode(encoding='utf8')) subset_md5.update(''.join(extra_subset_commands).encode(encoding='utf8')) if False: #nonfree and not sc.config.app['debug']: outname = '' else: outname = name_to_var(subset_details['name']) + '_' outname += weight.replace('normal', 'regular') if subset_languages != '*': outname += '_' + '_'.join(subset_languages) outname += '_' + subset_md5.hexdigest()[:12] base_outfile = fonts_output_dir / outname suffix = font_details['save_as'][0] primary_out_file = base_outfile.with_suffix('.' + suffix) valid_compiled_fonts.add(primary_out_file) if not primary_out_file.exists(): changed = True with NamedTemporaryFile('w+t') as subset_text_file: subset_text_file.write(subset_text) subset_text_file.flush() fontTools.subset.main(args=[str(file), #'--layout-features=*', #'--glyph-names', #'--symbol-cmap', #'--legacy-cmap', #'--notdef-glyph', #'--notdef-outline', #'--recommended-glyphs', #'--name-IDs=*', #'--name-legacy', #'--name-languages=*', '--output-file={}'.format(str(primary_out_file)), '--text-file={}'.format(subset_text_file.name), '--flavor={}'.format(suffix)] + extra_subset_commands) font = None size = {} for flavor in font_details['save_as'][1:]: suffix = '.{}'.format(flavor) outfile = base_outfile.with_suffix(suffix) valid_compiled_fonts.add(outfile) if not outfile.exists(): changed = True if font is None: font = TTFont(str(primary_out_file)) font.flavor = flavor font.save(file=str(outfile)) size[flavor] = sizeof_fmt(outfile.stat().st_size) font_face_decls.append(font_face_template.format(url='/fonts/compiled/{}'.format(outname), family=subset_details['name'], weight=font_details['weight'], style=font_details['style'], size=size, )) else: size = {} md5sum = md5.hexdigest()[:12] outname = md5sum if (nonfree and not sc.config.app['debug']) else "{}_{}".format(base_name, md5sum) base_outfile = fonts_output_dir / outname font = None for flavor in flavors: suffix = '.{}'.format(flavor) outfile = base_outfile.with_suffix(suffix) valid_compiled_fonts.add(outfile) if not outfile.exists(): changed = True if outfile.suffix == file.suffix: with outfile.open('wb') as f: f.write(font_binary_data) else: if font is None: font = TTFont(str(file)) font.flavor = flavor font.save(file=str(outfile)) size[flavor] = sizeof_fmt(outfile.stat().st_size) if font_details: font_face_decls.append(font_face_template.format(url='/fonts/compiled/{}'.format(outname), size=size, **font_details)) if changed: print('Processed {!s}'.format(file.name)) unneeded_fonts = compiled_fonts - valid_compiled_fonts if unneeded_fonts: print('Removing {} unused compiled fonts'.format(len(unneeded_fonts))) for file in unneeded_fonts: file.unlink() def details_to_variable(details): if isinstance(details, list): raise TypeError('details should not be a list') if isinstance(details, str): name = details variable = name_to_var(name) else: if 'var' in details: variable = details['var'] name = details['name'] else: name = details['name'] variable = name_to_var(name) return {"variable": variable, "name": name} variable_decls = [] for key, details in sorted(font_data["families"].items()): if details: variable_decls.extend(details_to_variable(details) for details in (details if isinstance(details, list) else [details])) with (sc.static_dir / 'css' / 'fonts' / 'fonts-auto.scss').open('w') as f: f.write(font_header) f.writelines("${variable}: '{name}';\n".format(**e) for e in variable_decls) f.writelines(font_face_decls) for key in set(font_data["families"]) - fonts_seen: logger.error('Font family mapping matches no font file: {} ({})'.format(key, font_data["families"][key]))
] font['cmap'].tables = [ table for table in font['cmap'].tables if table.platformID != 1 ] # fix OS/2 and hhea metrics glyf = font['glyf'] ascent = int( max(glyf[c].yMax for c in font.getGlyphOrder() if hasattr(glyf[c], "yMax"))) descent = -int( min(glyf[c].yMin for c in font.getGlyphOrder() if hasattr(glyf[c], "yMin"))) font['OS/2'].usWinAscent = ascent font['OS/2'].usWinDescent = descent font['hhea'].ascent = ascent font['hhea'].descent = -descent # save TTF font.save(font_file, reorderTables=None) # save WOFF font.flavor = 'woff' font.save(os.path.join('woff', font_name + '.woff'), reorderTables=None) # save WOFF2 font.flavor = 'woff2' font.save(os.path.join('woff2', font_name + '.woff2'), reorderTables=None)
string = string.replace(value, data[value]) # 将编码按照映射表进行替换 return string with urllib.request.urlopen( 'http://piaofang.maoyan.com/?ver=normal') as f: # 爬去地址 html = BeautifulSoup( f.read(), "html.parser") # 使用BeautifulSoup用html.parser解析器进行html解析 font = html.find(id="js-nuwa") # 读取存放字体文件的css内容 font = re.search(r'(?<=,)\S*(?=\))', str(font)) # 用正则表达式找出woff的base64字符串 if not font: print('未发现字体') exit() font = base64.b64decode(font.group()) # base64解码成二进制文件 with open(basedir + 'temp.woff', 'wb') as f: f.write(font) # 保存二进制到脚本根目录 font = TTFont(basedir + 'temp.woff', recalcBBoxes=False, recalcTimestamp=False) # Pillow不支持用woff生成文字图片 font.flavor = None font.save(basedir + 'temp.ttf', reorderTables=True) # 将字体文件从woff转换成ttf for n in html.find_all('ul', 'canTouch'): print( n.find('li', 'c1').b.string, distinguish(n.find('li', 'c1').find('i', 'cs').string), distinguish(n.find('li', 'c2').b.i.string), distinguish(n.find('li', 'c3').i.string), distinguish(n.find('li', 'c4').i.string), distinguish(n.find('li', 'c5').span.i.string), )
# We round-trip through a buffer to workaround PIL not # resetting the dpi value. data = im.tobytes() im = Image.frombytes(im.mode, im.size, data) if dst.exists(): dstIm = Image.open(dst) if data == dstIm.tobytes(): # Don't save when the image data is the same, some # meta data may still have changed, making us do # unwanted commits. print("-- same image, skipping", dst) continue im.save(dst) print("Subsetting fonts...") for src in sorted(docsSourceFonts.glob("*.woff2")): dst = docsFonts / src.name font = TTFont(src) subsetter = Subsetter() unicodes = set(ord(c) for c in markdownSource) subsetter.populate(unicodes=unicodes) subsetter.subset(font) if dst.exists(): existing = TTFont(dst, lazy=True) if sorted(font.getBestCmap()) == sorted(existing.getBestCmap()): print("-- same cmap, skipping", dst) continue font.flavor = "woff2" font.save(dst)
def compile_fonts(flavors=["woff", "woff2"]): import json import hashlib from fontTools.ttLib import TTFont import fontTools.subset tim = sc.textdata.tim() font_face_decls = [] if not fonts_output_dir.exists(): fonts_output_dir.mkdir() font_data = get_fonts_data() fonts_seen = set() font_keys = set() def get_font_details(font_name): font_name = font_name.lower() result = {} result.update(font_data["defaults"]) for key, value in sorted(font_data["families"].items(), key=lambda t: len(t[0]), reverse=True): if font_name.startswith(key.lower()): fonts_seen.add(key) result["key"] = key leftovers = font_name[len(key) :] if not value: return None if isinstance(value, str): result["family"] = value font_keys.add((key, value)) elif isinstance(value, list): result["subset"] = value elif isinstance(value, dict): result["subset"] = [value] elif not value: pass break else: logger.error("Font file %s does not have a matching entry in fonts.json", font_name) return None for key, weight in sorted(font_data["weights"].items(), key=lambda t: len(t[0]), reverse=True): if key in leftovers: result["weight"] = weight break else: result["weight"] = "normal" for key, style in sorted(font_data["styles"].items(), key=lambda t: len(t[0]), reverse=True): if key in leftovers: result["style"] = style break else: result["style"] = "normal" return result seen = {} compiled_fonts = set(fonts_output_dir.glob("**/*")) valid_compiled_fonts = set() extra_global_subset_glyphs = get_glyphs_from_table_data() for file in sorted(fonts_dir.glob("**/*")): changed = False nonfree = "nonfree" in file.parts if file in compiled_fonts: continue if file.suffix not in {".ttf", ".otf", ".woff", ".woff2"}: continue base_name = sanitize_font_name(file.stem) if base_name in seen: logger.error("Font file %s is too similiar to other file %s, skipping", file, seen[base_name]) seen[base_name] = file with file.open("rb") as f: font_binary_data = f.read() md5 = hashlib.md5(font_binary_data) font_details = get_font_details(file.stem) if not font_details: continue if font_details["subset"]: extra_subset_commands = ["--layout-features+=liga,dlig,smcp,c2sc,onum", "--desubroutinize"] if font_details["weight"] in {"bold", "semibold"}: weight = "bold" elif font_details["style"] in {"italic"}: weight = "italic" else: weight = "normal" for subset_details in font_details["subset"]: subset_languages = subset_details["subset_languages"] subset_unicodes = set() if subset_languages == "*": # global subset subset_unicodes.update(extra_global_subset_glyphs) subset_unicodes.update(tim.get_codepoints_used(lang_uid=None, weight_or_style=weight)) else: if isinstance(subset_languages, str): subset_languages = [subset_languages] for language in subset_languages: codepoints = tim.get_codepoints_used(lang_uid=language, weight_or_style=weight) if codepoints is None: logger.error('Error in fonts.json, language uid "{}" not found in TIM'.format(language)) else: subset_unicodes.update(codepoints) subset_text = "".join(subset_unicodes) subset_text = "".join(sorted(subset_text.lower() + subset_text.upper())) subset_md5 = md5.copy() subset_md5.update(subset_text.encode(encoding="utf8")) subset_md5.update("".join(extra_subset_commands).encode(encoding="utf8")) if False: # nonfree and not sc.config.app['debug']: outname = "" else: outname = name_to_var(subset_details["name"]) + "_" outname += weight.replace("normal", "regular") if subset_languages != "*": outname += "_" + "_".join(subset_languages) outname += "_" + subset_md5.hexdigest()[:12] base_outfile = fonts_output_dir / outname suffix = font_details["save_as"][0] primary_out_file = base_outfile.with_suffix("." + suffix) valid_compiled_fonts.add(primary_out_file) if not primary_out_file.exists(): changed = True with NamedTemporaryFile("w+t") as subset_text_file: subset_text_file.write(subset_text) subset_text_file.flush() fontTools.subset.main( args=[ str(file), #'--layout-features=*', #'--glyph-names', #'--symbol-cmap', #'--legacy-cmap', #'--notdef-glyph', #'--notdef-outline', #'--recommended-glyphs', #'--name-IDs=*', #'--name-legacy', #'--name-languages=*', "--output-file={}".format(str(primary_out_file)), "--text-file={}".format(subset_text_file.name), "--flavor={}".format(suffix), ] + extra_subset_commands ) font = None size = {} for flavor in font_details["save_as"][1:]: suffix = ".{}".format(flavor) outfile = base_outfile.with_suffix(suffix) valid_compiled_fonts.add(outfile) if not outfile.exists(): changed = True if font is None: font = TTFont(str(primary_out_file)) font.flavor = flavor font.save(file=str(outfile)) size[flavor] = sizeof_fmt(outfile.stat().st_size) font_face_decls.append( font_face_template.format( url="/fonts/compiled/{}".format(outname), family=subset_details["name"], weight=font_details["weight"], style=font_details["style"], size=size, ) ) else: size = {} md5sum = md5.hexdigest()[:12] outname = md5sum if (nonfree and not sc.config.app["debug"]) else "{}_{}".format(base_name, md5sum) base_outfile = fonts_output_dir / outname font = None for flavor in flavors: suffix = ".{}".format(flavor) outfile = base_outfile.with_suffix(suffix) valid_compiled_fonts.add(outfile) if not outfile.exists(): changed = True if outfile.suffix == file.suffix: with outfile.open("wb") as f: f.write(font_binary_data) else: if font is None: font = TTFont(str(file)) font.flavor = flavor font.save(file=str(outfile)) size[flavor] = sizeof_fmt(outfile.stat().st_size) if font_details: font_face_decls.append( font_face_template.format(url="/fonts/compiled/{}".format(outname), size=size, **font_details) ) if changed: print("Processed {!s}".format(file.name)) unneeded_fonts = compiled_fonts - valid_compiled_fonts if unneeded_fonts: print("Removing {} unused compiled fonts".format(len(unneeded_fonts))) for file in unneeded_fonts: file.unlink() def details_to_variable(details): if isinstance(details, list): raise TypeError("details should not be a list") if isinstance(details, str): name = details variable = name_to_var(name) else: if "var" in details: variable = details["var"] name = details["name"] else: name = details["name"] variable = name_to_var(name) return {"variable": variable, "name": name} variable_decls = [] for key, details in sorted(font_data["families"].items()): if details: variable_decls.extend( details_to_variable(details) for details in (details if isinstance(details, list) else [details]) ) with (sc.static_dir / "css" / "fonts" / "fonts-auto.scss").open("w") as f: f.write(font_header) f.writelines("${variable}: '{name}';\n".format(**e) for e in variable_decls) f.writelines(font_face_decls) for key in set(font_data["families"]) - fonts_seen: logger.error("Font family mapping matches no font file: {} ({})".format(key, font_data["families"][key]))
def WOFF2Builder(sourcePath, destinationPath): font = TTFont(sourcePath) font.flavor = "woff2" font.save(destinationPath)
def woff_size(font: TTFont, path: Path) -> int: font.flavor = "woff2" woff_path = path.with_suffix(".woff2") font.save(woff_path) return woff_path.stat().st_size
import os import sys from fontTools.ttLib import TTFont dir = './' if len(sys.argv) > 1: dir = sys.argv[1] if dir[-1] != '/': dir += '/' for file in os.listdir(dir): if file.endswith('.ttf'): f = TTFont(dir + file) f.flavor = 'woff2' f.save(dir + file[:-4] + '.woff2')