1import os 2import shutil 3import unittest 4import tempfile 5from io import open 6from fontTools.ufoLib import UFOReader, UFOWriter 7from fontTools.ufoLib import plistlib 8from .testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion 9 10 11# the format version 1 lib.plist contains some data 12# that these tests shouldn't be concerned about. 13removeFromFormatVersion1Lib = [ 14 "org.robofab.opentype.classes", 15 "org.robofab.opentype.features", 16 "org.robofab.opentype.featureorder", 17 "org.robofab.postScriptHintData" 18] 19 20 21class ConversionFunctionsTestCase(unittest.TestCase): 22 23 def tearDown(self): 24 path = self.getFontPath("TestFont1 (UFO1) converted.ufo") 25 if os.path.exists(path): 26 shutil.rmtree(path) 27 path = self.getFontPath("TestFont1 (UFO2) converted.ufo") 28 if os.path.exists(path): 29 shutil.rmtree(path) 30 31 def getFontPath(self, fileName): 32 testdata = os.path.join(os.path.dirname(__file__), "testdata") 33 return os.path.join(testdata, fileName) 34 35 def compareFileStructures(self, path1, path2, expectedInfoData, testFeatures): 36 # result 37 metainfoPath1 = os.path.join(path1, "metainfo.plist") 38 fontinfoPath1 = os.path.join(path1, "fontinfo.plist") 39 kerningPath1 = os.path.join(path1, "kerning.plist") 40 groupsPath1 = os.path.join(path1, "groups.plist") 41 libPath1 = os.path.join(path1, "lib.plist") 42 featuresPath1 = os.path.join(path1, "features.plist") 43 glyphsPath1 = os.path.join(path1, "glyphs") 44 glyphsPath1_contents = os.path.join(glyphsPath1, "contents.plist") 45 glyphsPath1_A = os.path.join(glyphsPath1, "A_.glif") 46 glyphsPath1_B = os.path.join(glyphsPath1, "B_.glif") 47 # expected result 48 metainfoPath2 = os.path.join(path2, "metainfo.plist") 49 fontinfoPath2 = os.path.join(path2, "fontinfo.plist") 50 kerningPath2 = os.path.join(path2, "kerning.plist") 51 groupsPath2 = os.path.join(path2, "groups.plist") 52 libPath2 = os.path.join(path2, "lib.plist") 53 featuresPath2 = os.path.join(path2, "features.plist") 54 glyphsPath2 = os.path.join(path2, "glyphs") 55 glyphsPath2_contents = os.path.join(glyphsPath2, "contents.plist") 56 glyphsPath2_A = os.path.join(glyphsPath2, "A_.glif") 57 glyphsPath2_B = os.path.join(glyphsPath2, "B_.glif") 58 # look for existence 59 self.assertEqual(os.path.exists(metainfoPath1), True) 60 self.assertEqual(os.path.exists(fontinfoPath1), True) 61 self.assertEqual(os.path.exists(kerningPath1), True) 62 self.assertEqual(os.path.exists(groupsPath1), True) 63 self.assertEqual(os.path.exists(libPath1), True) 64 self.assertEqual(os.path.exists(glyphsPath1), True) 65 self.assertEqual(os.path.exists(glyphsPath1_contents), True) 66 self.assertEqual(os.path.exists(glyphsPath1_A), True) 67 self.assertEqual(os.path.exists(glyphsPath1_B), True) 68 if testFeatures: 69 self.assertEqual(os.path.exists(featuresPath1), True) 70 # look for aggrement 71 with open(metainfoPath1, "rb") as f: 72 data1 = plistlib.load(f) 73 with open(metainfoPath2, "rb") as f: 74 data2 = plistlib.load(f) 75 self.assertEqual(data1, data2) 76 with open(fontinfoPath1, "rb") as f: 77 data1 = plistlib.load(f) 78 self.assertEqual(sorted(data1.items()), sorted(expectedInfoData.items())) 79 with open(kerningPath1, "rb") as f: 80 data1 = plistlib.load(f) 81 with open(kerningPath2, "rb") as f: 82 data2 = plistlib.load(f) 83 self.assertEqual(data1, data2) 84 with open(groupsPath1, "rb") as f: 85 data1 = plistlib.load(f) 86 with open(groupsPath2, "rb") as f: 87 data2 = plistlib.load(f) 88 self.assertEqual(data1, data2) 89 with open(libPath1, "rb") as f: 90 data1 = plistlib.load(f) 91 with open(libPath2, "rb") as f: 92 data2 = plistlib.load(f) 93 if "UFO1" in libPath1: 94 for key in removeFromFormatVersion1Lib: 95 if key in data1: 96 del data1[key] 97 if "UFO1" in libPath2: 98 for key in removeFromFormatVersion1Lib: 99 if key in data2: 100 del data2[key] 101 self.assertEqual(data1, data2) 102 with open(glyphsPath1_contents, "rb") as f: 103 data1 = plistlib.load(f) 104 with open(glyphsPath2_contents, "rb") as f: 105 data2 = plistlib.load(f) 106 self.assertEqual(data1, data2) 107 with open(glyphsPath1_A, "rb") as f: 108 data1 = plistlib.load(f) 109 with open(glyphsPath2_A, "rb") as f: 110 data2 = plistlib.load(f) 111 self.assertEqual(data1, data2) 112 with open(glyphsPath1_B, "rb") as f: 113 data1 = plistlib.load(f) 114 with open(glyphsPath2_B, "rb") as f: 115 data2 = plistlib.load(f) 116 self.assertEqual(data1, data2) 117 118 119# --------------------- 120# kerning up conversion 121# --------------------- 122 123class TestInfoObject: pass 124 125 126class KerningUpConversionTestCase(unittest.TestCase): 127 128 expectedKerning = { 129 ("public.kern1.BGroup", "public.kern2.CGroup"): 7, 130 ("public.kern1.BGroup", "public.kern2.DGroup"): 8, 131 ("public.kern1.BGroup", "A"): 5, 132 ("public.kern1.BGroup", "B"): 6, 133 ("public.kern1.CGroup", "public.kern2.CGroup"): 11, 134 ("public.kern1.CGroup", "public.kern2.DGroup"): 12, 135 ("public.kern1.CGroup", "A"): 9, 136 ("public.kern1.CGroup", "B"): 10, 137 ("A", "public.kern2.CGroup"): 3, 138 ("A", "public.kern2.DGroup"): 4, 139 ("A", "A"): 1, 140 ("A", "B"): 2, 141 ("X", "A"): 13, 142 ("X", "public.kern2.CGroup"): 14 143 } 144 145 expectedGroups = { 146 "BGroup": ["B"], 147 "CGroup": ["C", "Ccedilla"], 148 "DGroup": ["D"], 149 "public.kern1.BGroup": ["B"], 150 "public.kern1.CGroup": ["C", "Ccedilla"], 151 "public.kern2.CGroup": ["C", "Ccedilla"], 152 "public.kern2.DGroup": ["D"], 153 "Not A Kerning Group" : ["A"], 154 "X": ["X", "X.sc"] 155 } 156 157 def setUp(self): 158 self.tempDir = tempfile.mktemp() 159 os.mkdir(self.tempDir) 160 self.ufoPath = os.path.join(self.tempDir, "test.ufo") 161 162 def tearDown(self): 163 shutil.rmtree(self.tempDir) 164 165 def makeUFO(self, formatVersion): 166 self.clearUFO() 167 if not os.path.exists(self.ufoPath): 168 os.mkdir(self.ufoPath) 169 170 # glyphs 171 glyphsPath = os.path.join(self.ufoPath, "glyphs") 172 if not os.path.exists(glyphsPath): 173 os.mkdir(glyphsPath) 174 glyphFile = "X_.glif" 175 glyphsContents = dict(X=glyphFile) 176 path = os.path.join(glyphsPath, "contents.plist") 177 with open(path, "wb") as f: 178 plistlib.dump(glyphsContents, f) 179 path = os.path.join(glyphsPath, glyphFile) 180 with open(path, "w") as f: 181 f.write('<?xml version="1.0" encoding="UTF-8"?>\n') 182 183 # metainfo.plist 184 metaInfo = dict(creator="test", formatVersion=formatVersion) 185 path = os.path.join(self.ufoPath, "metainfo.plist") 186 with open(path, "wb") as f: 187 plistlib.dump(metaInfo, f) 188 # kerning 189 kerning = { 190 "A" : { 191 "A" : 1, 192 "B" : 2, 193 "CGroup" : 3, 194 "DGroup" : 4 195 }, 196 "BGroup" : { 197 "A" : 5, 198 "B" : 6, 199 "CGroup" : 7, 200 "DGroup" : 8 201 }, 202 "CGroup" : { 203 "A" : 9, 204 "B" : 10, 205 "CGroup" : 11, 206 "DGroup" : 12 207 }, 208 "X": { 209 "A" : 13, 210 "CGroup" : 14 211 } 212 } 213 path = os.path.join(self.ufoPath, "kerning.plist") 214 with open(path, "wb") as f: 215 plistlib.dump(kerning, f) 216 # groups 217 groups = { 218 "BGroup" : ["B"], 219 "CGroup" : ["C", "Ccedilla"], 220 "DGroup" : ["D"], 221 "Not A Kerning Group" : ["A"], 222 "X" : ["X", "X.sc"] # a group with a name that is also a glyph name 223 } 224 path = os.path.join(self.ufoPath, "groups.plist") 225 with open(path, "wb") as f: 226 plistlib.dump(groups, f) 227 # font info 228 fontInfo = { 229 "familyName" : "Test" 230 } 231 path = os.path.join(self.ufoPath, "fontinfo.plist") 232 with open(path, "wb") as f: 233 plistlib.dump(fontInfo, f) 234 235 def clearUFO(self): 236 if os.path.exists(self.ufoPath): 237 shutil.rmtree(self.ufoPath) 238 239 def testUFO1(self): 240 self.makeUFO(formatVersion=2) 241 reader = UFOReader(self.ufoPath, validate=True) 242 kerning = reader.readKerning() 243 self.assertEqual(self.expectedKerning, kerning) 244 groups = reader.readGroups() 245 self.assertEqual(self.expectedGroups, groups) 246 info = TestInfoObject() 247 reader.readInfo(info) 248 249 def testUFO2(self): 250 self.makeUFO(formatVersion=2) 251 reader = UFOReader(self.ufoPath, validate=True) 252 kerning = reader.readKerning() 253 self.assertEqual(self.expectedKerning, kerning) 254 groups = reader.readGroups() 255 self.assertEqual(self.expectedGroups, groups) 256 info = TestInfoObject() 257 reader.readInfo(info) 258 259 260class KerningDownConversionTestCase(unittest.TestCase): 261 262 expectedKerning = { 263 ("public.kern1.BGroup", "public.kern2.CGroup"): 7, 264 ("public.kern1.BGroup", "public.kern2.DGroup"): 8, 265 ("public.kern1.BGroup", "A"): 5, 266 ("public.kern1.BGroup", "B"): 6, 267 ("public.kern1.CGroup", "public.kern2.CGroup"): 11, 268 ("public.kern1.CGroup", "public.kern2.DGroup"): 12, 269 ("public.kern1.CGroup", "A"): 9, 270 ("public.kern1.CGroup", "B"): 10, 271 ("A", "public.kern2.CGroup"): 3, 272 ("A", "public.kern2.DGroup"): 4, 273 ("A", "A"): 1, 274 ("A", "B"): 2 275 } 276 277 groups = { 278 "BGroup": ["B"], 279 "CGroup": ["C"], 280 "DGroup": ["D"], 281 "public.kern1.BGroup": ["B"], 282 "public.kern1.CGroup": ["C", "Ccedilla"], 283 "public.kern2.CGroup": ["C", "Ccedilla"], 284 "public.kern2.DGroup": ["D"], 285 "Not A Kerning Group" : ["A"] 286 } 287 expectedWrittenGroups = { 288 "BGroup": ["B"], 289 "CGroup": ["C", "Ccedilla"], 290 "DGroup": ["D"], 291 "Not A Kerning Group" : ["A"] 292 } 293 294 kerning = { 295 ("public.kern1.BGroup", "public.kern2.CGroup"): 7, 296 ("public.kern1.BGroup", "public.kern2.DGroup"): 8, 297 ("public.kern1.BGroup", "A"): 5, 298 ("public.kern1.BGroup", "B"): 6, 299 ("public.kern1.CGroup", "public.kern2.CGroup"): 11, 300 ("public.kern1.CGroup", "public.kern2.DGroup"): 12, 301 ("public.kern1.CGroup", "A"): 9, 302 ("public.kern1.CGroup", "B"): 10, 303 ("A", "public.kern2.CGroup"): 3, 304 ("A", "public.kern2.DGroup"): 4, 305 ("A", "A"): 1, 306 ("A", "B"): 2 307 } 308 expectedWrittenKerning = { 309 "BGroup" : { 310 "CGroup" : 7, 311 "DGroup" : 8, 312 "A" : 5, 313 "B" : 6 314 }, 315 "CGroup" : { 316 "CGroup" : 11, 317 "DGroup" : 12, 318 "A" : 9, 319 "B" : 10 320 }, 321 "A" : { 322 "CGroup" : 3, 323 "DGroup" : 4, 324 "A" : 1, 325 "B" : 2 326 } 327 } 328 329 330 downConversionMapping = { 331 "side1" : { 332 "BGroup" : "public.kern1.BGroup", 333 "CGroup" : "public.kern1.CGroup" 334 }, 335 "side2" : { 336 "CGroup" : "public.kern2.CGroup", 337 "DGroup" : "public.kern2.DGroup" 338 } 339 } 340 341 def setUp(self): 342 self.tempDir = tempfile.mktemp() 343 os.mkdir(self.tempDir) 344 self.dstDir = os.path.join(self.tempDir, "test.ufo") 345 346 def tearDown(self): 347 shutil.rmtree(self.tempDir) 348 349 def tearDownUFO(self): 350 shutil.rmtree(self.dstDir) 351 352 def testWrite(self): 353 writer = UFOWriter(self.dstDir, formatVersion=2) 354 writer.setKerningGroupConversionRenameMaps(self.downConversionMapping) 355 writer.writeKerning(self.kerning) 356 writer.writeGroups(self.groups) 357 # test groups 358 path = os.path.join(self.dstDir, "groups.plist") 359 with open(path, "rb") as f: 360 writtenGroups = plistlib.load(f) 361 self.assertEqual(writtenGroups, self.expectedWrittenGroups) 362 # test kerning 363 path = os.path.join(self.dstDir, "kerning.plist") 364 with open(path, "rb") as f: 365 writtenKerning = plistlib.load(f) 366 self.assertEqual(writtenKerning, self.expectedWrittenKerning) 367 self.tearDownUFO() 368