| 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 = '''\ |
| <?xml version="1.0" encoding="UTF-8"?> |
| <ttFont> |
| <name> |
| <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> |
| %s |
| </namerecord> |
| </name> |
| </ttFont> |
| ''' % 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 = ( |
| '<ttFont>\r' # \r -> \n |
| ' <test>\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 |
| ' </test>\n' # \n |
| '</ttFont>') |
| |
| 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 = ( |
| '<ttFont>\n' |
| ' <test>\n' |
| ' %s\n' |
| ' </test>\n' |
| '</ttFont>\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"<ttFont></ttFont>") 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'<ttFont></ttFont>') |
| 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'<ttFont>"hello"</ttFont>') |
| 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 = ( |
| '<ttFont ttLibVersion="3.15">' |
| '<name>' |
| '<namerecord nameID="%s" platformID="%s" platEncID="1" langID="%s">' |
| '%s' |
| '</namerecord>' |
| '</name>' |
| '</ttFont>' |
| ) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent) |
| tmp.write(subFileData.encode("utf-8")) |
| |
| with tempfile.NamedTemporaryFile(delete=False) as tmp2: |
| fileData = ( |
| '<ttFont ttLibVersion="3.15">' |
| '<name>' |
| '<namerecord src="%s"/>' |
| '</name>' |
| '</ttFont>' |
| ) % 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()) |