from io import BytesIO import os import unittest from fontTools.ttLib import TTFont from fontTools.misc.textTools import strjoin from fontTools.misc.xmlReader import XMLReader, ProgressPrinter, BUFSIZE import tempfile class TestXMLReader(unittest.TestCase): def test_decode_utf8(self): class DebugXMLReader(XMLReader): def __init__(self, fileOrPath, ttFont, progress=None): super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress) self.contents = [] def _endElementHandler(self, name): if self.stackSize == 3: name, attrs, content = self.root self.contents.append(content) super(DebugXMLReader, self)._endElementHandler(name) expected = "fôôbär" data = ( """\ %s """ % expected ) with BytesIO(data.encode("utf-8")) as tmp: reader = DebugXMLReader(tmp, TTFont()) reader.read() content = strjoin(reader.contents[0]).strip() self.assertEqual(expected, content) def test_normalise_newlines(self): class DebugXMLReader(XMLReader): def __init__(self, fileOrPath, ttFont, progress=None): super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress) self.newlines = [] def _characterDataHandler(self, data): self.newlines.extend([c for c in data if c in ("\r", "\n")]) # notice how when CR is escaped, it is not normalised by the XML parser data = ( "\r" # \r -> \n " \r\n" # \r\n -> \n " a line of text\n" # \n " escaped CR and unix newline \n" # \n -> \r\n " escaped CR and macintosh newline \r" # \r -> \r\n " escaped CR and windows newline \r\n" # \r\n -> \r\n " \n" # \n "" ) with BytesIO(data.encode("utf-8")) as tmp: reader = DebugXMLReader(tmp, TTFont()) reader.read() expected = ["\n"] * 3 + ["\r", "\n"] * 3 + ["\n"] self.assertEqual(expected, reader.newlines) def test_progress(self): class DummyProgressPrinter(ProgressPrinter): def __init__(self, title, maxval=100): self.label = title self.maxval = maxval self.pos = 0 def set(self, val, maxval=None): if maxval is not None: self.maxval = maxval self.pos = val def increment(self, val=1): self.pos += val def setLabel(self, text): self.label = text data = ( "\n" " \n" " %s\n" " \n" "\n" % ("z" * 2 * BUFSIZE) ).encode("utf-8") dataSize = len(data) progressBar = DummyProgressPrinter("test") with BytesIO(data) as tmp: reader = XMLReader(tmp, TTFont(), progress=progressBar) self.assertEqual(progressBar.pos, 0) reader.read() self.assertEqual(progressBar.pos, dataSize // 100) self.assertEqual(progressBar.maxval, dataSize // 100) self.assertTrue("test" in progressBar.label) with BytesIO(b"") as tmp: reader = XMLReader(tmp, TTFont(), progress=progressBar) reader.read() # when data size is less than 100 bytes, 'maxval' is 1 self.assertEqual(progressBar.maxval, 1) def test_close_file_path(self): with tempfile.NamedTemporaryFile(delete=False) as tmp: tmp.write(b"") reader = XMLReader(tmp.name, TTFont()) reader.read() # when reading from path, the file is closed automatically at the end self.assertTrue(reader.file.closed) # this does nothing reader.close() self.assertTrue(reader.file.closed) os.remove(tmp.name) def test_close_file_obj(self): with tempfile.NamedTemporaryFile(delete=False) as tmp: tmp.write(b'"hello"') with open(tmp.name, "rb") as f: reader = XMLReader(f, TTFont()) reader.read() # when reading from a file or file-like object, the latter is kept open self.assertFalse(reader.file.closed) # ... until the user explicitly closes it reader.close() self.assertTrue(reader.file.closed) os.remove(tmp.name) def test_read_sub_file(self): # Verifies that sub-file content is able to be read to a table. expectedContent = "testContent" expectedNameID = "1" expectedPlatform = "3" expectedLangId = "0x409" with tempfile.NamedTemporaryFile(delete=False) as tmp: subFileData = ( '' "" '' "%s" "" "" "" ) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent) tmp.write(subFileData.encode("utf-8")) with tempfile.NamedTemporaryFile(delete=False) as tmp2: fileData = ( '' "" '' "" "" ) % tmp.name tmp2.write(fileData.encode("utf-8")) ttf = TTFont() with open(tmp2.name, "rb") as f: reader = XMLReader(f, ttf) reader.read() reader.close() nameTable = ttf["name"] self.assertTrue(int(expectedNameID) == nameTable.names[0].nameID) self.assertTrue(int(expectedLangId, 16) == nameTable.names[0].langID) self.assertTrue(int(expectedPlatform) == nameTable.names[0].platformID) self.assertEqual( expectedContent, nameTable.names[0].string.decode(nameTable.names[0].getEncoding()), ) os.remove(tmp.name) os.remove(tmp2.name) if __name__ == "__main__": import sys sys.exit(unittest.main())