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)
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)
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)
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
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
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)
def testContentsExist(self): with self.assertRaises(GlifLibError): GlyphSet( self.dstDir, ufoFormatVersion=2, validateRead=True, validateWrite=True, expectContentsFile=True, )
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
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 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
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
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
# 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)
def testRebuildContents(self): gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) contents = gset.contents gset.rebuildContents() self.assertEqual(contents, gset.contents)
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))
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')