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