def run_ufolib_import_validation(self):
        """
        ufoLib GlyphSet.readLayerInfo method performs validations of layerinfo.plist file(s)
        :return: (list) list of test failure Result objects
        """
        ss = StdStreamer(self.ufopath)
        for glyphs_dir in self.ufoobj.glyphsdir_list:
            res = Result(glyphs_dir[1])
            rel_dir_path = os.path.join(self.ufopath, glyphs_dir[1])

            try:
                gs = GlyphSet(
                    rel_dir_path, ufoFormatVersion=self.ufoversion, validateRead=True
                )
                gs.readLayerInfo(self.layerinfo_obj)
                res.test_failed = False
                ss.stream_result(res)
            except Exception as e:
                res.test_failed = True
                res.test_long_stdstream_string = (
                    "layerinfo.plist in "
                    + rel_dir_path
                    + " failed ufoLib import test with error: "
                    + str(e)
                )
                self.test_fail_list.append(res)
                ss.stream_result(res)
        return self.test_fail_list
Exemple #2
0
    def read(cls,
             name: str,
             glyphSet: GlyphSet,
             lazy: bool = True,
             default: bool = False) -> Layer:
        """Instantiates a Layer object from a
        :class:`fontTools.ufoLib.glifLib.GlyphSet`.

        Args:
            name: The name of the layer.
            glyphSet: The GlyphSet object to read from.
            lazy: If True, load glyphs as they are accessed. If False, load everything
                up front.
        """
        glyphNames = glyphSet.keys()
        glyphs: dict[str, Glyph]
        if lazy:
            glyphs = {gn: _GLYPH_NOT_LOADED for gn in glyphNames}
        else:
            glyphs = {}
            for glyphName in glyphNames:
                glyph = Glyph(glyphName)
                glyphSet.readGlyph(glyphName, glyph, glyph.getPointPen())
                glyphs[glyphName] = glyph
        self = cls(name, glyphs, default=default)
        if lazy:
            self._glyphSet = glyphSet
        glyphSet.readLayerInfo(self)
        return self
Exemple #3
0
 def testReverseContents(self):
     gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
     d = {}
     for k, v in gset.getReverseContents().items():
         d[v] = k
     org = {}
     for k, v in gset.contents.items():
         org[k] = v.lower()
     self.assertEqual(d, org)
Exemple #4
0
 def testGetUnicodes(self):
     src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
     unicodes = src.getUnicodes()
     for glyphName in src.keys():
         g = src[glyphName]
         g.drawPoints(None)  # load attrs
         if not hasattr(g, "unicodes"):
             self.assertEqual(unicodes[glyphName], [])
         else:
             self.assertEqual(g.unicodes, unicodes[glyphName])
def run_all_glif_validations(ufoobj):
    glyphsdir_path_list = ufoobj.get_glyphsdir_path_list()
    ufoversion = ufoobj.get_ufo_version()
    ss = StdStreamer(ufoobj.ufopath)
    test_error_list = []
    for (
        glyphsdir
    ) in glyphsdir_path_list:  # for each directory that containts .glif files
        print(" ")
        sys.stdout.write(" - " + glyphsdir + "  ")
        sys.stdout.flush()
        res = Result(glyphsdir)
        try:
            gs = GlyphSet(
                glyphsdir, ufoFormatVersion=ufoversion, validateRead=True
            )  # create a ufoLib GlyphSet
            # do not report success for this, previous testing has passed this
        except Exception as e:
            res.test_failed = True
            res.test_long_stdstream_string = (
                " Failed to read glif file paths from "
                + glyphsdir
                + ". Error: "
                + str(e)
            )
            ss.stream_result(res)
            test_error_list.append(res)
            break  # break out loop as it was not possible to read the GlyphSet for this directory, gs not instantiated

        glif_count = 0  # reset glyphs directory .glif file counter
        for (
            glyphname
        ) in (
            gs.contents.keys()
        ):  # for each .glif file (read from glyph name in glyph set contents dict)
            res = Result(gs.contents[glyphname])
            try:
                go = GlifObj()
                gs.readGlyph(
                    glyphname, glyphObject=go
                )  # read the glif file and perform ufoLib validations, requires the glyphObject for validations
                res.test_failed = False
                ss.stream_result(res)
                glif_count += 1
            except Exception as e:
                res.test_failed = True
                filename = os.path.join(glyphsdir, glyphNameToFileName(glyphname, None))
                res.test_long_stdstream_string = '{} (glyph "{}"): Test failed with error: {}'.format(
                    filename, glyphname, e
                )
                ss.stream_result(res)
                test_error_list.append(res)
                glif_count += 1
        print("   " + str(glif_count) + " .glif tests completed")
    return test_error_list
Exemple #6
0
 def testRoundTrip(self):
     import difflib
     srcDir = GLYPHSETDIR
     dstDir = self.dstDir
     src = GlyphSet(srcDir,
                    ufoFormatVersion=2,
                    validateRead=True,
                    validateWrite=True)
     dst = GlyphSet(dstDir,
                    ufoFormatVersion=2,
                    validateRead=True,
                    validateWrite=True)
     for glyphName in src.keys():
         g = src[glyphName]
         g.drawPoints(None)  # load attrs
         dst.writeGlyph(glyphName, g, g.drawPoints)
     # compare raw file data:
     for glyphName in sorted(src.keys()):
         fileName = src.contents[glyphName]
         with open(os.path.join(srcDir, fileName), "r") as f:
             org = f.read()
         with open(os.path.join(dstDir, fileName), "r") as f:
             new = f.read()
         added = []
         removed = []
         for line in difflib.unified_diff(org.split("\n"), new.split("\n")):
             if line.startswith("+ "):
                 added.append(line[1:])
             elif line.startswith("- "):
                 removed.append(line[1:])
         self.assertEqual(
             added, removed,
             "%s.glif file differs after round tripping" % glyphName)
Exemple #7
0
    def write(self, glyphSet: GlyphSet, saveAs: bool = True) -> None:
        """Write Layer to a :class:`fontTools.ufoLib.glifLib.GlyphSet`.

        Args:
            glyphSet: The GlyphSet object 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.
        """
        glyphs = self._glyphs
        if not saveAs:
            for name in set(glyphSet.contents).difference(glyphs):
                glyphSet.deleteGlyph(name)
        for name, glyph in glyphs.items():
            if glyph is _GLYPH_NOT_LOADED:
                if saveAs:
                    glyph = self.loadGlyph(name)
                else:
                    continue
            _prune_object_libs(glyph.lib, _fetch_glyph_identifiers(glyph))
            glyphSet.writeGlyph(name,
                                glyphObject=glyph,
                                drawPointsFunc=glyph.drawPoints)
        glyphSet.writeContents()
        glyphSet.writeLayerInfo(self)
        if saveAs:
            # all glyphs are loaded by now, no need to keep ref to glyphSet
            self._glyphSet = None
 def run_ufolib_import_validation(self):
     """
     ufoLib GlyphSet instantiation validates the contents.plist file
     :return: (list) list of test failure Result objects
     """
     ss = StdStreamer(self.ufopath)
     for glyphs_dir in self.ufoobj.glyphsdir_list:
         res = Result(glyphs_dir[1])
         rel_dir_path = os.path.join(self.ufopath, glyphs_dir[1])
         try:
             # read contents.plist with ufoLib as GlyphSet instantiation
             # the ufoLib library performs type validations on values on read
             # glyphs_dir_list is a list of lists mapped to glyphs dir name, glyphs dir path
             GlyphSet(
                 rel_dir_path, ufoFormatVersion=self.ufoversion, validateRead=True
             )  # test for raised exceptions
             res.test_failed = False
             ss.stream_result(res)
         except Exception as e:
             res.test_failed = True
             res.exit_failure = True  # mandatory file
             res.test_long_stdstream_string = (
                 "contents.plist in "
                 + rel_dir_path
                 + " failed ufoLib import test with error: "
                 + str(e)
             )
             self.test_fail_list.append(res)
             ss.stream_result(res)
     return self.test_fail_list
Exemple #9
0
 def testContentsExist(self):
     with self.assertRaises(GlifLibError):
         GlyphSet(
             self.dstDir,
             ufoFormatVersion=2,
             validateRead=True,
             validateWrite=True,
             expectContentsFile=True,
         )
Exemple #10
0
def test_GlyphSet_writeGlyph_formatVersion(tmp_path):
    src = GlyphSet(GLYPHSETDIR)
    dst = GlyphSet(tmp_path, ufoFormatVersion=(2, 0))
    glyph = src["A"]

    # no explicit formatVersion passed: use the more recent GLIF formatVersion
    # that is supported by given ufoFormatVersion (GLIF 1 for UFO 2)
    dst.writeGlyph("A", glyph)
    glif = dst.getGLIF("A")
    assert b'format="1"' in glif
    assert b'formatMinor' not in glif  # omitted when 0

    # explicit, unknown formatVersion
    with pytest.raises(UnsupportedGLIFFormat):
        dst.writeGlyph("A", glyph, formatVersion=(0, 0))

    # explicit, known formatVersion but unsupported by given ufoFormatVersion
    with pytest.raises(
            UnsupportedGLIFFormat,
            match="Unsupported GLIF format version .*for UFO format version",
    ):
        dst.writeGlyph("A", glyph, formatVersion=(2, 0))
	def testCustomFileNamingScheme(self):
		def myGlyphNameToFileName(glyphName, glyphSet):
			return "prefix" + glyphNameToFileName(glyphName, glyphSet)
		src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
		dst = GlyphSet(self.dstDir, myGlyphNameToFileName, validateRead=True, validateWrite=True)
		for glyphName in src.keys():
			g = src[glyphName]
			g.drawPoints(None)  # load attrs
			dst.writeGlyph(glyphName, g, g.drawPoints)
		d = {}
		for k, v in src.contents.items():
			d[k] = "prefix" + v
		self.assertEqual(d, dst.contents)
Exemple #12
0
    def run_ufolib_import_validation(self):
        """
        ufoLib GlyphSet instantiation validates the contents.plist file
        :return: (list) list of test failure Result objects
        """
        ss = StdStreamer(self.ufopath)
        for glyphs_dir in self.ufoobj.glyphsdir_list:
            res = Result(glyphs_dir[1])
            rel_dir_path = Path(self.ufopath, glyphs_dir[1])
            try:
                # read contents.plist with ufoLib as GlyphSet instantiation
                # the ufoLib library performs type validations on values on read
                # glyphs_dir_list is a list of lists mapped to glyphs dir name,
                # glyphs dir path
                gs = GlyphSet(rel_dir_path,
                              ufoFormatVersion=self.ufoversion,
                              validateRead=True)  # test for raised exceptions
                res.test_failed = False

                # Check for unlisted files if contents.plist exists. It is mandated by
                # the spec, but if it does not exist, glifLib will shrug and say the
                # glyph set was empty.
                if (rel_dir_path / self.testfile).exists():
                    files = set(gs.contents.values())
                    files.update(("contents.plist", "layerinfo.plist"))
                    files_actually = set(
                        str(p.relative_to(rel_dir_path))
                        for p in rel_dir_path.glob("**/*"))
                    unlisted_files = files_actually - files
                    if unlisted_files:
                        res.test_failed = True
                        res.exit_failure = True  # sacrilege
                        res.test_long_stdstream_string = (
                            f"{str(rel_dir_path)} contains rogue files not listed "
                            f"in contents.plist: {', '.join(unlisted_files)}")
                        self.test_fail_list.append(res)

                ss.stream_result(res)
            except Exception as e:
                res.test_failed = True
                res.exit_failure = True  # mandatory file
                res.test_long_stdstream_string = (
                    "contents.plist in " + str(rel_dir_path) +
                    " failed ufoLib import test with error: " + str(e))
                self.test_fail_list.append(res)
                ss.stream_result(res)
        return self.test_fail_list
Exemple #13
0
    def test_conflicting_case_insensitive_file_names(self, tmp_path):
        src = GlyphSet(GLYPHSETDIR)
        dst = GlyphSet(tmp_path)
        glyph = src["a"]

        dst.writeGlyph("a", glyph)
        dst.writeGlyph("A", glyph)
        dst.writeGlyph("a_", glyph)
        dst.writeGlyph("A_", glyph)
        dst.writeGlyph("i_j", glyph)

        assert dst.contents == {
            'a': 'a.glif',
            'A': 'A_.glif',
            'a_': 'a_000000000000001.glif',
            'A_': 'A__.glif',
            'i_j': 'i_j.glif',
        }

        # make sure filenames are unique even on case-insensitive filesystems
        assert len({fileName.lower()
                    for fileName in dst.contents.values()}) == 5
#

from fontTools.ufoLib.glifLib import GlyphSet

# this path must be to the `glyphs` subdirectory of the root UFO directory
GLYPH_DIR = "../ufo_source/GenericSans-GreenHighlight.ufo/glyphs"


# dummy object that is used to set glyph-specific attributes
# by fontTools.ufoLib.glifLib.GlyphObject.readGlyph() method
class GlyphObj(object):
    def __init__(self):
        pass


gs = GlyphSet(GLYPH_DIR)
for glif in gs.contents:
    go = GlyphObj()
    gs.readGlyph(glif, glyphObject=go)

    # define useful glyph data fields through read of the glyph object
    # Includes:
    # - name
    # - unicode(s)
    # - glyph highlight color (defined by designer in font editor)
    #      - defined in RGBA format
    #      - defined in sRGB color space (https://en.wikipedia.org/wiki/SRGB)
    #      - returns comma-separated string of these four integer or float values in set [0.0,1.0]
    #      - defined as None if no highlighting used on glyph
    # - last write time/date
    # - note (glyph-specific free text entered by designer in editor)
Exemple #15
0
 def testReverseContents2(self):
     src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
     dst = GlyphSet(self.dstDir, validateRead=True, validateWrite=True)
     dstMap = dst.getReverseContents()
     self.assertEqual(dstMap, {})
     for glyphName in src.keys():
         g = src[glyphName]
         g.drawPoints(None)  # load attrs
         dst.writeGlyph(glyphName, g, g.drawPoints)
     self.assertNotEqual(dstMap, {})
     srcMap = dict(src.getReverseContents())  # copy
     self.assertEqual(dstMap, srcMap)
     del srcMap["a.glif"]
     dst.deleteGlyph("a")
     self.assertEqual(dstMap, srcMap)
Exemple #16
0
    def exportButtonCallback(self, sender):

        f = CurrentFont()

        if f is None:
            return

        if self.verbose:
            print('exporting selected glyphs back to their sources...\n')

        for glyphName in f.selectedGlyphNames:

            if self.importMode == 1:

                glyph = f[glyphName].getLayer('foreground')

                if self.glyphSetPathKey not in glyph.lib:
                    continue

                glyphsFolder = glyph.lib[self.glyphSetPathKey]
                ufoName = splitall(glyphsFolder)[-2]
                glyphNameExtension = os.path.splitext(ufoName)[0]
                glyphNameParts = glyphName.split('.')

                if not (len(glyphNameParts) > 1 and glyphNameParts[-1] == os.path.splitext(ufoName)[0]):
                    print(f'{glyphName} does not have the expected glyph name extension, skipping...')
                    continue

                if self.verbose:
                    print(f'\twriting {glyphName} to {ufoName}...')

                outputGlyphName = '.'.join(glyphNameParts[:-1])
                glyphSet = GlyphSet(glyphsFolder, validateWrite=True)
                glyphSet.writeGlyph(outputGlyphName, glyph.naked(), glyph.drawPoints)
                glyphSet.writeContents()

            else:

                for layerName in f.layerOrder:
                    glyph = f[glyphName].getLayer(layerName)

                    if self.glyphSetPathKey not in glyph.lib:
                        continue

                    glyphsFolder = glyph.lib[self.glyphSetPathKey]

                    # mode 0
                    if not '.ufoz' in glyphsFolder:
                        glyphSet = GlyphSet(glyphsFolder, validateWrite=True)
                        ufoName = splitall(glyphsFolder)[-2]
                        if self.verbose:
                            print(f'\twriting {glyphName} to {ufoName}...')
                        glyphSet.writeGlyph(glyphName, glyph.naked(), glyph.drawPoints)
                        glyphSet.writeContents()

                    # mode 2
                    else:
                        ufoPath = os.path.dirname(glyphsFolder)
                        ufoName = splitall(ufoPath)[-1]
                        dstFont = OpenFont(ufoPath, showInterface=False)
                        if self.verbose:
                            print(f'\twriting {glyphName} to {ufoName}...')
                        dstFont.insertGlyph(glyph, name=glyph.name)
                        dstFont.save()
                        dstFont.close()

        if self.verbose:
            print()
            print('...done.\n')
Exemple #17
0
 def testRebuildContents(self):
     gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
     contents = gset.contents
     gset.rebuildContents()
     self.assertEqual(contents, gset.contents)
Exemple #18
0
import os
from fontTools.ufoLib.glifLib import GlyphSet
import pkg_resources

DATADIR = os.path.join(os.path.dirname(__file__), 'data')
CUBIC_GLYPHS = GlyphSet(os.path.join(DATADIR, 'cubic'))
QUAD_GLYPHS = GlyphSet(os.path.join(DATADIR, 'quadratic'))

import unittest
# Python 3 renamed 'assertRaisesRegexp' to 'assertRaisesRegex', and fires
# deprecation warnings if a program uses the old name.
if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
    unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
Exemple #19
0
def test_GlyphSet_unsupported_ufoFormatVersion(tmp_path, caplog):
    with pytest.raises(UnsupportedUFOFormat):
        GlyphSet(tmp_path, ufoFormatVersion=0)
    with pytest.raises(UnsupportedUFOFormat):
        GlyphSet(tmp_path, ufoFormatVersion=(0, 1))