def test_check_names_are_ascii_only(self): """ NAME and CFF tables must not contain non-ascii characters """ font = Font.get_ttfont(self.operator.path) for name in font.names: string = Font.bin2unistring(name) expected = normalizestr(string) self.assertEqual(string, expected)
def test_check_names_are_ascii_only(self): """ NAME and CFF tables must not contain non-ascii characters """ font = Font.get_ttfont(self.operator.path) for name in font.names: string = Font.bin2unistring(name) marks = CharacterSymbolsFixer.unicode_marks(string) if marks: self.fail('Contains {}'.format(marks))
def fix_style_names(fontpath): from bakery_cli.ttfont import Font try: font = Font(fontpath) except TTLibError as ex: print("ERROR: %s" % ex) return # font['name'].fsType = 0 font.save(fontpath + '.fix')
def reset_fstype(fontpath): from bakery_cli.ttfont import Font try: font = Font(fontpath) except TTLibError as ex: print("ERROR: %s" % ex, file=sys.stderr) return font['OS/2'].fsType = 0 font.save(fontpath + '.fix')
def test_check_names_are_ascii_only(self): """ NAME and CFF tables must not contain non-ascii characters """ font = Font.get_ttfont(self.path) for name_record in font.names: string = Font.bin2unistring(name_record) try: string.encode('ascii') except UnicodeEncodeError: self.fail("%s contain non-ascii chars" % name_record.nameID)
def test_check_canonical_weights(self): """ Check that weights have canonical value """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: weight = font_metadata.weight first_digit = weight / 100 is_invalid = (weight % 100) != 0 or (first_digit < 1 or first_digit > 9) _ = ("%s: The weight is %d which is not a " "multiple of 100 between 1 and 9") self.assertFalse( is_invalid, _ % (op.basename(self.operator.path), font_metadata.weight)) tf = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) _ = ("%s: METADATA.json overwrites the weight. " " The METADATA.json weight is %d and the font" " file %s weight is %d") _ = _ % (font_metadata.filename, font_metadata.weight, font_metadata.filename, tf.OS2_usWeightClass) self.assertEqual(tf.OS2_usWeightClass, font_metadata.weight)
def test_metadata_contains_current_font(self): """ METADATA.json should contains testing font, under canonic name""" contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) is_canonical = False for font_metadata in fm.fonts: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) _weights = [] for value, intvalue in weights.items(): if intvalue == font.OS2_usWeightClass: _weights.append(value) for w in _weights: current_font = "%s %s" % (font.familyname, w) if font_metadata.full_name != current_font: is_canonical = True if not is_canonical: v = map(lambda x: font.familyname + ' ' + x, _weights) msg = 'Canonical name in font expected: [%s] but %s' self.fail(msg % (v, font_metadata.full_name))
def test_metadata_family(self): """ Font and METADATA.json have the same name """ for font_metadata in self.metadata.fonts: font = Font.get_ttfont_from_metadata(self.path, font_metadata) if font.familyname != font_metadata.name: msg = 'Family name in TTF is "%s" but in METADATA "%s"' self.fail(msg % (font.familyname, font_metadata.name))
def show_stylenames(fontpath): from bakery_cli.ttfont import Font try: font = Font(fontpath) except TTLibError, ex: print("ERROR: %s" % ex) return
def reset_fstype(fontpath): from bakery_cli.ttfont import Font try: font = Font(fontpath) except TTLibError, ex: print >> sys.stderr, "ERROR: %s" % ex return
def test_check_font_has_dsig_table(self): """ Check that font has DSIG table """ font = Font.get_ttfont(self.operator.path) try: font['DSIG'] except KeyError: self.fail('Font does not have "DSIG" table')
def test_fontname_is_equal_to_macstyle(self): """ Check that fontname is equal to macstyle flags """ font = Font.get_ttfont(self.operator.path) macStyle = font.macStyle try: fontname_style = font.fullname.split('-')[1] except IndexError: fontname_style = '' expected_style = '' if macStyle & 0b01: expected_style += 'Bold' if macStyle & 0b10: expected_style += 'Italic' if not bool(macStyle & 0b11): expected_style = 'Regular' if fontname_style != expected_style: _ = 'macStyle ({0}) supposed style ended with "{1}"' if fontname_style: _ += ' but ends with "{2}"' self.fail(_.format(bin(macStyle)[-2:], expected_style, fontname_style))
def test_metrics_descents_equal_bbox(self): """ Check that descents values are same as min glyph point """ contents = self.read_metadata_contents() family_metadata = Metadata.get_family_metadata(contents) fonts_descents_not_bbox = [] ymin = 0 _cache = {} for font_metadata in family_metadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) ymin_, ymax_ = ttfont.get_bounding() ymin = min(ymin, ymin_) _cache[font_metadata.filename] = { 'os2typo': abs(ttfont.descents.os2typo), 'os2win': abs(ttfont.descents.os2win), 'hhea': abs(ttfont.descents.hhea) } for filename, data in _cache.items(): if [data['os2typo'], data['os2win'], data['hhea']] != [abs(ymin)] * 3: fonts_descents_not_bbox.append(filename) if fonts_descents_not_bbox: _ = '[%s] ascents differ to minimum value: %s' self.fail(_ % (', '.join(fonts_descents_not_bbox), ymin))
def test_check_italic_angle_agreement(self): """ Check italicangle property zero or negative """ font = Font.get_ttfont(self.operator.path) if font.italicAngle > 0: self.fail('italicAngle must be less or equal zero') if abs(font.italicAngle) > 20: self.fail('italicAngle can\'t be larger than 20 degrees')
def test_check_menu_contains_proper_glyphs(self): """ Check menu file contains proper glyphs """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: tf = Font.get_ttfont_from_metadata(self.operator.path, font_metadata, is_menu=True) self.check_retrieve_glyphs(tf, font_metadata)
def test_check_names_same_across_platforms(self): """ Font names are same across specific-platforms """ font = Font.get_ttfont(self.operator.path) for name in font.names: for name2 in font.names: if name.nameID != name2.nameID: continue if self.diff_platform(name, name2) or self.diff_platform(name2, name): _name = Font.bin2unistring(name) _name2 = Font.bin2unistring(name2) if _name != _name2: msg = ('Names in "name" table are not the same' ' across specific-platforms') self.fail(msg)
def test_check_normal_style_matches_names(self): """ Check metadata.json font.style `italic` matches font internal """ contents = self.read_metadata_contents() family = Metadata.get_family_metadata(contents) for font_metadata in family.fonts: if font_metadata.style != 'normal': continue font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) if bool(font.macStyle & 0b10): self.fail(('Metadata style has been set to normal' ' but font second bit (italic) in macStyle has' ' been set')) style = font.familyname.split('-')[-1] if style.endswith('Italic'): self.fail(('macStyle second bit is not set but postScriptName "%s"' ' is ended with "Italic"') % font.familyname) style = font.fullname.split('-')[-1] if style.endswith('Italic'): self.fail(('macStyle second bit is not set but fullName "%s"' ' is ended with "Italic"') % font.fullname)
def test_metrics_descents_equal_bbox(self): """ Check that descents values are same as min glyph point """ dirname = os.path.dirname(self.operator.path) directory = UpstreamDirectory(dirname) fonts_descents_not_bbox = [] ymin = 0 _cache = {} for filename in directory.get_binaries(): ttfont = Font.get_ttfont(os.path.join(dirname, filename)) ymin_, _ = ttfont.get_bounding() ymin = min(ymin, ymin_) _cache[filename] = { 'os2typo': abs(ttfont.descents.os2typo), 'os2win': abs(ttfont.descents.os2win), 'hhea': abs(ttfont.descents.hhea) } for filename, data in _cache.items(): datas = [data['os2typo'], data['os2win'], data['hhea']] if datas != [abs(ymin)] * 3: fonts_descents_not_bbox.append(filename) if fonts_descents_not_bbox: _ = '[%s] ascents differ to minimum value: %s' self.fail(_ % (', '.join(fonts_descents_not_bbox), ymin))
def test_fontname_is_equal_to_macstyle(self): """ Check that fontname is equal to macstyle flags """ font = Font.get_ttfont(self.path) fontname = font.fullname macStyle = font.macStyle try: fontname_style = fontname.split('-')[1] except IndexError: self.fail(('Fontname is not canonical. Expected it contains ' 'style. eg.: Italic, BoldItalic, Regular')) style = '' if macStyle & 0b01: style += 'Bold' if macStyle & 0b10: style += 'Italic' if not bool(macStyle & 0b11): style = 'Regular' if not fontname_style.endswith(style): _ = 'macStyle (%s) supposed style ended with "%s" but ends with "%s"' self.fail(_ % (bin(macStyle)[-2:], style, fontname_style))
def test_metrics_descents_equal_bbox(self): """ Check that descents values are same as min glyph point """ contents = self.read_metadata_contents() family_metadata = Metadata.get_family_metadata(contents) fonts_descents_not_bbox = [] ymin = 0 _cache = {} for font_metadata in family_metadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) ymin_, ymax_ = ttfont.get_bounding() ymin = min(ymin, ymin_) _cache[font_metadata.filename] = { 'os2typo': abs(ttfont.descents.os2typo), 'os2win': abs(ttfont.descents.os2win), 'hhea': abs(ttfont.descents.hhea) } for filename, data in _cache.items(): if [data['os2typo'], data['os2win'], data['hhea'] ] != [abs(ymin)] * 3: fonts_descents_not_bbox.append(filename) if fonts_descents_not_bbox: _ = '[%s] ascents differ to minimum value: %s' self.fail(_ % (', '.join(fonts_descents_not_bbox), ymin))
def test_fontname_is_equal_to_macstyle(self): """ Check that fontname is equal to macstyle flags """ font = Font.get_ttfont(self.operator.path) macStyle = font.macStyle try: fontname_style = font.fullname.split('-')[1] except IndexError: fontname_style = '' expected_style = '' if macStyle & 0b01: expected_style += 'Bold' if macStyle & 0b10: expected_style += 'Italic' if not bool(macStyle & 0b11): expected_style = 'Regular' if fontname_style != expected_style: _ = 'macStyle ({0}) supposed style ended with "{1}"' if fontname_style: _ += ' but ends with "{2}"' self.fail( _.format(bin(macStyle)[-2:], expected_style, fontname_style))
def test_check_italic_angle_agreement(self): """ Check italicangle property zero or negative """ font = Font.get_ttfont(self.path) if font.italicAngle > 0: self.fail('italicAngle must be less or equal zero') if abs(font.italicAngle) > 20: self.fail('italicAngle can\'t be larger than 20 degrees')
def metricfix(fonts): from bakery_cli.ttfont import Font ymin = 0 ymax = 0 for f in fonts: metrics = Font(f) font_ymin, font_ymax = metrics.get_bounding() ymin = min(font_ymin, ymin) ymax = max(font_ymax, ymax) for f in fonts: metrics = Font(f) metrics.ascents.set(ymax) metrics.descents.set(ymin) metrics.linegaps.set(0) metrics.save(f + '.fix')
def fix_name_table(fontfile): try: font = Font(fontfile) except TTLibError: print("Unable to open {}".format(os.path.basename(fontfile)), file=sys.stderr) return for name in font['name'].names: title = Font.bin2unistring(name) title = normalizestr(title) if name.platformID == 3: name.string = title.encode('utf-16-be') else: name.string = title font.save(fontfile + '.fix')
def test_suggested_subfamily_name(self): """ Family does not contain subfamily in `name` table """ # Currently we just look that family does not contain any spaces # in its name. This prevent us from incorrect suggestions of names font = Font.get_ttfont(self.operator.path) suggestedvalues = getSuggestedFontNameValues(font.ttfont) self.assertEqual(font.familyname, suggestedvalues['family']) self.assertEqual(font.stylename, suggestedvalues['subfamily'])
def show_stylenames(fontpath): from bakery_cli.ttfont import Font try: font = Font(fontpath) except TTLibError as ex: print("ERROR: %s" % ex) return print(font['name'].names[2].string)
def test_check_upm_heigths_less_120(self): """ Check if UPM Heights NOT more than 120% """ ttfont = Font.get_ttfont(self.path) value = ttfont.ascents.get_max() + abs(ttfont.descents.get_min()) value = value * 100 / float(ttfont.get_upm_height()) if value > 120: _ = "UPM:Height is %d%%, consider redesigning to 120%% or less" self.fail(_ % value)
def test_check_names_same_across_platforms(self): """ Font names are same across specific-platforms """ font = Font.get_ttfont(self.operator.path) for name in font.names: for name2 in font.names: if name.nameID != name2.nameID: continue if self.diff_platform(name, name2) \ or self.diff_platform(name2, name): _name = Font.bin2unistring(name) _name2 = Font.bin2unistring(name2) if _name != _name2: msg = ('Names in "name" table are not the same' ' across specific-platforms') self.fail(msg)
def test_no_kern_table_exists(self): """ Check that no "KERN" table exists """ font = Font.get_ttfont(self.operator.path) try: font['KERN'] self.fail('Font does have "KERN" table') except KeyError: pass
def fix(self): for name in self.font['name'].names: title = Font.bin2unistring(name) title = CharacterSymbolsFixer.normalizestr(title) if name.platformID == 3 and name.isUnicode(): name.string = title.encode('utf-16-be') else: name.string = title return True
def test_check_menu_contains_proper_glyphs(self): """ Check menu file contains proper glyphs """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: tf = Font.get_ttfont_from_metadata(self.path, font_metadata, is_menu=True) self.check_retrieve_glyphs(tf, font_metadata)
def test_font_name_matches_family(self): """ METADATA.json fonts 'name' property should be same as font familyname """ for font_metadata in self.metadata.fonts: font = Font.get_ttfont_from_metadata(self.path, font_metadata) if font_metadata.name != font.familyname: msg = '"fonts.name" property is not the same as TTF familyname' self.fail(msg)
def test_font_name_matches_family(self): """ METADATA.json fonts 'name' property should be same as font familyname """ for font_metadata in self.metadata.fonts: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) if font_metadata.name != font.familyname: msg = '"fonts.name" property is not the same as TTF familyname' self.fail(msg)
def test_the_same_number_of_glyphs_across_family(self): """ The same number of glyphs across family? """ glyphs_count = 0 for font_metadata in self.familymetadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) if not glyphs_count: glyphs_count = len(ttfont.glyphs) if glyphs_count != len(ttfont.glyphs): self.fail('Family has a different glyphs\'s count in fonts')
def test_the_same_names_of_glyphs_across_family(self): """ The same names of glyphs across family? """ glyphs = None for font_metadata in self.familymetadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) if not glyphs: glyphs = len(ttfont.glyphs) if glyphs != len(ttfont.glyphs): self.fail('Family has a different glyphs\'s names in fonts')
def test_prep_magic_code(self): """ Font contains in PREP table magic code """ magiccode = '\xb8\x01\xff\x85\xb0\x04\x8d' font = Font.get_ttfont(self.operator.path) try: bytecode = font.get_program_bytecode() except KeyError: bytecode = '' self.assertTrue(bytecode == magiccode, msg='PREP does not contain magic code')
def test_the_same_names_of_glyphs_across_family(self): """ The same names of glyphs across family? """ glyphs = None for font_metadata in self.familymetadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) if not glyphs: glyphs = len(ttfont.glyphs) if glyphs != len(ttfont.glyphs): self.fail('Family has a different glyphs\'s names in fonts')
def test_the_same_number_of_glyphs_across_family(self): """ The same number of glyphs across family? """ glyphs_count = 0 for font_metadata in self.familymetadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) if not glyphs_count: glyphs_count = len(ttfont.glyphs) if glyphs_count != len(ttfont.glyphs): self.fail('Family has a different glyphs\'s count in fonts')
def test_check_hmtx_hhea_max_advance_width_agreement(self): """ Check if MaxAdvanceWidth agree in the Hmtx and Hhea tables """ font = Font.get_ttfont(self.path) hmtx_advance_width_max = font.get_hmtx_max_advanced_width() hhea_advance_width_max = font.advance_width_max error = ("AdvanceWidthMax mismatch: expected %s (from hmtx);" " got %s (from hhea)") % (hmtx_advance_width_max, hhea_advance_width_max) self.assertEqual(hmtx_advance_width_max, hhea_advance_width_max, error)
def test_check_metadata_matches_nametable(self): contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) report = '%s: Family name was supposed to be "%s" but is "%s"' report = report % (font_metadata.name, fm.name, ttfont.familyname) self.assertEqual(ttfont.familyname, fm.name, report) self.assertEqual(ttfont.fullname, font_metadata.full_name)
def metricview(fonts): from bakery_cli.ttfont import Font view = TextMetricsView() for f in fonts: try: metrics = Font(f) except TTLibError, ex: print("ERROR: %s" % ex) continue view.add_metric(os.path.basename(f), metrics)
def test_font_weight_same_as_in_metadata(self): """ Font weight matches metadata.json value of key "weight" """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) if font.OS2_usWeightClass != font_metadata.weight: msg = 'METADATA.JSON has weight %s but in TTF it is %s' self.fail(msg % (font_metadata.weight, font.OS2_usWeightClass))
def test_check_hmtx_hhea_max_advance_width_agreement(self): """ Check if MaxAdvanceWidth agree in the Hmtx and Hhea tables """ font = Font.get_ttfont(self.operator.path) hmtx_advance_width_max = font.get_hmtx_max_advanced_width() hhea_advance_width_max = font.advance_width_max error = ("AdvanceWidthMax mismatch: expected %s (from hmtx);" " got %s (from hhea)") % (hmtx_advance_width_max, hhea_advance_width_max) self.assertEqual(hmtx_advance_width_max, hhea_advance_width_max, error)
def test_check_full_font_name_begins_with_family_name(self): """ Check if full font name begins with the font family name """ font = Font.get_ttfont(self.operator.path) for entry in font.names: if entry.nameID != 1: continue for entry2 in font.names: if entry2.nameID != 4: continue if (entry.platformID == entry2.platformID and entry.platEncID == entry2.platEncID and entry.langID == entry2.langID): entry2value = Font.bin2unistring(entry2) entryvalue = Font.bin2unistring(entry) if not entry2value.startswith(entryvalue): _ = ('Full font name does not begin with family' ' name: FontFamilyName = "%s";' ' FullFontName = "%s"') self.fail(_ % (entryvalue, entry2value))
def test_postscriptname_contains_correct_weight(self): """ Metadata weight matches postScriptName """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: font = Font.get_ttfont_from_metadata(self.path, font_metadata) if font.OS2_usWeightClass != font_metadata.weight: msg = 'METADATA.JSON has weight %s but in TTF it is %s' self.fail(msg % (font_metadata.weight, font.OS2_usWeightClass))
def test_the_same_encodings_of_glyphs_across_family(self): """ The same unicode encodings of glyphs across family? """ encoding = None for font_metadata in self.familymetadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) cmap = ttfont.retrieve_cmap_format_4() if not encoding: encoding = cmap.platEncID if encoding != cmap.platEncID: self.fail('Family has different encoding across fonts')
def test_metadata_fonts_fields_have_fontname(self): """ METADATA.json fonts items fields "name", "postScriptName", "fullName", "filename" contains font name right format """ for x in self.metadata.fonts: font = Font.get_ttfont_from_metadata(self.operator.path, x) self.assertIn(font.familyname, x.name) self.assertIn(font.familyname, x.full_name) self.assertIn("".join(str(font.familyname).split()), x.filename) self.assertIn("".join(str(font.familyname).split()), x.post_script_name)