1*e1fe3e4aSElliott Hughesimport logging 2*e1fe3e4aSElliott Hughesimport os 3*e1fe3e4aSElliott Hughesimport tempfile 4*e1fe3e4aSElliott Hughesimport shutil 5*e1fe3e4aSElliott Hughesimport unittest 6*e1fe3e4aSElliott Hughesfrom pathlib import Path 7*e1fe3e4aSElliott Hughesfrom io import open 8*e1fe3e4aSElliott Hughesfrom .testSupport import getDemoFontGlyphSetPath 9*e1fe3e4aSElliott Hughesfrom fontTools.ufoLib.glifLib import ( 10*e1fe3e4aSElliott Hughes GlyphSet, 11*e1fe3e4aSElliott Hughes glyphNameToFileName, 12*e1fe3e4aSElliott Hughes readGlyphFromString, 13*e1fe3e4aSElliott Hughes writeGlyphToString, 14*e1fe3e4aSElliott Hughes) 15*e1fe3e4aSElliott Hughesfrom fontTools.ufoLib.errors import ( 16*e1fe3e4aSElliott Hughes GlifLibError, 17*e1fe3e4aSElliott Hughes UnsupportedGLIFFormat, 18*e1fe3e4aSElliott Hughes UnsupportedUFOFormat, 19*e1fe3e4aSElliott Hughes) 20*e1fe3e4aSElliott Hughesfrom fontTools.misc.etree import XML_DECLARATION 21*e1fe3e4aSElliott Hughesfrom fontTools.pens.recordingPen import RecordingPointPen 22*e1fe3e4aSElliott Hughesimport pytest 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott HughesGLYPHSETDIR = getDemoFontGlyphSetPath() 25*e1fe3e4aSElliott Hughes 26*e1fe3e4aSElliott Hughes 27*e1fe3e4aSElliott Hughesclass GlyphSetTests(unittest.TestCase): 28*e1fe3e4aSElliott Hughes def setUp(self): 29*e1fe3e4aSElliott Hughes self.dstDir = tempfile.mktemp() 30*e1fe3e4aSElliott Hughes os.mkdir(self.dstDir) 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes def tearDown(self): 33*e1fe3e4aSElliott Hughes shutil.rmtree(self.dstDir) 34*e1fe3e4aSElliott Hughes 35*e1fe3e4aSElliott Hughes def testRoundTrip(self): 36*e1fe3e4aSElliott Hughes import difflib 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughes srcDir = GLYPHSETDIR 39*e1fe3e4aSElliott Hughes dstDir = self.dstDir 40*e1fe3e4aSElliott Hughes src = GlyphSet( 41*e1fe3e4aSElliott Hughes srcDir, ufoFormatVersion=2, validateRead=True, validateWrite=True 42*e1fe3e4aSElliott Hughes ) 43*e1fe3e4aSElliott Hughes dst = GlyphSet( 44*e1fe3e4aSElliott Hughes dstDir, ufoFormatVersion=2, validateRead=True, validateWrite=True 45*e1fe3e4aSElliott Hughes ) 46*e1fe3e4aSElliott Hughes for glyphName in src.keys(): 47*e1fe3e4aSElliott Hughes g = src[glyphName] 48*e1fe3e4aSElliott Hughes g.drawPoints(None) # load attrs 49*e1fe3e4aSElliott Hughes dst.writeGlyph(glyphName, g, g.drawPoints) 50*e1fe3e4aSElliott Hughes # compare raw file data: 51*e1fe3e4aSElliott Hughes for glyphName in sorted(src.keys()): 52*e1fe3e4aSElliott Hughes fileName = src.contents[glyphName] 53*e1fe3e4aSElliott Hughes with open(os.path.join(srcDir, fileName), "r") as f: 54*e1fe3e4aSElliott Hughes org = f.read() 55*e1fe3e4aSElliott Hughes with open(os.path.join(dstDir, fileName), "r") as f: 56*e1fe3e4aSElliott Hughes new = f.read() 57*e1fe3e4aSElliott Hughes added = [] 58*e1fe3e4aSElliott Hughes removed = [] 59*e1fe3e4aSElliott Hughes for line in difflib.unified_diff(org.split("\n"), new.split("\n")): 60*e1fe3e4aSElliott Hughes if line.startswith("+ "): 61*e1fe3e4aSElliott Hughes added.append(line[1:]) 62*e1fe3e4aSElliott Hughes elif line.startswith("- "): 63*e1fe3e4aSElliott Hughes removed.append(line[1:]) 64*e1fe3e4aSElliott Hughes self.assertEqual( 65*e1fe3e4aSElliott Hughes added, removed, "%s.glif file differs after round tripping" % glyphName 66*e1fe3e4aSElliott Hughes ) 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott Hughes def testContentsExist(self): 69*e1fe3e4aSElliott Hughes with self.assertRaises(GlifLibError): 70*e1fe3e4aSElliott Hughes GlyphSet( 71*e1fe3e4aSElliott Hughes self.dstDir, 72*e1fe3e4aSElliott Hughes ufoFormatVersion=2, 73*e1fe3e4aSElliott Hughes validateRead=True, 74*e1fe3e4aSElliott Hughes validateWrite=True, 75*e1fe3e4aSElliott Hughes expectContentsFile=True, 76*e1fe3e4aSElliott Hughes ) 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott Hughes def testRebuildContents(self): 79*e1fe3e4aSElliott Hughes gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) 80*e1fe3e4aSElliott Hughes contents = gset.contents 81*e1fe3e4aSElliott Hughes gset.rebuildContents() 82*e1fe3e4aSElliott Hughes self.assertEqual(contents, gset.contents) 83*e1fe3e4aSElliott Hughes 84*e1fe3e4aSElliott Hughes def testReverseContents(self): 85*e1fe3e4aSElliott Hughes gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) 86*e1fe3e4aSElliott Hughes d = {} 87*e1fe3e4aSElliott Hughes for k, v in gset.getReverseContents().items(): 88*e1fe3e4aSElliott Hughes d[v] = k 89*e1fe3e4aSElliott Hughes org = {} 90*e1fe3e4aSElliott Hughes for k, v in gset.contents.items(): 91*e1fe3e4aSElliott Hughes org[k] = v.lower() 92*e1fe3e4aSElliott Hughes self.assertEqual(d, org) 93*e1fe3e4aSElliott Hughes 94*e1fe3e4aSElliott Hughes def testReverseContents2(self): 95*e1fe3e4aSElliott Hughes src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) 96*e1fe3e4aSElliott Hughes dst = GlyphSet(self.dstDir, validateRead=True, validateWrite=True) 97*e1fe3e4aSElliott Hughes dstMap = dst.getReverseContents() 98*e1fe3e4aSElliott Hughes self.assertEqual(dstMap, {}) 99*e1fe3e4aSElliott Hughes for glyphName in src.keys(): 100*e1fe3e4aSElliott Hughes g = src[glyphName] 101*e1fe3e4aSElliott Hughes g.drawPoints(None) # load attrs 102*e1fe3e4aSElliott Hughes dst.writeGlyph(glyphName, g, g.drawPoints) 103*e1fe3e4aSElliott Hughes self.assertNotEqual(dstMap, {}) 104*e1fe3e4aSElliott Hughes srcMap = dict(src.getReverseContents()) # copy 105*e1fe3e4aSElliott Hughes self.assertEqual(dstMap, srcMap) 106*e1fe3e4aSElliott Hughes del srcMap["a.glif"] 107*e1fe3e4aSElliott Hughes dst.deleteGlyph("a") 108*e1fe3e4aSElliott Hughes self.assertEqual(dstMap, srcMap) 109*e1fe3e4aSElliott Hughes 110*e1fe3e4aSElliott Hughes def testCustomFileNamingScheme(self): 111*e1fe3e4aSElliott Hughes def myGlyphNameToFileName(glyphName, glyphSet): 112*e1fe3e4aSElliott Hughes return "prefix" + glyphNameToFileName(glyphName, glyphSet) 113*e1fe3e4aSElliott Hughes 114*e1fe3e4aSElliott Hughes src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) 115*e1fe3e4aSElliott Hughes dst = GlyphSet( 116*e1fe3e4aSElliott Hughes self.dstDir, myGlyphNameToFileName, validateRead=True, validateWrite=True 117*e1fe3e4aSElliott Hughes ) 118*e1fe3e4aSElliott Hughes for glyphName in src.keys(): 119*e1fe3e4aSElliott Hughes g = src[glyphName] 120*e1fe3e4aSElliott Hughes g.drawPoints(None) # load attrs 121*e1fe3e4aSElliott Hughes dst.writeGlyph(glyphName, g, g.drawPoints) 122*e1fe3e4aSElliott Hughes d = {} 123*e1fe3e4aSElliott Hughes for k, v in src.contents.items(): 124*e1fe3e4aSElliott Hughes d[k] = "prefix" + v 125*e1fe3e4aSElliott Hughes self.assertEqual(d, dst.contents) 126*e1fe3e4aSElliott Hughes 127*e1fe3e4aSElliott Hughes def testGetUnicodes(self): 128*e1fe3e4aSElliott Hughes src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True) 129*e1fe3e4aSElliott Hughes unicodes = src.getUnicodes() 130*e1fe3e4aSElliott Hughes for glyphName in src.keys(): 131*e1fe3e4aSElliott Hughes g = src[glyphName] 132*e1fe3e4aSElliott Hughes g.drawPoints(None) # load attrs 133*e1fe3e4aSElliott Hughes if not hasattr(g, "unicodes"): 134*e1fe3e4aSElliott Hughes self.assertEqual(unicodes[glyphName], []) 135*e1fe3e4aSElliott Hughes else: 136*e1fe3e4aSElliott Hughes self.assertEqual(g.unicodes, unicodes[glyphName]) 137*e1fe3e4aSElliott Hughes 138*e1fe3e4aSElliott Hughes def testReadGlyphInvalidXml(self): 139*e1fe3e4aSElliott Hughes """Test that calling readGlyph() to read a .glif with invalid XML raises 140*e1fe3e4aSElliott Hughes a library error, instead of an exception from the XML dependency that is 141*e1fe3e4aSElliott Hughes used internally. In addition, check that the raised exception describes 142*e1fe3e4aSElliott Hughes the glyph by name and gives the location of the broken .glif file.""" 143*e1fe3e4aSElliott Hughes 144*e1fe3e4aSElliott Hughes # Create a glyph set with three empty glyphs. 145*e1fe3e4aSElliott Hughes glyph_set = GlyphSet(self.dstDir) 146*e1fe3e4aSElliott Hughes glyph_set.writeGlyph("a", _Glyph()) 147*e1fe3e4aSElliott Hughes glyph_set.writeGlyph("b", _Glyph()) 148*e1fe3e4aSElliott Hughes glyph_set.writeGlyph("c", _Glyph()) 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes # Corrupt the XML of /c. 151*e1fe3e4aSElliott Hughes invalid_xml = b"<abc></def>" 152*e1fe3e4aSElliott Hughes Path(self.dstDir, glyph_set.contents["c"]).write_bytes(invalid_xml) 153*e1fe3e4aSElliott Hughes 154*e1fe3e4aSElliott Hughes # Confirm that reading /a and /b is fine... 155*e1fe3e4aSElliott Hughes glyph_set.readGlyph("a", _Glyph()) 156*e1fe3e4aSElliott Hughes glyph_set.readGlyph("b", _Glyph()) 157*e1fe3e4aSElliott Hughes 158*e1fe3e4aSElliott Hughes # ...but that reading /c raises a descriptive library error. 159*e1fe3e4aSElliott Hughes expected_message = ( 160*e1fe3e4aSElliott Hughes r"GLIF contains invalid XML\.\n" 161*e1fe3e4aSElliott Hughes r"The issue is in glyph 'c', located in '.*c\.glif.*\." 162*e1fe3e4aSElliott Hughes ) 163*e1fe3e4aSElliott Hughes with pytest.raises(GlifLibError, match=expected_message): 164*e1fe3e4aSElliott Hughes glyph_set.readGlyph("c", _Glyph()) 165*e1fe3e4aSElliott Hughes 166*e1fe3e4aSElliott Hughes 167*e1fe3e4aSElliott Hughesclass FileNameTest: 168*e1fe3e4aSElliott Hughes def test_default_file_name_scheme(self): 169*e1fe3e4aSElliott Hughes assert glyphNameToFileName("a", None) == "a.glif" 170*e1fe3e4aSElliott Hughes assert glyphNameToFileName("A", None) == "A_.glif" 171*e1fe3e4aSElliott Hughes assert glyphNameToFileName("Aring", None) == "A_ring.glif" 172*e1fe3e4aSElliott Hughes assert glyphNameToFileName("F_A_B", None) == "F__A__B_.glif" 173*e1fe3e4aSElliott Hughes assert glyphNameToFileName("A.alt", None) == "A_.alt.glif" 174*e1fe3e4aSElliott Hughes assert glyphNameToFileName("A.Alt", None) == "A_.A_lt.glif" 175*e1fe3e4aSElliott Hughes assert glyphNameToFileName(".notdef", None) == "_notdef.glif" 176*e1fe3e4aSElliott Hughes assert glyphNameToFileName("T_H", None) == "T__H_.glif" 177*e1fe3e4aSElliott Hughes assert glyphNameToFileName("T_h", None) == "T__h.glif" 178*e1fe3e4aSElliott Hughes assert glyphNameToFileName("t_h", None) == "t_h.glif" 179*e1fe3e4aSElliott Hughes assert glyphNameToFileName("F_F_I", None) == "F__F__I_.glif" 180*e1fe3e4aSElliott Hughes assert glyphNameToFileName("f_f_i", None) == "f_f_i.glif" 181*e1fe3e4aSElliott Hughes assert glyphNameToFileName("AE", None) == "A_E_.glif" 182*e1fe3e4aSElliott Hughes assert glyphNameToFileName("Ae", None) == "A_e.glif" 183*e1fe3e4aSElliott Hughes assert glyphNameToFileName("ae", None) == "ae.glif" 184*e1fe3e4aSElliott Hughes assert glyphNameToFileName("aE", None) == "aE_.glif" 185*e1fe3e4aSElliott Hughes assert glyphNameToFileName("a.alt", None) == "a.alt.glif" 186*e1fe3e4aSElliott Hughes assert glyphNameToFileName("A.aLt", None) == "A_.aL_t.glif" 187*e1fe3e4aSElliott Hughes assert glyphNameToFileName("A.alT", None) == "A_.alT_.glif" 188*e1fe3e4aSElliott Hughes assert glyphNameToFileName("Aacute_V.swash", None) == "A_acute_V_.swash.glif" 189*e1fe3e4aSElliott Hughes assert glyphNameToFileName(".notdef", None) == "_notdef.glif" 190*e1fe3e4aSElliott Hughes assert glyphNameToFileName("con", None) == "_con.glif" 191*e1fe3e4aSElliott Hughes assert glyphNameToFileName("CON", None) == "C_O_N_.glif" 192*e1fe3e4aSElliott Hughes assert glyphNameToFileName("con.alt", None) == "_con.alt.glif" 193*e1fe3e4aSElliott Hughes assert glyphNameToFileName("alt.con", None) == "alt._con.glif" 194*e1fe3e4aSElliott Hughes 195*e1fe3e4aSElliott Hughes def test_conflicting_case_insensitive_file_names(self, tmp_path): 196*e1fe3e4aSElliott Hughes src = GlyphSet(GLYPHSETDIR) 197*e1fe3e4aSElliott Hughes dst = GlyphSet(tmp_path) 198*e1fe3e4aSElliott Hughes glyph = src["a"] 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes dst.writeGlyph("a", glyph) 201*e1fe3e4aSElliott Hughes dst.writeGlyph("A", glyph) 202*e1fe3e4aSElliott Hughes dst.writeGlyph("a_", glyph) 203*e1fe3e4aSElliott Hughes dst.deleteGlyph("a_") 204*e1fe3e4aSElliott Hughes dst.writeGlyph("a_", glyph) 205*e1fe3e4aSElliott Hughes dst.writeGlyph("A_", glyph) 206*e1fe3e4aSElliott Hughes dst.writeGlyph("i_j", glyph) 207*e1fe3e4aSElliott Hughes 208*e1fe3e4aSElliott Hughes assert dst.contents == { 209*e1fe3e4aSElliott Hughes "a": "a.glif", 210*e1fe3e4aSElliott Hughes "A": "A_.glif", 211*e1fe3e4aSElliott Hughes "a_": "a_000000000000001.glif", 212*e1fe3e4aSElliott Hughes "A_": "A__.glif", 213*e1fe3e4aSElliott Hughes "i_j": "i_j.glif", 214*e1fe3e4aSElliott Hughes } 215*e1fe3e4aSElliott Hughes 216*e1fe3e4aSElliott Hughes # make sure filenames are unique even on case-insensitive filesystems 217*e1fe3e4aSElliott Hughes assert len({fileName.lower() for fileName in dst.contents.values()}) == 5 218*e1fe3e4aSElliott Hughes 219*e1fe3e4aSElliott Hughes 220*e1fe3e4aSElliott Hughesclass _Glyph: 221*e1fe3e4aSElliott Hughes pass 222*e1fe3e4aSElliott Hughes 223*e1fe3e4aSElliott Hughes 224*e1fe3e4aSElliott Hughesclass ReadWriteFuncTest: 225*e1fe3e4aSElliott Hughes def test_roundtrip(self): 226*e1fe3e4aSElliott Hughes glyph = _Glyph() 227*e1fe3e4aSElliott Hughes glyph.name = "a" 228*e1fe3e4aSElliott Hughes glyph.unicodes = [0x0061] 229*e1fe3e4aSElliott Hughes 230*e1fe3e4aSElliott Hughes s1 = writeGlyphToString(glyph.name, glyph) 231*e1fe3e4aSElliott Hughes 232*e1fe3e4aSElliott Hughes glyph2 = _Glyph() 233*e1fe3e4aSElliott Hughes readGlyphFromString(s1, glyph2) 234*e1fe3e4aSElliott Hughes assert glyph.__dict__ == glyph2.__dict__ 235*e1fe3e4aSElliott Hughes 236*e1fe3e4aSElliott Hughes s2 = writeGlyphToString(glyph2.name, glyph2) 237*e1fe3e4aSElliott Hughes assert s1 == s2 238*e1fe3e4aSElliott Hughes 239*e1fe3e4aSElliott Hughes def test_xml_declaration(self): 240*e1fe3e4aSElliott Hughes s = writeGlyphToString("a", _Glyph()) 241*e1fe3e4aSElliott Hughes assert s.startswith(XML_DECLARATION % "UTF-8") 242*e1fe3e4aSElliott Hughes 243*e1fe3e4aSElliott Hughes def test_parse_xml_remove_comments(self): 244*e1fe3e4aSElliott Hughes s = b"""<?xml version='1.0' encoding='UTF-8'?> 245*e1fe3e4aSElliott Hughes <!-- a comment --> 246*e1fe3e4aSElliott Hughes <glyph name="A" format="2"> 247*e1fe3e4aSElliott Hughes <advance width="1290"/> 248*e1fe3e4aSElliott Hughes <unicode hex="0041"/> 249*e1fe3e4aSElliott Hughes <!-- another comment --> 250*e1fe3e4aSElliott Hughes </glyph> 251*e1fe3e4aSElliott Hughes """ 252*e1fe3e4aSElliott Hughes 253*e1fe3e4aSElliott Hughes g = _Glyph() 254*e1fe3e4aSElliott Hughes readGlyphFromString(s, g) 255*e1fe3e4aSElliott Hughes 256*e1fe3e4aSElliott Hughes assert g.name == "A" 257*e1fe3e4aSElliott Hughes assert g.width == 1290 258*e1fe3e4aSElliott Hughes assert g.unicodes == [0x0041] 259*e1fe3e4aSElliott Hughes 260*e1fe3e4aSElliott Hughes def test_read_invalid_xml(self): 261*e1fe3e4aSElliott Hughes """Test that calling readGlyphFromString() with invalid XML raises a 262*e1fe3e4aSElliott Hughes library error, instead of an exception from the XML dependency that is 263*e1fe3e4aSElliott Hughes used internally.""" 264*e1fe3e4aSElliott Hughes 265*e1fe3e4aSElliott Hughes invalid_xml = b"<abc></def>" 266*e1fe3e4aSElliott Hughes empty_glyph = _Glyph() 267*e1fe3e4aSElliott Hughes 268*e1fe3e4aSElliott Hughes with pytest.raises(GlifLibError, match="GLIF contains invalid XML"): 269*e1fe3e4aSElliott Hughes readGlyphFromString(invalid_xml, empty_glyph) 270*e1fe3e4aSElliott Hughes 271*e1fe3e4aSElliott Hughes def test_read_unsupported_format_version(self, caplog): 272*e1fe3e4aSElliott Hughes s = """<?xml version='1.0' encoding='utf-8'?> 273*e1fe3e4aSElliott Hughes <glyph name="A" format="0" formatMinor="0"> 274*e1fe3e4aSElliott Hughes <advance width="500"/> 275*e1fe3e4aSElliott Hughes <unicode hex="0041"/> 276*e1fe3e4aSElliott Hughes </glyph> 277*e1fe3e4aSElliott Hughes """ 278*e1fe3e4aSElliott Hughes 279*e1fe3e4aSElliott Hughes with pytest.raises(UnsupportedGLIFFormat): 280*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph()) # validate=True by default 281*e1fe3e4aSElliott Hughes 282*e1fe3e4aSElliott Hughes with pytest.raises(UnsupportedGLIFFormat): 283*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), validate=True) 284*e1fe3e4aSElliott Hughes 285*e1fe3e4aSElliott Hughes caplog.clear() 286*e1fe3e4aSElliott Hughes with caplog.at_level(logging.WARNING, logger="fontTools.ufoLib.glifLib"): 287*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), validate=False) 288*e1fe3e4aSElliott Hughes 289*e1fe3e4aSElliott Hughes assert len(caplog.records) == 1 290*e1fe3e4aSElliott Hughes assert "Unsupported GLIF format" in caplog.text 291*e1fe3e4aSElliott Hughes assert "Assuming the latest supported version" in caplog.text 292*e1fe3e4aSElliott Hughes 293*e1fe3e4aSElliott Hughes def test_read_allow_format_versions(self): 294*e1fe3e4aSElliott Hughes s = """<?xml version='1.0' encoding='utf-8'?> 295*e1fe3e4aSElliott Hughes <glyph name="A" format="2"> 296*e1fe3e4aSElliott Hughes <advance width="500"/> 297*e1fe3e4aSElliott Hughes <unicode hex="0041"/> 298*e1fe3e4aSElliott Hughes </glyph> 299*e1fe3e4aSElliott Hughes """ 300*e1fe3e4aSElliott Hughes 301*e1fe3e4aSElliott Hughes # these two calls are are equivalent 302*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), formatVersions=[1, 2]) 303*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), formatVersions=[(1, 0), (2, 0)]) 304*e1fe3e4aSElliott Hughes 305*e1fe3e4aSElliott Hughes # if at least one supported formatVersion, unsupported ones are ignored 306*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), formatVersions=[(2, 0), (123, 456)]) 307*e1fe3e4aSElliott Hughes 308*e1fe3e4aSElliott Hughes with pytest.raises( 309*e1fe3e4aSElliott Hughes ValueError, match="None of the requested GLIF formatVersions are supported" 310*e1fe3e4aSElliott Hughes ): 311*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), formatVersions=[0, 2001]) 312*e1fe3e4aSElliott Hughes 313*e1fe3e4aSElliott Hughes with pytest.raises(GlifLibError, match="Forbidden GLIF format version"): 314*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), formatVersions=[1]) 315*e1fe3e4aSElliott Hughes 316*e1fe3e4aSElliott Hughes def test_read_ensure_x_y(self): 317*e1fe3e4aSElliott Hughes """Ensure that a proper GlifLibError is raised when point coordinates are 318*e1fe3e4aSElliott Hughes missing, regardless of validation setting.""" 319*e1fe3e4aSElliott Hughes 320*e1fe3e4aSElliott Hughes s = """<?xml version='1.0' encoding='utf-8'?> 321*e1fe3e4aSElliott Hughes <glyph name="A" format="2"> 322*e1fe3e4aSElliott Hughes <outline> 323*e1fe3e4aSElliott Hughes <contour> 324*e1fe3e4aSElliott Hughes <point x="545" y="0" type="line"/> 325*e1fe3e4aSElliott Hughes <point x="638" type="line"/> 326*e1fe3e4aSElliott Hughes </contour> 327*e1fe3e4aSElliott Hughes </outline> 328*e1fe3e4aSElliott Hughes </glyph> 329*e1fe3e4aSElliott Hughes """ 330*e1fe3e4aSElliott Hughes pen = RecordingPointPen() 331*e1fe3e4aSElliott Hughes 332*e1fe3e4aSElliott Hughes with pytest.raises(GlifLibError, match="Required y attribute"): 333*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), pen) 334*e1fe3e4aSElliott Hughes 335*e1fe3e4aSElliott Hughes with pytest.raises(GlifLibError, match="Required y attribute"): 336*e1fe3e4aSElliott Hughes readGlyphFromString(s, _Glyph(), pen, validate=False) 337*e1fe3e4aSElliott Hughes 338*e1fe3e4aSElliott Hughes 339*e1fe3e4aSElliott Hughesdef test_GlyphSet_unsupported_ufoFormatVersion(tmp_path, caplog): 340*e1fe3e4aSElliott Hughes with pytest.raises(UnsupportedUFOFormat): 341*e1fe3e4aSElliott Hughes GlyphSet(tmp_path, ufoFormatVersion=0) 342*e1fe3e4aSElliott Hughes with pytest.raises(UnsupportedUFOFormat): 343*e1fe3e4aSElliott Hughes GlyphSet(tmp_path, ufoFormatVersion=(0, 1)) 344*e1fe3e4aSElliott Hughes 345*e1fe3e4aSElliott Hughes 346*e1fe3e4aSElliott Hughesdef test_GlyphSet_writeGlyph_formatVersion(tmp_path): 347*e1fe3e4aSElliott Hughes src = GlyphSet(GLYPHSETDIR) 348*e1fe3e4aSElliott Hughes dst = GlyphSet(tmp_path, ufoFormatVersion=(2, 0)) 349*e1fe3e4aSElliott Hughes glyph = src["A"] 350*e1fe3e4aSElliott Hughes 351*e1fe3e4aSElliott Hughes # no explicit formatVersion passed: use the more recent GLIF formatVersion 352*e1fe3e4aSElliott Hughes # that is supported by given ufoFormatVersion (GLIF 1 for UFO 2) 353*e1fe3e4aSElliott Hughes dst.writeGlyph("A", glyph) 354*e1fe3e4aSElliott Hughes glif = dst.getGLIF("A") 355*e1fe3e4aSElliott Hughes assert b'format="1"' in glif 356*e1fe3e4aSElliott Hughes assert b"formatMinor" not in glif # omitted when 0 357*e1fe3e4aSElliott Hughes 358*e1fe3e4aSElliott Hughes # explicit, unknown formatVersion 359*e1fe3e4aSElliott Hughes with pytest.raises(UnsupportedGLIFFormat): 360*e1fe3e4aSElliott Hughes dst.writeGlyph("A", glyph, formatVersion=(0, 0)) 361*e1fe3e4aSElliott Hughes 362*e1fe3e4aSElliott Hughes # explicit, known formatVersion but unsupported by given ufoFormatVersion 363*e1fe3e4aSElliott Hughes with pytest.raises( 364*e1fe3e4aSElliott Hughes UnsupportedGLIFFormat, 365*e1fe3e4aSElliott Hughes match="Unsupported GLIF format version .*for UFO format version", 366*e1fe3e4aSElliott Hughes ): 367*e1fe3e4aSElliott Hughes dst.writeGlyph("A", glyph, formatVersion=(2, 0)) 368