1*e1fe3e4aSElliott Hughesfrom io import BytesIO 2*e1fe3e4aSElliott Hughesimport os 3*e1fe3e4aSElliott Hughesimport unittest 4*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont 5*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import strjoin 6*e1fe3e4aSElliott Hughesfrom fontTools.misc.xmlReader import XMLReader, ProgressPrinter, BUFSIZE 7*e1fe3e4aSElliott Hughesimport tempfile 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughes 10*e1fe3e4aSElliott Hughesclass TestXMLReader(unittest.TestCase): 11*e1fe3e4aSElliott Hughes def test_decode_utf8(self): 12*e1fe3e4aSElliott Hughes class DebugXMLReader(XMLReader): 13*e1fe3e4aSElliott Hughes def __init__(self, fileOrPath, ttFont, progress=None): 14*e1fe3e4aSElliott Hughes super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress) 15*e1fe3e4aSElliott Hughes self.contents = [] 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughes def _endElementHandler(self, name): 18*e1fe3e4aSElliott Hughes if self.stackSize == 3: 19*e1fe3e4aSElliott Hughes name, attrs, content = self.root 20*e1fe3e4aSElliott Hughes self.contents.append(content) 21*e1fe3e4aSElliott Hughes super(DebugXMLReader, self)._endElementHandler(name) 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes expected = "fôôbär" 24*e1fe3e4aSElliott Hughes data = ( 25*e1fe3e4aSElliott Hughes """\ 26*e1fe3e4aSElliott Hughes<?xml version="1.0" encoding="UTF-8"?> 27*e1fe3e4aSElliott Hughes<ttFont> 28*e1fe3e4aSElliott Hughes <name> 29*e1fe3e4aSElliott Hughes <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> 30*e1fe3e4aSElliott Hughes %s 31*e1fe3e4aSElliott Hughes </namerecord> 32*e1fe3e4aSElliott Hughes </name> 33*e1fe3e4aSElliott Hughes</ttFont> 34*e1fe3e4aSElliott Hughes""" 35*e1fe3e4aSElliott Hughes % expected 36*e1fe3e4aSElliott Hughes ) 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughes with BytesIO(data.encode("utf-8")) as tmp: 39*e1fe3e4aSElliott Hughes reader = DebugXMLReader(tmp, TTFont()) 40*e1fe3e4aSElliott Hughes reader.read() 41*e1fe3e4aSElliott Hughes content = strjoin(reader.contents[0]).strip() 42*e1fe3e4aSElliott Hughes self.assertEqual(expected, content) 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes def test_normalise_newlines(self): 45*e1fe3e4aSElliott Hughes class DebugXMLReader(XMLReader): 46*e1fe3e4aSElliott Hughes def __init__(self, fileOrPath, ttFont, progress=None): 47*e1fe3e4aSElliott Hughes super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress) 48*e1fe3e4aSElliott Hughes self.newlines = [] 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes def _characterDataHandler(self, data): 51*e1fe3e4aSElliott Hughes self.newlines.extend([c for c in data if c in ("\r", "\n")]) 52*e1fe3e4aSElliott Hughes 53*e1fe3e4aSElliott Hughes # notice how when CR is escaped, it is not normalised by the XML parser 54*e1fe3e4aSElliott Hughes data = ( 55*e1fe3e4aSElliott Hughes "<ttFont>\r" # \r -> \n 56*e1fe3e4aSElliott Hughes " <test>\r\n" # \r\n -> \n 57*e1fe3e4aSElliott Hughes " a line of text\n" # \n 58*e1fe3e4aSElliott Hughes " escaped CR and unix newline \n" # \n -> \r\n 59*e1fe3e4aSElliott Hughes " escaped CR and macintosh newline \r" # \r -> \r\n 60*e1fe3e4aSElliott Hughes " escaped CR and windows newline \r\n" # \r\n -> \r\n 61*e1fe3e4aSElliott Hughes " </test>\n" # \n 62*e1fe3e4aSElliott Hughes "</ttFont>" 63*e1fe3e4aSElliott Hughes ) 64*e1fe3e4aSElliott Hughes 65*e1fe3e4aSElliott Hughes with BytesIO(data.encode("utf-8")) as tmp: 66*e1fe3e4aSElliott Hughes reader = DebugXMLReader(tmp, TTFont()) 67*e1fe3e4aSElliott Hughes reader.read() 68*e1fe3e4aSElliott Hughes expected = ["\n"] * 3 + ["\r", "\n"] * 3 + ["\n"] 69*e1fe3e4aSElliott Hughes self.assertEqual(expected, reader.newlines) 70*e1fe3e4aSElliott Hughes 71*e1fe3e4aSElliott Hughes def test_progress(self): 72*e1fe3e4aSElliott Hughes class DummyProgressPrinter(ProgressPrinter): 73*e1fe3e4aSElliott Hughes def __init__(self, title, maxval=100): 74*e1fe3e4aSElliott Hughes self.label = title 75*e1fe3e4aSElliott Hughes self.maxval = maxval 76*e1fe3e4aSElliott Hughes self.pos = 0 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott Hughes def set(self, val, maxval=None): 79*e1fe3e4aSElliott Hughes if maxval is not None: 80*e1fe3e4aSElliott Hughes self.maxval = maxval 81*e1fe3e4aSElliott Hughes self.pos = val 82*e1fe3e4aSElliott Hughes 83*e1fe3e4aSElliott Hughes def increment(self, val=1): 84*e1fe3e4aSElliott Hughes self.pos += val 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott Hughes def setLabel(self, text): 87*e1fe3e4aSElliott Hughes self.label = text 88*e1fe3e4aSElliott Hughes 89*e1fe3e4aSElliott Hughes data = ( 90*e1fe3e4aSElliott Hughes "<ttFont>\n" 91*e1fe3e4aSElliott Hughes " <test>\n" 92*e1fe3e4aSElliott Hughes " %s\n" 93*e1fe3e4aSElliott Hughes " </test>\n" 94*e1fe3e4aSElliott Hughes "</ttFont>\n" % ("z" * 2 * BUFSIZE) 95*e1fe3e4aSElliott Hughes ).encode("utf-8") 96*e1fe3e4aSElliott Hughes 97*e1fe3e4aSElliott Hughes dataSize = len(data) 98*e1fe3e4aSElliott Hughes progressBar = DummyProgressPrinter("test") 99*e1fe3e4aSElliott Hughes with BytesIO(data) as tmp: 100*e1fe3e4aSElliott Hughes reader = XMLReader(tmp, TTFont(), progress=progressBar) 101*e1fe3e4aSElliott Hughes self.assertEqual(progressBar.pos, 0) 102*e1fe3e4aSElliott Hughes reader.read() 103*e1fe3e4aSElliott Hughes self.assertEqual(progressBar.pos, dataSize // 100) 104*e1fe3e4aSElliott Hughes self.assertEqual(progressBar.maxval, dataSize // 100) 105*e1fe3e4aSElliott Hughes self.assertTrue("test" in progressBar.label) 106*e1fe3e4aSElliott Hughes with BytesIO(b"<ttFont></ttFont>") as tmp: 107*e1fe3e4aSElliott Hughes reader = XMLReader(tmp, TTFont(), progress=progressBar) 108*e1fe3e4aSElliott Hughes reader.read() 109*e1fe3e4aSElliott Hughes # when data size is less than 100 bytes, 'maxval' is 1 110*e1fe3e4aSElliott Hughes self.assertEqual(progressBar.maxval, 1) 111*e1fe3e4aSElliott Hughes 112*e1fe3e4aSElliott Hughes def test_close_file_path(self): 113*e1fe3e4aSElliott Hughes with tempfile.NamedTemporaryFile(delete=False) as tmp: 114*e1fe3e4aSElliott Hughes tmp.write(b"<ttFont></ttFont>") 115*e1fe3e4aSElliott Hughes reader = XMLReader(tmp.name, TTFont()) 116*e1fe3e4aSElliott Hughes reader.read() 117*e1fe3e4aSElliott Hughes # when reading from path, the file is closed automatically at the end 118*e1fe3e4aSElliott Hughes self.assertTrue(reader.file.closed) 119*e1fe3e4aSElliott Hughes # this does nothing 120*e1fe3e4aSElliott Hughes reader.close() 121*e1fe3e4aSElliott Hughes self.assertTrue(reader.file.closed) 122*e1fe3e4aSElliott Hughes os.remove(tmp.name) 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughes def test_close_file_obj(self): 125*e1fe3e4aSElliott Hughes with tempfile.NamedTemporaryFile(delete=False) as tmp: 126*e1fe3e4aSElliott Hughes tmp.write(b'<ttFont>"hello"</ttFont>') 127*e1fe3e4aSElliott Hughes with open(tmp.name, "rb") as f: 128*e1fe3e4aSElliott Hughes reader = XMLReader(f, TTFont()) 129*e1fe3e4aSElliott Hughes reader.read() 130*e1fe3e4aSElliott Hughes # when reading from a file or file-like object, the latter is kept open 131*e1fe3e4aSElliott Hughes self.assertFalse(reader.file.closed) 132*e1fe3e4aSElliott Hughes # ... until the user explicitly closes it 133*e1fe3e4aSElliott Hughes reader.close() 134*e1fe3e4aSElliott Hughes self.assertTrue(reader.file.closed) 135*e1fe3e4aSElliott Hughes os.remove(tmp.name) 136*e1fe3e4aSElliott Hughes 137*e1fe3e4aSElliott Hughes def test_read_sub_file(self): 138*e1fe3e4aSElliott Hughes # Verifies that sub-file content is able to be read to a table. 139*e1fe3e4aSElliott Hughes expectedContent = "testContent" 140*e1fe3e4aSElliott Hughes expectedNameID = "1" 141*e1fe3e4aSElliott Hughes expectedPlatform = "3" 142*e1fe3e4aSElliott Hughes expectedLangId = "0x409" 143*e1fe3e4aSElliott Hughes 144*e1fe3e4aSElliott Hughes with tempfile.NamedTemporaryFile(delete=False) as tmp: 145*e1fe3e4aSElliott Hughes subFileData = ( 146*e1fe3e4aSElliott Hughes '<ttFont ttLibVersion="3.15">' 147*e1fe3e4aSElliott Hughes "<name>" 148*e1fe3e4aSElliott Hughes '<namerecord nameID="%s" platformID="%s" platEncID="1" langID="%s">' 149*e1fe3e4aSElliott Hughes "%s" 150*e1fe3e4aSElliott Hughes "</namerecord>" 151*e1fe3e4aSElliott Hughes "</name>" 152*e1fe3e4aSElliott Hughes "</ttFont>" 153*e1fe3e4aSElliott Hughes ) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent) 154*e1fe3e4aSElliott Hughes tmp.write(subFileData.encode("utf-8")) 155*e1fe3e4aSElliott Hughes 156*e1fe3e4aSElliott Hughes with tempfile.NamedTemporaryFile(delete=False) as tmp2: 157*e1fe3e4aSElliott Hughes fileData = ( 158*e1fe3e4aSElliott Hughes '<ttFont ttLibVersion="3.15">' 159*e1fe3e4aSElliott Hughes "<name>" 160*e1fe3e4aSElliott Hughes '<namerecord src="%s"/>' 161*e1fe3e4aSElliott Hughes "</name>" 162*e1fe3e4aSElliott Hughes "</ttFont>" 163*e1fe3e4aSElliott Hughes ) % tmp.name 164*e1fe3e4aSElliott Hughes tmp2.write(fileData.encode("utf-8")) 165*e1fe3e4aSElliott Hughes 166*e1fe3e4aSElliott Hughes ttf = TTFont() 167*e1fe3e4aSElliott Hughes with open(tmp2.name, "rb") as f: 168*e1fe3e4aSElliott Hughes reader = XMLReader(f, ttf) 169*e1fe3e4aSElliott Hughes reader.read() 170*e1fe3e4aSElliott Hughes reader.close() 171*e1fe3e4aSElliott Hughes nameTable = ttf["name"] 172*e1fe3e4aSElliott Hughes self.assertTrue(int(expectedNameID) == nameTable.names[0].nameID) 173*e1fe3e4aSElliott Hughes self.assertTrue(int(expectedLangId, 16) == nameTable.names[0].langID) 174*e1fe3e4aSElliott Hughes self.assertTrue(int(expectedPlatform) == nameTable.names[0].platformID) 175*e1fe3e4aSElliott Hughes self.assertEqual( 176*e1fe3e4aSElliott Hughes expectedContent, 177*e1fe3e4aSElliott Hughes nameTable.names[0].string.decode(nameTable.names[0].getEncoding()), 178*e1fe3e4aSElliott Hughes ) 179*e1fe3e4aSElliott Hughes 180*e1fe3e4aSElliott Hughes os.remove(tmp.name) 181*e1fe3e4aSElliott Hughes os.remove(tmp2.name) 182*e1fe3e4aSElliott Hughes 183*e1fe3e4aSElliott Hughes 184*e1fe3e4aSElliott Hughesif __name__ == "__main__": 185*e1fe3e4aSElliott Hughes import sys 186*e1fe3e4aSElliott Hughes 187*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 188