def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Set ascent, descent, and lineGap values to Android K values hhea = font['hhea'] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap( font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table] # Set bold bits for Black (macStyle bit 0, fsSelection bit 5, subfamily) name_records = font_data.get_name_records(font) family_name = name_records[1] subfam_name = name_records[2] if family_name.endswith('Black'): font['head'].macStyle |= (1 << 0) font['OS/2'].fsSelection |= (1 << 5) font['OS/2'].fsSelection &= ~(1 << 6) new_subfam_name = (('Bold ' + subfam_name) if subfam_name != 'Regular' else 'Bold') font_data.set_name_record(font, 2, new_subfam_name)
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Set ascent, descent, and lineGap values to Android K values hhea = font["hhea"] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap( font, [0x20E3, 0x2191, 0x2193] # COMBINING ENCLOSING KEYCAP # UPWARDS ARROW # DOWNWARDS ARROW ) # Drop tables not useful on Android for table in ["LTSH", "hdmx", "VDMX", "gasp"]: if table in font: del font[table] cmap = font["cmap"] cmap.tables = [t for t in cmap.tables if t.format == 12] assert cmap.tables, "No format 12 cmap found in font" # Set bold bits for Black (macStyle bit 0, fsSelection bit 5, subfamily) name_records = font_data.get_name_records(font) family_name = name_records[1] subfam_name = name_records[2] if family_name.endswith("Black"): font["head"].macStyle |= 1 << 0 font["OS/2"].fsSelection |= 1 << 5 font["OS/2"].fsSelection &= ~(1 << 6) new_subfam_name = ("Bold " + subfam_name) if subfam_name != "Regular" else "Bold" font_data.set_name_record(font, 2, new_subfam_name)
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Set ascent, descent, and lineGap values to Android K values hhea = font['hhea'] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap(font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table] # Set bold bits for Black (macStyle bit 0, fsSelection bit 5, subfamily) name_records = font_data.get_name_records(font) family_name = name_records[1] subfam_name = name_records[2] if family_name.endswith('Black'): font['head'].macStyle |= (1 << 0) font['OS/2'].fsSelection |= (1 << 5) font['OS/2'].fsSelection &= ~(1 << 6) new_subfam_name = ( ('Bold ' + subfam_name) if subfam_name != 'Regular' else 'Bold') font_data.set_name_record(font, 2, new_subfam_name)
def main(font_path): font = TTFont(font_path, recalcBBoxes=False) # Force yMin and yMax font['head'].yMin = YMIN font['head'].yMax = YMAX # Enable Bold bits for Black styles if "Black" in font_path and "fvar" not in font: if "Italic" in font_path: font["OS/2"].fsSelection |= 32 else: font["OS/2"].fsSelection ^= 64 | 32 font["head"].macStyle |= 1 # turn off round-to-grid flags in certain problem components # https://github.com/google/roboto/issues/153 glyph_set = font.getGlyphSet() ellipsis = glyph_set['ellipsis']._glyph for component in ellipsis.components: component.flags &= ~(1 << 2) font_data.delete_from_cmap( font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) font.save(font_path)
def main(argv): """Modify all the fonts given in the command line.""" for font_name in argv[1:]: font = ttLib.TTFont(font_name) assert "GSUB" not in font font["GSUB"] = create_simple_gsub([create_lookup(EMOJI_KEYCAPS, font), create_lookup(EMOJI_FLAGS, font)]) font_data.delete_from_cmap(font, EMOJI_FLAGS.keys() + EMOJI_KEYCAPS.keys()) font.save(font_name + "-fixed")
def main(argv): """Modify all the fonts given in the command line.""" for font_name in argv[1:]: font = ttLib.TTFont(font_name) assert 'GSUB' not in font font['GSUB'] = create_simple_gsub([ create_lookup(EMOJI_KEYCAPS, font), create_lookup(EMOJI_FLAGS, font) ]) font_data.delete_from_cmap(font, EMOJI_FLAGS.keys() + EMOJI_KEYCAPS.keys()) font.save(font_name + '-fixed')
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Set ascent, descent, and lineGap values to Android K values hhea = font['hhea'] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: # https://code.google.com/a/google.com/p/roboto/issues/detail?id=52 font_data.delete_from_cmap(font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table]
def main(font_path): font = TTFont(font_path, recalcBBoxes=False) # turn off round-to-grid flags in certain problem components # https://github.com/google/roboto/issues/153 glyph_set = font.getGlyphSet() ellipsis = glyph_set['ellipsis']._glyph for component in ellipsis.components: component.flags &= ~(1 << 2) font_data.delete_from_cmap( font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Update vertical metrics to match v2.136 update_attribs(font, **android_and_cros_vert_metrics) update_psname_and_fullname(font, include_year=True) update_font_version(font) font.save(font_path)
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Remove combining keycap and the arrows from the cmap table: # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap(font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table] # turn off round-to-grid flags in certain problem components # https://github.com/google/roboto/issues/153 glyph_set = font.getGlyphSet() ellipsis = glyph_set['ellipsis']._glyph for component in ellipsis.components: component.flags &= ~(1 << 2)
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Set ascent, descent, and lineGap values to Android K values hhea = font['hhea'] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Remove combining keycap and the arrows from the cmap table: # https://code.google.com/a/google.com/p/roboto/issues/detail?id=52 font_data.delete_from_cmap( font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table]
def apply_android_specific_fixes(font): """Apply fixes needed for Android.""" # Remove combining keycap and the arrows from the cmap table: # https://github.com/google/roboto/issues/99 font_data.delete_from_cmap( font, [ 0x20E3, # COMBINING ENCLOSING KEYCAP 0x2191, # UPWARDS ARROW 0x2193, # DOWNWARDS ARROW ]) # Drop tables not useful on Android for table in ['LTSH', 'hdmx', 'VDMX', 'gasp']: if table in font: del font[table] # turn off round-to-grid flags in certain problem components # https://github.com/google/roboto/issues/153 glyph_set = font.getGlyphSet() ellipsis = glyph_set['ellipsis']._glyph for component in ellipsis.components: component.flags &= ~(1 << 2)
def _remove_from_cmap(infile, outfile, exclude=[]): font = ttLib.TTFont(infile) font_data.delete_from_cmap(font, exclude) font.save(outfile)
def remove_from_cmap(infile, outfile, exclude=frozenset()): """Removes a set of characters from a font file's cmap table.""" font = ttLib.TTFont(f'{SCRIPT_PATH}/{infile}') font_data.delete_from_cmap(font, exclude) font.save(outfile)
def main(argv): import glob from fontTools import ttx, ttLib options = [] option_map = { "-V": "verbose", "-O": "keep_outlines", "-U": "uncompressed", "-S": "small_glyph_metrics", "-C": "keep_chunks", } for key, value in option_map.items(): if key in argv: options.append(value) argv.remove(key) if len(argv) < 4: print(""" Usage: emoji_builder.py [-V] [-O] [-U] [-S] [-A] font.ttf out-font.ttf strike-prefix... This will search for files that have strike-prefix followed by a hex number, and end in ".png". For example, if strike-prefix is "icons/uni", then files with names like "icons/uni1f4A9.png" will be loaded. All images for the same strike should have the same size for best results. If multiple strike-prefix parameters are provided, multiple strikes will be embedded, in the order provided. The script then embeds color bitmaps in the font, for characters that the font already supports, and writes the new font out. If -V is given, verbose mode is enabled. If -U is given, uncompressed images are stored (imageFormat=1). If -S is given, PNG images are stored with small glyph metrics (imageFormat=17). By default, PNG images are stored with big glyph metrics (imageFormat=18). If -O is given, the outline tables ('glyf', 'CFF ') and related tables are NOT dropped from the font. By default they are dropped. If -C is given, unused chunks (color profile, etc) are NOT dropped from the PNG images when embedding. By default they are dropped. """, file=sys.stderr) sys.exit(1) font_file = argv[1] out_file = argv[2] img_prefixes = argv[3:] del argv def add_font_table(font, tag, data): tab = ttLib.tables.DefaultTable.DefaultTable(tag) tab.data = str(data) font[tag] = tab def drop_outline_tables(font): for tag in ['cvt ', 'fpgm', 'glyf', 'loca', 'prep', 'CFF ', 'VORG']: try: del font[tag] except KeyError: pass print() font = ttx.TTFont(font_file) print("Loaded font '%s'." % font_file) font_metrics = FontMetrics(font['head'].unitsPerEm, font['hhea'].ascent, -font['hhea'].descent) print("Font metrics: upem=%d ascent=%d descent=%d." % \ (font_metrics.upem, font_metrics.ascent, font_metrics.descent)) glyph_metrics = font['hmtx'].metrics unicode_cmap = font['cmap'].getcmap(3, 10) if not unicode_cmap: unicode_cmap = font['cmap'].getcmap(3, 1) if not unicode_cmap: raise Exception("Failed to find a Unicode cmap.") image_format = 1 if 'uncompressed' in options else ( 17 if 'small_glyph_metrics' in options else 18) ebdt = CBDT(font_metrics, options) ebdt.write_header() eblc = CBLC(font_metrics, options) eblc.write_header() eblc.start_strikes(len(img_prefixes)) def is_vs(cp): return cp >= 0xfe00 and cp <= 0xfe0f for img_prefix in img_prefixes: print() img_files = {} glb = "%s*.png" % img_prefix print("Looking for images matching '%s'." % glb) for img_file in glob.glob(glb): codes = img_file[len(img_prefix):-4] if "_" in codes: pieces = codes.split("_") cps = [int(code, 16) for code in pieces] uchars = "".join([unichr(cp) for cp in cps if not is_vs(cp)]) else: cp = int(codes, 16) if is_vs(cp): print("ignoring unexpected vs input %04x" % cp) continue uchars = unichr(cp) img_files[uchars] = img_file if not img_files: raise Exception("No image files found in '%s'." % glb) print("Found images for %d characters in '%s'." % (len(img_files), glb)) glyph_imgs = {} advance = width = height = 0 for uchars, img_file in img_files.items(): if len(uchars) == 1: try: glyph_name = unicode_cmap.cmap[ord(uchars)] except: print("no cmap entry for %x" % ord(uchars)) raise ValueError("%x" % ord(uchars)) else: glyph_name = get_glyph_name_from_gsub(uchars, font, unicode_cmap.cmap) glyph_id = font.getGlyphID(glyph_name) glyph_imgs[glyph_id] = img_file if "verbose" in options: uchars_name = ",".join(["%04X" % ord(char) for char in uchars]) # print "Matched U+%s: id=%d name=%s image=%s" % ( # uchars_name, glyph_id, glyph_name, img_file) advance += glyph_metrics[glyph_name][0] w, h = PNG(img_file).get_size() width += w height += h glyphs = sorted(glyph_imgs.keys()) if not glyphs: raise Exception( "No common characters found between font and '%s'." % glb) print("Embedding images for %d glyphs for this strike." % len(glyphs)) advance, width, height = (div(x, len(glyphs)) for x in (advance, width, height)) strike_metrics = StrikeMetrics(font_metrics, advance, width, height) print("Strike ppem set to %d." % (strike_metrics.y_ppem)) ebdt.start_strike(strike_metrics) ebdt.write_glyphs(glyphs, glyph_imgs, image_format) glyph_maps = ebdt.end_strike() eblc.write_strike(strike_metrics, glyph_maps) print() ebdt = ebdt.data() add_font_table(font, 'CBDT', ebdt) print("CBDT table synthesized: %d bytes." % len(ebdt)) eblc.end_strikes() eblc = eblc.data() add_font_table(font, 'CBLC', eblc) print("CBLC table synthesized: %d bytes." % len(eblc)) print() if 'keep_outlines' not in options: drop_outline_tables(font) print("Dropped outline ('glyf', 'CFF ') and related tables.") # hack removal of cmap pua entry for unknown flag glyph. If we try to # remove it earlier, getGlyphID dies. Need to restructure all of this # code. font_data.delete_from_cmap(font, [0xfe82b]) font.save(out_file) print("Output font '%s' generated." % out_file)
def remove_from_cmap(infile, outfile, exclude=frozenset()): """Removes a set of characters from a font file's cmap table.""" font = ttLib.TTFont(infile) font_data.delete_from_cmap(font, exclude) font.save(outfile)
def main (argv): import glob from fontTools import ttx, ttLib options = [] option_map = { "-V": "verbose", "-O": "keep_outlines", "-U": "uncompressed", "-S": "small_glyph_metrics", "-C": "keep_chunks", } for key, value in option_map.items (): if key in argv: options.append (value) argv.remove (key) if len (argv) < 4: print(""" Usage: emoji_builder.py [-V] [-O] [-U] [-S] [-A] font.ttf out-font.ttf strike-prefix... This will search for files that have strike-prefix followed by a hex number, and end in ".png". For example, if strike-prefix is "icons/uni", then files with names like "icons/uni1f4A9.png" will be loaded. All images for the same strike should have the same size for best results. If multiple strike-prefix parameters are provided, multiple strikes will be embedded, in the order provided. The script then embeds color bitmaps in the font, for characters that the font already supports, and writes the new font out. If -V is given, verbose mode is enabled. If -U is given, uncompressed images are stored (imageFormat=1). If -S is given, PNG images are stored with small glyph metrics (imageFormat=17). By default, PNG images are stored with big glyph metrics (imageFormat=18). If -O is given, the outline tables ('glyf', 'CFF ') and related tables are NOT dropped from the font. By default they are dropped. If -C is given, unused chunks (color profile, etc) are NOT dropped from the PNG images when embedding. By default they are dropped. """, file=sys.stderr) sys.exit (1) font_file = argv[1] out_file = argv[2] img_prefixes = argv[3:] del argv def add_font_table (font, tag, data): tab = ttLib.tables.DefaultTable.DefaultTable (tag) tab.data = str(data) font[tag] = tab def drop_outline_tables (font): for tag in ['cvt ', 'fpgm', 'glyf', 'loca', 'prep', 'CFF ', 'VORG']: try: del font[tag] except KeyError: pass print() font = ttx.TTFont (font_file) print("Loaded font '%s'." % font_file) font_metrics = FontMetrics (font['head'].unitsPerEm, font['hhea'].ascent, -font['hhea'].descent) print("Font metrics: upem=%d ascent=%d descent=%d." % \ (font_metrics.upem, font_metrics.ascent, font_metrics.descent)) glyph_metrics = font['hmtx'].metrics unicode_cmap = font['cmap'].getcmap (3, 10) if not unicode_cmap: unicode_cmap = font['cmap'].getcmap (3, 1) if not unicode_cmap: raise Exception ("Failed to find a Unicode cmap.") image_format = 1 if 'uncompressed' in options else (17 if 'small_glyph_metrics' in options else 18) ebdt = CBDT (font_metrics, options) ebdt.write_header () eblc = CBLC (font_metrics, options) eblc.write_header () eblc.start_strikes (len (img_prefixes)) def is_vs(cp): return cp >= 0xfe00 and cp <= 0xfe0f for img_prefix in img_prefixes: print() img_files = {} glb = "%s*.png" % img_prefix print("Looking for images matching '%s'." % glb) for img_file in glob.glob (glb): codes = img_file[len (img_prefix):-4] if "_" in codes: pieces = codes.split ("_") cps = [int(code, 16) for code in pieces] uchars = "".join ([unichr(cp) for cp in cps if not is_vs(cp)]) else: cp = int(codes, 16) if is_vs(cp): print("ignoring unexpected vs input %04x" % cp) continue uchars = unichr(cp) img_files[uchars] = img_file if not img_files: raise Exception ("No image files found in '%s'." % glb) print("Found images for %d characters in '%s'." % (len (img_files), glb)) glyph_imgs = {} advance = width = height = 0 for uchars, img_file in img_files.items (): if len (uchars) == 1: try: glyph_name = unicode_cmap.cmap[ord (uchars)] except: print("no cmap entry for %x" % ord(uchars)) raise ValueError("%x" % ord(uchars)) else: glyph_name = get_glyph_name_from_gsub (uchars, font, unicode_cmap.cmap) glyph_id = font.getGlyphID (glyph_name) glyph_imgs[glyph_id] = img_file if "verbose" in options: uchars_name = ",".join (["%04X" % ord (char) for char in uchars]) # print "Matched U+%s: id=%d name=%s image=%s" % ( # uchars_name, glyph_id, glyph_name, img_file) advance += glyph_metrics[glyph_name][0] w, h = PNG (img_file).get_size () width += w height += h glyphs = sorted (glyph_imgs.keys ()) if not glyphs: raise Exception ("No common characters found between font and '%s'." % glb) print("Embedding images for %d glyphs for this strike." % len (glyphs)) advance, width, height = (div (x, len (glyphs)) for x in (advance, width, height)) strike_metrics = StrikeMetrics (font_metrics, advance, width, height) print("Strike ppem set to %d." % (strike_metrics.y_ppem)) ebdt.start_strike (strike_metrics) ebdt.write_glyphs (glyphs, glyph_imgs, image_format) glyph_maps = ebdt.end_strike () eblc.write_strike (strike_metrics, glyph_maps) print() ebdt = ebdt.data () add_font_table (font, 'CBDT', ebdt) print("CBDT table synthesized: %d bytes." % len (ebdt)) eblc.end_strikes () eblc = eblc.data () add_font_table (font, 'CBLC', eblc) print("CBLC table synthesized: %d bytes." % len (eblc)) print() if 'keep_outlines' not in options: drop_outline_tables (font) print("Dropped outline ('glyf', 'CFF ') and related tables.") # hack removal of cmap pua entry for unknown flag glyph. If we try to # remove it earlier, getGlyphID dies. Need to restructure all of this # code. font_data.delete_from_cmap(font, [0xfe82b]) font.save (out_file) print("Output font '%s' generated." % out_file)