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())