import os import shutil import unittest import tempfile from io import open from fontTools.ufoLib import UFOReader, UFOWriter from fontTools.ufoLib import plistlib from .testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion # the format version 1 lib.plist contains some data # that these tests shouldn't be concerned about. removeFromFormatVersion1Lib = [ "org.robofab.opentype.classes", "org.robofab.opentype.features", "org.robofab.opentype.featureorder", "org.robofab.postScriptHintData" ] class ConversionFunctionsTestCase(unittest.TestCase): def tearDown(self): path = self.getFontPath("TestFont1 (UFO1) converted.ufo") if os.path.exists(path): shutil.rmtree(path) path = self.getFontPath("TestFont1 (UFO2) converted.ufo") if os.path.exists(path): shutil.rmtree(path) def getFontPath(self, fileName): testdata = os.path.join(os.path.dirname(__file__), "testdata") return os.path.join(testdata, fileName) def compareFileStructures(self, path1, path2, expectedInfoData, testFeatures): # result metainfoPath1 = os.path.join(path1, "metainfo.plist") fontinfoPath1 = os.path.join(path1, "fontinfo.plist") kerningPath1 = os.path.join(path1, "kerning.plist") groupsPath1 = os.path.join(path1, "groups.plist") libPath1 = os.path.join(path1, "lib.plist") featuresPath1 = os.path.join(path1, "features.plist") glyphsPath1 = os.path.join(path1, "glyphs") glyphsPath1_contents = os.path.join(glyphsPath1, "contents.plist") glyphsPath1_A = os.path.join(glyphsPath1, "A_.glif") glyphsPath1_B = os.path.join(glyphsPath1, "B_.glif") # expected result metainfoPath2 = os.path.join(path2, "metainfo.plist") fontinfoPath2 = os.path.join(path2, "fontinfo.plist") kerningPath2 = os.path.join(path2, "kerning.plist") groupsPath2 = os.path.join(path2, "groups.plist") libPath2 = os.path.join(path2, "lib.plist") featuresPath2 = os.path.join(path2, "features.plist") glyphsPath2 = os.path.join(path2, "glyphs") glyphsPath2_contents = os.path.join(glyphsPath2, "contents.plist") glyphsPath2_A = os.path.join(glyphsPath2, "A_.glif") glyphsPath2_B = os.path.join(glyphsPath2, "B_.glif") # look for existence self.assertEqual(os.path.exists(metainfoPath1), True) self.assertEqual(os.path.exists(fontinfoPath1), True) self.assertEqual(os.path.exists(kerningPath1), True) self.assertEqual(os.path.exists(groupsPath1), True) self.assertEqual(os.path.exists(libPath1), True) self.assertEqual(os.path.exists(glyphsPath1), True) self.assertEqual(os.path.exists(glyphsPath1_contents), True) self.assertEqual(os.path.exists(glyphsPath1_A), True) self.assertEqual(os.path.exists(glyphsPath1_B), True) if testFeatures: self.assertEqual(os.path.exists(featuresPath1), True) # look for aggrement with open(metainfoPath1, "rb") as f: data1 = plistlib.load(f) with open(metainfoPath2, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) with open(fontinfoPath1, "rb") as f: data1 = plistlib.load(f) self.assertEqual(sorted(data1.items()), sorted(expectedInfoData.items())) with open(kerningPath1, "rb") as f: data1 = plistlib.load(f) with open(kerningPath2, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) with open(groupsPath1, "rb") as f: data1 = plistlib.load(f) with open(groupsPath2, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) with open(libPath1, "rb") as f: data1 = plistlib.load(f) with open(libPath2, "rb") as f: data2 = plistlib.load(f) if "UFO1" in libPath1: for key in removeFromFormatVersion1Lib: if key in data1: del data1[key] if "UFO1" in libPath2: for key in removeFromFormatVersion1Lib: if key in data2: del data2[key] self.assertEqual(data1, data2) with open(glyphsPath1_contents, "rb") as f: data1 = plistlib.load(f) with open(glyphsPath2_contents, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) with open(glyphsPath1_A, "rb") as f: data1 = plistlib.load(f) with open(glyphsPath2_A, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) with open(glyphsPath1_B, "rb") as f: data1 = plistlib.load(f) with open(glyphsPath2_B, "rb") as f: data2 = plistlib.load(f) self.assertEqual(data1, data2) # --------------------- # kerning up conversion # --------------------- class TestInfoObject: pass class KerningUpConversionTestCase(unittest.TestCase): expectedKerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2, ("X", "A"): 13, ("X", "public.kern2.CGroup"): 14 } expectedGroups = { "BGroup": ["B"], "CGroup": ["C", "Ccedilla"], "DGroup": ["D"], "public.kern1.BGroup": ["B"], "public.kern1.CGroup": ["C", "Ccedilla"], "public.kern2.CGroup": ["C", "Ccedilla"], "public.kern2.DGroup": ["D"], "Not A Kerning Group" : ["A"], "X": ["X", "X.sc"] } def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeUFO(self, formatVersion): self.clearUFO() if not os.path.exists(self.ufoPath): os.mkdir(self.ufoPath) # glyphs glyphsPath = os.path.join(self.ufoPath, "glyphs") if not os.path.exists(glyphsPath): os.mkdir(glyphsPath) glyphFile = "X_.glif" glyphsContents = dict(X=glyphFile) path = os.path.join(glyphsPath, "contents.plist") with open(path, "wb") as f: plistlib.dump(glyphsContents, f) path = os.path.join(glyphsPath, glyphFile) with open(path, "w") as f: f.write('\n') # metainfo.plist metaInfo = dict(creator="test", formatVersion=formatVersion) path = os.path.join(self.ufoPath, "metainfo.plist") with open(path, "wb") as f: plistlib.dump(metaInfo, f) # kerning kerning = { "A" : { "A" : 1, "B" : 2, "CGroup" : 3, "DGroup" : 4 }, "BGroup" : { "A" : 5, "B" : 6, "CGroup" : 7, "DGroup" : 8 }, "CGroup" : { "A" : 9, "B" : 10, "CGroup" : 11, "DGroup" : 12 }, "X": { "A" : 13, "CGroup" : 14 } } path = os.path.join(self.ufoPath, "kerning.plist") with open(path, "wb") as f: plistlib.dump(kerning, f) # groups groups = { "BGroup" : ["B"], "CGroup" : ["C", "Ccedilla"], "DGroup" : ["D"], "Not A Kerning Group" : ["A"], "X" : ["X", "X.sc"] # a group with a name that is also a glyph name } path = os.path.join(self.ufoPath, "groups.plist") with open(path, "wb") as f: plistlib.dump(groups, f) # font info fontInfo = { "familyName" : "Test" } path = os.path.join(self.ufoPath, "fontinfo.plist") with open(path, "wb") as f: plistlib.dump(fontInfo, f) def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) def testUFO1(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath, validate=True) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info) def testUFO2(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath, validate=True) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info) class KerningDownConversionTestCase(unittest.TestCase): expectedKerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2 } groups = { "BGroup": ["B"], "CGroup": ["C"], "DGroup": ["D"], "public.kern1.BGroup": ["B"], "public.kern1.CGroup": ["C", "Ccedilla"], "public.kern2.CGroup": ["C", "Ccedilla"], "public.kern2.DGroup": ["D"], "Not A Kerning Group" : ["A"] } expectedWrittenGroups = { "BGroup": ["B"], "CGroup": ["C", "Ccedilla"], "DGroup": ["D"], "Not A Kerning Group" : ["A"] } kerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2 } expectedWrittenKerning = { "BGroup" : { "CGroup" : 7, "DGroup" : 8, "A" : 5, "B" : 6 }, "CGroup" : { "CGroup" : 11, "DGroup" : 12, "A" : 9, "B" : 10 }, "A" : { "CGroup" : 3, "DGroup" : 4, "A" : 1, "B" : 2 } } downConversionMapping = { "side1" : { "BGroup" : "public.kern1.BGroup", "CGroup" : "public.kern1.CGroup" }, "side2" : { "CGroup" : "public.kern2.CGroup", "DGroup" : "public.kern2.DGroup" } } def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def tearDownUFO(self): shutil.rmtree(self.dstDir) 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()