def _to_designspace_source(self, master, is_regular): source = self._sources[master.id] ufo = source.font if is_regular: source.copyLib = True source.copyInfo = True source.copyGroups = True source.copyFeatures = True source.familyName = ufo.info.familyName source.styleName = ufo.info.styleName # TODO: recover original source name from userData # UFO_SOURCE_NAME_KEY source.name = f"{source.familyName} {source.styleName}" if UFO_FILENAME_CUSTOM_PARAM in master.customParameters: source.filename = master.customParameters[UFO_FILENAME_CUSTOM_PARAM] elif UFO_FILENAME_KEY in master.userData: source.filename = master.userData[UFO_FILENAME_KEY] else: source.filename = build_ufo_path("", source.familyName, source.styleName) # Make sure UFO filenames are unique, lest we overwrite masters that # happen to have the same weight name. Careful, name clashes can also happen when # the masters have a UFO_FILENAME_CUSTOM_PARAM: when the user duplicates a master # in Glyphs but forgets to change the custom parameter. # Attention: The following is done regardless of where source.filename came from. # Depending on the duplicate's order in the master list, this could lead to the # legitimate master's filename getting an underscore appended! n = 1 while any(s is not source and s.filename == source.filename for s in self._sources.values()): filename_stem, filename_ext = os.path.splitext(source.filename) source.filename = f"{filename_stem}#{n}{filename_ext}" if (UFO_FILENAME_CUSTOM_PARAM in master.customParameters or UFO_FILENAME_KEY in master.userData): logger.warning( "The master with the style name '{}' (ID {}) would be written to the " "same destination as another master. Change the " "master's custom parameter '{}' " "to select a different destination. Proceeding, but appending" " '#{}' to the filename on disk.".format( source.styleName, master.id, UFO_FILENAME_CUSTOM_PARAM, n)) else: logger.warning( "The master with the style name '{}' (ID {}) has the same style name " "as another one. All masters should have distinctive " "(style) names. Use the 'Master name' custom parameter" " on a master to give it a unique name. Proceeding " "with an unchanged name, but appending '#{}' to the file" " name on disk.".format(source.styleName, master.id, n)) n += 1 location = {} for axis_def in get_axis_definitions(self.font): location[axis_def.name] = axis_def.get_design_loc(master) source.location = location
def _to_designspace_source(self, master, is_regular): source = self._sources[master.id] ufo = source.font if is_regular: source.copyLib = True source.copyInfo = True source.copyGroups = True source.copyFeatures = True source.familyName = ufo.info.familyName source.styleName = ufo.info.styleName # TODO: recover original source name from userData # UFO_SOURCE_NAME_KEY source.name = "{} {}".format(source.familyName, source.styleName) if UFO_FILENAME_KEY in master.userData: source.filename = master.userData[UFO_FILENAME_KEY] else: # TODO: (jany) allow another naming convention? source.filename = build_ufo_path("", source.familyName, source.styleName) # Make sure UFO filenames are unique, lest we overwrite masters that # happen to have the same weight name. n = "_" while any( s is not source and s.filename == source.filename for s in self._sources.values() ): source.filename = os.path.basename( build_ufo_path("", source.familyName, source.styleName + n) ) n += "_" logger.warning( "The master with id {} has the same style name ({}) " "as another one. All masters should have distinctive " "(style) names. Use the 'Master name' custom parameter" " on a master to give it a unique name. Proceeding " "with an unchanged name, but appending '_' to the file" " name on disk.".format(master.id, source.styleName) ) location = {} for axis_def in get_axis_definitions(self.font): location[axis_def.name] = axis_def.get_design_loc(master) source.location = location
def add_instances_to_writer(writer, family_name, axes, instances, out_dir): """Add instances from Glyphs data to a MutatorMath document writer. Returns a list of <ufo_path, font_data> pairs, corresponding to the instances which will be output by the document writer. The font data is the Glyphs data for this instance as a dict. """ ofiles = [] for instance in instances: familyName, postScriptFontName, ufo_path = None, None, None for p in instance.customParameters: param, value = p.name, p.value if param == 'familyName': familyName = value elif param == 'postscriptFontName': # Glyphs uses "postscriptFontName", not "postScriptFontName" postScriptFontName = value elif param == 'fileName': ufo_path = os.path.join(out_dir, value + '.ufo') if familyName is None: familyName = family_name styleName = instance.name if not ufo_path: ufo_path = build_ufo_path(out_dir, familyName, styleName) ofiles.append((ufo_path, instance)) # MutatorMath.DesignSpaceDocumentWriter iterates over the location # dictionary, which is non-deterministic so it can cause test failures. # We therefore use an OrderedDict to which we insert in axis order. # Since glyphsLib will switch to DesignSpaceDocument once that is # integrated into fonttools, it's not worth fixing upstream. # https://github.com/googlei18n/glyphsLib/issues/165 location = OrderedDict() for axis in axes: location[axis] = getattr(instance, 'interpolation' + axis.title(), DEFAULT_LOCS[axis]) styleMapFamilyName, styleMapStyleName = build_stylemap_names( family_name=familyName, style_name=styleName, is_bold=instance.isBold, is_italic=instance.isItalic, linked_style=instance.linkStyle, ) writer.startInstance(name=' '.join((familyName, styleName)), location=location, familyName=familyName, styleName=styleName, postScriptFontName=postScriptFontName, styleMapFamilyName=styleMapFamilyName, styleMapStyleName=styleMapStyleName, fileName=ufo_path) writer.writeInfo() writer.writeKerning() writer.endInstance() return ofiles
def add_instances_to_writer(writer, family_name, instance_data, out_dir): """Add instances from Glyphs data to a MutatorMath document writer. Returns a list of <ufo_path, font_data> pairs, corresponding to the instances which will be output by the document writer. The font data is the Glyphs data for this instance as a dict. """ default_family_name = instance_data.pop('defaultFamilyName') instance_data = instance_data.pop('data') ofiles = [] # only write dimension elements if defined in at least one of the instances dimension_names = [] for s in ('weight', 'width', 'custom'): key = 'interpolation' + s.title() if any(key in instance for instance in instance_data): dimension_names.append(s) for instance in instance_data: # Glyphs.app recognizes both "exports=0" and "active=0" as a flag # to mark instances as inactive. Those should not be instantiated. # https://github.com/googlei18n/glyphsLib/issues/129 if (not int(instance.pop('exports', 1)) or not int(instance.pop('active', 1))): continue instance_family = default_family_name custom_params = instance.get('customParameters', ()) for i in range(len(custom_params)): if custom_params[i]['name'] == 'familyName': instance_family = custom_params[i]['value'] break if not instance_family: continue style_name = instance.pop('name') ufo_path = build_ufo_path(out_dir, instance_family, style_name) ofiles.append((ufo_path, instance)) writer.startInstance( name=' '.join((instance_family, style_name)), location={ s: instance.pop('interpolation' + s.title(), DEFAULT_LOC) for s in dimension_names}, familyName=instance_family, styleName=style_name, fileName=ufo_path) writer.writeInfo() writer.writeKerning() writer.endInstance() return ofiles
def add_instances_to_writer(writer, family_name, instance_data, out_dir): """Add instances from Glyphs data to a MutatorMath document writer. Returns a list of <ufo_path, font_data> pairs, corresponding to the instances which will be output by the document writer. The font data is the Glyphs data for this instance as a dict. """ default_family_name = instance_data.pop('defaultFamilyName') instance_data = instance_data.pop('data') ofiles = [] # only write dimension elements if defined in at least one of the instances dimension_names = [] for s in ('weight', 'width', 'custom'): key = 'interpolation' + s.title() if any(key in instance for instance in instance_data): dimension_names.append(s) for instance in instance_data: if not instance.pop('active', True): continue # only use instances with the masters' family name instance_family = default_family_name custom_params = instance.get('customParameters', ()) for i in range(len(custom_params)): if custom_params[i]['name'] == 'familyName': instance_family = custom_params[i]['value'] del custom_params[i] break if instance_family != family_name: continue style_name = instance.pop('name') ufo_path = build_ufo_path(out_dir, instance_family, style_name) ofiles.append((ufo_path, instance)) writer.startInstance( name=' '.join((instance_family, style_name)), location={ s: instance.pop('interpolation' + s.title(), DEFAULT_LOC) for s in dimension_names}, familyName=instance_family, styleName=style_name, fileName=ufo_path) writer.writeInfo() writer.writeKerning() writer.endInstance() return ofiles
def _to_designspace_instance(self, instance): ufo_instance = self.designspace.newInstanceDescriptor() # FIXME: (jany) most of these customParameters are actually attributes, # at least according to https://docu.glyphsapp.com/#fontName for p in instance.customParameters: param, value = p.name, p.value if param == 'familyName': ufo_instance.familyName = value elif param == 'postscriptFontName': # Glyphs uses "postscriptFontName", not "postScriptFontName" ufo_instance.postScriptFontName = value elif param == 'fileName': fname = value + '.ufo' if self.instance_dir is not None: fname = self.instance_dir + '/' + fname ufo_instance.filename = fname if ufo_instance.familyName is None: ufo_instance.familyName = self.family_name ufo_instance.styleName = instance.name # TODO: (jany) investigate the possibility of storing a relative path in # the `filename` custom parameter. If yes, drop the key below. # Maybe do the same for masters? # https://github.com/googlei18n/glyphsLib/issues/319 fname = instance.customParameters[FULL_FILENAME_KEY] if fname is not None: if self.instance_dir: fname = self.instance_dir + '/' + os.path.basename(fname) ufo_instance.filename = fname if not ufo_instance.filename: instance_dir = self.instance_dir or 'instance_ufos' ufo_instance.filename = build_ufo_path(instance_dir, ufo_instance.familyName, ufo_instance.styleName) designspace_axis_tags = set(a.tag for a in self.designspace.axes) location = {} for axis_def in get_axis_definitions(self.font): # Only write locations along defined axes if axis_def.tag in designspace_axis_tags: location[axis_def.name] = axis_def.get_design_loc(instance) ufo_instance.location = location # FIXME: (jany) should be the responsibility of ufo2ft? # Anyway, only generate the styleMap names if the Glyphs instance already # has a linkStyle set up, or if we're not round-tripping (i.e. generating # UFOs for fontmake, the traditional use-case of glyphsLib.) if instance.linkStyle or not self.minimize_glyphs_diffs: ufo_instance.styleMapFamilyName, ufo_instance.styleMapStyleName = \ build_stylemap_names( family_name=ufo_instance.familyName, style_name=ufo_instance.styleName, is_bold=instance.isBold, is_italic=instance.isItalic, linked_style=instance.linkStyle, ) ufo_instance.name = ' '.join((ufo_instance.familyName or '', ufo_instance.styleName or '')) if self.minimize_glyphs_diffs: ufo_instance.lib[EXPORT_KEY] = instance.active ufo_instance.lib[WEIGHT_KEY] = instance.weight ufo_instance.lib[WIDTH_KEY] = instance.width ufo_instance.lib[ INSTANCE_INTERPOLATIONS_KEY] = instance.instanceInterpolations ufo_instance.lib[ MANUAL_INTERPOLATION_KEY] = instance.manualInterpolation # Strategy: dump all custom parameters into the InstanceDescriptor. # Later, when using `apply_instance_data`, we will dig out those custom # parameters using `InstanceDescriptorAsGSInstance` and apply them to the # instance UFO with `to_ufo_custom_params`. # NOTE: customParameters are not a dict! One key can have several values params = [] for p in instance.customParameters: if p.name in ('familyName', 'postscriptFontName', 'fileName', FULL_FILENAME_KEY): # These will be stored in the official descriptor attributes continue if p.name in ('weightClass', 'widthClass'): # No need to store these ones because we can recover them by # reading the mapping backward, because the mapping is built from # where the instances are. continue params.append((p.name, p.value)) if params: ufo_instance.lib[CUSTOM_PARAMETERS_KEY] = params self.designspace.addInstance(ufo_instance)