• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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