2021-03-29 11:45:58 +02:00
|
|
|
from io import BytesIO
|
2015-07-25 17:48:31 +01:00
|
|
|
import os
|
|
|
|
import unittest
|
|
|
|
from fontTools.ttLib import TTFont
|
2021-08-20 00:45:43 +02:00
|
|
|
from fontTools.misc.textTools import strjoin
|
2017-01-15 21:45:57 +00:00
|
|
|
from fontTools.misc.xmlReader import XMLReader, ProgressPrinter, BUFSIZE
|
2015-07-25 17:48:31 +01:00
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
class TestXMLReader(unittest.TestCase):
|
2022-12-13 11:26:36 +00:00
|
|
|
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 = (
|
|
|
|
"""\
|
2015-07-25 17:48:31 +01:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<ttFont>
|
|
|
|
<name>
|
|
|
|
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
|
|
|
%s
|
|
|
|
</namerecord>
|
|
|
|
</name>
|
|
|
|
</ttFont>
|
2022-12-13 11:26:36 +00:00
|
|
|
"""
|
|
|
|
% 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())
|