def main(args): """Main program to run preprocessing of the font Arguments: font-file --hinting=(False|True) ,default is false """ options = Options() args = options.parse_opts(args, ignore_unknown=True) if len(args) < 1: print('usage: ./pyprepfnt font-file [--option=value]...', file=sys.stderr) sys.exit(1) fontfile = args[0] args = args[1:] filename, extension = os.path.splitext(fontfile) cleanfile = filename + '_clean' + extension cleanup.cleanup(fontfile, False, cleanfile) closure.dump_closure_map(cleanfile, '.') preprocess = Preprocess(cleanfile, '.') preprocess.base_font() preprocess.cmap_dump() preprocess.serial_glyphs()
def run(self, filename, pipedata): if 'optimize' in pipedata and not pipedata['optimize']: return self.bakery.logging_raw('### Optimize TTF {}'.format(filename)) # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py from fontTools.subset import Options, Subsetter, load_font, save_font options = Options() options.layout_features = "*" options.name_IDs = "*" options.hinting = True options.notdef_outline = True font = load_font(op.join(self.builddir, filename), options) subsetter = Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) save_font(font, op.join(self.builddir, filename + '.opt'), options) newsize = op.getsize(op.join(self.builddir, filename + '.opt')) origsize = op.getsize(op.join(self.builddir, filename)) # compare filesizes TODO print analysis of this :) comment = "# look at the size savings of that subset process" self.bakery.logging_cmd("ls -l '%s'* %s" % (filename, comment)) statusmessage = "{0}.opt: {1} bytes\n{0}: {2} bytes\n" self.bakery.logging_raw(statusmessage.format(filename, newsize, origsize)) # move ttx files to src shutil.move(op.join(self.builddir, filename + '.opt'), op.join(self.builddir, filename), log=self.bakery.logger)
def generate_subset(unicode_range, flavor, font_file, output_dir): """ Generate font subset. You can do the same with the following command. $ pyftsubset YOUR_FONT.otf \ --unicodes=U+943a-943b \ --layout-features='*' \ --flavor=woff \ --name-IDs='*' \ --output-file=style/font-subsets/YOUR_FONT-subset-1.woff """ args = ["--layout-features='*'", "--flavor=%s" % flavor] options = Options() options.parse_opts(args) subsetter = Subsetter(options) font = load_font(font_file, options) subsetter.populate(unicodes=parse_unicodes(unicode_range)) subsetter.subset(font) font_path = Path(font_file) name = font_path.stem unicode_range_hash = _get_unicode_range_hash(unicode_range) outfile = "%s/%s/%s-subset-%s.%s" % ( output_dir, FONT_DIR, name, unicode_range_hash, flavor, ) save_font(font, outfile, options) font.close()
def execute_pyftsubset(self, pipedata, subsetname, name, glyphs="", args=""): from fontTools.subset import Subsetter, Options, load_font, save_font target_file = '{0}.{1}'.format(op.join(self.builddir, name)[:-4], subsetname) options = Options() cmd_options = '' if pipedata.get('pyftsubset'): cmd_options = pipedata['pyftsubset'] options.parse_opts(pipedata['pyftsubset'].split()) if pipedata.get('pyftsubset.%s' % subsetname): cmd_options = pipedata['pyftsubset.%s' % subsetname] options.parse_opts(pipedata['pyftsubset.%s' % subsetname].split()) font = load_font(op.join(self.builddir, name), options) unicodes = re_range([int(g.replace('U+', ''), 16) for g in glyphs.split()]) self.bakery.logging_cmd('pyftsubset --unicodes="{0}" {2} {1}'.format(unicodes, name, cmd_options)) subsetter = Subsetter(options=options) subsetter.populate(unicodes=[int(g.replace('U+', ''), 16) for g in glyphs.split()]) subsetter.subset(font) self.bakery.logging_cmd('mv {0}.subset {1}'.format(name, target_file)) save_font(font, target_file, options)
def subsetFont(base64, subset): # tmp file names tmpInputFontName = tmpFileName(".ttf") tmpOutputFontName = tmpFileName(".woff") # remove data header from base64 fontbase64 = base64.split(",")[1] with open(tmpInputFontName, "wb") as f: fontinput = f.write(fontbase64.decode('base64')) f.close() # open the font with fontTools font = TTFont(tmpInputFontName) options = Options() options.desubroutinize = True # export the font as woff for web use options.with_zopfli = True options.flavor = "woff" subsetter = Subsetter(options=options) subsetter.populate(text=subset) subsetter.subset(font) save_font(font, tmpOutputFontName, options) subsettedFont = open(tmpOutputFontName, "rb").read().encode("base64") os.unlink(tmpOutputFontName) os.unlink(tmpInputFontName) return {'subset': subsettedFont}
def main(args): """Subset a font (useful for making small test fonts). Args: args: list, arguments the user typed. """ parser = argparse.ArgumentParser() parser.add_argument('fontfile', help='Input font file') parser.add_argument('--subset_postfix', default='', help='Postfix to the subset extension') parser.add_argument('--text', default='', help='Text to include in the subset') parser.add_argument('--unicodes', default='', help='Comma separated list of Unicode codepoints (hex) ' 'to include in the subset; eg, "e7,0xe8,U+00e9"') parser.add_argument('--glyphs', default='', help='Comma separated list of glyph IDs (decimal) to ' 'include in the subset; eg, "1,27"') parser.add_argument('--hinting', default=False, action='store_true', help='Enable hinting if specified, no hinting if not ' 'present') cmd_args = parser.parse_args(args) options = Options() # Definitely want the .notdef glyph and outlines. options.notdef_glyph = True options.notdef_outline = True # Get the item. to keep in the subset. text = cmd_args.text unicodes_str = cmd_args.unicodes.lower().replace('0x', '').replace('u+', '') # TODO(bstell) replace this whole files by using the new subset.py code unicodes_input = [c for c in unicodes_str.split(',') if c] unicodes = [] for c in unicodes_input: if '-' in c: uni_range = c.split('-') uni_range_expanded = range(int(uni_range[0], 16), int(uni_range[1], 16) + 1) unicodes.extend(uni_range_expanded) else: unicodes.append(int(c, 16)) #unicodes = [int(c, 16) for c in unicodes_input_expanded] glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c] fontfile = cmd_args.fontfile options.hinting = cmd_args.hinting # False => no hinting options.hinting = True # hint stripping for CFF is currently broken dirname = os.path.dirname(fontfile) basename = os.path.basename(fontfile) filename, extension = os.path.splitext(basename) subset_postfix = cmd_args.subset_postfix output_file = dirname + '/' + filename + '_subset' + subset_postfix + extension print "output_file =", output_file font = load_font(fontfile, options, lazy=False) subsetter = Subsetter(options) subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs) subsetter.subset(font) save_font(font, output_file, options)
def closure(self): """Takes closure of glyphs specified by glyph_names and glyph_codes. """ options = Options() options.notdef_glyph = False subsetter = Subsetter(options=options) subsetter.populate(glyphs=self.glyph_names, unicodes=self.glyph_codes) subsetter._closure_glyphs(self.font) gids = sorted(self.glyph_name_to_id[gg] for gg in subsetter.glyphs_all if gg != '.notdef') return gids
def subset(fontfile, outfile_basename, glyphs): options = Options() # Fonttools has this feature that if you enable 'dlig', it will also give # you glyphs that you did not ask for, but if you do not enable 'dlig', # then discretionary ligatures do not render properly. # See https://github.com/behdad/fonttools/issues/43. # As a workaround, only enable 'dlig' if there are glyphs for discretionary # ligatures. # TODO: This should be fixed, consider upgrading. # https://github.com/fonttools/fonttools/commit/022536212be4cf022a2cb9a286fec8be1931d19b. dligs = set(glyphs).intersection(['c_b', 'c_h', 'c_k', 'c_p', 'ct', 'g_i', 'q_u', 's_b', 's_h', 's_k', 's_p', 'st']) if len(dligs) > 0: options.layout_features.append('dlig') else: # Due to a bug in Fonttools, options are actually global, so the # remnants of the previous instance are visible here. # See https://github.com/behdad/fonttools/issues/413. if 'dlig' in options.layout_features: options.layout_features.remove('dlig') # Same for small caps, it needs to be enabled explicitly. Luckily, only the # glyphs in the list get included, no extra ones. if any(g.endswith('.smcp') for g in glyphs): options.layout_features.append('smcp') options.layout_features.append('c2sc') else: if 'smcp' in options.layout_features: options.layout_features.remove('smcp') if 'c2sc' in options.layout_features: options.layout_features.remove('c2sc') # Fonts that went through the FontForge roundtrip will have subroutinized # programs in the CFF table. This presumably reduces file size for full # fonts, but on subsetted fonts it hurts file size and compressability, so # desubroutinize. options.desubroutinize = True font = load_font(fontfile, options) subsetter = Subsetter(options = options) subsetter.populate(glyphs = glyphs) subsetter.subset(font) prune_cmaps(font) options.flavor = "woff" save_font(font, outfile_basename + ".woff", options) options.flavor = "woff2" save_font(font, outfile_basename + ".woff2", options) font.close()
def subset(fontfile, outfile_basename, glyphs): options = Options() # Fonttools has this "feature" that if you enable 'dlig', it will also give # you glyphs that you did not ask for, but if you do not enable 'dlig', # then discretionary ligatures do not render properly. # See https://github.com/behdad/fonttools/issues/43. # As a workaround, only enable 'dlig' if there are glyphs for discretionary # ligatures. dligs = set(glyphs).intersection([ 'c_b', 'c_h', 'c_k', 'c_p', 'ct', 'g_i', 'q_u', 's_b', 's_h', 's_k', 's_p', 'st' ]) if len(dligs) > 0: options.layout_features.append('dlig') else: # Due to a bug in Fonttools, options are actually global, so the # remnants of the previous instance are visible here. # See https://github.com/behdad/fonttools/issues/413. if 'dlig' in options.layout_features: options.layout_features.remove('dlig') # Same for small caps, it needs to be enabled explicitly. Luckily, only the # glyphs in the list get included, no extra ones. if any(g.endswith('.smcp') for g in glyphs): options.layout_features.append('smcp') options.layout_features.append('c2sc') else: if 'smcp' in options.layout_features: options.layout_features.remove('smcp') if 'c2sc' in options.layout_features: options.layout_features.remove('c2sc') # Fonts that went through the FontForge roundtrip will have subroutinized # programs in the CFF table. This presumably reduces file size for full # fonts, but on subsetted fonts it hurts file size and compressability, so # desubroutinize. options.desubroutinize = True font = load_font(fontfile, options) subsetter = Subsetter(options=options) subsetter.populate(glyphs=glyphs) subsetter.subset(font) prune_cmaps(font) options.flavor = "woff" save_font(font, outfile_basename + ".woff", options) options.flavor = "woff2" save_font(font, outfile_basename + ".woff2", options) font.close()
def subset(fontfile, outfile_basename, glyphs): options = Options() # Fonttools has this "feature" that if you enable 'dlig', it will also give # you glyphs that you did not ask for, but if you do not enable 'dlig', # then discretionary ligatures do not render properly. # See https://github.com/behdad/fonttools/issues/43. # As a workaround, only enable 'dlig' if there are glyphs for discretionary # ligatures. dligs = set(glyphs).intersection(["c_b", "c_h", "c_k", "c_p", "ct", "g_i", "q_u", "s_b", "s_h", "s_k", "s_p", "st"]) if len(dligs) > 0: options.layout_features.append("dlig") else: # Due to a bug in Fonttools, options are actually global, so the # remnants of the previous instance are visible here. # See https://github.com/behdad/fonttools/issues/413. if "dlig" in options.layout_features: options.layout_features.remove("dlig") # Same for small caps, it needs to be enabled explicitly. Luckily, only the # glyphs in the list get included, no extra ones. if any(g.endswith(".smcp") for g in glyphs): options.layout_features.append("smcp") options.layout_features.append("c2sc") else: if "smcp" in options.layout_features: options.layout_features.remove("smcp") if "c2sc" in options.layout_features: options.layout_features.remove("c2sc") # Fonts that went through the FontForge roundtrip will have subroutinized # programs in the CFF table. This presumably reduces file size for full # fonts, but on subsetted fonts it hurts file size and compressability, so # desubroutinize. options.desubroutinize = True font = load_font(fontfile, options) subsetter = Subsetter(options=options) subsetter.populate(glyphs=glyphs) subsetter.subset(font) prune_cmaps(font) options.flavor = "woff" save_font(font, outfile_basename + ".woff", options) options.flavor = "woff2" save_font(font, outfile_basename + ".woff2", options) font.close()
def make_options(self, featurs): options = Options() options.recalc_bounds = True options.recalc_timestamp = True options.recalc_average_width = True options.recalc_max_context = True options.drop_tables = [] options.passthrough_tables = True options.layout_features = featurs return options
def subset_font(basefile_path, buff, text): options = Options() options.name_IDs = [] options.obfuscate_names = True options.flavor = 'woff' font = load_font(basefile_path, options) subsetter = Subsetter(options=options) subsetter.populate(text=text) subsetter.subset(font) save_font(font, buff, options) font.close() buff.seek(0) return
def main(args): """Subset a font (useful for making small test fonts). Args: args: list, arguments the user typed. """ parser = argparse.ArgumentParser() parser.add_argument('fontfile', help='Input font file') parser.add_argument('--text', default='', help='Text to include in the subset') parser.add_argument('--unicodes', default='', help='Comma separated list of Unicode codepoints (hex) ' 'to include in the subset; eg, "e7,0xe8,U+00e9"') parser.add_argument('--glyphs', default='', help='Comma separated list of glyph IDs (decimal) to ' 'include in the subset; eg, "1,27"') parser.add_argument('--hinting', default=False, action='store_true', help='Enable hinting if specified, no hinting if not ' 'present') cmd_args = parser.parse_args(args) options = Options() # Definitely want the .notdef glyph and outlines. options.notdef_glyph = True options.notdef_outline = True # Get the item. to keep in the subset. text = cmd_args.text unicodes_str = cmd_args.unicodes.lower().replace('0x', '').replace('u+', '') unicodes = [int(c, 16) for c in unicodes_str.split(',') if c] glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c] fontfile = cmd_args.fontfile options.hinting = cmd_args.hinting # False => no hinting dirname = os.path.dirname(fontfile) basename = os.path.basename(fontfile) filename, extension = os.path.splitext(basename) output_file = dirname + '/' + filename + '_subset' + extension font = load_font(fontfile, options, lazy=False) subsetter = Subsetter(options) subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs) subsetter.subset(font) save_font(font, output_file, options)
def __init__(self, fontfile, hinting, whitespace_and_ignorable_list): self.fontfile = fontfile self.options = Options() self.options.hinting = hinting # Want the .notdef glyph and outlines. self.options.notdef_glyph = True self.options.notdef_outline = True self.options.desubroutinize = True self.font = load_font(fontfile, self.options, lazy=False) self.whitespace_and_ignorable_list = whitespace_and_ignorable_list
def __generateForWeight(self, weight: str, font: Font) -> None: package = self.__core.package base_dir = self.__core.directories.webfonts output_dir = base_dir.joinpath(f"./{package.version}/{weight}") font_path = self.__core.findFontfilePath(font) output_dir.mkdir(parents=True, exist_ok=True) metadata = self.__generateMetadata() fake = Faker() fake.seed(package.id) subset_fontname: str = fake.name() options = Options() options.font_number = font.number options.hinting = False options.desubroutinize = True options.drop_tables += [ 'FFTM', 'PfEd', 'TeX', 'BDF', 'cvt', 'fpgm', 'prep', 'gasp', 'VORG', 'CBDT', 'CBLC', 'sbix' ] for ignored in ['rvrn', 'locl']: options.layout_features.remove(ignored) for unicodes_file in FILE_DIR.UNICODE_TEXT.glob('./**/*.txt'): idx = unicodes_file.stem unicodes: List[str] = [] with open(unicodes_file, 'r') as unicode_read_io: for line in unicode_read_io.readlines(): unicodes.extend(parse_unicodes(line.split('#')[0])) with load_font(font_path, options) as ttfont: subsetter = Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(ttfont) for record in ttfont['name'].names: if record.nameID == NAME_ID.COPYRIGHT: record.string = '\n'.join(package.copyrights) elif record.nameID in FAMILY_RELATED_NAME_ID: record.string = subset_fontname woff_file = output_dir.joinpath(f"{idx}.woff") with open(woff_file, 'wb') as woff_write_io: options.flavor = 'woff' ttfont.flavorData = WOFFFlavorData() ttfont.flavorData.metaData = metadata save_font(ttfont, woff_write_io, options) woff2_file = output_dir.joinpath(f"{idx}.woff2") with open(woff2_file, 'wb') as woff2_write_io: options.flavor = 'woff2' ttfont.flavorData = WOFF2FlavorData() ttfont.flavorData.metaData = metadata save_font(ttfont, woff2_write_io, options)
def convertFont(fontPath, fontType): options = Options() tmpOutputTtf = tmpFileName(".ttf") tmpOutputWoff = tmpFileName(".woff") font = TTFont(fontPath) ttfOptions = Options() # export the font as woff for web use woffOptions = Options() woffOptions.with_zopfli = True woffOptions.flavor = "woff" if fontType == "otf": # convert the font to ttf ttfFont = otf_to_ttf(font) # save font can also convert to woff! save_font(ttfFont, tmpOutputTtf, ttfOptions) save_font(ttfFont, tmpOutputWoff, woffOptions) elif fontType == "ttf": save_font(font, tmpOutputTtf, ttfOptions) save_font(font, tmpOutputWoff, woffOptions) else: print "wrong type" ttfBase64 = "data:;base64," + toBase64(tmpOutputTtf) woffBase64 = "data:;base64," + toBase64(tmpOutputWoff) #cleanup files cleanUp([tmpOutputWoff, tmpOutputTtf]) print woffBase64.replace("\n", "") print ttfBase64.replace("\n", "")
def run(self, pipedata): if 'optimize' in pipedata and not pipedata['optimize']: return from bakery_cli.utils import ProcessedFile filename = ProcessedFile() self.bakery.logging_raw('### Optimize TTF {}'.format(filename)) # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py from fontTools.subset import Options, Subsetter, load_font, save_font options = Options() options.layout_features = ["*"] options.name_IDs = ["*"] options.hinting = True options.legacy_kern = True options.notdef_outline = True options.no_subset_tables += ['DSIG'] options.drop_tables = list( set(options._drop_tables_default) - set(['DSIG'])) cmd_options = ('--glyphs=*' ' --layout-features=*' ' --name-IDs=*' ' --hinting' ' --legacy-kern --notdef-outline' ' --no-subset-tables+=DSIG' ' --drop-tables-=DSIG') font = load_font(op.join(self.builddir, filename), options) cmdline = 'pyftsubset {1} {0}'.format(cmd_options, op.join(self.builddir, filename)) self.bakery.logging_cmd(cmdline) subsetter = Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) save_font(font, op.join(self.builddir, filename + '.fix'), options) # compare filesizes TODO print analysis of this :) comment = "# look at the size savings of that subset process" self.bakery.logging_cmd(comment) run(u"ls -la {0} {0}.fix | awk '{{ print $5 \"\t\" $9 }}'".format( unicode(op.join(self.builddir, filename)))) comment = "# copy back optimized ttf to original filename" self.bakery.logging_cmd(comment) shutil.move(op.join(self.builddir, filename + '.fix'), op.join(self.builddir, filename))
def execute_pyftsubset(self, pipedata, subsetname, name, glyphs="", args=""): from fontTools.subset import Subsetter, Options, load_font, save_font target_file = '{0}.{1}'.format( op.join(self.builddir, name)[:-4], subsetname) options = Options() cmd_options = '' if pipedata.get('pyftsubset'): cmd_options = pipedata['pyftsubset'] options.parse_opts(pipedata['pyftsubset'].split()) if pipedata.get('pyftsubset.%s' % subsetname): cmd_options = pipedata['pyftsubset.%s' % subsetname] options.parse_opts(pipedata['pyftsubset.%s' % subsetname].split()) font = load_font(op.join(self.builddir, name), options) unicodes = re_range( [int(g.replace('U+', ''), 16) for g in glyphs.split()]) self.bakery.logging_cmd('pyftsubset --unicodes="{0}" {2} {1}'.format( unicodes, name, cmd_options)) subsetter = Subsetter(options=options) subsetter.populate( unicodes=[int(g.replace('U+', ''), 16) for g in glyphs.split()]) subsetter.subset(font) self.bakery.logging_cmd('mv {0}.subset {1}'.format(name, target_file)) save_font(font, target_file, options)
def subsetFont(fontPath, subset): tmpOutputFontName = os.path.dirname( os.path.abspath(__file__)) + "/tmp/" + str(uuid.uuid4()) + ".woff" font = TTFont(fontPath) options = Options() options.desubroutinize = True options.with_zopfli = True options.flavor = "woff" subsetter = Subsetter(options=options) subsetter.populate(text=subset) subsetter.subset(font) save_font(font, tmpOutputFontName, options) subsettedFont = 'data:;base64,' + open(tmpOutputFontName, "rb").read().encode("base64") cleanUp([tmpOutputFontName]) print subsettedFont.replace('\n', '')
def run(self, filename, pipedata): if 'optimize' in pipedata and not pipedata['optimize']: return self.bakery.logging_raw('### Optimize TTF {}'.format(filename)) # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py from fontTools.subset import Options, Subsetter, load_font, save_font options = Options() options.layout_features = ["*"] options.name_IDs = ["*"] options.hinting = True options.legacy_kern = True options.notdef_outline = True options.no_subset_tables += ['DSIG'] options.drop_tables = list( set(options._drop_tables_default) - set(['DSIG'])) font = load_font(op.join(self.builddir, filename), options) self.bakery.logging_raw('Before: {}'.format(font.keys())) self.bakery.logging_raw('{}'.format(options.__dict__)) subsetter = Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) save_font(font, op.join(self.builddir, filename + '.opt'), options) newsize = op.getsize(op.join(self.builddir, filename + '.opt')) origsize = op.getsize(op.join(self.builddir, filename)) # compare filesizes TODO print analysis of this :) comment = "# look at the size savings of that subset process" self.bakery.logging_cmd("ls -l '%s'* %s" % (filename, comment)) statusmessage = "{0}.opt: {1} bytes\n{0}: {2} bytes\n" self.bakery.logging_raw( statusmessage.format(filename, newsize, origsize)) self.bakery.logging_raw('Now: {}'.format(font.keys())) # move ttx files to src shutil.move(op.join(self.builddir, filename + '.opt'), op.join(self.builddir, filename), log=self.bakery.logger)
def run(self, pipedata): if 'optimize' in pipedata and not pipedata['optimize']: return from bakery_cli.utils import ProcessedFile filename = ProcessedFile() self.bakery.logging_raw('### Optimize TTF {}'.format(filename)) # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py from fontTools.subset import Options, Subsetter, load_font, save_font options = Options() options.layout_features = ["*"] options.name_IDs = ["*"] options.hinting = True options.legacy_kern = True options.notdef_outline = True options.no_subset_tables += ['DSIG'] options.drop_tables = list(set(options._drop_tables_default) - set(['DSIG'])) cmd_options = ('--glyphs=*' ' --layout-features=*' ' --name-IDs=*' ' --hinting' ' --legacy-kern --notdef-outline' ' --no-subset-tables+=DSIG' ' --drop-tables-=DSIG') font = load_font(op.join(self.builddir, filename), options) cmdline = 'pyftsubset {1} {0}'.format(cmd_options, op.join(self.builddir, filename)) self.bakery.logging_cmd(cmdline) subsetter = Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) save_font(font, op.join(self.builddir, filename + '.fix'), options) # compare filesizes TODO print analysis of this :) comment = "# look at the size savings of that subset process" self.bakery.logging_cmd(comment) run(u"ls -la {0} {0}.fix | awk '{{ print $5 \"\t\" $9 }}'".format(unicode(op.join(self.builddir, filename)))) comment = "# copy back optimized ttf to original filename" self.bakery.logging_cmd(comment) shutil.move(op.join(self.builddir, filename + '.fix'), op.join(self.builddir, filename))
def stamp(svgfile, fontfile, text, font_family=None, xml_transform=None): options = Options(flavor="woff2") with load_font(fontfile, options) as font: font_family = font_family or font_name(font) font_data = subset_font(font, text, options) with open(svgfile) as f: xml = xmltodict.parse(f.read()) # Embed font in root style tag style = xml["svg"].get("style", {"#text": ""}) if type(style) == str: style = {"#text": style} style["#text"] += f""" <![CDATA[ @font-face {{ font-family: "{font_family}"; src: url("data:font/woff2;base64,{font_data}"); }} ]]> """ xml["svg"]["style"] = style if xml_transform: xml = xml_transform(xml) return unescape(xmltodict.unparse(xml))
def convertFont(base64, type): options = Options() # tmp file names tmpInputFontName = tmpFileName("." + type) tmpOutputTtf = tmpFileName(".ttf") tmpOutputWoff = tmpFileName(".woff") # remove data header from base64 # now we have a clean input source fontbase64 = base64.split(",")[1] with open(tmpInputFontName, "wb") as f: fontinput = f.write(fontbase64.decode('base64')) f.close() # we always work from a TTFont Object (also takes OTF) font = TTFont(tmpInputFontName) ttfOptions = Options() # export the font as woff for web use woffOptions = Options() woffOptions.with_zopfli = True woffOptions.flavor = "woff" if type == 'otf': # convert the font to ttf ttfFont = otf_to_ttf(font) # save font can also convert to woff! save_font(ttfFont, tmpOutputTtf, ttfOptions) save_font(ttfFont, tmpOutputWoff, woffOptions) elif type == 'ttf': save_font(font, tmpOutputTtf, ttfOptions) save_font(font, tmpOutputWoff, woffOptions) else: return {'error': 'please give a valid type'} ttfBase64 = toBase64(tmpOutputTtf) woffBase64 = toBase64(tmpOutputWoff) #cleanup files cleanUp([tmpInputFontName, tmpOutputWoff, tmpOutputTtf]) return {'woff': woffBase64, 'ttf': ttfBase64}
def _subset(self, otf, fmt): from fontTools.subset import Options, Subsetter for name, subset in self.subsets.items(): logger.info(f"Creating {name}.{fmt.value} subset") new = deepcopy(otf) options = Options() options.name_IDs = ["*"] options.name_legacy = True options.name_languages = ["*"] options.recommended_glyphs = True options.layout_features = ["*"] options.notdef_outline = True options.notdef_glyph = True options.glyph_names = True options.hinting = True options.legacy_kern = True options.symbol_cmap = True options.layout_closure = False options.prune_unicode_ranges = False options.passthrough_tables = False options.recalc_average_width = True options.ignore_missing_glyphs = True options.layout_scripts = subset["langsys"] subsetter = Subsetter(options=options) subsetter.populate(subset["glyphlist"]) with TemporaryLogLevel(logging.WARNING): subsetter.subset(new) new = self._optimize(new, name, fmt) names = subset.get("names") if names: logger.info(f"Adding name entries to {name}.{fmt.value} susbet") self._setnames(new, names) self._buildwoff(new, name, fmt) self._save(new, name, fmt)
def main(args): """Subset a font (useful for making small test fonts). Args: args: list, arguments the user typed. """ parser = argparse.ArgumentParser() parser.add_argument('fontfile', help='Input font file') parser.add_argument('--subset_postfix', default='', help='Postfix to the subset extension') parser.add_argument('--text', default='', help='Text to include in the subset') parser.add_argument( '--unicodes', default='', help='Comma separated list of Unicode codepoints (hex) ' 'to include in the subset; eg, "e7,0xe8,U+00e9"') parser.add_argument('--glyphs', default='', help='Comma separated list of glyph IDs (decimal) to ' 'include in the subset; eg, "1,27"') parser.add_argument('--hinting', default=False, action='store_true', help='Enable hinting if specified, no hinting if not ' 'present') cmd_args = parser.parse_args(args) options = Options() # Definitely want the .notdef glyph and outlines. options.notdef_glyph = True options.notdef_outline = True # Get the item. to keep in the subset. text = cmd_args.text unicodes_str = cmd_args.unicodes.lower().replace('0x', '').replace('u+', '') # TODO(bstell) replace this whole files by using the new subset.py code unicodes_input = [c for c in unicodes_str.split(',') if c] unicodes = [] for c in unicodes_input: if '-' in c: uni_range = c.split('-') uni_range_expanded = range(int(uni_range[0], 16), int(uni_range[1], 16) + 1) unicodes.extend(uni_range_expanded) else: unicodes.append(int(c, 16)) #unicodes = [int(c, 16) for c in unicodes_input_expanded] glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c] fontfile = cmd_args.fontfile options.hinting = cmd_args.hinting # False => no hinting options.hinting = True # hint stripping for CFF is currently broken dirname = os.path.dirname(fontfile) basename = os.path.basename(fontfile) filename, extension = os.path.splitext(basename) subset_postfix = cmd_args.subset_postfix output_file = dirname + '/' + filename + '_subset' + subset_postfix + extension print "output_file =", output_file font = load_font(fontfile, options, lazy=False) subsetter = Subsetter(options) subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs) subsetter.subset(font) save_font(font, output_file, options)
def arabicSubsetter(self, font, subset): options = Options() options.layout_features = "*" # keep all GSUB/GPOS features options.no_layout_closure = True options.glyph_names = False # keep post glyph names options.legacy_cmap = True # keep non-Unicode cmaps options.name_legacy = True # keep non-Unicode names options.name_IDs = ["*"] # keep all nameIDs options.name_languages = ["*"] # keep all name languages options.notdef_outline = False options.ignore_missing_glyphs = False options.recommended_glyphs = True options.prune_unicode_ranges = True subsetter = Subsetter(options=options) subsetter.populate(glyphs=subset) subsetter.subset(font) return font
def subsetFonts(family, writingSystem, flavor=["ttf"], familyNewName=" ", jsonpath=" ", keepFea=True): print(familyNewName) # if len(flavor) == 0: flavor = ["ttf"] latinProCodePageRange = [0, 1, 4, 7, 8] cyrProCodePageRange = [2] greekProCodePageRange = [3] ASCII = [0, 1] SecureSet = [0] coreArabicCodePageRange = [0, 6] unicodePageRangeDict = { "Cyrillic": latinProCodePageRange, "CyrillicPro": latinProCodePageRange, "Greek": greekProCodePageRange, "Latin": latinProCodePageRange, "ASCII": ASCII, "SecureSet": SecureSet, "Core_Arabic": coreArabicCodePageRange } pageRangeToApply = [] subsetFolder = "" for i in writingSystem: subsetFolder += i formats = ["ttf", "woff", "woff2"] toKeep = list() folder = getFolder(family) folderFonts = os.path.join(folder, "fonts") options = Options() options.layout_features = '*' # keep all GSUB/GPOS features # options.legacy_kern = True # keep kern table options.glyph_names = False # keep post glyph names options.legacy_cmap = True # keep non-Unicode cmaps options.symbol_cmap = True # keep Symbol cmaps options.name_legacy = True # keep non-Unicode names options.name_IDs = ['*'] # keep all nameIDs options.name_languages = ['*'] # keep all name languages options.notdef_outline = True # keep outline of .notdef options.ignore_missing_glyphs = True options.prune_unicode_ranges = True keep = [] if family in pan_european_fonts: jsonpath = "subsets/lgc_glyphset.json" elif family in arabic_fonts: jsonpath = "subsets/arabic_glyphset.json" keep, pageRangeToApply = readJsonStoredSubset(jsonpath, writingSystem) for i in flavor: if not os.path.exists(os.path.join(folderFonts, i.upper())): print(">> Make {} fonts.".format(family)) designSpace2Instances(family, i, secureSet=False) for i in flavor: fontspath = [os.path.join(folderFonts, i.upper(), font) \ for font in os.listdir(folder + "/fonts/" + i.upper())] for f in fontspath: if f.split(".")[-1] in formats: newfont = TTFont(f) for namerecord in newfont['name'].names: namerecord.string = namerecord.toUnicode() if namerecord.nameID == 2: WeightName = namerecord.string if namerecord.nameID == 17: WeightName = "".join(namerecord.string.split(" ")) # print(family, WeightName) subsetter = Subsetter(options=options) subsetter.populate(glyphs=keep) subsetter.subset(newfont) destination = os.path.join(folder, "fonts", subsetFolder + "_subset", "fonts") if not os.path.exists(os.path.join(destination, i.upper())): os.makedirs(os.path.join(destination, i.upper())) subsetName = family + subsetFolder + "-" + WeightName + "." + i newfont.save(os.path.join(destination, i.upper(), subsetName)) folder = family + "/fonts/" + subsetFolder + "_subset" if familyNewName != " ": renameFonts(folder, familyNewName, codePageRange=pageRangeToApply) else: familyNewName = family + subsetFolder renameFonts(folder, familyNewName, codePageRange=pageRangeToApply) shutil.rmtree(destination)