def apply_web_specific_fixes(font, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert (subfamily_name in [ 'Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic' ]) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name # Family name font_data.set_name_record(font, 1, family_name) font_data.set_name_record(font, 16, family_name) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record(font, 6, (family_name + '-' + subfamily_name).replace( ' ', ''))
def apply_web_specific_fixes(font, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert(subfamily_name in ['Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic']) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name # Family name font_data.set_name_record(font, 1, family_name) font_data.set_name_record(font, 16, family_name) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record( font, 6, (family_name+'-'+subfamily_name).replace(' ', ''))
def apply_web_specific_fixes(font, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert(subfamily_name in ['Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic']) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] if subfamily_name in style_map: font_data.set_name_record(font, 1, family_name) else: weight = subfamily_name.split()[0] new_family_name = family_name if weight != 'Regular': new_family_name += ' ' + weight font_data.set_name_record(font, 1, new_family_name) # all weights outside regular and bold should only have subfamily # "Regular" or "Italic" italic = subfamily_name.endswith('Italic') font_data.set_name_record(font, 2, style_map[italic << 1]) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name.replace(' Regular', '')) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record( font, 6, (family_name+'-'+subfamily_name).replace(' ', '')) # Copyright message font_data.set_name_record( font, 0, 'Copyright 2011 Google Inc. All Rights Reserved.')
def setUp(self): font_files, self.fonts = self.loaded_fonts self.font_files = [path.basename(f) for f in font_files] self.condensed_family_name = self.family_name + ' Condensed' self.names = [] for font in self.fonts: self.names.append(font_data.get_name_records(font))
def summarize_file(root, path): font = ttLib.TTFont(path) table_info = {} reader = font.reader for tag in reader.keys(): entry = reader.tables[tag] entry_len = entry.length entry_checkSum = int(entry.checkSum) if entry_checkSum < 0: entry_checkSum += 0x100000000 table_info[tag] = (entry_len, entry_checkSum) relpath = path[len(root) + 1:] size = os.path.getsize(path) # Printable_font_revision requires you specify the accuracy of digits. # ttLib apparently reads the fixed values as a float, so it loses the info. # Adobe fonts use 3 digits, so the default from printable_font_revision of 2 # is insufficient. # Assume that the name from the name table is accurate, and use it instead. version_string = noto_lint.font_version(font) match = re.match(r'Version (\d+\.\d+)', version_string) if match: version = match.group(1) else: version = noto_lint.printable_font_revision(font) # default 2 num_glyphs = len(font.getGlyphOrder()) full_name = font_data.get_name_records(font)[4] cmap = set( get_largest_cmap(font).keys()) # copy needed? what's the lifespan? num_chars = len(cmap) font.close() return (relpath, version, full_name, size, num_glyphs, num_chars, cmap, table_info)
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 _load_font(data, codelist_map): if len(data) < 4: data = data + tuple([None] * (4 - len(data))) key, fname, name, codelistfile = data if not fname: if not name: raise Exception('must have name if no font provided') if not codelistfile: raise Exception('must have codelist file if no font provided') fontpath = None else: fontpath = path.join(data_dir, fname) if not path.isfile(fontpath): raise Exception('font "%s" not found' % fontpath) if codelistfile: codelist = _load_codelist(codelistfile, data_dir, codelist_map) if fname and (not codelistfile or not name): font = ttLib.TTFont(fontpath) if not name: names = font_data.get_name_records(font) name = names[16] if 16 in names else names[1] if 1 in names else None if not name: raise Exception('cannot read name from font "%s"' % fontpath) if not codelistfile: codelist = CodeList.fromset(font_data.get_cmap(font)) return key, fontpath, name, codelist
def fix_font(f, dstdir, version_info, autohint, dry_run): print '\n-----\nfont:', f font = ttLib.TTFont(f) fname = path.basename(f) expected_font_revision = get_revision(font, fname) if expected_font_revision != None: font_revision = font_data.printable_font_revision(font, 3) if font_revision != expected_font_revision: _alert('revision', font_revision, expected_font_revision) font['head'].fontRevision = float(expected_font_revision) names = font_data.get_name_records(font) NAME_ID = 5 font_version = names[NAME_ID] expected_version = ( 'Version %s;%s' % (expected_font_revision, version_info)) if font_version != expected_version: _alert('version string', font_version, expected_version) font_data.set_name_record(font, NAME_ID, expected_version) expected_upem = 2048 upem = font['head'].unitsPerEm if upem != expected_upem: print 'expected %d upem but got %d upem' % (expected_upem, upem) if _is_ui_metrics(fname): if upem == 2048: expected_ascent = 2163 expected_descent = -555 elif upem == 1000: expected_ascent = 1069 expected_descent = -293 else: raise Exception('no expected ui ascent/descent for upem: %d' % upem) font_ascent = font['hhea'].ascent font_descent = font['hhea'].descent if font_ascent != expected_ascent: _alert_and_check('ascent', font_ascent, expected_ascent, 2) font['hhea'].ascent = expected_ascent font['OS/2'].sTypoAscender = expected_ascent font['OS/2'].usWinAscent = expected_ascent if font_descent != expected_descent: _alert_and_check('descent', font_descent, expected_descent, 2) font['hhea'].descent = expected_descent font['OS/2'].sTypoDescender = expected_descent font['OS/2'].usWinDescent = -expected_descent tool_utils.ensure_dir_exists(path.join(dstdir, 'unhinted')) dst = path.join(dstdir, 'unhinted', fname) if dry_run: print 'dry run would write:\n "%s"' % dst else: font.save(dst) print 'wrote %s' % dst if autohint: autohint_font(dst, path.join(dstdir, 'hinted', fname), autohint, dry_run)
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_web_specific_fixes(font, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert(subfamily_name in ['Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic']) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name # macStyle bold = subfamily_name.startswith(('Bold', 'Black')) italic = subfamily_name.endswith('Italic') macStyle = (italic << 1) | bold font['head'].macStyle = macStyle # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] if subfamily_name in style_map: font_data.set_name_record(font, 1, family_name) else: weight = subfamily_name.split()[0] font_data.set_name_record(font, 1, '%s %s' % (family_name, weight)) font_data.set_name_record(font, 2, style_map[macStyle]) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record( font, 6, (family_name+'-'+subfamily_name).replace(' ', ''))
def apply_web_specific_fixes(font, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert (subfamily_name in [ 'Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic' ]) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name + ' ' + subfamily_name # macStyle bold = subfamily_name.startswith(('Bold', 'Black')) italic = subfamily_name.endswith('Italic') macStyle = (italic << 1) | bold font['head'].macStyle = macStyle # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] if subfamily_name in style_map: font_data.set_name_record(font, 1, family_name) else: weight = subfamily_name.split()[0] font_data.set_name_record(font, 1, '%s %s' % (family_name, weight)) font_data.set_name_record(font, 2, style_map[macStyle]) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record(font, 6, (family_name + '-' + subfamily_name).replace( ' ', ''))
def get_font_family_name(font_file): font = ttLib.TTFont(font_file, fontNumber=0) name_record = font_data.get_name_records(font) try: name = name_record[16] except KeyError: name = name_record[1] if name.endswith("Regular"): name = name.rsplit(" ", 1)[0] return name
def get_font_family_name(font_file): font = ttLib.TTFont(font_file, fontNumber=0) name_record = font_data.get_name_records(font) try: name = name_record[16] except KeyError: name = name_record[1] if name.endswith('Regular'): name = name.rsplit(' ', 1)[0] return name
def fix_name_table(font): """Fix copyright and reversed values in the 'name' table.""" modified = False name_records = font_data.get_name_records(font) copyright_data = name_records[0] years = re.findall('20[0-9][0-9]', copyright_data) year = min(years) copyright_data = u'Copyright %s Google Inc. All Rights Reserved.' % year if copyright_data != name_records[0]: print 'Updated copyright message to "%s"' % copyright_data font_data.set_name_record(font, 0, copyright_data) modified = True for name_id in [1, 3, 4, 6]: record = name_records[name_id] for source in NAME_CORRECTIONS: if source in record: oldrecord = record record = record.replace(source, NAME_CORRECTIONS[source]) break if record != name_records[name_id]: font_data.set_name_record(font, name_id, record) print 'Updated name table record #%d from "%s" to "%s"' % ( name_id, oldrecord, record) modified = True trademark_names = ['Noto', 'Arimo', 'Tinos', 'Cousine'] trademark_name = None font_family = name_records[1] for name in trademark_names: if font_family.find(name) != -1: trademark_name = name break if not trademark_name: print 'no trademarked name in \'%s\'' % font_family else: trademark_line = TRADEMARK_TEMPLATE % trademark_name if name_records[7] != trademark_line: old_line = name_records[7] font_data.set_name_record(font, 7, trademark_line) modified = True print 'Updated name table record 7 from "%s" to "%s"' % ( old_line, trademark_line) if name_records[11] != NOTO_URL: font_data.set_name_record(font, 11, NOTO_URL) modified = True print 'Updated name table record 11 to "%s"' % NOTO_URL # TODO: check preferred family/subfamily(16&17) return modified
def fix_name_table(font): """Fix copyright and reversed values in the 'name' table.""" modified = False name_records = font_data.get_name_records(font) copyright_data = name_records[0] years = re.findall('20[0-9][0-9]', copyright_data) year = min(years) copyright_data = u'Copyright %s Google Inc. All Rights Reserved.' % year if copyright_data != name_records[0]: print 'Updated copyright message to "%s"' % copyright_data font_data.set_name_record(font, 0, copyright_data) modified = True for name_id in [1, 3, 4, 6]: record = name_records[name_id] for source in NAME_CORRECTIONS: if source in record: oldrecord = record record = record.replace(source, NAME_CORRECTIONS[source]) break if record != name_records[name_id]: font_data.set_name_record(font, name_id, record) print 'Updated name table record #%d from "%s" to "%s"' % ( name_id, oldrecord, record) modified = True trademark_names = ['Noto', 'Arimo', 'Tinos', 'Cousine'] trademark_name = None font_family = name_records[1] for name in trademark_names: if font_family.find(name) != -1: trademark_name = name break if not trademark_name: print 'no trademarked name in \'%s\'' % font_family else: trademark_line = TRADEMARK_TEMPLATE % trademark_name if name_records[7] != trademark_line: old_line = name_records[7] font_data.set_name_record(font, 7, trademark_line) modified = True print 'Updated name table record 7 from "%s" to "%s"' % (old_line, trademark_line) if name_records[11] != NOTO_URL: font_data.set_name_record(font, 11, NOTO_URL) modified = True print 'Updated name table record 11 to "%s"' % NOTO_URL # TODO: check preferred family/subfamily(16&17) return modified
def get_bumped_version(ttfont, is_hinted=None): """Return bumped values for the header and name tables.""" names = font_data.get_name_records(ttfont) version = names[_VERSION_ID] m = re.match(r'Version (\d{1,5})\.(\d{1,5})( uh)?(;.*)?', version) if not m: print '! Could not match version string (%s)' % version return None, None major_version = m.group(1) minor_version = m.group(2) print 'old version: "%s"' % version if is_hinted == None: is_hinted = not bool(m.group(3)) print 'computed hinted = %s' % is_hinted version_remainder = m.group(4) accuracy = len(minor_version) print_revision = font_data.printable_font_revision(ttfont, accuracy) # sanity check expected_revision = major_version + '.' + minor_version if expected_revision != print_revision: raise ValueError('! Expected revision \'%s\' but got revision \'%s\'' % ( expected_revision, print_revision)) # bump the minor version keeping significant digits: new_minor_version = str(int(minor_version) + 1).zfill(accuracy) new_revision = major_version + '.' + new_minor_version print 'Update revision from \'%s\' to \'%s\'' % ( expected_revision, new_revision) # double check we are going to properly round-trip this value float_revision = float(new_revision) fixed_revision = misc.fixedTools.floatToFixed(float_revision, 16) rt_float_rev = misc.fixedTools.fixedToFloat(fixed_revision, 16) rt_float_rev_int = int(rt_float_rev) rt_float_rev_frac = int(round((rt_float_rev - rt_float_rev_int) * 10 ** accuracy)) rt_new_revision = (str(rt_float_rev_int) + '.' + str(rt_float_rev_frac).zfill(accuracy)) if new_revision != rt_new_revision: raise ValueError( '! Could not update new revision, expected \'%s\' but got \'%s\'' % ( new_revision, rt_new_revision)) new_version_string = 'Version ' + new_revision if not is_hinted: new_version_string += ' uh' if version_remainder: new_version_string += version_remainder return float_revision, new_version_string
def get_bumped_version(ttfont, is_hinted=None): """Return bumped values for the header and name tables.""" names = font_data.get_name_records(ttfont) version = names[_VERSION_ID] m = re.match(r'Version (\d{1,5})\.(\d{1,5})( uh)?(;.*)?', version) if not m: print('! Could not match version string (%s)' % version) return None, None major_version = m.group(1) minor_version = m.group(2) print('old version: "%s"' % version) if is_hinted == None: is_hinted = not bool(m.group(3)) print('computed hinted = %s' % is_hinted) version_remainder = m.group(4) accuracy = len(minor_version) print_revision = font_data.printable_font_revision(ttfont, accuracy) # sanity check expected_revision = major_version + '.' + minor_version if expected_revision != print_revision: raise ValueError('! Expected revision \'%s\' but got revision \'%s\'' % (expected_revision, print_revision)) # bump the minor version keeping significant digits: new_minor_version = str(int(minor_version) + 1).zfill(accuracy) new_revision = major_version + '.' + new_minor_version print('Update revision from \'%s\' to \'%s\'' % (expected_revision, new_revision)) # double check we are going to properly round-trip this value float_revision = float(new_revision) fixed_revision = misc.fixedTools.floatToFixed(float_revision, 16) rt_float_rev = misc.fixedTools.fixedToFloat(fixed_revision, 16) rt_float_rev_int = int(rt_float_rev) rt_float_rev_frac = int( round((rt_float_rev - rt_float_rev_int) * 10**accuracy)) rt_new_revision = (str(rt_float_rev_int) + '.' + str(rt_float_rev_frac).zfill(accuracy)) if new_revision != rt_new_revision: raise ValueError( '! Could not update new revision, expected \'%s\' but got \'%s\'' % (new_revision, rt_new_revision)) new_version_string = 'Version ' + new_revision if not is_hinted: new_version_string += ' uh' if version_remainder: new_version_string += version_remainder return float_revision, new_version_string
def get_family_filename(family): """Returns a filename to use for a family zip of hinted/unhinted members. This is basically the postscript name with weight/style removed. """ font = ttLib.TTFont(family.rep_member.filepath, fontNumber=0) name_record = font_data.get_name_records(font) try: name = name_record[6] ix = name.find('-') if ix >= 0: name = name[:ix] except KeyError: name = name_record[1] if name.endswith('Regular'): name = name.rsplit(' ', 1)[0] name = name.replace(' ', '') return name
def get_family_filename(family): """Returns a filename to use for a family zip of hinted/unhinted members. This is basically the postscript name with weight/style removed. """ font = ttLib.TTFont(family.rep_member.filepath, fontNumber=0) name_record = font_data.get_name_records(font) try: name = name_record[6] ix = name.find("-") if ix >= 0: name = name[:ix] except KeyError: name = name_record[1] if name.endswith("Regular"): name = name.rsplit(" ", 1)[0] name = name.replace(" ", "") return name
def apply_temporary_fixes(font, is_for_cros=False): """Apply some temporary fixes.""" # Fix usWeight: font_name = font_data.font_name(font) weight = noto_fonts.parse_weight(font_name) weight_number = noto_fonts.WEIGHTS[weight] # Chrome OS wants Thin to have usWeightClass=100 if is_for_cros and weight == 'Thin': weight_number = 100 font['OS/2'].usWeightClass = weight_number # Set bold bits for Black (macStyle bit 0, fsSelection bit 5) name_records = font_data.get_name_records(font) family_name = name_records[1] if family_name.endswith('Black'): font['head'].macStyle |= (1 << 0) font['OS/2'].fsSelection |= (1 << 5) font['OS/2'].fsSelection &= ~(1 << 6)
def apply_temporary_fixes(font): """Apply some temporary fixes.""" # Fix usWeight: font_name = font_data.font_name(font) weight = noto_fonts.parse_weight(font_name) weight_number = noto_fonts.WEIGHTS[weight] font['OS/2'].usWeightClass = weight_number # Set ascent, descent, and lineGap values to Android K values hhea = font['hhea'] hhea.ascent = 1900 hhea.descent = -500 hhea.lineGap = 0 # Set bold bits for Black (macStyle bit 0, fsSelection bit 5) name_records = font_data.get_name_records(font) family_name = name_records[1] if family_name.endswith('Black'): font['head'].macStyle |= (1 << 0) font['OS/2'].fsSelection |= (1 << 5) font['OS/2'].fsSelection &= ~(1 << 6)
def fix_name_table(font): """Fix copyright and reversed values in the 'name' table.""" modified = False name_records = font_data.get_name_records(font) copyright_data = name_records[0] years = re.findall('20[0-9][0-9]', copyright_data) year = min(years) copyright_data = u'Copyright %s Google Inc. All Rights Reserved.' % year if copyright_data != name_records[0]: print 'Updated copyright message to "%s"' % copyright_data font_data.set_name_record(font, 0, copyright_data) modified = True for name_id in [1, 3, 4, 6]: record = name_records[name_id] for source in NAME_CORRECTIONS: if source in record: oldrecord = record record = record.replace(source, NAME_CORRECTIONS[source]) break if record != name_records[name_id]: font_data.set_name_record(font, name_id, record) print 'Updated name table record #%d from "%s" to "%s"' % ( name_id, oldrecord, record) modified = True if name_records[7] != TRADEMARK_LINE: font_data.set_name_record(font, 7, TRADEMARK_LINE) modified = True print 'Updated name table record 7 to "%s"' % TRADEMARK_LINE # TODO: check preferred family/subfamily(16&17) return modified
def get_font_family_name(font_file): font = ttLib.TTFont(font_file) name_record = font_data.get_name_records(font) return name_record[1]
def _swat_font(noto_font, dst_root, dry_run): filepath = noto_font.filepath basename = path.basename(filepath) if noto_font.is_cjk: print '# Skipping cjk font %s' % basename return if noto_font.fmt == 'ttc': print '# Deferring ttc font %s' % basename _ttc_fonts[noto_font] = ttc_utils.ttcfile_filenames(filepath) return ttfont = ttLib.TTFont(filepath, fontNumber=0) names = font_data.get_name_records(ttfont) # create relative root path rel_filepath = _noto_relative_path(filepath) if not rel_filepath: raise ValueError('Could not identify noto root of %s' % filepath) print '-----\nUpdating %s' % rel_filepath dst_file = path.join(dst_root, rel_filepath) try: new_revision, new_version_string = get_bumped_version( ttfont, noto_font.is_hinted) except ValueError as e: print e return print '%s: %s' % ('Would write' if dry_run else 'Writing', dst_file) new_trademark = "%s is a trademark of Google Inc." % noto_font.family # description field should be set. # Roozbeh has note, make sure design field has information # on whether the font is hinted. # Missing in Lao and Khmer, default in Cham. if (cldr_data.get_english_script_name(noto_font.script) in ['Lao', 'Khmer', 'Cham']): new_description = 'Data %shinted.' % ('' if noto_font.is_hinted else 'un') # elif noto_font.vendor is 'Monotype': elif not noto_font.is_cjk and noto_font.family == 'Noto': new_description = ( 'Data %shinted. Designed by Monotype design team.' % ('' if noto_font.is_hinted else 'un')) else: new_description = None if re.match(r'^Copyright 201\d Google Inc. All Rights Reserved\.$', names[_COPYRIGHT_ID]): new_copyright = None else: new_copyright = '!!' if names.get(_DESIGNER_ID) in [ 'Steve Matteson', 'Monotype Design Team', 'Danh Hong', ]: new_designer = None elif names.get(_DESIGNER_ID) == 'Monotype Design team': new_designer = 'Monotype Design Team' elif (_DESIGNER_ID not in names and cldr_data.get_english_script_name(noto_font.script) == 'Khmer'): new_designer = 'Danh Hong' else: new_designer = '!!' if names.get(_DESIGNER_URL_ID) in [ 'http://www.monotype.com/studio', 'http://www.khmertype.org', ]: new_designer_url = None elif names.get(_DESIGNER_URL_ID) in [ 'http://www.monotypeimaging.com/ProductsServices/TypeDesignerShowcase', ]: new_designer_url = 'http://www.monotype.com/studio' elif names.get(_DESIGNER_URL_ID) in [ 'http://www.khmertype.blogspot.com', 'http://www.khmertype.blogspot.com/', 'http://khmertype.blogspot.com/', 'http://wwwkhmertype.blogspot.com.com/', ]: new_designer_url = 'http://www.khmertype.org' else: new_designer_url = '!!!' if names.get(_MANUFACTURER_ID) in [ 'Monotype Imaging Inc.', 'Danh Hong', ]: new_manufacturer = None else: new_manufacturer = '!!!' def update(name_id, new, newText=None): old = names.get(name_id) if new and (new != old): if not dry_run and not '!!!' in new: font_data.set_name_record(ttfont, name_id, new, addIfMissing='win') label = _NAME_ID_LABELS[name_id] oldText = '\'%s\'' % old if old else 'None' newText = newText or ('\'%s\'' % new) print '%s:\n old: %s\n new: %s' % (label, oldText, newText or new) label_change = _changes.get(label) if not label_change: label_change = {} _changes[label] = label_change new_val_change = label_change.get(new) if not new_val_change: new_val_change = {} label_change[new] = new_val_change old_val_fonts = new_val_change.get(old) if not old_val_fonts: old_val_fonts = [] new_val_change[old] = old_val_fonts old_val_fonts.append(noto_font.filepath) update(_COPYRIGHT_ID, new_copyright) update(_VERSION_ID, new_version_string) update(_TRADEMARK_ID, new_trademark) update(_MANUFACTURER_ID, new_manufacturer) update(_DESIGNER_ID, new_designer) update(_DESCRIPTION_ID, new_description) update(_VENDOR_URL_ID, _NOTO_URL) update(_DESIGNER_URL_ID, new_designer_url) update(_LICENSE_ID, _SIL_LICENSE, newText='(OFL)') update(_LICENSE_URL_ID, _SIL_LICENSE_URL) if autofix_for_release.fix_fstype(ttfont): _autofix['fstype'].append(noto_font.filepath) if autofix_for_release.fix_vendor_id(ttfont): _autofix['vendor_id'].append(noto_font.filepath) if autofix_for_release.fix_attachlist(ttfont): _autofix['attachlist'].append(noto_font.filepath) if noto_font.is_hinted: tables_to_drop = _HINTED_TABLES_TO_DROP else: tables_to_drop = _UNHINTED_TABLES_TO_DROP if autofix_for_release.drop_hints(ttfont): _autofix['drop_hints'].append(noto_font.filepath) if autofix_for_release.drop_tables(ttfont, tables_to_drop): _autofix['drop_tables'].append(noto_font.filepath) if noto_font.family == 'Noto': if autofix_for_release.fix_linegap(ttfont): _autofix['linegap'].append(noto_font.filepath) if autofix_for_release.fix_os2_unicoderange(ttfont): _autofix['os2_unicoderange'].append(noto_font.filepath) if dry_run: return ttfont['head'].fontRevision = float_revision dst_dir = path.dirname(dst_file) if not path.isdir(dst_dir): os.makedirs(dst_dir) ttfont.save(dst_file) print 'Wrote file.'
def setUp(self): _, self.fonts = self.loaded_fonts self.condensed_family_name = self.family_name + ' Condensed' self.names = [] for font in self.fonts: self.names.append(font_data.get_name_records(font))
def _swat_font(noto_font, dst_root, dry_run): filepath = noto_font.filepath basename = path.basename(filepath) if noto_font.is_cjk: print '# Skipping cjk font %s' % basename return if noto_font.fmt == 'ttc': print '# Deferring ttc font %s' % basename _ttc_fonts[noto_font] = ttc_utils.ttcfile_filenames(filepath) return ttfont = ttLib.TTFont(filepath, fontNumber=0) names = font_data.get_name_records(ttfont) # create relative root path rel_filepath = _noto_relative_path(filepath) if not rel_filepath: raise ValueError('Could not identify noto root of %s' % filepath) print '-----\nUpdating %s' % rel_filepath dst_file = path.join(dst_root, rel_filepath) version = names[_VERSION_ID] m = re.match(r'Version (\d{1,5})\.(\d{1,5})(.*)', version) if not m: print '! Could not match version string (%s)' % version return major_version = m.group(1) minor_version = m.group(2) version_remainder = m.group(3) accuracy = len(minor_version) print_revision = font_data.printable_font_revision(ttfont, accuracy) # sanity check expected_revision = major_version + '.' + minor_version if expected_revision != print_revision: print '! Expected revision \'%s\' but got revision \'%s\'' % ( expected_revision, print_revision) return # bump the minor version keeping significant digits: new_minor_version = str(int(minor_version) + 1).zfill(accuracy) new_revision = major_version + '.' + new_minor_version print 'Update revision from \'%s\' to \'%s\'' % (expected_revision, new_revision) # double check we are going to properly round-trip this value float_revision = float(new_revision) fixed_revision = misc.fixedTools.floatToFixed(float_revision, 16) rt_float_rev = misc.fixedTools.fixedToFloat(fixed_revision, 16) rt_float_rev_int = int(rt_float_rev) rt_float_rev_frac = int( round((rt_float_rev - rt_float_rev_int) * 10**accuracy)) rt_new_revision = (str(rt_float_rev_int) + '.' + str(rt_float_rev_frac).zfill(accuracy)) if new_revision != rt_new_revision: print '! Could not update new revision, expected \'%s\' but got \'%s\'' % ( new_revision, rt_new_revision) return new_version_string = 'Version ' + new_revision if not noto_font.is_hinted: new_version_string += ' uh' print '%s: %s' % ('Would write' if dry_run else 'Writing', dst_file) new_trademark = "%s is a trademark of Google Inc." % noto_font.family # description field should be set. # Roozbeh has note, make sure design field has information # on whether the font is hinted. # Missing in Lao and Khmer, default in Cham. if (cldr_data.get_english_script_name(noto_font.script) in ['Lao', 'Khmer', 'Cham']): new_description = 'Data %shinted.' % ('' if noto_font.is_hinted else 'un') # elif noto_font.vendor is 'Monotype': elif not noto_font.is_cjk and noto_font.family == 'Noto': new_description = ('Data %shinted. Designed by Monotype design team.' % ('' if noto_font.is_hinted else 'un')) else: new_description = None if re.match(r'^Copyright 201\d Google Inc. All Rights Reserved\.$', names[_COPYRIGHT_ID]): new_copyright = None else: new_copyright = '!!' if names.get(_DESIGNER_ID) in [ 'Steve Matteson', 'Monotype Design Team', 'Danh Hong', ]: new_designer = None elif names.get(_DESIGNER_ID) == 'Monotype Design team': new_designer = 'Monotype Design Team' elif (_DESIGNER_ID not in names and cldr_data.get_english_script_name(noto_font.script) == 'Khmer'): new_designer = 'Danh Hong' else: new_designer = '!!' if names.get(_DESIGNER_URL_ID) in [ 'http://www.monotype.com/studio', 'http://www.khmertype.org', ]: new_designer_url = None elif names.get(_DESIGNER_URL_ID) in [ 'http://www.monotypeimaging.com/ProductsServices/TypeDesignerShowcase', ]: new_designer_url = 'http://www.monotype.com/studio' elif names.get(_DESIGNER_URL_ID) in [ 'http://www.khmertype.blogspot.com', 'http://www.khmertype.blogspot.com/', 'http://khmertype.blogspot.com/', 'http://wwwkhmertype.blogspot.com.com/', ]: new_designer_url = 'http://www.khmertype.org' else: new_designer_url = '!!!' if names.get(_MANUFACTURER_ID) in [ 'Monotype Imaging Inc.', 'Danh Hong', ]: new_manufacturer = None else: new_manufacturer = '!!!' def update(name_id, new, newText=None): old = names.get(name_id) if new and (new != old): if not dry_run and not '!!!' in new: font_data.set_name_record(ttfont, name_id, new, addIfMissing='win') label = _NAME_ID_LABELS[name_id] oldText = '\'%s\'' % old if old else 'None' newText = newText or ('\'%s\'' % new) print '%s:\n old: %s\n new: %s' % (label, oldText, newText or new) label_change = _changes.get(label) if not label_change: label_change = {} _changes[label] = label_change new_val_change = label_change.get(new) if not new_val_change: new_val_change = {} label_change[new] = new_val_change old_val_fonts = new_val_change.get(old) if not old_val_fonts: old_val_fonts = [] new_val_change[old] = old_val_fonts old_val_fonts.append(noto_font.filepath) update(_COPYRIGHT_ID, new_copyright) update(_VERSION_ID, new_version_string) update(_TRADEMARK_ID, new_trademark) update(_MANUFACTURER_ID, new_manufacturer) update(_DESIGNER_ID, new_designer) update(_DESCRIPTION_ID, new_description) update(_VENDOR_URL_ID, _NOTO_URL) update(_DESIGNER_URL_ID, new_designer_url) update(_LICENSE_ID, _SIL_LICENSE, newText='(OFL)') update(_LICENSE_URL_ID, _SIL_LICENSE_URL) if autofix_for_release.fix_fstype(ttfont): _autofix['fstype'].append(noto_font.filepath) if autofix_for_release.fix_vendor_id(ttfont): _autofix['vendor_id'].append(noto_font.filepath) if autofix_for_release.fix_attachlist(ttfont): _autofix['attachlist'].append(noto_font.filepath) if noto_font.is_hinted: tables_to_drop = _HINTED_TABLES_TO_DROP else: tables_to_drop = _UNHINTED_TABLES_TO_DROP if autofix_for_release.drop_hints(ttfont): _autofix['drop_hints'].append(noto_font.filepath) if autofix_for_release.drop_tables(ttfont, tables_to_drop): _autofix['drop_tables'].append(noto_font.filepath) if noto_font.family == 'Noto': if autofix_for_release.fix_linegap(ttfont): _autofix['linegap'].append(noto_font.filepath) if autofix_for_release.fix_os2_unicoderange(ttfont): _autofix['os2_unicoderange'].append(noto_font.filepath) if dry_run: return ttfont['head'].fontRevision = float_revision dst_dir = path.dirname(dst_file) if not path.isdir(dst_dir): os.makedirs(dst_dir) ttfont.save(dst_file) print 'Wrote file.'
def _swat_font(noto_font, dst_root, dry_run): filepath = noto_font.filepath basename = path.basename(filepath) if noto_font.is_cjk: print("# Skipping cjk font %s" % basename) return if noto_font.fmt == "ttc": print("# Deferring ttc font %s" % basename) _ttc_fonts[noto_font] = ttc_utils.ttcfile_filenames(filepath) return ttfont = ttLib.TTFont(filepath, fontNumber=0) names = font_data.get_name_records(ttfont) # create relative root path rel_filepath = _noto_relative_path(filepath) if not rel_filepath: raise ValueError("Could not identify noto root of %s" % filepath) print("-----\nUpdating %s" % rel_filepath) dst_file = path.join(dst_root, rel_filepath) try: new_revision, new_version_string = get_bumped_version( ttfont, noto_font.is_hinted) except ValueError as e: print(e) return print("%s: %s" % ("Would write" if dry_run else "Writing", dst_file)) new_trademark = "%s is a trademark of Google Inc." % noto_font.family # description field should be set. # Roozbeh has note, make sure design field has information # on whether the font is hinted. # Missing in Lao and Khmer, default in Cham. if cldr_data.get_english_script_name( noto_font.script) in ["Lao", "Khmer", "Cham"]: new_description = "Data %shinted." % ("" if noto_font.is_hinted else "un") # elif noto_font.vendor is 'Monotype': elif not noto_font.is_cjk and noto_font.family == "Noto": new_description = "Data %shinted. Designed by Monotype design team." % ( "" if noto_font.is_hinted else "un") else: new_description = None if re.match(r"^Copyright 201\d Google Inc. All Rights Reserved\.$", names[_COPYRIGHT_ID]): new_copyright = None else: new_copyright = "!!" if names.get(_DESIGNER_ID) in [ "Steve Matteson", "Monotype Design Team", "Danh Hong", ]: new_designer = None elif names.get(_DESIGNER_ID) == "Monotype Design team": new_designer = "Monotype Design Team" elif (_DESIGNER_ID not in names and cldr_data.get_english_script_name(noto_font.script) == "Khmer"): new_designer = "Danh Hong" else: new_designer = "!!" if names.get(_DESIGNER_URL_ID) in [ "http://www.monotype.com/studio", "http://www.khmertype.org", ]: new_designer_url = None elif names.get(_DESIGNER_URL_ID) in [ "http://www.monotypeimaging.com/ProductsServices/TypeDesignerShowcase", ]: new_designer_url = "http://www.monotype.com/studio" elif names.get(_DESIGNER_URL_ID) in [ "http://www.khmertype.blogspot.com", "http://www.khmertype.blogspot.com/", "http://khmertype.blogspot.com/", "http://wwwkhmertype.blogspot.com.com/", ]: new_designer_url = "http://www.khmertype.org" else: new_designer_url = "!!!" if names.get(_MANUFACTURER_ID) in [ "Monotype Imaging Inc.", "Danh Hong", ]: new_manufacturer = None else: new_manufacturer = "!!!" def update(name_id, new, newText=None): old = names.get(name_id) if new and (new != old): if not dry_run and not "!!!" in new: font_data.set_name_record(ttfont, name_id, new, addIfMissing="win") label = _NAME_ID_LABELS[name_id] oldText = "'%s'" % old if old else "None" newText = newText or ("'%s'" % new) print("%s:\n old: %s\n new: %s" % (label, oldText, newText or new)) label_change = _changes.get(label) if not label_change: label_change = {} _changes[label] = label_change new_val_change = label_change.get(new) if not new_val_change: new_val_change = {} label_change[new] = new_val_change old_val_fonts = new_val_change.get(old) if not old_val_fonts: old_val_fonts = [] new_val_change[old] = old_val_fonts old_val_fonts.append(noto_font.filepath) update(_COPYRIGHT_ID, new_copyright) update(_VERSION_ID, new_version_string) update(_TRADEMARK_ID, new_trademark) update(_MANUFACTURER_ID, new_manufacturer) update(_DESIGNER_ID, new_designer) update(_DESCRIPTION_ID, new_description) update(_VENDOR_URL_ID, _NOTO_URL) update(_DESIGNER_URL_ID, new_designer_url) update(_LICENSE_ID, _SIL_LICENSE, newText="(OFL)") update(_LICENSE_URL_ID, _SIL_LICENSE_URL) if autofix_for_release.fix_fstype(ttfont): _autofix["fstype"].append(noto_font.filepath) if autofix_for_release.fix_vendor_id(ttfont): _autofix["vendor_id"].append(noto_font.filepath) if autofix_for_release.fix_attachlist(ttfont): _autofix["attachlist"].append(noto_font.filepath) if noto_font.is_hinted: tables_to_drop = _HINTED_TABLES_TO_DROP else: tables_to_drop = _UNHINTED_TABLES_TO_DROP if autofix_for_release.drop_hints(ttfont): _autofix["drop_hints"].append(noto_font.filepath) if autofix_for_release.drop_tables(ttfont, tables_to_drop): _autofix["drop_tables"].append(noto_font.filepath) if noto_font.family == "Noto": if autofix_for_release.fix_linegap(ttfont): _autofix["linegap"].append(noto_font.filepath) if autofix_for_release.fix_os2_unicoderange(ttfont): _autofix["os2_unicoderange"].append(noto_font.filepath) if dry_run: return ttfont["head"].fontRevision = float_revision dst_dir = path.dirname(dst_file) if not path.isdir(dst_dir): os.makedirs(dst_dir) ttfont.save(dst_file) print("Wrote file.")
def apply_web_specific_fixes(font, unhinted, family_name): """Apply fixes needed for web fonts.""" # Set OS/2 table values to old values os2 = font['OS/2'] os2.sTypoAscender = 1536 os2.sTypoDescender = -512 os2.sTypoLineGap = 102 os2.usWinAscent = 1946 os2.usWinDescent = 512 subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert (subfamily_name in [ 'Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic' ]) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name if subfamily_name != 'Regular': full_name += ' ' + subfamily_name # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] if subfamily_name in style_map: font_data.set_name_record(font, 1, family_name) else: weight = subfamily_name.split()[0] new_family_name = family_name if weight != 'Regular': new_family_name += ' ' + weight font_data.set_name_record(font, 1, new_family_name) # all weights outside regular and bold should only have subfamily # "Regular" or "Italic" italic = subfamily_name.endswith('Italic') font_data.set_name_record(font, 2, style_map[italic << 1]) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record(font, 6, (family_name + '-' + subfamily_name).replace( ' ', '')) # Copyright message font_data.set_name_record( font, 0, 'Copyright 2011 Google Inc. All Rights Reserved.') # hotpatch glyphs by swapping # https://github.com/google/roboto/issues/18 glyf = font['glyf'] tmp = glyf['chi'] glyf['chi'] = glyf['chi.alt'] glyf['chi.alt'] = tmp # make glyph orders consistent for feature copying # https://github.com/google/roboto/issues/71 glyph_order = font.getGlyphOrder() for i, glyph_name in enumerate(glyph_order): if glyph_name.endswith('.lnum'): new_name = glyph_name.replace('.lnum', '.pnum') glyph_order[i] = new_name font['glyf'][new_name] = font['glyf'][glyph_name] # append old name to glyph order so del succeeds glyph_order.append(glyph_name) del font['glyf'][glyph_name] # copy features from unhinted # https://github.com/google/roboto/pull/163 for table in ['GDEF', 'GPOS', 'GSUB']: font[table] = unhinted[table]
def _swat_font(noto_font, dst_root, dry_run): filepath = noto_font.filepath basename = path.basename(filepath) if noto_font.is_cjk: print '# Skipping cjk font %s' % basename return if noto_font.fmt == 'ttc': print '# Deferring ttc font %s' % basename _ttc_fonts[noto_font] = ttc_utils.ttcfile_filenames(filepath) return ttfont = ttLib.TTFont(filepath, fontNumber=0) names = font_data.get_name_records(ttfont) # create relative root path rel_filepath = _noto_relative_path(filepath) if not rel_filepath: raise ValueError('Could not identify noto root of %s' % filepath) print '-----\nUpdating %s' % rel_filepath dst_file = path.join(dst_root, rel_filepath) version = names[_VERSION_ID] m = re.match(r'Version (\d{1,5})\.(\d{1,5})(.*)', version) if not m: print '! Could not match version string (%s)' % version return major_version = m.group(1) minor_version = m.group(2) version_remainder = m.group(3) accuracy = len(minor_version) print_revision = font_data.printable_font_revision(ttfont, accuracy) # sanity check expected_revision = major_version + '.' + minor_version if expected_revision != print_revision: print '! Expected revision \'%s\' but got revision \'%s\'' % ( expected_revision, print_revision) return # bump the minor version keeping significant digits: new_minor_version = str(int(minor_version) + 1).zfill(accuracy) new_revision = major_version + '.' + new_minor_version print 'Update revision from \'%s\' to \'%s\'' % ( expected_revision, new_revision) # double check we are going to properly round-trip this value float_revision = float(new_revision) fixed_revision = misc.fixedTools.floatToFixed(float_revision, 16) rt_float_rev = misc.fixedTools.fixedToFloat(fixed_revision, 16) rt_float_rev_int = int(rt_float_rev) rt_float_rev_frac = int(round((rt_float_rev - rt_float_rev_int) * 10 ** accuracy)) rt_new_revision = (str(rt_float_rev_int) + '.' + str(rt_float_rev_frac).zfill(accuracy)) if new_revision != rt_new_revision: print '! Could not update new revision, expected \'%s\' but got \'%s\'' % ( new_revision, rt_new_revision) return new_version_string = 'Version ' + new_revision if not noto_font.is_hinted: new_version_string += ' uh' print '%s: %s' % ('Would write' if dry_run else 'Writing', dst_file) new_trademark = "%s is a trademark of Google Inc." % noto_font.family # description field should be set. # Roozbeh has note, make sure design field has information # on whether the font is hinted. # Missing in Lao and Khmer, default in Cham. if (cldr_data.get_english_script_name(noto_font.script) in ['Lao', 'Khmer', 'Cham']): new_description = 'Data %shinted.' % ('' if noto_font.is_hinted else 'un') # elif noto_font.vendor is 'Monotype': elif not noto_font.is_cjk and noto_font.family == 'Noto': new_description = ( 'Data %shinted. Designed by Monotype design team.' % ('' if noto_font.is_hinted else 'un')) else: new_description = None if re.match(r'^Copyright 201\d Google Inc. All Rights Reserved\.$', names[_COPYRIGHT_ID]): new_copyright = None else: new_copyright = '!!' if names.get(_DESIGNER_ID) in [ 'Steve Matteson', 'Monotype Design Team', 'Danh Hong', ]: new_designer = None elif names.get(_DESIGNER_ID) == 'Monotype Design team': new_designer = 'Monotype Design Team' elif (_DESIGNER_ID not in names and cldr_data.get_english_script_name(noto_font.script) == 'Khmer'): new_designer = 'Danh Hong' else: new_designer = '!!' if names.get(_DESIGNER_URL_ID) in [ 'http://www.monotype.com/studio', 'http://www.khmertype.org', ]: new_designer_url = None elif names.get(_DESIGNER_URL_ID) in [ 'http://www.monotypeimaging.com/ProductsServices/TypeDesignerShowcase', ]: new_designer_url = 'http://www.monotype.com/studio' elif names.get(_DESIGNER_URL_ID) in [ 'http://www.khmertype.blogspot.com', 'http://www.khmertype.blogspot.com/', 'http://khmertype.blogspot.com/', 'http://wwwkhmertype.blogspot.com.com/', ]: new_designer_url = 'http://www.khmertype.org' else: new_designer_url = '!!!' if names.get(_MANUFACTURER_ID) in [ 'Monotype Imaging Inc.', 'Danh Hong', ]: new_manufacturer = None else: new_manufacturer = '!!!' def update(name_id, new, newText=None): old = names.get(name_id) if new and (new != old): if not dry_run and not '!!!' in new: font_data.set_name_record(ttfont, name_id, new, addIfMissing='win') label = _NAME_ID_LABELS[name_id] oldText = '\'%s\'' % old if old else 'None' newText = newText or ('\'%s\'' % new) print '%s:\n old: %s\n new: %s' % (label, oldText, newText or new) label_change = _changes.get(label) if not label_change: label_change = {} _changes[label] = label_change new_val_change = label_change.get(new) if not new_val_change: new_val_change = {} label_change[new] = new_val_change old_val_fonts = new_val_change.get(old) if not old_val_fonts: old_val_fonts = [] new_val_change[old] = old_val_fonts old_val_fonts.append(noto_font.filepath) update(_COPYRIGHT_ID, new_copyright) update(_VERSION_ID, new_version_string) update(_TRADEMARK_ID, new_trademark) update(_MANUFACTURER_ID, new_manufacturer) update(_DESIGNER_ID, new_designer) update(_DESCRIPTION_ID, new_description) update(_VENDOR_URL_ID, _NOTO_URL) update(_DESIGNER_URL_ID, new_designer_url) update(_LICENSE_ID, _SIL_LICENSE, newText='(OFL)') update(_LICENSE_URL_ID, _SIL_LICENSE_URL) if autofix_for_release.fix_fstype(ttfont): _autofix['fstype'].append(noto_font.filepath) if autofix_for_release.fix_vendor_id(ttfont): _autofix['vendor_id'].append(noto_font.filepath) if autofix_for_release.fix_attachlist(ttfont): _autofix['attachlist'].append(noto_font.filepath) if noto_font.is_hinted: tables_to_drop = _HINTED_TABLES_TO_DROP else: tables_to_drop = _UNHINTED_TABLES_TO_DROP if autofix_for_release.drop_hints(ttfont): _autofix['drop_hints'].append(noto_font.filepath) if autofix_for_release.drop_tables(ttfont, tables_to_drop): _autofix['drop_tables'].append(noto_font.filepath) if noto_font.family == 'Noto': if autofix_for_release.fix_linegap(ttfont): _autofix['linegap'].append(noto_font.filepath) if autofix_for_release.fix_os2_unicoderange(ttfont): _autofix['os2_unicoderange'].append(noto_font.filepath) if dry_run: return ttfont['head'].fontRevision = float_revision dst_dir = path.dirname(dst_file) if not path.isdir(dst_dir): os.makedirs(dst_dir) ttfont.save(dst_file) print 'Wrote file.'
def fix_font(f, dst_dir, rel_dir, version, version_info, autohint, dry_run): print '\n-----\nfont:', f font = ttLib.TTFont(f) relfont = _get_release_font(f, rel_dir) expected_font_revision = get_new_version(font, relfont, version) if expected_font_revision != None: font_revision = font_data.printable_font_revision(font, 3) if font_revision != expected_font_revision: _alert('revision', font_revision, expected_font_revision) font['head'].fontRevision = float(expected_font_revision) names = font_data.get_name_records(font) NAME_ID = 5 font_version = names[NAME_ID] expected_version = ( 'Version %s;%s' % (expected_font_revision, version_info)) if font_version != expected_version: _alert('version string', font_version, expected_version) font_data.set_name_record(font, NAME_ID, expected_version) expected_upem = 1000 upem = font['head'].unitsPerEm if upem != expected_upem: print 'expected %d upem but got %d upem' % (expected_upem, upem) if _is_ui_metrics(f): if upem == 2048: expected_ascent = 2163 expected_descent = -555 elif upem == 1000: expected_ascent = 1069 expected_descent = -293 else: raise Exception('no expected ui ascent/descent for upem: %d' % upem) font_ascent = font['hhea'].ascent font_descent = font['hhea'].descent if font_ascent != expected_ascent: _alert_and_check('ascent', font_ascent, expected_ascent, 2) font['hhea'].ascent = expected_ascent font['OS/2'].sTypoAscender = expected_ascent font['OS/2'].usWinAscent = expected_ascent if font_descent != expected_descent: _alert_and_check('descent', font_descent, expected_descent, -2) font['hhea'].descent = expected_descent font['OS/2'].sTypoDescender = expected_descent font['OS/2'].usWinDescent = -expected_descent tool_utils.ensure_dir_exists(path.join(dst_dir, 'unhinted')) fname = path.basename(f) udst = path.join(dst_dir, 'unhinted', fname) if dry_run: print 'dry run would write:\n "%s"' % udst else: font.save(udst) print 'wrote %s' % udst if autohint: hdst = path.join(dst_dir, 'hinted', fname) autohint_font(udst, hdst, autohint, dry_run)
def _swat_font(noto_font, dst_root, dry_run): filepath = noto_font.filepath basename = path.basename(filepath) if noto_font.is_cjk: print('# Skipping cjk font %s' % basename) return if noto_font.fmt == 'ttc': print('# Deferring ttc font %s' % basename) _ttc_fonts[noto_font] = ttc_utils.ttcfile_filenames(filepath) return ttfont = ttLib.TTFont(filepath, fontNumber=0) names = font_data.get_name_records(ttfont) # create relative root path rel_filepath = _noto_relative_path(filepath) if not rel_filepath: raise ValueError('Could not identify noto root of %s' % filepath) print('-----\nUpdating %s' % rel_filepath) dst_file = path.join(dst_root, rel_filepath) try: new_revision, new_version_string = get_bumped_version( ttfont, noto_font.is_hinted) except ValueError as e: print(e) return print('%s: %s' % ('Would write' if dry_run else 'Writing', dst_file)) new_trademark = "%s is a trademark of Google Inc." % noto_font.family # description field should be set. # Roozbeh has note, make sure design field has information # on whether the font is hinted. # Missing in Lao and Khmer, default in Cham. if (cldr_data.get_english_script_name(noto_font.script) in ['Lao', 'Khmer', 'Cham']): new_description = 'Data %shinted.' % ('' if noto_font.is_hinted else 'un') # elif noto_font.vendor is 'Monotype': elif not noto_font.is_cjk and noto_font.family == 'Noto': new_description = ('Data %shinted. Designed by Monotype design team.' % ('' if noto_font.is_hinted else 'un')) else: new_description = None if re.match(r'^Copyright 201\d Google Inc. All Rights Reserved\.$', names[_COPYRIGHT_ID]): new_copyright = None else: new_copyright = '!!' if names.get(_DESIGNER_ID) in [ 'Steve Matteson', 'Monotype Design Team', 'Danh Hong', ]: new_designer = None elif names.get(_DESIGNER_ID) == 'Monotype Design team': new_designer = 'Monotype Design Team' elif (_DESIGNER_ID not in names and cldr_data.get_english_script_name(noto_font.script) == 'Khmer'): new_designer = 'Danh Hong' else: new_designer = '!!' if names.get(_DESIGNER_URL_ID) in [ 'http://www.monotype.com/studio', 'http://www.khmertype.org', ]: new_designer_url = None elif names.get(_DESIGNER_URL_ID) in [ 'http://www.monotypeimaging.com/ProductsServices/TypeDesignerShowcase', ]: new_designer_url = 'http://www.monotype.com/studio' elif names.get(_DESIGNER_URL_ID) in [ 'http://www.khmertype.blogspot.com', 'http://www.khmertype.blogspot.com/', 'http://khmertype.blogspot.com/', 'http://wwwkhmertype.blogspot.com.com/', ]: new_designer_url = 'http://www.khmertype.org' else: new_designer_url = '!!!' if names.get(_MANUFACTURER_ID) in [ 'Monotype Imaging Inc.', 'Danh Hong', ]: new_manufacturer = None else: new_manufacturer = '!!!' def update(name_id, new, newText=None): old = names.get(name_id) if new and (new != old): if not dry_run and not '!!!' in new: font_data.set_name_record(ttfont, name_id, new, addIfMissing='win') label = _NAME_ID_LABELS[name_id] oldText = '\'%s\'' % old if old else 'None' newText = newText or ('\'%s\'' % new) print('%s:\n old: %s\n new: %s' % (label, oldText, newText or new)) label_change = _changes.get(label) if not label_change: label_change = {} _changes[label] = label_change new_val_change = label_change.get(new) if not new_val_change: new_val_change = {} label_change[new] = new_val_change old_val_fonts = new_val_change.get(old) if not old_val_fonts: old_val_fonts = [] new_val_change[old] = old_val_fonts old_val_fonts.append(noto_font.filepath) update(_COPYRIGHT_ID, new_copyright) update(_VERSION_ID, new_version_string) update(_TRADEMARK_ID, new_trademark) update(_MANUFACTURER_ID, new_manufacturer) update(_DESIGNER_ID, new_designer) update(_DESCRIPTION_ID, new_description) update(_VENDOR_URL_ID, _NOTO_URL) update(_DESIGNER_URL_ID, new_designer_url) update(_LICENSE_ID, _SIL_LICENSE, newText='(OFL)') update(_LICENSE_URL_ID, _SIL_LICENSE_URL) if autofix_for_release.fix_fstype(ttfont): _autofix['fstype'].append(noto_font.filepath) if autofix_for_release.fix_vendor_id(ttfont): _autofix['vendor_id'].append(noto_font.filepath) if autofix_for_release.fix_attachlist(ttfont): _autofix['attachlist'].append(noto_font.filepath) if noto_font.is_hinted: tables_to_drop = _HINTED_TABLES_TO_DROP else: tables_to_drop = _UNHINTED_TABLES_TO_DROP if autofix_for_release.drop_hints(ttfont): _autofix['drop_hints'].append(noto_font.filepath) if autofix_for_release.drop_tables(ttfont, tables_to_drop): _autofix['drop_tables'].append(noto_font.filepath) if noto_font.family == 'Noto': if autofix_for_release.fix_linegap(ttfont): _autofix['linegap'].append(noto_font.filepath) if autofix_for_release.fix_os2_unicoderange(ttfont): _autofix['os2_unicoderange'].append(noto_font.filepath) if dry_run: return ttfont['head'].fontRevision = float_revision dst_dir = path.dirname(dst_file) if not path.isdir(dst_dir): os.makedirs(dst_dir) ttfont.save(dst_file) print('Wrote file.')
def apply_web_cros_common_fixes(font, unhinted, family_name): """Apply fixes needed for web and CrOS targets""" subfamily_name = font_data.get_name_records(font)[2].encode('ASCII') assert(subfamily_name in ['Thin', 'Thin Italic', 'Light', 'Light Italic', 'Regular', 'Italic', 'Medium', 'Medium Italic', 'Bold', 'Bold Italic', 'Black', 'Black Italic']) if 'Condensed' in font_data.get_name_records(font)[1]: family_name += ' Condensed' full_name = family_name if subfamily_name != 'Regular': full_name += ' ' + subfamily_name # Family, subfamily names font_data.set_name_record(font, 16, family_name) style_map = ['Regular', 'Bold', 'Italic', 'Bold Italic'] if subfamily_name in style_map: font_data.set_name_record(font, 1, family_name) else: weight = subfamily_name.split()[0] new_family_name = family_name if weight != 'Regular': new_family_name += ' ' + weight font_data.set_name_record(font, 1, new_family_name) # all weights outside regular and bold should only have subfamily # "Regular" or "Italic" italic = subfamily_name.endswith('Italic') font_data.set_name_record(font, 2, style_map[italic << 1]) # Unique identifier and full name font_data.set_name_record(font, 3, full_name) font_data.set_name_record(font, 4, full_name) font_data.set_name_record(font, 18, None) # PostScript name font_data.set_name_record( font, 6, (family_name+'-'+subfamily_name).replace(' ', '')) # Copyright message font_data.set_name_record( font, 0, 'Copyright 2011 Google Inc. All Rights Reserved.') # hotpatch glyphs by swapping # https://github.com/google/roboto/issues/18 glyf = font['glyf'] tmp = glyf['chi'] glyf['chi'] = glyf['chi.alt'] glyf['chi.alt'] = tmp # make glyph orders consistent for feature copying # https://github.com/google/roboto/issues/71 glyph_order = font.getGlyphOrder() for i, glyph_name in enumerate(glyph_order): if glyph_name.endswith('.lnum'): new_name = glyph_name.replace('.lnum', '.pnum') glyph_order[i] = new_name font['glyf'][new_name] = font['glyf'][glyph_name] # append old name to glyph order so del succeeds glyph_order.append(glyph_name) del font['glyf'][glyph_name] # copy features from unhinted # https://github.com/google/roboto/pull/163 for table in ['GDEF', 'GPOS', 'GSUB']: font[table] = unhinted[table]
def fix_name_table(font): """Fix copyright and reversed values in the 'name' table.""" modified = False name_records = font_data.get_name_records(font) copyright_data = name_records[0] years = re.findall("20[0-9][0-9]", copyright_data) year = min(years) copyright_data = u"Copyright %s Google Inc. All Rights Reserved." % year if copyright_data != name_records[0]: print('Updated copyright message to "%s"' % copyright_data) font_data.set_name_record(font, 0, copyright_data) modified = True for name_id in [1, 3, 4, 6]: record = name_records[name_id] for source in NAME_CORRECTIONS: if source in record: oldrecord = record record = record.replace(source, NAME_CORRECTIONS[source]) break if record != name_records[name_id]: font_data.set_name_record(font, name_id, record) print( 'Updated name table record #%d from "%s" to "%s"' % (name_id, oldrecord, record) ) modified = True trademark_names = ["Noto", "Arimo", "Tinos", "Cousine"] trademark_name = None font_family = name_records[1] for name in trademark_names: if font_family.find(name) != -1: trademark_name = name break if not trademark_name: print("no trademarked name in '%s'" % font_family) else: trademark_line = TRADEMARK_TEMPLATE % trademark_name if name_records[7] != trademark_line: old_line = name_records[7] font_data.set_name_record(font, 7, trademark_line) modified = True print( 'Updated name table record 7 from "%s" to "%s"' % (old_line, trademark_line) ) if name_records[11] != NOTO_URL: font_data.set_name_record(font, 11, NOTO_URL) modified = True print('Updated name table record 11 to "%s"' % NOTO_URL) if name_records[_LICENSE_ID] != _SIL_LICENSE: font_data.set_name_record(font, _LICENSE_ID, _SIL_LICENSE) modified = True print("Updated license id") if name_records[_LICENSE_URL_ID] != _SIL_LICENSE_URL: font_data.set_name_record(font, _LICENSE_URL_ID, _SIL_LICENSE_URL) modified = True print("Updated license url") # TODO: check preferred family/subfamily(16&17) return modified