def interpolate(ufos, master_dir, out_dir, instance_data, debug=False): """Create MutatorMath designspace and generate instances. Returns instance UFOs, or unused instance data if debug is True. """ from mutatorMath.ufo import build designspace_path, instance_files = build_designspace( ufos, master_dir, out_dir, instance_data) logger.info('Building instances') for path, _ in instance_files: clean_ufo(path) build(designspace_path, outputUFOFormatVersion=3) instance_ufos = apply_instance_data(instance_files) if debug: return clear_data(instance_data) return instance_ufos
def to_ufos(data, include_instances=False, family_name=None, debug=False): """Take .glyphs file data and load it into UFOs. Takes in data as a dictionary structured according to https://github.com/schriftgestalt/GlyphsSDK/blob/master/GlyphsFileFormat.md and returns a list of UFOs, one per master. If debug is True, returns unused input data instead of the resulting UFOs. """ # check that source was generated with at least stable version 2.3 # https://github.com/googlei18n/glyphsLib/pull/65#issuecomment-237158140 if data.pop('.appVersion', 0) < 895: logger.warn( 'This Glyphs source was generated with an outdated version ' 'of Glyphs. The resulting UFOs may be incorrect.') source_family_name = data.pop('familyName') if family_name is None: family_name = source_family_name feature_prefixes, classes, features = [], [], [] for f in data.get('featurePrefixes', []): feature_prefixes.append( (f.pop('name'), f.pop('code'), f.pop('automatic', None))) for c in data.get('classes', []): classes.append((c.pop('name'), c.pop('code'), c.pop('automatic', None))) for f in data.get('features', []): features.append( (f.pop('name'), f.pop('code'), f.pop('automatic', None), f.pop('disabled', None), f.pop('notes', None))) kerning_groups = {} # stores background data from "associated layers" supplementary_bg_data = [] #TODO(jamesgk) maybe create one font at a time to reduce memory usage ufos, master_id_order = generate_base_fonts(data, family_name) # get the 'glyphOrder' custom parameter as stored in the lib.plist. # We assume it's the same for all ufos. first_ufo = ufos[master_id_order[0]] glyphOrder_key = PUBLIC_PREFIX + 'glyphOrder' if glyphOrder_key in first_ufo.lib: glyph_order = first_ufo.lib[glyphOrder_key] else: glyph_order = [] sorted_glyphset = set(glyph_order) for glyph in data['glyphs']: add_glyph_to_groups(kerning_groups, glyph) glyph_name = glyph.pop('glyphname') if glyph_name not in sorted_glyphset: # glyphs not listed in the 'glyphOrder' custom parameter but still # in the font are appended after the listed glyphs, in the order # in which they appear in the source file glyph_order.append(glyph_name) # pop glyph metadata only once, i.e. not when looping through layers metadata_keys = [ 'unicode', 'color', 'export', 'lastChange', 'leftMetricsKey', 'note', 'production', 'rightMetricsKey', 'widthMetricsKey', 'category', 'subCategory' ] glyph_data = {k: glyph.pop(k) for k in metadata_keys if k in glyph} for layer in glyph['layers']: layer_id = layer.pop('layerId') layer_name = layer.pop('name', None) assoc_id = layer.pop('associatedMasterId', None) if assoc_id is not None: if layer_name is not None: supplementary_bg_data.append( (assoc_id, glyph_name, layer_name, layer)) continue ufo = ufos[layer_id] glyph = ufo.newGlyph(glyph_name) load_glyph(glyph, layer, glyph_data) for layer_id, glyph_name, bg_name, bg_data in supplementary_bg_data: glyph = ufos[layer_id][glyph_name] set_robofont_glyph_background(glyph, bg_name, bg_data) for ufo in ufos.values(): ufo.lib[glyphOrder_key] = glyph_order propagate_font_anchors(ufo) add_features_to_ufo(ufo, feature_prefixes, classes, features) add_groups_to_ufo(ufo, kerning_groups) for master_id, kerning in data.pop('kerning', {}).items(): load_kerning(ufos[master_id], kerning) result = [ufos[master_id] for master_id in master_id_order] instances = { 'defaultFamilyName': source_family_name, 'data': data.pop('instances', []) } # the 'Variation Font Origin' is a font-wide custom parameter, thus it is # shared by all the master ufos; here we just get it from the first one varfont_origin_key = "Variation Font Origin" varfont_origin = first_ufo.lib.get(GLYPHS_PREFIX + varfont_origin_key) if varfont_origin: instances[varfont_origin_key] = varfont_origin if debug: return clear_data(data) elif include_instances: return result, instances return result
def to_ufos(data, include_instances=False, family_name=None, debug=False): """Take .glyphs file data and load it into UFOs. Takes in data as a dictionary structured according to https://github.com/schriftgestalt/GlyphsSDK/blob/master/GlyphsFileFormat.md and returns a list of UFOs, one per master. If debug is True, returns unused input data instead of the resulting UFOs. """ # check that source was generated with at least stable version 2.3 # https://github.com/googlei18n/glyphsLib/pull/65#issuecomment-237158140 if data.pop('.appVersion', 0) < 895: logger.warn('This Glyphs source was generated with an outdated version ' 'of Glyphs. The resulting UFOs may be incorrect.') source_family_name = data.pop('familyName') if family_name is None: family_name = source_family_name feature_prefixes, classes, features = [], [], [] for f in data.get('featurePrefixes', []): feature_prefixes.append((f.pop('name'), f.pop('code'), f.pop('automatic', None))) for c in data.get('classes', []): classes.append((c.pop('name'), c.pop('code'), c.pop('automatic', None))) for f in data.get('features', []): features.append((f.pop('name'), f.pop('code'), f.pop('automatic', None), f.pop('disabled', None), f.pop('notes', None))) kerning_groups = {} # stores background data from "associated layers" supplementary_bg_data = [] #TODO(jamesgk) maybe create one font at a time to reduce memory usage ufos, master_id_order = generate_base_fonts(data, family_name) glyph_order = [] for glyph in data['glyphs']: add_glyph_to_groups(kerning_groups, glyph) glyph_name = glyph.pop('glyphname') glyph_order.append(glyph_name) if not re.match(r'^([A-Za-z_][\w.]*|\.notdef)$', glyph_name): logger.warn( 'Illegal glyph name "%s". If this is used in the font\'s ' 'feature syntax, it could cause errors.' % glyph_name) # pop glyph metadata only once, i.e. not when looping through layers metadata_keys = ['unicode', 'color', 'export', 'lastChange', 'leftMetricsKey', 'note', 'production', 'rightMetricsKey', 'widthMetricsKey'] glyph_data = {k: glyph.pop(k) for k in metadata_keys if k in glyph} for layer in glyph['layers']: layer_id = layer.pop('layerId') layer_name = layer.pop('name', None) assoc_id = layer.pop('associatedMasterId', None) if assoc_id is not None: if layer_name is not None: supplementary_bg_data.append( (assoc_id, glyph_name, layer_name, layer)) continue ufo = ufos[layer_id] glyph = ufo.newGlyph(glyph_name) load_glyph(glyph, layer, glyph_data) for layer_id, glyph_name, bg_name, bg_data in supplementary_bg_data: glyph = ufos[layer_id][glyph_name] set_robofont_glyph_background(glyph, bg_name, bg_data) for ufo in ufos.values(): propagate_font_anchors(ufo) add_features_to_ufo(ufo, feature_prefixes, classes, features) add_groups_to_ufo(ufo, kerning_groups) ufo.lib[PUBLIC_PREFIX + 'glyphOrder'] = glyph_order for master_id, kerning in data.pop('kerning', {}).items(): load_kerning(ufos[master_id], kerning) result = [ufos[master_id] for master_id in master_id_order] instances = {'defaultFamilyName': source_family_name, 'data': data.pop('instances', [])} if debug: return clear_data(data) elif include_instances: return result, instances return result