xref: /aosp_15_r20/external/fonttools/Tests/misc/xmlReader_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom io import BytesIO
2*e1fe3e4aSElliott Hughesimport os
3*e1fe3e4aSElliott Hughesimport unittest
4*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont
5*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import strjoin
6*e1fe3e4aSElliott Hughesfrom fontTools.misc.xmlReader import XMLReader, ProgressPrinter, BUFSIZE
7*e1fe3e4aSElliott Hughesimport tempfile
8*e1fe3e4aSElliott Hughes
9*e1fe3e4aSElliott Hughes
10*e1fe3e4aSElliott Hughesclass TestXMLReader(unittest.TestCase):
11*e1fe3e4aSElliott Hughes    def test_decode_utf8(self):
12*e1fe3e4aSElliott Hughes        class DebugXMLReader(XMLReader):
13*e1fe3e4aSElliott Hughes            def __init__(self, fileOrPath, ttFont, progress=None):
14*e1fe3e4aSElliott Hughes                super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress)
15*e1fe3e4aSElliott Hughes                self.contents = []
16*e1fe3e4aSElliott Hughes
17*e1fe3e4aSElliott Hughes            def _endElementHandler(self, name):
18*e1fe3e4aSElliott Hughes                if self.stackSize == 3:
19*e1fe3e4aSElliott Hughes                    name, attrs, content = self.root
20*e1fe3e4aSElliott Hughes                    self.contents.append(content)
21*e1fe3e4aSElliott Hughes                super(DebugXMLReader, self)._endElementHandler(name)
22*e1fe3e4aSElliott Hughes
23*e1fe3e4aSElliott Hughes        expected = "fôôbär"
24*e1fe3e4aSElliott Hughes        data = (
25*e1fe3e4aSElliott Hughes            """\
26*e1fe3e4aSElliott Hughes<?xml version="1.0" encoding="UTF-8"?>
27*e1fe3e4aSElliott Hughes<ttFont>
28*e1fe3e4aSElliott Hughes  <name>
29*e1fe3e4aSElliott Hughes    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
30*e1fe3e4aSElliott Hughes      %s
31*e1fe3e4aSElliott Hughes    </namerecord>
32*e1fe3e4aSElliott Hughes  </name>
33*e1fe3e4aSElliott Hughes</ttFont>
34*e1fe3e4aSElliott Hughes"""
35*e1fe3e4aSElliott Hughes            % expected
36*e1fe3e4aSElliott Hughes        )
37*e1fe3e4aSElliott Hughes
38*e1fe3e4aSElliott Hughes        with BytesIO(data.encode("utf-8")) as tmp:
39*e1fe3e4aSElliott Hughes            reader = DebugXMLReader(tmp, TTFont())
40*e1fe3e4aSElliott Hughes            reader.read()
41*e1fe3e4aSElliott Hughes        content = strjoin(reader.contents[0]).strip()
42*e1fe3e4aSElliott Hughes        self.assertEqual(expected, content)
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughes    def test_normalise_newlines(self):
45*e1fe3e4aSElliott Hughes        class DebugXMLReader(XMLReader):
46*e1fe3e4aSElliott Hughes            def __init__(self, fileOrPath, ttFont, progress=None):
47*e1fe3e4aSElliott Hughes                super(DebugXMLReader, self).__init__(fileOrPath, ttFont, progress)
48*e1fe3e4aSElliott Hughes                self.newlines = []
49*e1fe3e4aSElliott Hughes
50*e1fe3e4aSElliott Hughes            def _characterDataHandler(self, data):
51*e1fe3e4aSElliott Hughes                self.newlines.extend([c for c in data if c in ("\r", "\n")])
52*e1fe3e4aSElliott Hughes
53*e1fe3e4aSElliott Hughes        # notice how when CR is escaped, it is not normalised by the XML parser
54*e1fe3e4aSElliott Hughes        data = (
55*e1fe3e4aSElliott Hughes            "<ttFont>\r"  #        \r -> \n
56*e1fe3e4aSElliott Hughes            "  <test>\r\n"  #      \r\n -> \n
57*e1fe3e4aSElliott Hughes            "    a line of text\n"  #              \n
58*e1fe3e4aSElliott Hughes            "    escaped CR and unix newline &#13;\n"  #   &#13;\n -> \r\n
59*e1fe3e4aSElliott Hughes            "    escaped CR and macintosh newline &#13;\r"  #   &#13;\r -> \r\n
60*e1fe3e4aSElliott Hughes            "    escaped CR and windows newline &#13;\r\n"  # &#13;\r\n -> \r\n
61*e1fe3e4aSElliott Hughes            "  </test>\n"  #              \n
62*e1fe3e4aSElliott Hughes            "</ttFont>"
63*e1fe3e4aSElliott Hughes        )
64*e1fe3e4aSElliott Hughes
65*e1fe3e4aSElliott Hughes        with BytesIO(data.encode("utf-8")) as tmp:
66*e1fe3e4aSElliott Hughes            reader = DebugXMLReader(tmp, TTFont())
67*e1fe3e4aSElliott Hughes            reader.read()
68*e1fe3e4aSElliott Hughes        expected = ["\n"] * 3 + ["\r", "\n"] * 3 + ["\n"]
69*e1fe3e4aSElliott Hughes        self.assertEqual(expected, reader.newlines)
70*e1fe3e4aSElliott Hughes
71*e1fe3e4aSElliott Hughes    def test_progress(self):
72*e1fe3e4aSElliott Hughes        class DummyProgressPrinter(ProgressPrinter):
73*e1fe3e4aSElliott Hughes            def __init__(self, title, maxval=100):
74*e1fe3e4aSElliott Hughes                self.label = title
75*e1fe3e4aSElliott Hughes                self.maxval = maxval
76*e1fe3e4aSElliott Hughes                self.pos = 0
77*e1fe3e4aSElliott Hughes
78*e1fe3e4aSElliott Hughes            def set(self, val, maxval=None):
79*e1fe3e4aSElliott Hughes                if maxval is not None:
80*e1fe3e4aSElliott Hughes                    self.maxval = maxval
81*e1fe3e4aSElliott Hughes                self.pos = val
82*e1fe3e4aSElliott Hughes
83*e1fe3e4aSElliott Hughes            def increment(self, val=1):
84*e1fe3e4aSElliott Hughes                self.pos += val
85*e1fe3e4aSElliott Hughes
86*e1fe3e4aSElliott Hughes            def setLabel(self, text):
87*e1fe3e4aSElliott Hughes                self.label = text
88*e1fe3e4aSElliott Hughes
89*e1fe3e4aSElliott Hughes        data = (
90*e1fe3e4aSElliott Hughes            "<ttFont>\n"
91*e1fe3e4aSElliott Hughes            "  <test>\n"
92*e1fe3e4aSElliott Hughes            "    %s\n"
93*e1fe3e4aSElliott Hughes            "  </test>\n"
94*e1fe3e4aSElliott Hughes            "</ttFont>\n" % ("z" * 2 * BUFSIZE)
95*e1fe3e4aSElliott Hughes        ).encode("utf-8")
96*e1fe3e4aSElliott Hughes
97*e1fe3e4aSElliott Hughes        dataSize = len(data)
98*e1fe3e4aSElliott Hughes        progressBar = DummyProgressPrinter("test")
99*e1fe3e4aSElliott Hughes        with BytesIO(data) as tmp:
100*e1fe3e4aSElliott Hughes            reader = XMLReader(tmp, TTFont(), progress=progressBar)
101*e1fe3e4aSElliott Hughes            self.assertEqual(progressBar.pos, 0)
102*e1fe3e4aSElliott Hughes            reader.read()
103*e1fe3e4aSElliott Hughes        self.assertEqual(progressBar.pos, dataSize // 100)
104*e1fe3e4aSElliott Hughes        self.assertEqual(progressBar.maxval, dataSize // 100)
105*e1fe3e4aSElliott Hughes        self.assertTrue("test" in progressBar.label)
106*e1fe3e4aSElliott Hughes        with BytesIO(b"<ttFont></ttFont>") as tmp:
107*e1fe3e4aSElliott Hughes            reader = XMLReader(tmp, TTFont(), progress=progressBar)
108*e1fe3e4aSElliott Hughes            reader.read()
109*e1fe3e4aSElliott Hughes        # when data size is less than 100 bytes, 'maxval' is 1
110*e1fe3e4aSElliott Hughes        self.assertEqual(progressBar.maxval, 1)
111*e1fe3e4aSElliott Hughes
112*e1fe3e4aSElliott Hughes    def test_close_file_path(self):
113*e1fe3e4aSElliott Hughes        with tempfile.NamedTemporaryFile(delete=False) as tmp:
114*e1fe3e4aSElliott Hughes            tmp.write(b"<ttFont></ttFont>")
115*e1fe3e4aSElliott Hughes        reader = XMLReader(tmp.name, TTFont())
116*e1fe3e4aSElliott Hughes        reader.read()
117*e1fe3e4aSElliott Hughes        # when reading from path, the file is closed automatically at the end
118*e1fe3e4aSElliott Hughes        self.assertTrue(reader.file.closed)
119*e1fe3e4aSElliott Hughes        # this does nothing
120*e1fe3e4aSElliott Hughes        reader.close()
121*e1fe3e4aSElliott Hughes        self.assertTrue(reader.file.closed)
122*e1fe3e4aSElliott Hughes        os.remove(tmp.name)
123*e1fe3e4aSElliott Hughes
124*e1fe3e4aSElliott Hughes    def test_close_file_obj(self):
125*e1fe3e4aSElliott Hughes        with tempfile.NamedTemporaryFile(delete=False) as tmp:
126*e1fe3e4aSElliott Hughes            tmp.write(b'<ttFont>"hello"</ttFont>')
127*e1fe3e4aSElliott Hughes        with open(tmp.name, "rb") as f:
128*e1fe3e4aSElliott Hughes            reader = XMLReader(f, TTFont())
129*e1fe3e4aSElliott Hughes            reader.read()
130*e1fe3e4aSElliott Hughes            # when reading from a file or file-like object, the latter is kept open
131*e1fe3e4aSElliott Hughes            self.assertFalse(reader.file.closed)
132*e1fe3e4aSElliott Hughes        # ... until the user explicitly closes it
133*e1fe3e4aSElliott Hughes        reader.close()
134*e1fe3e4aSElliott Hughes        self.assertTrue(reader.file.closed)
135*e1fe3e4aSElliott Hughes        os.remove(tmp.name)
136*e1fe3e4aSElliott Hughes
137*e1fe3e4aSElliott Hughes    def test_read_sub_file(self):
138*e1fe3e4aSElliott Hughes        # Verifies that sub-file content is able to be read to a table.
139*e1fe3e4aSElliott Hughes        expectedContent = "testContent"
140*e1fe3e4aSElliott Hughes        expectedNameID = "1"
141*e1fe3e4aSElliott Hughes        expectedPlatform = "3"
142*e1fe3e4aSElliott Hughes        expectedLangId = "0x409"
143*e1fe3e4aSElliott Hughes
144*e1fe3e4aSElliott Hughes        with tempfile.NamedTemporaryFile(delete=False) as tmp:
145*e1fe3e4aSElliott Hughes            subFileData = (
146*e1fe3e4aSElliott Hughes                '<ttFont ttLibVersion="3.15">'
147*e1fe3e4aSElliott Hughes                "<name>"
148*e1fe3e4aSElliott Hughes                '<namerecord nameID="%s" platformID="%s" platEncID="1" langID="%s">'
149*e1fe3e4aSElliott Hughes                "%s"
150*e1fe3e4aSElliott Hughes                "</namerecord>"
151*e1fe3e4aSElliott Hughes                "</name>"
152*e1fe3e4aSElliott Hughes                "</ttFont>"
153*e1fe3e4aSElliott Hughes            ) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent)
154*e1fe3e4aSElliott Hughes            tmp.write(subFileData.encode("utf-8"))
155*e1fe3e4aSElliott Hughes
156*e1fe3e4aSElliott Hughes        with tempfile.NamedTemporaryFile(delete=False) as tmp2:
157*e1fe3e4aSElliott Hughes            fileData = (
158*e1fe3e4aSElliott Hughes                '<ttFont ttLibVersion="3.15">'
159*e1fe3e4aSElliott Hughes                "<name>"
160*e1fe3e4aSElliott Hughes                '<namerecord src="%s"/>'
161*e1fe3e4aSElliott Hughes                "</name>"
162*e1fe3e4aSElliott Hughes                "</ttFont>"
163*e1fe3e4aSElliott Hughes            ) % tmp.name
164*e1fe3e4aSElliott Hughes            tmp2.write(fileData.encode("utf-8"))
165*e1fe3e4aSElliott Hughes
166*e1fe3e4aSElliott Hughes        ttf = TTFont()
167*e1fe3e4aSElliott Hughes        with open(tmp2.name, "rb") as f:
168*e1fe3e4aSElliott Hughes            reader = XMLReader(f, ttf)
169*e1fe3e4aSElliott Hughes            reader.read()
170*e1fe3e4aSElliott Hughes            reader.close()
171*e1fe3e4aSElliott Hughes            nameTable = ttf["name"]
172*e1fe3e4aSElliott Hughes            self.assertTrue(int(expectedNameID) == nameTable.names[0].nameID)
173*e1fe3e4aSElliott Hughes            self.assertTrue(int(expectedLangId, 16) == nameTable.names[0].langID)
174*e1fe3e4aSElliott Hughes            self.assertTrue(int(expectedPlatform) == nameTable.names[0].platformID)
175*e1fe3e4aSElliott Hughes            self.assertEqual(
176*e1fe3e4aSElliott Hughes                expectedContent,
177*e1fe3e4aSElliott Hughes                nameTable.names[0].string.decode(nameTable.names[0].getEncoding()),
178*e1fe3e4aSElliott Hughes            )
179*e1fe3e4aSElliott Hughes
180*e1fe3e4aSElliott Hughes        os.remove(tmp.name)
181*e1fe3e4aSElliott Hughes        os.remove(tmp2.name)
182*e1fe3e4aSElliott Hughes
183*e1fe3e4aSElliott Hughes
184*e1fe3e4aSElliott Hughesif __name__ == "__main__":
185*e1fe3e4aSElliott Hughes    import sys
186*e1fe3e4aSElliott Hughes
187*e1fe3e4aSElliott Hughes    sys.exit(unittest.main())
188