Ejemplo n.º 1
0
    def write(self, writer: UFOWriter, saveAs: Optional[bool] = None) -> None:
        """Writes this Font to a :class:`fontTools.ufoLib.UFOWriter`.

        Args:
            writer: The :class:`fontTools.ufoLib.UFOWriter` to write to.
            saveAs: If True, tells the writer to save out-of-place. If False, tells the
                writer to save in-place. This affects how resources are cleaned before
                writing.
        """
        if saveAs is None:
            saveAs = self._reader is not writer
        # TODO move this check to fontTools UFOWriter
        if self.layers.defaultLayer.name != DEFAULT_LAYER_NAME:
            assert DEFAULT_LAYER_NAME not in self.layers.layerOrder
        # save font attrs
        writer.writeFeatures(self.features.text)
        writer.writeGroups(self.groups)
        writer.writeInfo(self.info)
        writer.writeKerning(self.kerning)
        writer.writeLib(self.lib)
        # save the layers
        self.layers.write(writer, saveAs=saveAs)
        # save bin parts
        self.data.write(writer, saveAs=saveAs)
        self.images.write(writer, saveAs=saveAs)
Ejemplo n.º 2
0
def test_UFOWriter_formatVersion(tmp_path):
    ufo_path = tmp_path / "TestFont.ufo"
    with UFOWriter(ufo_path, formatVersion=3) as writer:
        assert writer.formatVersionTuple == (3, 0)

    shutil.rmtree(str(ufo_path))
    with UFOWriter(ufo_path, formatVersion=(2, 0)) as writer:
        assert writer.formatVersionTuple == (2, 0)
Ejemplo n.º 3
0
	def testWrite(self):
		infoObject = self.makeInfoObject()
		writer = UFOWriter(self.dstDir, formatVersion=1)
		writer.writeInfo(infoObject)
		writtenData = self.readPlist()
		for attr, originalValue in list(fontInfoVersion1.items()):
			newValue = writtenData[attr]
			self.assertEqual(newValue, originalValue)
Ejemplo n.º 4
0
 def testWrite(self):
     infoObject = self.makeInfoObject()
     writer = UFOWriter(self.dstDir, formatVersion=1)
     writer.writeInfo(infoObject)
     writtenData = self.readPlist()
     for attr, originalValue in list(fontInfoVersion1.items()):
         newValue = writtenData[attr]
         self.assertEqual(newValue, originalValue)
Ejemplo n.º 5
0
 def testFontStyleConversion(self):
     fontStyle1To2 = {
         64: "regular",
         1: "italic",
         32: "bold",
         33: "bold italic"
     }
     for old, new in list(fontStyle1To2.items()):
         infoObject = self.makeInfoObject()
         infoObject.styleMapStyleName = new
         writer = UFOWriter(self.dstDir, formatVersion=1)
         writer.writeInfo(infoObject)
         writtenData = self.readPlist()
         self.assertEqual(writtenData["fontStyle"], old)
Ejemplo n.º 6
0
	def testFontStyleConversion(self):
		fontStyle1To2 = {
			64 : "regular",
			1  : "italic",
			32 : "bold",
			33 : "bold italic"
		}
		for old, new in list(fontStyle1To2.items()):
			infoObject = self.makeInfoObject()
			infoObject.styleMapStyleName = new
			writer = UFOWriter(self.dstDir, formatVersion=1)
			writer.writeInfo(infoObject)
			writtenData = self.readPlist()
			self.assertEqual(writtenData["fontStyle"], old)
Ejemplo n.º 7
0
    def save(self):
        if self.save_to_default_layer:
            self.defcon_font.save()
        else:
            if self.ufo_format <= UFOFormatVersion.FORMAT_2_0:
                # Up-convert the UFO to format 3
                warnings.warn("The UFO was up-converted to format 3.")
                self.ufo_format = UFOFormatVersion.FORMAT_3_0

                with UFOWriter(self.defcon_font.path,
                               formatVersion=self.ufo_format) as writer:
                    writer.getGlyphSet()
                    writer.writeLayerContents()

            writer = UFOWriter(self.defcon_font.path,
                               formatVersion=self.ufo_format)
            writer.layerContents[PROCD_GLYPHS_LAYER_NAME] = PROCD_GLYPHS_LAYER
            layers = self.defcon_font.layers
            layer = layers[PROCD_GLYPHS_LAYER_NAME]
            glyph_set = writer.getGlyphSet(layerName=PROCD_GLYPHS_LAYER_NAME,
                                           defaultLayer=False)
            if self.font_format == "PFC":
                libs = {}
                for f in layers.defaultLayer._glyphs.items():
                    libs[f[0]] = f[1].lib
                for g in layer._glyphs.items():
                    g[1].lib = libs[g[0]]
            writer.writeLayerContents(layers.layerOrder)
            layer.save(glyph_set)

        if self.font_type == UFO_FONT_TYPE:
            ufotools.regenerate_glyph_hashes(self.ufo_font_hash_data)
            # Write the hash data, if it has changed.
            self.ufo_font_hash_data.close()

        elif self.font_type == TYPE1_FONT_TYPE:
            args = ['tx', '-t1']
            if self.font_format == 'PFB':
                args.append('-pfb')
            if not run_shell_command(args +
                                     [self.temp_ufo_path, self.font_path]):
                raise FocusFontError('Failed to convert UFO font to Type 1.')

        else:
            temp_cff_path = get_temp_file_path()
            if not run_shell_command([
                    'tx', '-cff', '+S', '+b', '-std', self.temp_ufo_path,
                    temp_cff_path
            ],
                                     suppress_output=True):
                raise FocusFontError('Failed to convert UFO font to CFF.')

            if self.font_type == CFF_FONT_TYPE:
                shutil.copy2(temp_cff_path, self.font_path)

            else:  # OTF_FONT_TYPE
                if not run_shell_command(
                    ['sfntedit', '-a', f'CFF={temp_cff_path}', self.font_path
                     ]):
                    raise FocusFontError('Failed to add CFF table to OTF.')
Ejemplo n.º 8
0
    def save(
        self, path=None, formatVersion=3, structure=None, overwrite=False, validate=True
    ):
        if formatVersion != 3:
            raise NotImplementedError("unsupported format version: %s" % formatVersion)
        # validate 'structure' argument
        if structure is not None:
            structure = UFOFileStructure(structure)
        elif self._fileStructure is not None:
            # if structure is None, fall back to the same as when first loaded
            structure = self._fileStructure

        if hasattr(path, "__fspath__"):
            path = path.__fspath__()
        if isinstance(path, str):
            path = os.path.normpath(path)
        # else we assume it's an fs.BaseFS and we pass it on to UFOWriter

        overwritePath = tmp = None

        saveAs = path is not None
        if saveAs:
            if isinstance(path, str) and os.path.exists(path):
                if overwrite:
                    overwritePath = path
                    tmp = fs.tempfs.TempFS()
                    path = tmp.getsyspath(os.path.basename(path))
                else:
                    import errno

                    raise OSError(errno.EEXIST, "path %r already exists" % path)
        elif self.path is None:
            raise TypeError("'path' is required when saving a new Font")
        else:
            path = self.path

        try:
            with UFOWriter(path, structure=structure, validate=validate) as writer:
                self.write(writer, saveAs=saveAs)
            writer.setModificationTime()
        except Exception:
            raise
        else:
            if overwritePath is not None:
                # remove existing then move file to destination
                if os.path.isdir(overwritePath):
                    shutil.rmtree(overwritePath)
                elif os.path.isfile(overwritePath):
                    os.remove(overwritePath)
                shutil.move(path, overwritePath)
                path = overwritePath
        finally:
            # clean up the temporary directory
            if tmp is not None:
                tmp.close()

        self._path = path
Ejemplo n.º 9
0
 def testWidthNameConversion(self):
     widthName1To2 = {
         "Ultra-condensed": 1,
         "Extra-condensed": 2,
         "Condensed": 3,
         "Semi-condensed": 4,
         "Medium (normal)": 5,
         "Semi-expanded": 6,
         "Expanded": 7,
         "Extra-expanded": 8,
         "Ultra-expanded": 9
     }
     for old, new in list(widthName1To2.items()):
         infoObject = self.makeInfoObject()
         infoObject.openTypeOS2WidthClass = new
         writer = UFOWriter(self.dstDir, formatVersion=1)
         writer.writeInfo(infoObject)
         writtenData = self.readPlist()
         self.assertEqual(writtenData["widthName"], old)
Ejemplo n.º 10
0
	def testWidthNameConversion(self):
		widthName1To2 = {
			"Ultra-condensed" : 1,
			"Extra-condensed" : 2,
			"Condensed"		  : 3,
			"Semi-condensed"  : 4,
			"Medium (normal)" : 5,
			"Semi-expanded"	  : 6,
			"Expanded"		  : 7,
			"Extra-expanded"  : 8,
			"Ultra-expanded"  : 9
		}
		for old, new in list(widthName1To2.items()):
			infoObject = self.makeInfoObject()
			infoObject.openTypeOS2WidthClass = new
			writer = UFOWriter(self.dstDir, formatVersion=1)
			writer.writeInfo(infoObject)
			writtenData = self.readPlist()
			self.assertEqual(writtenData["widthName"], old)
Ejemplo n.º 11
0
    def save(self, path):
        if path is None:
            path = self.path

        if os.path.abspath(self.path) != os.path.abspath(path):
            # If user has specified a path other than the source font path,
            # then copy the entire UFO font, and operate on the copy.
            log.info("Copying from source UFO font to output UFO font before "
                     "processing...")
            if os.path.exists(path):
                shutil.rmtree(path)
            shutil.copytree(self.path, path)

        writer = UFOWriter(path, self._reader.formatVersion, validate=False)

        if self.hashMapChanged:
            self.writeHashMap(writer)
        self.hashMapChanged = False

        layer = PROCESSED_LAYER_NAME
        if self.writeToDefaultLayer:
            layer = None

        # Write layer contents.
        layers = {DEFAULT_LAYER_NAME: DEFAULT_GLYPHS_DIRNAME}
        if self.processedLayerGlyphMap or not self.writeToDefaultLayer:
            layers[PROCESSED_LAYER_NAME] = PROCESSED_GLYPHS_DIRNAME
        writer.layerContents.update(layers)
        writer.writeLayerContents([DEFAULT_LAYER_NAME, PROCESSED_LAYER_NAME])

        # Write glyphs.
        glyphset = writer.getGlyphSet(layer, defaultLayer=layer is None)
        for name, glyph in self.newGlyphMap.items():
            filename = self.glyphMap[name]
            if not self.writeToDefaultLayer and \
                    name in self.processedLayerGlyphMap:
                filename = self.processedLayerGlyphMap[name]
            glyphset.contents[name] = filename
            glyphset.writeGlyph(name, glyph, glyph.drawPoints)
        glyphset.writeContents()
Ejemplo n.º 12
0
    def write(self, writer: UFOWriter, saveAs: bool | None = None) -> None:
        """Writes this LayerSet to a :class:`fontTools.ufoLib.UFOWriter`.

        Args:
            writer(fontTools.ufoLib.UFOWriter): The writer to write to.
            saveAs: If True, tells the writer to save out-of-place. If False, tells the
                writer to save in-place. This affects how resources are cleaned before
                writing.
        """
        if saveAs is None:
            saveAs = self._reader is not writer
        # if in-place, remove deleted layers
        layers = self._layers
        if not saveAs:
            for name in set(writer.getLayerNames()).difference(layers):
                writer.deleteGlyphSet(name)
        # write layers
        defaultLayer = self.defaultLayer
        for name, layer in layers.items():
            default = layer is defaultLayer
            if layer is _LAYER_NOT_LOADED:
                if saveAs:
                    layer = self.loadLayer(name, lazy=False)
                else:
                    continue
            glyphSet = writer.getGlyphSet(name, defaultLayer=default)
            layer.write(glyphSet, saveAs=saveAs)
        writer.writeLayerContents(self.layerOrder)
Ejemplo n.º 13
0
	def testWrite(self):
		writer = UFOWriter(self.dstDir, formatVersion=2)
		writer.setKerningGroupConversionRenameMaps(self.downConversionMapping)
		writer.writeKerning(self.kerning)
		writer.writeGroups(self.groups)
		# test groups
		path = os.path.join(self.dstDir, "groups.plist")
		with open(path, "rb") as f:
			writtenGroups = plistlib.load(f)
		self.assertEqual(writtenGroups, self.expectedWrittenGroups)
		# test kerning
		path = os.path.join(self.dstDir, "kerning.plist")
		with open(path, "rb") as f:
			writtenKerning = plistlib.load(f)
		self.assertEqual(writtenKerning, self.expectedWrittenKerning)
		self.tearDownUFO()
Ejemplo n.º 14
0
def test_pathlike(testufo):
    class PathLike(object):
        def __init__(self, s):
            self._path = s

        def __fspath__(self):
            return tostr(self._path, sys.getfilesystemencoding())

    path = PathLike(testufo)

    with UFOReader(path) as reader:
        assert reader._path == path.__fspath__()

    with UFOWriter(path) as writer:
        assert writer._path == path.__fspath__()
Ejemplo n.º 15
0
    def save(self, path):
        if path is None:
            path = self.path

        if os.path.abspath(self.path) != os.path.abspath(path):
            # If user has specified a path other than the source font path,
            # then copy the entire UFO font, and operate on the copy.
            log.info("Copying from source UFO font to output UFO font before "
                     "processing...")
            if os.path.exists(path):
                shutil.rmtree(path)
            shutil.copytree(self.path, path)

        writer = UFOWriter(path,
                           self._reader.formatVersionTuple,
                           validate=False)

        layer = PROCESSED_LAYER_NAME
        if self.writeToDefaultLayer:
            layer = None

        # Write layer contents.
        layers = writer.layerContents.copy()
        if self.writeToDefaultLayer and PROCESSED_LAYER_NAME in layers:
            # Delete processed glyphs directory
            writer.deleteGlyphSet(PROCESSED_LAYER_NAME)
            # Remove entry from 'layercontents.plist' file
            del layers[PROCESSED_LAYER_NAME]
        elif self.processedLayerGlyphMap or not self.writeToDefaultLayer:
            layers[PROCESSED_LAYER_NAME] = PROCESSED_GLYPHS_DIRNAME
        writer.layerContents.update(layers)
        writer.writeLayerContents()

        # Write glyphs.
        glyphset = writer.getGlyphSet(layer, defaultLayer=layer is None)
        for name, glyph in self.newGlyphMap.items():
            filename = self.glyphMap[name]
            if not self.writeToDefaultLayer and \
                    name in self.processedLayerGlyphMap:
                filename = self.processedLayerGlyphMap[name]
            # Recalculate glyph hashes
            if self.writeToDefaultLayer:
                self.recalcHashEntry(name, glyph)
            glyphset.contents[name] = filename
            glyphset.writeGlyph(name, glyph, glyph.drawPoints)
        glyphset.writeContents()

        # Write hashmap
        if self.hashMapChanged:
            self.writeHashMap(writer)
Ejemplo n.º 16
0
	def testWrite(self):
		writer = UFOWriter(self.dstDir, formatVersion=2)
		writer.setKerningGroupConversionRenameMaps(self.downConversionMapping)
		writer.writeKerning(self.kerning)
		writer.writeGroups(self.groups)
		# test groups
		path = os.path.join(self.dstDir, "groups.plist")
		with open(path, "rb") as f:
			writtenGroups = plistlib.load(f)
		self.assertEqual(writtenGroups, self.expectedWrittenGroups)
		# test kerning
		path = os.path.join(self.dstDir, "kerning.plist")
		with open(path, "rb") as f:
			writtenKerning = plistlib.load(f)
		self.assertEqual(writtenKerning, self.expectedWrittenKerning)
		self.tearDownUFO()
Ejemplo n.º 17
0
 def test_init_writer(self):
     m = fs.memoryfs.MemoryFS()
     with UFOWriter(m) as writer:
         assert m.exists("metainfo.plist")
         assert writer._path == "<memfs>"
Ejemplo n.º 18
0
def test_UFOWriter_formatVersion_default_latest(tmp_path):
    writer = UFOWriter(tmp_path / "TestFont.ufo")
    assert writer.formatVersionTuple == UFOFormatVersion.default()
Ejemplo n.º 19
0
def test_path_attribute_deprecated(testufo):
    with UFOWriter(testufo) as writer:
        with pytest.warns(DeprecationWarning, match="The 'path' attribute"):
            writer.path
Ejemplo n.º 20
0
    def save(
        self,
        path: Optional[Union[PathLike, fs.base.FS]] = None,
        formatVersion: int = 3,
        structure: Optional[UFOFileStructure] = None,
        overwrite: bool = False,
        validate: bool = True,
    ) -> None:
        """Saves the font to ``path``.

        Args:
            path: The target path. If it is None, the path from the last save (except
                when that was a ``fs.base.FS``) or when the font was first opened will
                be used.
            formatVersion: The version to save the UFO as. Only version 3 is supported
                currently.
            structure (fontTools.ufoLib.UFOFileStructure): How to store the UFO.
                Can be either None, "zip" or "package". If None, it tries to use the
                same structure as the original UFO at the output path. If "zip", the
                UFO will be saved as compressed archive. If "package", it is saved as
                a regular folder or "package".
            overwrite: If False, raises OSError when the target path exists. If True,
                overwrites the target path.
            validate: If True, will validate the data in Font before writing it out. If
                False, will write out whatever is serializable.
        """
        if formatVersion != 3:
            raise NotImplementedError(f"unsupported format version: {formatVersion}")

        # validate 'structure' argument
        if structure is not None:
            structure = UFOFileStructure(structure)
        elif self._fileStructure is not None:
            # if structure is None, fall back to the same as when first loaded
            structure = self._fileStructure

        # Normalize path unless we're given a fs.base.FS, which we pass to UFOWriter.
        if path is not None and not isinstance(path, fs.base.FS):
            path = os.path.normpath(os.fspath(path))

        overwritePath = tmp = None

        saveAs = path is not None
        if saveAs:
            if isinstance(path, str) and os.path.exists(path):
                if overwrite:
                    overwritePath = path
                    tmp = fs.tempfs.TempFS()
                    path = tmp.getsyspath(os.path.basename(path))
                else:
                    import errno

                    raise OSError(errno.EEXIST, "path %r already exists" % path)
        elif self.path is None:
            raise TypeError("'path' is required when saving a new Font")
        else:
            path = self.path

        try:
            with UFOWriter(path, structure=structure, validate=validate) as writer:
                self.write(writer, saveAs=saveAs)
            writer.setModificationTime()
        except Exception:
            raise
        else:
            if overwritePath is not None:
                assert isinstance(path, str)

                # remove existing then move file to destination
                if os.path.isdir(overwritePath):
                    shutil.rmtree(overwritePath)
                elif os.path.isfile(overwritePath):
                    os.remove(overwritePath)
                shutil.move(path, overwritePath)
                path = overwritePath
        finally:
            # clean up the temporary directory
            if tmp is not None:
                tmp.close()

        # Only remember path if it isn't a fs.base.FS because not all FS objects are
        # OsFS with a corresponding filesystem path. E.g. think about MemoryFS.
        # If you want, you can call getsyspath("") method of OsFS object and set that to
        # self._path. But you then have to catch the fs.errors.NoSysPath and skip if
        # the FS object does not implement a filesystem path.
        if not isinstance(path, fs.base.FS):
            self._path = path
Ejemplo n.º 21
0
 def test_write(self, testufoz):
     with UFOWriter(testufoz, structure="zip") as writer:
         writer.writeLib({"hello world": 123})
     with UFOReader(testufoz) as reader:
         assert reader.readLib() == {"hello world": 123}
Ejemplo n.º 22
0
def test_UFOWriter_unsupported_format_version(tmp_path):
    with pytest.raises(UnsupportedUFOFormat):
        UFOWriter(tmp_path, formatVersion=(123, 456))
Ejemplo n.º 23
0
def test_UFOWriter_previous_higher_format_version(ufo_path):
    with pytest.raises(UnsupportedUFOFormat,
                       match="UFO located at this path is a higher version"):
        UFOWriter(ufo_path, formatVersion=(2, 0))
Ejemplo n.º 24
0
 def write_data(writer: UFOWriter, filename: str, data: bytes) -> None:
     """Writes the image data to filename within the store."""
     writer.writeImage(filename, data)
Ejemplo n.º 25
0
 def remove_data(writer: UFOWriter, filename: str) -> None:
     """Remove the image data at filename within the store."""
     writer.removeImage(filename)
Ejemplo n.º 26
0
    def save(self):
        if self.save_to_default_layer:
            self.defcon_font.save()
        else:
            """
            XXX A real hack here XXX
            RoboFont did not support layers (UFO3 feature) until version 3.
            So in order to allow editing (in RF 1.x) UFOs that contain
            a processed glyphs layer, checkoutlinesufo generates UFOs that
            are structured like UFO3, but advertise themselves as UFO2.
            To achieve this, the code below hacks ufoLib to surgically save
            only the processed layer.
            This hack is only performed if the original UFO is format 2.

            NOTE: this is deprecated and will be removed from AFDKO.
            """
            writer = UFOWriter(
                self.defcon_font.path, formatVersion=self.ufo_format)
            writer.layerContents[
                PROCD_GLYPHS_LAYER_NAME] = PROCD_GLYPHS_LAYER
            layers = self.defcon_font.layers
            layer = layers[PROCD_GLYPHS_LAYER_NAME]

            if self.ufo_format == UFOFormatVersion.FORMAT_2_0:
                # Override the UFO's formatVersion. This disguises a UFO2 to
                # be seen as UFO3 by ufoLib, thus enabling it to write the
                # layer without raising an error.
                warn_txt = ("Using a ‘hybrid’ UFO2-as-UFO3 is deprecated and "
                            "will be removed from AFDKO by the end of 2020. "
                            "This behavior (hack) was primarily to support "
                            "older versions of RoboFont which did not support "
                            "UFO3/layers. RoboFont 3 now supports UFO3 so the "
                            "hack is no longer required. Please update your "
                            "toolchain as needed.")
                warnings.warn(warn_txt, category=FutureWarning)
                writer._formatVersion = UFOFormatVersion.FORMAT_3_0

            glyph_set = writer.getGlyphSet(
                layerName=PROCD_GLYPHS_LAYER_NAME, defaultLayer=False)
            writer.writeLayerContents(layers.layerOrder)

            if self.ufo_format == UFOFormatVersion.FORMAT_2_0:
                # Restore the UFO's formatVersion to the original value.
                # This makes the glif files be set to format 1 instead of 2.
                glyph_set.ufoFormatVersionTuple = UFOFormatVersion.FORMAT_2_0

            layer.save(glyph_set)

        if self.font_type == UFO_FONT_TYPE:
            ufotools.regenerate_glyph_hashes(self.ufo_font_hash_data)
            # Write the hash data, if it has changed.
            self.ufo_font_hash_data.close()

        elif self.font_type == TYPE1_FONT_TYPE:
            args = ['tx', '-t1']
            if self.font_format == 'PFB':
                args.append('-pfb')
            if not run_shell_command(
                    args + [self.temp_ufo_path, self.font_path]):
                raise FocusFontError('Failed to convert UFO font to Type 1.')

        else:
            temp_cff_path = get_temp_file_path()
            if not run_shell_command([
                    'tx', '-cff', '+S', '+b', '-std',
                    self.temp_ufo_path, temp_cff_path], suppress_output=True):
                raise FocusFontError('Failed to convert UFO font to CFF.')

            if self.font_type == CFF_FONT_TYPE:
                copy2(temp_cff_path, self.font_path)

            else:  # OTF_FONT_TYPE
                if not run_shell_command([
                        'sfntedit', '-a',
                        f'CFF={temp_cff_path}', self.font_path]):
                    raise FocusFontError('Failed to add CFF table to OTF.')
Ejemplo n.º 27
0
def main(args=None):
    from io import open

    options = parse_args(args)
    config = getConfig(options.config)
    svg_file = options.infile

    # Parse SVG to read the width, height attributes defined in it
    svgObj = parseSvg(svg_file)
    width = float(svgObj.attrib['width'].replace("px", " "))
    height = float(svgObj.attrib['height'].replace("px", " "))
    name = os.path.splitext(os.path.basename(svg_file))[0]
    ufo_font_path = config['font']['ufo']
    # Get the font metadata from UFO
    reader = UFOReader(ufo_font_path)
    writer = UFOWriter(ufo_font_path)

    infoObject = InfoObject()
    reader.readInfo(infoObject)
    fontName = config['font']['name']

    # Get the configuration for this svg
    try:
        svg_config = config['svgs'][name]
    except KeyError:
        print("\033[93mSkip: Configuration not found for svg : %r\033[0m" %
              name)
        return
    if 'unicode' in svg_config:
        unicodeVal = unicode_hex_list(svg_config['unicode'])
    else:
        unicodeVal = None
    glyphWidth = width + int(svg_config['left']) + int(svg_config['right'])
    if glyphWidth < 0:
        raise UFOLibError("Glyph %s has negative width." % name)

    contentsPlistPath = ufo_font_path + '/glyphs/contents.plist'
    try:
        with open(contentsPlistPath, "rb") as f:
            contentsPlist = load(f)
    except:
        raise UFOLibError("The file %s could not be read." % contentsPlistPath)

    glyph_name = svg_config['glyph_name']
    # Replace all capital letters with a following '_' to avoid file name clash in Windows
    glyph_file_name = re.sub(r'([A-Z]){1}', lambda pat: pat.group(1) + '_',
                             glyph_name) + '.glif'
    if glyph_name in contentsPlist:
        existing_glyph = True
    else:
        existing_glyph = False

    # Calculate the transformation to do
    transform = transform_list(config['font']['transform'])
    base = 0
    if 'base' in svg_config:
        base = int(svg_config['base'])
    transform[4] += int(svg_config['left'])  # X offset = left bearing
    transform[5] += height + base  # Y offset

    glif = svg2glif(svg_file,
                    name=svg_config['glyph_name'],
                    width=glyphWidth,
                    height=getattr(infoObject, 'unitsPerEm'),
                    unicodes=unicodeVal,
                    transform=transform,
                    version=config['font']['version'])

    if options.outfile is None:
        output_file = ufo_font_path + '/glyphs/' + glyph_file_name
    else:
        output_file = options.outfile
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(glif)

    print(
        "\033[94m[%s]\033[0m \033[92mConvert\033[0m %s -> %s \033[92m✔️\033[0m"
        % (fontName, name, output_file))

    # If this is a new glyph, add it to the UFO/glyphs/contents.plist
    if not existing_glyph:
        contentsPlist[glyph_name] = glyph_file_name
        writePlistAtomically(contentsPlist, contentsPlistPath)
        print(
            "\033[94m[%s]\033[0m \033[92mAdd\033[0m %s -> %s \033[92m✔️\033[0m"
            % (fontName, glyph_name, glyph_file_name))
        lib_obj = reader.readLib()
        lib_obj['public.glyphOrder'].append(glyph_name)
        writer.writeLib(lib_obj)
Ejemplo n.º 28
0
    def save(self):
        if self.save_to_default_layer:
            self.defcon_font.save()
        else:
            """
            XXX A real hack here XXX
            RoboFont did not support layers (UFO3 feature) until version 3.
            So in order to allow editing (in RF 1.x) UFOs that contain
            a processed glyphs layer, checkoutlinesufo generates UFOs that
            are structured like UFO3, but advertise themselves as UFO2.
            To achieve this, the code below hacks ufoLib to surgically save
            only the processed layer.
            This hack is only performed if the original UFO is format 2.
            """
            writer = UFOWriter(self.defcon_font.path,
                               formatVersion=self.ufo_format)
            writer.layerContents[PROCD_GLYPHS_LAYER_NAME] = PROCD_GLYPHS_LAYER
            layers = self.defcon_font.layers
            layer = layers[PROCD_GLYPHS_LAYER_NAME]

            if self.ufo_format == 2:
                # Override the UFO's formatVersion. This disguises a UFO2 to
                # be seen as UFO3 by ufoLib, thus enabling it to write the
                # layer without raising an error.
                writer._formatVersion = 3

            glyph_set = writer.getGlyphSet(layerName=PROCD_GLYPHS_LAYER_NAME,
                                           defaultLayer=False)
            writer.writeLayerContents(layers.layerOrder)

            if self.ufo_format == 2:
                # Restore the UFO's formatVersion to the original value.
                # This makes the glif files be set to format 1 instead of 2.
                glyph_set.ufoFormatVersion = self.ufo_format

            layer.save(glyph_set)

        if self.font_type == UFO_FONT_TYPE:
            ufotools.regenerate_glyph_hashes(self.ufo_font_hash_data)
            # Write the hash data, if it has changed.
            self.ufo_font_hash_data.close()

        elif self.font_type == TYPE1_FONT_TYPE:
            args = ['tx', '-t1']
            if self.font_format == 'PFB':
                args.append('-pfb')
            if not run_shell_command(args +
                                     [self.temp_ufo_path, self.font_path]):
                raise FocusFontError('Failed to convert UFO font to Type 1.')

        else:
            temp_cff_path = get_temp_file_path()
            if not run_shell_command([
                    'tx', '-cff', '+S', '+b', '-std', self.temp_ufo_path,
                    temp_cff_path
            ],
                                     suppress_output=True):
                raise FocusFontError('Failed to convert UFO font to CFF.')

            if self.font_type == CFF_FONT_TYPE:
                copy2(temp_cff_path, self.font_path)

            else:  # OTF_FONT_TYPE
                if not run_shell_command(
                    ['sfntedit', '-a', f'CFF={temp_cff_path}', self.font_path
                     ]):
                    raise FocusFontError('Failed to add CFF table to OTF.')