def generate(config, outfile='review.html'): directory = UpstreamDirectory(config['path']) fonts = [(path, FontFactory.openfont(op.join(config['path'], path))) for path in directory.BIN] metadata_file = open(op.join(config['path'], 'METADATA.json')).read() family_metadata = Metadata.get_family_metadata(metadata_file) faces = [] for f in family_metadata.fonts: faces.append({'name': f.full_name, 'basename': f.post_script_name, 'path': f.filename, 'meta': f}) destfile = open(op.join(config['path'], 'review.html'), 'w') app_version = report_utils.git_info(config) report_app = report_utils.ReportApp(config) fonts_orthography = get_orthography(fonts) report_app.review_page.dump_file(fonts_orthography, 'orthography.json') print(report_utils.render_template( outfile, fonts=faces, markdown=markdown, current_page=outfile, get_weight_name=get_weight_name, build_repo_url=report_utils.build_repo_url, app_version=app_version, get_orthography=get_orthography_old, fontaineFonts=fonts), file=destfile)
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_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_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_check_canonical_filenames(self): """ Test If filename is canonical """ contents = self.read_metadata_contents() family_metadata = Metadata.get_family_metadata(contents) for font_metadata in family_metadata.fonts: canonical_filename = self.create_canonical_filename(font_metadata) self.assertEqual(canonical_filename, font_metadata.filename)
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_metadata_copyright_size(self): """ Copyright notice should be less than 500 chars """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: self.assertLessEqual(len(font_metadata.copyright), 500)
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_check_metadata_fields(self): """ Check METADATA.json "fonts" property items have required field """ contents = self.read_metadata_contents() family = Metadata.get_family_metadata(contents) keys = [("name", str), ("postScriptName", str), ("fullName", str), ("style", str), ("weight", int), ("filename", str), ("copyright", str)] missing = set([]) unknown = set([]) for j, itemtype in keys: for font_metadata in family.fonts: if j not in font_metadata: missing.add(j) for k in font_metadata: if k not in map(lambda x: x[0], keys): unknown.add(k) if unknown: msg = 'METADATA.json "fonts" property has unknown items [%s]' self.fail(msg % ', '.join(unknown)) if missing: msg = 'METADATA.json "fonts" property items missed [%s] items' self.fail(msg % ', '.join(missing))
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_copyright_matches_pattern(self): """ Copyright string matches to Copyright * 20\d\d * (*@*.*) """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: self.assertRegexpMatches(font_metadata.copyright, r'Copyright\s+\(c\)\s+20\d{2}.*\(.*@.*.*\)')
def test_copyright_matches_pattern(self): """ Copyright notice matches canonical pattern """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: self.assertRegexpMatches(font_metadata.copyright, r'Copyright\s+\(c\)\s+20\d{2}.*\(.*@.*.*\)')
def test_check_familyname_matches_fontnames(self): """ Check font name is the same as family name """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: _ = '%s: Family name "%s" does not match font name: "%s"' _ = _ % (font_metadata.filename, fm.name, font_metadata.name) self.assertEqual(font_metadata.name, fm.name, _)
def test_copyright_contains_correct_rfn(self): """ Copyright notice does not contain Reserved File Name """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: if 'Reserved Font Name' in font_metadata.copyright: msg = '"%s" contains "Reserved File Name"' self.fail(msg % font_metadata.copyright)
def test_copyright_matches_pattern(self): """ Copyright string matches to Copyright * 20\d\d * (*@*.*) """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: self.assertRegexpMatches( font_metadata.copyright, r'Copyright\s+\(c\)\s+20\d{2}.*\(.*@.*.*\)')
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_check_canonical_filenames(self): """ Test If filename is canonical """ contents = self.read_metadata_contents() family_metadata = Metadata.get_family_metadata(contents) for font_metadata in family_metadata.fonts: canonical_filename = self.create_canonical_filename(font_metadata) if canonical_filename != font_metadata.filename: self.fail('{} != {}'.format(canonical_filename, font_metadata.filename))
def test_copyright_is_consistent_across_family(self): """ Copyright notice is the same in all fonts? """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) copyright = '' for font_metadata in fm.fonts: if copyright and font_metadata.copyright != copyright: self.fail('Copyright is inconsistent across family') copyright = font_metadata.copyright
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: if 'Reserved Font Name' not in font_metadata.copyright: msg = '"%s" should have "Reserved File Name"' self.fail(msg % font_metadata.copyright)
def test_copyright_is_consistent_across_family(self): """ METADATA.json fonts copyright string is the same for all items """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) copyright = '' for font_metadata in fm.fonts: if copyright and font_metadata.copyright != copyright: self.fail('Copyright is not in consistent across family') copyright = font_metadata.copyright
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 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_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_family_metadata_is_loaded(self): """ Check if Metadata can read family metadata correctly """ fm = Metadata.get_family_metadata('{"name": "Family Name"}') self.assertEqual(type(fm), FamilyMetadata) self.assertEqual(fm.name, "Family Name") self.assertEqual(fm.designer, "") self.assertEqual(fm.license, "") self.assertEqual(fm.visibility, "Sandbox") self.assertEqual(fm.category, "") self.assertEqual(fm.size, 0) self.assertEqual(fm.date_added, "") self.assertEqual(fm.subsets, [])
def test_menu_file_agreement(self): """ Check fonts have corresponding menu files """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: menufile = self.menufile(font_metadata) path = op.join(op.dirname(self.operator.path), menufile) if not op.exists(path): self.fail('%s does not exist' % menufile) if magic.from_file(path) != 'TrueType font data': self.fail('%s is not actual TTF file' % menufile)
def test_fontname_not_in_camel_case(self): """ Check if fontname is not camel cased """ contents = self.read_metadata_contents() familymetadata = Metadata.get_family_metadata(contents) camelcased_fontnames = [] for font_metadata in familymetadata.fonts: if bool(re.match(r'([A-Z][a-z]+){2,}', font_metadata.name)): camelcased_fontnames.append(font_metadata.name) if camelcased_fontnames: self.fail(('%s are camel cased names. To solve this check just ' 'use spaces in names.'))
def test_check_canonical_styles(self): """ Test If font styles are canonical """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: self.assertIn(font_metadata.style, self.CANONICAL_STYLE_VALUES) if self.is_italic(font_metadata): if font_metadata.style != 'italic': _ = "%s: The font style is %s but it should be italic" self.fail(_ % (font_metadata.filename, font_metadata.style)) else: if font_metadata.style != 'normal': _ = "%s: The font style is %s but it should be normal" self.fail(_ % (font_metadata.filename, font_metadata.style))
def test_metrics_linegaps_are_zero(self): """ Check that linegaps in tables are zero """ contents = self.read_metadata_contents() family_metadata = Metadata.get_family_metadata(contents) fonts_gaps_are_not_zero = [] for font_metadata in family_metadata.fonts: ttfont = Font.get_ttfont_from_metadata(self.path, font_metadata) if bool(ttfont.linegaps.os2typo) or bool(ttfont.linegaps.hhea): fonts_gaps_are_not_zero.append(font_metadata.filename) if fonts_gaps_are_not_zero: _ = '[%s] have not zero linegaps' self.fail(_ % ', '.join(fonts_gaps_are_not_zero))
def test_postscriptname_in_metadata_equal_to_font_on_disk(self): """ Checks METADATA.json 'postScriptName' matches TTF 'postScriptName' """ contents = self.read_metadata_contents() metadata = Metadata.get_family_metadata(contents) for font_metadata in metadata.fonts: try: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) except IOError: continue if font.post_script_name != font_metadata.post_script_name: msg = 'In METADATA postScriptName="{0}", but in TTF "{1}"' self.fail(msg.format(font.post_script_name, font_metadata.post_script_name))
def test_font_metadata_is_loaded(self): """ Check if font metadata can be read from family metadata """ fm = Metadata.get_family_metadata( '{"name": "Family Name", "fonts": [{"name": "FontName"}]}') fonts_metadata = fm.fonts self.assertEqual(type(fonts_metadata), types.GeneratorType) fm = fonts_metadata.next() self.assertEqual(type(fm), FontMetadata) self.assertEqual(fm.name, "FontName") self.assertEqual(fm.post_script_name, "") self.assertEqual(fm.full_name, "") self.assertEqual(fm.style, "normal") self.assertEqual(fm.weight, 400) self.assertEqual(fm.filename, "") self.assertEqual(fm.copyright, "")
def test_postscriptname_in_metadata_equal_to_font_on_disk(self): """ Checks METADATA.json 'postScriptName' matches TTF 'postScriptName' """ contents = self.read_metadata_contents() metadata = Metadata.get_family_metadata(contents) for font_metadata in metadata.fonts: try: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) except IOError: continue if font.post_script_name != font_metadata.post_script_name: msg = 'In METADATA postScriptName="{0}", but in TTF "{1}"' self.fail( msg.format(font.post_script_name, font_metadata.post_script_name))
def test_check_nbsp_width_matches_sp_width(self): """ Check NO-BREAK SPACE advanceWidth is the same as SPACE """ 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) space_advance_width = tf.advance_width('space') nbsp_advance_width = tf.advance_width('uni00A0') _ = "%s: The font does not contain a sp glyph" self.assertTrue(space_advance_width, _ % font_metadata.filename) _ = "%s: The font does not contain a nbsp glyph" self.assertTrue(nbsp_advance_width, _ % font_metadata.filename) _ = ("%s: The nbsp advance width does not match " "the sp advance width") % font_metadata.filename self.assertEqual(space_advance_width, nbsp_advance_width, _)
def test_font_on_disk_family_equal_in_metadata_json(self): """ Font on disk and in METADATA.json have the same family name """ contents = self.read_metadata_contents() metadata = Metadata.get_family_metadata(contents) unmatched_fonts = [] for font_metadata in metadata.fonts: try: font = Font.get_ttfont_from_metadata(self.operator.path, font_metadata) except IOError: continue if font.familyname != font_metadata.name: unmatched_fonts.append(font_metadata.filename) if unmatched_fonts: msg = 'Unmatched family name are in fonts: {}' self.fail(msg.format(', '.join(unmatched_fonts)))
def generate(config, outfile='review.html'): directory = UpstreamDirectory(config['path']) fonts = [(path, FontFactory.openfont(op.join(config['path'], path))) for path in directory.BIN] metadata_file = open(op.join(config['path'], 'METADATA.json')).read() family_metadata = Metadata.get_family_metadata(metadata_file) faces = [] for f in family_metadata.fonts: faces.append({'name': f.full_name, 'basename': f.post_script_name, 'path': f.filename, 'meta': f}) report_app = report_utils.BuildInfo(config) fonts_orthography = get_orthography(fonts) report_app.review_page.dump_file(fonts_orthography, 'orthography.json')
def test_check_subsets_exists(self): """ Check that corresponding subset files exist for fonts """ contents = self.read_metadata_contents() fm = Metadata.get_family_metadata(contents) for font_metadata in fm.fonts: for subset in fm.subsets: subset_filename = self.get_subset_filename(font_metadata.filename, subset) error = "The subset file for the %s subset does not exist" error = error % subset_filename self.assertTrue(self.f.exists(subset_filename), error) error = "The subset file %s is bigger than the original file" error = error % subset_filename self.assertLessEqual(self.f.size(subset_filename), self.f.size(font_metadata.filename), error) self.assertEqual(self.f.mime(subset_filename), 'application/x-font-ttf')
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: pair = [] for k, weight in weights.items(): if weight == font_metadata.weight: pair.append((k, weight)) if not pair: self.fail('Font weight does not match for "postScriptName"') if not (font_metadata.post_script_name.endswith('-%s' % pair[0][0]) or font_metadata.post_script_name.endswith('-%s' % pair[1][0])): _ = ('postScriptName with weight %s must be ' 'ended with "%s" or "%s"') self.fail(_ % (pair[0][1], pair[0][0], pair[1][0]))
def generate(config, outfile='review.html'): directory = UpstreamDirectory(config['path']) fonts = [(path, FontFactory.openfont(op.join(config['path'], path))) for path in directory.BIN] metadata_file = open(op.join(config['path'], 'METADATA.json')).read() family_metadata = Metadata.get_family_metadata(metadata_file) faces = [] for f in family_metadata.fonts: faces.append({ 'name': f.full_name, 'basename': f.post_script_name, 'path': f.filename, 'meta': f }) report_app = report_utils.BuildInfo(config) fonts_orthography = get_orthography(fonts) report_app.review_page.dump_file(fonts_orthography, 'orthography.json')