def run_bakery(bakery_yml_file, verbose=False): try: config = yaml.safe_load(open(op.join(bakery_yml_file), 'r')) except IOError: config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) sourcedir = op.dirname(bakery_yml_file) try: builddir = 'build' if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse('HEAD', short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get('TRAVIS_COMMIT', builddir) if 'process_files' not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config['process_files'] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) b = Bakery('', sourcedir, 'builds', builddir) b.load_config(bakery_yml_file) b.run() except: if verbose or config.get('verbose'): raise sys.exit(1)
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 execute(self, pipedata, prefix=""): task = self.bakery.logging_task('Convert sources to TTF') if self.bakery.forcerun: return if pipedata.get('compiler') == 'make': import subprocess directory = UpstreamDirectory(op.join(self.builddir, 'sources')) snapshot_bin = directory.BIN process = subprocess.Popen(['make'], cwd=op.join(self.builddir, 'sources')) process.communicate() directory = UpstreamDirectory(op.join(self.builddir, 'sources')) snapshot_after_bin = directory.BIN for fontpath in set(snapshot_bin) ^ set(snapshot_after_bin): if fontpath.lower().endswith('.ttf'): os.copy(op.join(self.builddir, 'sources', fontpath), self.builddir) self.run_processes(op.basename(fontpath), pipedata) return pipedata self.convert(pipedata) return pipedata
def run_bakery(sourcedir, config=None): try: if config: config = yaml.safe_load(open(config, 'r')) else: config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) if 'process_files' not in config: directory = UpstreamDirectory(sourcedir) config['process_files'] = directory.get_fonts() bakeryyaml = os.path.abspath(os.path.join(sourcedir, '.bakery.yaml')) l = open(bakeryyaml, 'w') l.write(yaml.safe_dump(config)) l.close() b = Bakery('', sourcedir, 'builds', sourcedir) b.init_logging('%s.process.log' % sourcedir.replace('/', '-')) b.pipes = [ pipe.Copy, pipe.Build, pipe.Rename, pipe.Metadata, pipe.PyFtSubset, pipe.FontLint, pipe.Optimize, pipe.AutoFix, pipe.CopyLicense, pipe.CopyFontLog, pipe.CopyDescription, pipe.CopyMetadata, pipe.CopyTxtFiles, pipe.TTFAutoHint, pipe.PyFontaine ] config = os.path.join(sourcedir, '.bakery.yaml') b.load_config(config) b.run() except Exception, ex: print >> sys.stderr, 'FAILED: %s' % sourcedir print >> sys.stderr, ex
def run_bakery(path, verbose=False): # fontbakery-build supports passing arguments of directory or # concrete bakery.y[a]ml files. In case of passing directory # it looks at existing bakery.yml or bakery.yaml and runs on # first matched filepath # There can also be cases when directory does not contain any # bakery.y[a]ml or passed bakery.yml file does not exist. Then # fontbakery-build loads default configuration. bakery_yml_file = None sourcedir = path if os.path.isdir(path): for filename in ['bakery.yml', 'bakery.yaml']: if os.path.exists(os.path.join(path, filename)): bakery_yml_file = os.path.join(path, filename) break else: bakery_yml_file = path sourcedir = os.path.dirname(path) try: if bakery_yml_file: config = yaml.safe_load(open(bakery_yml_file, 'r')) else: raise IOError except IOError: bakery_yml_file = os.path.join(sourcedir, 'bakery.yml') config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) try: builddir = 'build' if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse('HEAD', short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get('TRAVIS_COMMIT', builddir) if 'process_files' not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config['process_files'] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) Bakery.verbose = verbose or config.get('verbose') b = Bakery('', sourcedir, 'builds', builddir) b.addLoggingToFile() b.load_config(bakery_yml_file) b.run() except: if verbose or config.get('verbose'): raise sys.exit(1)
def test_font_test_prepolation_glyph_names(self): """ Check glyph names are all the same across family """ directory = UpstreamDirectory(self.path) glyphs = [] for f in directory.get_fonts(): font = PiFont(op.join(self.path, f)) glyphs_ = font.get_glyphs() if glyphs and glyphs != glyphs_: self.fail('Family has different glyphs across fonts')
def test_family_glyph_names_match(self): """ Each font in family has matching glyph names? """ directory = UpstreamDirectory(self.operator.path) # TODO does this glyphs list object get populated? glyphs = [] for f in directory.get_fonts(): font = PiFont(os.path.join(self.operator.path, f)) glyphs_ = font.get_glyphs() if glyphs and glyphs != glyphs_: # TODO report which font self.fail('Family has different glyphs across fonts')
def convert(self, pipedata): directory = UpstreamDirectory(op.join(self.builddir, 'sources')) try: if directory.BIN: self.execute_bin([x for x in directory.BIN], pipedata) if directory.get_ttx(): self.execute_ttx([op.join('sources', x) for x in directory.get_ttx()], pipedata) if directory.UFO: self.execute_ufo_sfd([op.join('sources', x) for x in directory.UFO], pipedata) if directory.SFD: self.execute_ufo_sfd([op.join('sources', x) for x in directory.SFD], pipedata) except: raise finally: self.remove_sourcesdir()
def fix_metrics(testcase): """ Fix vmet table with actual min and max values """ targetpath = os.path.dirname(testcase.operator.path) SCRIPTPATH = 'fontbakery-fix-vertical-metrics.py' directory = UpstreamDirectory(targetpath) paths = [] for f in directory.BIN: path = op.join(targetpath, f) paths.append(path) command = "$ {0} --autofix {1}" command = command.format(SCRIPTPATH, ' '.join(paths)) if hasattr(testcase, 'operator'): testcase.operator.debug(command) metricfix(paths) for path in paths: try: shutil.move(path + '.fix', path, log=testcase.operator.logger) except IOError: pass command = "$ {0} {1}".format(SCRIPTPATH, ' '.join(paths)) if hasattr(testcase, 'operator'): testcase.operator.debug(command) testcase.operator.debug(metricview(paths))
def execute(self, pipedata): self.bakery.logging_task('Optimizing TTF') if self.bakery.forcerun: return for filename in UpstreamDirectory(self.builddir).BIN: self.run(op.join(self.builddir, filename), pipedata)
def __generateTests__(cls): pattern = re.compile(r'[\W_]+') library = Library(collections=['subsets']) directory = UpstreamDirectory(cls.operator.path) yamlpath = op.join(cls.operator.path, 'bakery.yaml') try: bakerydata = yaml.load(open(yamlpath)) except IOError: bakerydata = yaml.load(open(BAKERY_CONFIGURATION_DEFAULTS)) for fontpath in directory.UFO + directory.TTX: font = FontFactory.openfont(op.join(cls.operator.path, fontpath)) for charmap, _, coverage, _ in \ font.get_orthographies(_library=library): common_name = charmap.common_name.replace('Subset ', '') shortname = pattern.sub('', common_name) if shortname not in bakerydata['subset']: continue exec 'cls.test_charset_%s = get_test_subset_function(%s)' % ( shortname, coverage) exec 'cls.test_charset_%s.__func__.__doc__ = "Is %s covered 100%%?"' % ( shortname, common_name)
def test_font_prepolation_glyph_contours(self): """ Check that glyphs has same number of contours across family """ directory = UpstreamDirectory(self.operator.path) glyphs = {} for f in directory.get_fonts(): font = PiFont(os.path.join(self.operator.path, f)) glyphs_ = font.get_glyphs() for glyphcode, glyphname in glyphs_: contours = font.get_contours_count(glyphname) if glyphcode in glyphs and glyphs[glyphcode] != contours: msg = ('Number of contours of glyph "%s" does not match.' ' Expected %s contours, but actual is %s contours') self.fail(msg % (glyphname, glyphs[glyphcode], contours)) glyphs[glyphcode] = contours
def test_font_prepolation_glyph_points(self): """ Check that glyphs has same number of points across family """ directory = UpstreamDirectory(self.operator.path) glyphs = {} for f in directory.get_fonts(): font = PiFont(os.path.join(self.operator.path, f)) glyphs_ = font.get_glyphs() for g, glyphname in glyphs_: points = font.get_points_count(glyphname) if g in glyphs and glyphs[g] != points: msg = ('Number of points of glyph "%s" does not match.' ' Expected %s points, but actual is %s points') self.fail(msg % (glyphname, glyphs[g], points)) glyphs[g] = points
def setUp(self): path = os.path.realpath(os.path.dirname(__file__)) content = open(os.path.join(path, 'diacritics.txt')).read() self.diacriticglyphs = [ x.strip() for x in content.split() if x.strip() ] self.directory = UpstreamDirectory(self.operator.path)
def targettask(pyfontaine, pipedata, task): try: library = Library(collections=['subsets']) director = Director(_library=library) sourcedir = op.join(pyfontaine.builddir) directory = UpstreamDirectory(sourcedir) fonts = [] for font in directory.ALL_FONTS: if font.startswith('sources'): continue fonts.append(op.join(pyfontaine.builddir, font)) _ = ('fontaine --collections subsets --text %s' ' > fontaine.txt\n') % ' '.join(fonts) pyfontaine.bakery.logging_cmd(_) fontaine_log = op.join(pyfontaine.builddir, 'fontaine.txt') fp = codecs.open(fontaine_log, 'w', 'utf-8') result = Builder.text_(director.construct_tree(fonts)) fp.write(result.output) pyfontaine.bakery.logging_raw('end of pyfontaine process\n') pyfontaine.bakery.logging_task_done(task) except Exception as ex: pyfontaine.bakery.logging_raw('pyfontaine error: {}'.format(ex)) pyfontaine.bakery.logging_raw('pyfontaine process has been failed\n') pyfontaine.bakery.logging_task_done(task, failed=True)
def convert(self, pipedata): directory = UpstreamDirectory(op.join(self.builddir, 'sources')) try: if directory.BIN: self.execute_bin([x for x in directory.BIN], pipedata) if directory.get_ttx(): self.execute_ttx( [op.join('sources', x) for x in directory.get_ttx()], pipedata) if directory.UFO: self.execute_ufo_sfd( [op.join('sources', x) for x in directory.UFO], pipedata) if directory.SFD: self.execute_ufo_sfd( [op.join('sources', x) for x in directory.SFD], pipedata) except: raise finally: self.remove_sourcesdir()
def generate_subsets_coverage_list(): directory = UpstreamDirectory('.') source_fonts_paths = [] # `get_sources_list` returns list of paths relative to root. # To complete to absolute paths use python os.path.join method # on root and path for p in directory.ALL_FONTS: source_fonts_paths.append(p) return get_subsets_coverage_data(source_fonts_paths)
def convert(self, pipedata): directory = UpstreamDirectory(op.join(self.builddir, 'sources')) try: if directory.BIN: self.execute_bin([x for x in directory.BIN], pipedata) if directory.get_ttx(): self.execute_ttx([op.join('sources', x) for x in directory.get_ttx()], pipedata) if directory.UFO: self.execute_ufo_sfd([op.join('sources', x) for x in directory.UFO], pipedata) if directory.SFD: self.execute_ufo_sfd([op.join('sources', x) for x in directory.SFD], pipedata) # binfiles = self.movebin_to_builddir([op.join('sources', x) for x in directory.ALL_FONTS]) # self.print_vertical_metrics(binfiles) # pipedata['bin_files'] = binfiles except: raise
def get_fonts(self, dirname): directory = UpstreamDirectory(dirname) fonts_ascents_not_bbox = [] ymax = 0 _cache = {} for filename in directory.get_binaries(): ttfont = Font.get_ttfont(os.path.join(dirname, filename)) _, ymax_ = ttfont.get_bounding() ymax = max(ymax, ymax_) _cache[filename] = { 'os2typo': ttfont.ascents.os2typo, 'os2win': ttfont.ascents.os2win, 'hhea': ttfont.ascents.hhea } for filename, data in _cache.items(): if [data['os2typo'], data['os2win'], data['hhea']] != [ymax] * 3: fonts_ascents_not_bbox.append(filename) return ymax, fonts_ascents_not_bbox
def test_metrics_linegaps_are_zero(self): """ Check that linegaps in tables are zero """ dirname = os.path.dirname(self.operator.path) directory = UpstreamDirectory(dirname) fonts_gaps_are_not_zero = [] for filename in directory.BIN: ttfont = Font.get_ttfont(os.path.join(dirname, filename)) if bool(ttfont.linegaps.os2typo) or bool(ttfont.linegaps.hhea): fonts_gaps_are_not_zero.append(filename) if fonts_gaps_are_not_zero: _ = '[%s] have not zero linegaps' self.fail(_ % ', '.join(fonts_gaps_are_not_zero))
def inferLicense(familydir): from bakery_cli.utils import UpstreamDirectory directory = UpstreamDirectory(familydir) if not directory.LICENSE: return "" with io.open(directory.LICENSE[0]) as fp: content = fp.read() if 'Apache License' in content: return 'Apache2' if 'SIL Open Font License, Version 1.1' in content: return 'OFL' if 'UBUNTU FONT LICENCE Version 1.0' in content: return 'UFL' return ""
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 execute(self, pipedata): self.bakery.logging_task('Run upstream tests') if self.bakery.forcerun: return result = {} upstreamdir = op.join(self.builddir, 'sources') self.bakery.logging_cmd('fontbakery-check.py upstream-repo sources') result['Project'] = run_set(upstreamdir, 'upstream-repo') directory = UpstreamDirectory(upstreamdir) for font in directory.ALL_FONTS: if font[-4:] not in '.ttx': self.bakery.logging_cmd('fontbakery-check.py upstream {}'.format(font)) result[font] = run_set(op.join(upstreamdir, font), 'upstream') _out_yaml = op.join(self.builddir, 'upstream.yaml') l = codecs.open(_out_yaml, mode='w', encoding="utf-8") l.write(yaml.safe_dump(result)) l.close()
def generate(config, outfile='tests.html'): directory = UpstreamDirectory(config['path']) tests = {} data = {} for fp in directory.BIN: path = op.join(config['path'], '{}.yaml'.format(fp[:-4])) if op.exists(path): data[fp] = yaml.load(open(path)) tests[fp] = { 'success': len(data[fp].get('success', [])), 'error': len(data[fp].get('error', [])), 'failure': len(data[fp].get('failure', [])), 'fixed': len(data[fp].get('fixed', [])) } if not data: return new_data = [] for k in data: d = {'name': k} d.update(data[k]) new_data.append(d) tests_summary = {} tests_summary_filepath = op.join(config['path'], 'summary.tests.json') if op.exists(tests_summary_filepath): tests_summary = json.load(open(tests_summary_filepath)) tests_summary.update(tests) json.dump(tests_summary, open(tests_summary_filepath, 'w')) report_app = report_utils.BuildInfo(config) report_app.tests_page.dump_file(new_data, 'tests.json')
def run_bakery(path, verbose=False): # fontbakery-build supports passing arguments of directory or # concrete bakery.y[a]ml files. In case of passing directory # it looks at existing bakery.yml or bakery.yaml and runs on # first matched filepath # There can also be cases when directory does not contain any # bakery.y[a]ml or passed bakery.yml file does not exist. Then # fontbakery-build loads default configuration. bakery_yml_file = None sourcedir = path if os.path.isdir(path): for filename in ['bakery.yml', 'bakery.yaml']: if os.path.exists(os.path.join(path, filename)): bakery_yml_file = os.path.join(path, filename) break else: bakery_yml_file = path sourcedir = os.path.dirname(path) try: if bakery_yml_file: config = yaml.safe_load(open(bakery_yml_file, 'r')) else: raise IOError except IOError: bakery_yml_file = os.path.join(sourcedir, 'bakery.yml') config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) try: builddir = 'build' if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse('HEAD', short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get('TRAVIS_COMMIT', builddir) if 'process_files' not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config['process_files'] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) b = Bakery('', sourcedir, 'builds', builddir) b.addLoggingToFile() b.load_config(bakery_yml_file) b.run() if not ttfautohint_installed(): msg = ('Command line tool `ttfautohint` is required. Install it with' ' `apt-get install ttfautohint` or `brew install ttfautohint`') logger.error(msg) except: logger.error('BUILD FAILED') if verbose or config.get('verbose'): raise logger.error('Run with --verbose to get stacktrace info.') sys.exit(1)
for p in directory.ALL_FONTS: source_fonts_paths.append(p) return get_subsets_coverage_data(source_fonts_paths) import argparse parser = argparse.ArgumentParser() parser.add_argument('directory') args = parser.parse_args() directory = UpstreamDirectory(args.directory) process_files = directory.ALL_FONTS import urwid.curses_display import urwid.raw_display import urwid.web_display import urwid def show_or_exit(key): if key in ('q', 'Q', 'esc'): raise urwid.ExitMainLoop() header = urwid.Text("Fontbakery Setup. Q exits.") app = App(args.directory)
def generate(config): if config.get('failed'): return directory = UpstreamDirectory(config['path']) if op.exists(op.join(config['path'], 'METADATA.json.new')): metadata_file = open(op.join(config['path'], 'METADATA.json.new')).read() else: 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 }) metadata = yaml.load(open(op.join(config['path'], 'METADATA.yaml'))) upstreamdata = {} upstreamdatafile = op.join(config['path'], 'upstream.yaml') if op.exists(upstreamdatafile): upstreamdata = yaml.load(open(upstreamdatafile)) data = {} for fp in directory.BIN: path = op.join(config['path'], '{}.yaml'.format(fp[:-4])) if op.exists(path): data[fp] = yaml.load(open(path)) data.update(metadata) data.update(upstreamdata) fontpaths = [op.join(config['path'], path) for path in directory.BIN] ttftablesizes = get_fonts_table_sizes(fontpaths) ftables_data = get_fonts_table_sizes_grouped(fontpaths) buildstate = yaml.load(open(op.join(config['path'], 'build.state.yaml'))) autohint_sizes = buildstate.get('autohinting_sizes', []) vmet = get_metric_view(fontpaths) fonts = [(path, FontFactory.openfont(op.join(config['path'], path))) for path in directory.BIN] stems = [ get_stem_info(op.join(config['path'], path)) for path in directory.BIN ] new_data = [] for k in data: d = {'name': k} d.update(data[k]) new_data.append(d) report_app = report_utils.BuildInfo(config) metrics = {'data': vmet._its_metrics, 'headings': vmet._its_metrics_header} table_sizes = {'tables': ttftablesizes[0], 'sizes': ttftablesizes[1:]} report_app.summary_page.dump_file(metrics, 'metrics.json') report_app.summary_page.dump_file(stems, 'stems.json') report_app.summary_page.dump_file(table_sizes, 'table_sizes.json') report_app.summary_page.dump_file(autohint_sizes, 'autohint_sizes.json') report_app.summary_page.dump_file(new_data, 'tests.json') report_app.summary_page.dump_file( { 'mean': ftables_data.mean, 'grouped': ftables_data.grouped, 'delta': ftables_data.delta }, 'fonts_tables_grouped.json') for face in family_metadata.fonts: face_template = "@font-face {{ font-family: {}; src: url(fonts/{});}}\n".format( face.metadata_object['postScriptName'], face.metadata_object['filename']) report_app.write_file(face_template, op.join(report_app.css_dir, 'faces.css'), mode='a') fonts_serialized = dict([(str(path), font_factory_instance_to_dict(fontaine)) for path, fontaine in fonts]) report_app.summary_page.dump_file(fonts_serialized, 'fontaine_fonts.json') #Temporarily remove this broken piece of code if False: fonts_orthography = get_orthography(fonts) report_app.summary_page.dump_file( { 'fonts_list': fonts_orthography[0], 'coverage_averages': fonts_orthography[1], 'fonts_info': fonts_orthography[2] }, 'fonts_orthography.json')
def __init__(self, testcase, fontpath): super(Vmet, self).__init__(testcase, fontpath) d = os.path.dirname(fontpath) directory = UpstreamDirectory(d) self.fonts = [os.path.join(d, f) for f in directory.BIN]
def run_bakery(path, verbose=False): # fontbakery-build supports passing arguments of directory or # concrete bakery.y[a]ml files. In case of passing directory # it looks at existing bakery.yml or bakery.yaml and runs on # first matched filepath # There can also be cases when directory does not contain any # bakery.y[a]ml or passed bakery.yml file does not exist. Then # fontbakery-build loads default configuration. bakery_yml_file = None sourcedir = path if os.path.isdir(path): for filename in ["bakery.yml", "bakery.yaml"]: if os.path.exists(os.path.join(path, filename)): bakery_yml_file = os.path.join(path, filename) break else: bakery_yml_file = path sourcedir = os.path.dirname(path) try: if bakery_yml_file: config = yaml.safe_load(open(bakery_yml_file, "r")) else: raise IOError except IOError: bakery_yml_file = os.path.join(sourcedir, "bakery.yml") config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) try: builddir = "build" if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse("HEAD", short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get("TRAVIS_COMMIT", builddir) if "process_files" not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config["process_files"] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) b = Bakery("", sourcedir, "builds", builddir) b.addLoggingToFile() b.load_config(bakery_yml_file) b.run() if not ttfautohint_installed(): msg = ( "Command line tool `ttfautohint` is required. Install it with" " `apt-get install ttfautohint` or `brew install ttfautohint`" ) logger.error(msg) except: if verbose or config.get("verbose"): raise sys.exit(1)