def doit(args): global logger logger = args.logger designspace_path = args.designspace_path instance_font_name = args.instanceName instance_attr = args.instanceAttr instance_val = args.instanceVal output_path_prefix = args.output calc_glyphs = args.forceInterpolation build_folder = args.folder round_instances = args.roundInstances if instance_font_name and (instance_attr or instance_val): args.logger.log('--instanceName is mutually exclusive with --instanceAttr or --instanceVal','S') if (instance_attr and not instance_val) or (instance_val and not instance_attr): args.logger.log('--instanceAttr and --instanceVal must be used together', 'S') if (build_folder and (instance_font_name or instance_attr or instance_val or output_path_prefix or calc_glyphs)): args.logger.log('--folder cannot be used with options: -i, -a, -v, -o, --forceInterpolation', 'S') args.logger.log('Interpolating master UFOs from designspace', 'P') if not build_folder: if not os.path.isfile(designspace_path): args.logger.log('A designspace file (not a folder) is required', 'S') reader = DesignSpaceDocumentReader(designspace_path, ufoVersion=3, roundGeometry=round_instances, progressFunc=progress_func) # assignment to an internal object variable is a kludge, probably should use subclassing instead reader._instanceWriterClass = InstanceWriterCF(output_path_prefix, calc_glyphs, args.weightfix) if calc_glyphs: args.logger.log('Interpolating glyphs where an instance font location matches a master', 'P') if instance_font_name or instance_attr: key_attr = instance_attr if instance_val else 'name' key_val = instance_val if instance_attr else instance_font_name reader.readInstance((key_attr, key_val)) else: reader.readInstances() else: # The below uses a utility function that's part of mutatorMath # It will accept a folder and processes all designspace files there args.logger.log('Interpolating glyphs where an instance font location matches a master', 'P') build_designspace(designspace_path, outputUFOFormatVersion=3, roundGeometry=round_instances, progressFunc=progress_func) if not severe_error: args.logger.log('Done', 'P') else: args.logger.log('Done with severe error', 'S')
def interpolate_instance_ufos(self, designspace, include=None, round_instances=False): """Interpolate master UFOs with MutatorMath and return instance UFOs. Args: designspace: a DesignSpaceDocument object containing sources and instances. include (str): optional regular expression pattern to match the DS instance 'name' attribute and only interpolate the matching instances. round_instances (bool): round instances' coordinates to integer. Returns: list of defcon.Font objects corresponding to the UFO instances. Raises: FontmakeError: if any of the sources defines a custom 'layer', for this is not supported by MutatorMath. """ from glyphsLib.interpolation import apply_instance_data from mutatorMath.ufo.document import DesignSpaceDocumentReader if any(source.layerName is not None for source in designspace.sources): raise FontmakeError( "MutatorMath doesn't support DesignSpace sources with 'layer' " "attribute") # TODO: replace mutatorMath with ufoProcessor? builder = DesignSpaceDocumentReader(designspace.path, ufoVersion=3, roundGeometry=round_instances, verbose=True) logger.info("Interpolating master UFOs from designspace") if include is not None: instances = self._search_instances(designspace, pattern=include) for instance_name in instances: builder.readInstance(("name", instance_name)) filenames = set(instances.values()) else: builder.readInstances() filenames = None # will include all instances logger.info("Applying instance data from designspace") instance_ufos = apply_instance_data(designspace, include_filenames=filenames) return instance_ufos
def interpolate_instance_ufos_mutatormath( self, designspace, include=None, round_instances=False, expand_features_to_instances=False, ): """Interpolate master UFOs with MutatorMath and return instance UFOs. Args: designspace: a DesignSpaceDocument object containing sources and instances. include (str): optional regular expression pattern to match the DS instance 'name' attribute and only interpolate the matching instances. round_instances (bool): round instances' coordinates to integer. expand_features_to_instances: parses the master feature file, expands all include()s and writes the resulting full feature file to all instance UFOs. Use this if you share feature files among masters in external files. Otherwise, the relative include paths can break as instances may end up elsewhere. Only done on interpolation. Returns: list of defcon.Font objects corresponding to the UFO instances. Raises: FontmakeError: if any of the sources defines a custom 'layer', for this is not supported by MutatorMath. ValueError: "expand_features_to_instances" is True but no source in the designspace document is designated with '<features copy="1"/>'. """ from glyphsLib.interpolation import apply_instance_data from mutatorMath.ufo.document import DesignSpaceDocumentReader if any(source.layerName is not None for source in designspace.sources): raise FontmakeError( "MutatorMath doesn't support DesignSpace sources with 'layer' " "attribute") with temporarily_disabling_axis_maps( designspace.path) as temp_designspace_path: builder = DesignSpaceDocumentReader( temp_designspace_path, ufoVersion=3, roundGeometry=round_instances, verbose=True, ) logger.info("Interpolating master UFOs from designspace") if include is not None: instances = self._search_instances(designspace, pattern=include) for instance_name in instances: builder.readInstance(("name", instance_name)) filenames = set(instances.values()) else: builder.readInstances() filenames = None # will include all instances logger.info("Applying instance data from designspace") instance_ufos = apply_instance_data(designspace, include_filenames=filenames) if expand_features_to_instances: logger.debug("Expanding features to instance UFOs") master_source = next( (s for s in designspace.sources if s.copyFeatures), None) if not master_source: raise ValueError( "No source is designated as the master for features.") else: master_source_font = builder.sources[master_source.name][0] master_source_features = parseLayoutFeatures( master_source_font).asFea() for instance_ufo in instance_ufos: instance_ufo.features.text = master_source_features instance_ufo.save() return instance_ufos
def run_from_designspace(self, designspace_path, interpolate=False, masters_as_instances=False, interpolate_binary_layout=False, round_instances=False, **kwargs): """Run toolchain from a MutatorMath design space document. Args: designspace_path: Path to designspace document. interpolate: If True output all instance fonts, otherwise just masters. If the value is a string, only build instance(s) that match given name. The string is compiled into a regular expression and matched against the "name" attribute of designspace instances using `re.fullmatch`. masters_as_instances: If True, output master fonts as instances. interpolate_binary_layout: Interpolate layout tables from compiled master binaries. round_instances: apply integer rounding when interpolating with MutatorMath. kwargs: Arguments passed along to run_from_ufos. Raises: TypeError: "variable" output is incompatible with arguments "interpolate", "masters_as_instances", and "interpolate_binary_layout". """ if "variable" in kwargs.get("output", ()): for argname in ("interpolate", "masters_as_instances", "interpolate_binary_layout"): if locals()[argname]: raise TypeError( '"%s" argument incompatible with "variable" output' % argname) from glyphsLib.interpolation import apply_instance_data from mutatorMath.ufo.document import DesignSpaceDocumentReader ufos = [] reader = DesignSpaceDocumentReader(designspace_path, ufoVersion=3, roundGeometry=round_instances, verbose=True) if not interpolate or masters_as_instances: ufos.extend(reader.getSourcePaths()) if interpolate: logger.info('Interpolating master UFOs from designspace') if isinstance(interpolate, basestring): instances = self._search_instances(designspace_path, pattern=interpolate) for instance_name in instances: reader.readInstance(("name", instance_name)) filenames = set(instances.values()) else: reader.readInstances() filenames = None # will include all instances logger.info('Applying instance data from designspace') ufos.extend( apply_instance_data(designspace_path, include_filenames=filenames)) if interpolate_binary_layout is False: interpolate_layout_from = interpolate_layout_dir = None else: interpolate_layout_from = designspace_path if isinstance(interpolate_binary_layout, basestring): interpolate_layout_dir = interpolate_binary_layout else: interpolate_layout_dir = None self.run_from_ufos(ufos, designspace_path=designspace_path, is_instance=(interpolate or masters_as_instances), interpolate_layout_from=interpolate_layout_from, interpolate_layout_dir=interpolate_layout_dir, **kwargs)