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