def _valid_designspace(self, designspace, ufo_module): """Make sure that the user-provided designspace has loaded fonts and that names are the same as those from the UFOs. """ # TODO: (jany) really make a copy to avoid modifying the original object copy = designspace # Load only full UFO masters, sparse or "brace" layer sources are assumed # to point to existing layers within one of the full masters. for source in (s for s in copy.sources if not s.layerName): if not hasattr(source, "font") or source.font is None: if source.path: # FIXME: (jany) consider not changing the caller's objects source.font = util.open_ufo(source.path, ufo_module.Font) else: dirname = os.path.dirname(designspace.path) ufo_path = os.path.join(dirname, source.filename) source.font = util.open_ufo(ufo_path, ufo_module.Font) if source.location is None: source.location = {} for name in ("familyName", "styleName"): if getattr(source, name) != getattr(source.font.info, name): self.logger.warning( dedent( """\ The {name} is different between the UFO and the designspace source: source filename: {filename} source {name}: {source_name} ufo {name}: {ufo_name} The UFO name will be used. """ ).format( name=name, filename=source.filename, source_name=getattr(source, name), ufo_name=getattr(source.font.info, name), ) ) setattr(source, name, getattr(source.font.info, name)) # An axis without a mapping will see its range information (min and max # values) lost when converted to a Glyps.app file. To combat this we # add an explicit identity mapping. for axis in copy.axes: if axis.map: continue if axis.minimum == axis.maximum: axis.map = [(axis.minimum, axis.minimum)] else: axis.map = [ (axis.minimum, axis.minimum), (axis.maximum, axis.maximum), ] return copy
def ufo2glyphs(options): """Convert one designspace file or one or more UFOs to a Glyphs.app source file.""" import fontTools.designspaceLib from glyphsLib.util import open_ufo ufo_module = __import__(options.ufo_module) sources = options.designspace_file_or_UFOs designspace_file = None if (len(sources) == 1 and sources[0].endswith(".designspace") and os.path.isfile(sources[0])): designspace_file = sources[0] designspace = fontTools.designspaceLib.DesignSpaceDocument() designspace.read(designspace_file) object_to_read = designspace elif all( source.endswith(".ufo") and os.path.isdir(source) for source in sources): ufos = [open_ufo(source, ufo_module.Font) for source in sources] ufos.sort(key=lambda ufo: [ # Order the masters by weight and width ufo.info.openTypeOS2WeightClass or 400, ufo.info.openTypeOS2WidthClass or 5, ]) object_to_read = ufos else: print( "Please specify just one designspace file *or* one or more " "UFOs. They must end in '.designspace' or '.ufo', respectively.", file=sys.stderr, ) return 1 font = glyphsLib.to_glyphs( object_to_read, ufo_module=ufo_module, minimize_ufo_diffs=options.no_preserve_glyphsapp_metadata, ) # Make the Glyphs file more suitable for roundtrip: font.customParameters["Disable Last Change"] = options.enable_last_change font.disablesAutomaticAlignment = options.enable_automatic_alignment if options.output_path: font.save(options.output_path) else: if designspace_file: filename_to_write = os.path.splitext( designspace_file)[0] + ".glyphs" else: filename_to_write = os.path.join( os.path.dirname(sources[0]), font.familyName.replace(" ", "") + ".glyphs", ) font.save(filename_to_write)
def normalize_ufo_lib(path, ufo_module): """Go through each `lib` element recursively and transform `bools` into `int` because that's what's going to happen on round-trip with Glyphs. """ font = util.open_ufo(path, ufo_module.Font) deboolize(font.lib) for layer in font.layers: deboolize(layer.lib) for glyph in layer: deboolize(glyph.lib) _save_overwrite_ufo(font, path)
def _valid_designspace(self, designspace, ufo_module): """Make sure that the user-provided designspace has loaded fonts and that names are the same as those from the UFOs. """ # TODO: (jany) really make a copy to avoid modifying the original object copy = designspace # Load only full UFO masters, sparse or "brace" layer sources are assumed # to point to existing layers within one of the full masters. for source in (s for s in copy.sources if not s.layerName): if not hasattr(source, "font") or source.font is None: if source.path: # FIXME: (jany) consider not changing the caller's objects source.font = util.open_ufo(source.path, ufo_module.Font) else: dirname = os.path.dirname(designspace.path) ufo_path = os.path.join(dirname, source.filename) source.font = util.open_ufo(ufo_path, ufo_module.Font) if source.location is None: source.location = {} for name in ("familyName", "styleName"): if getattr(source, name) != getattr(source.font.info, name): self.logger.warning( dedent( """\ The {name} is different between the UFO and the designspace source: source filename: {filename} source {name}: {source_name} ufo {name}: {ufo_name} The UFO name will be used. """ ).format( name=name, filename=source.filename, source_name=getattr(source, name), ufo_name=getattr(source.font.info, name), ) ) setattr(source, name, getattr(source.font.info, name)) return copy
def test_designspace_generation_on_disk(datadir, tmpdir, ufo_module): glyphsLib.build_masters(str(datadir.join("BraceTestFont.glyphs")), str(tmpdir)) ufo_paths = list(tmpdir.visit(fil="*.ufo")) assert len(ufo_paths) == 4 # Source layers should not be written to disk. for ufo_path in ufo_paths: ufo = open_ufo(ufo_path, ufo_module.Font) # Check that all glyphs have contours (brace layers are in "b" only, writing # the brace layer to disk would result in empty other glyphs). for layer in ufo.layers: for glyph in layer: if glyph.name == "space": assert not glyph else: assert glyph