• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
2tool to store its hinting source data.
3
4TSI1 contains the text of the glyph programs in the form of low-level assembly
5code, as well as the 'extra' programs 'fpgm', 'ppgm' (i.e. 'prep'), and 'cvt'.
6"""
7from __future__ import print_function, division, absolute_import
8from fontTools.misc.py23 import *
9from . import DefaultTable
10from fontTools.misc.loggingTools import LogMixin
11
12
13class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
14
15	extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}
16
17	indextable = "TSI0"
18
19	def decompile(self, data, ttFont):
20		totalLength = len(data)
21		indextable = ttFont[self.indextable]
22		for indices, isExtra in zip(
23				(indextable.indices, indextable.extra_indices), (False, True)):
24			programs = {}
25			for i, (glyphID, textLength, textOffset) in enumerate(indices):
26				if isExtra:
27					name = self.extras[glyphID]
28				else:
29					name = ttFont.getGlyphName(glyphID)
30				if textOffset > totalLength:
31					self.log.warning("textOffset > totalLength; %r skipped" % name)
32					continue
33				if textLength < 0x8000:
34					# If the length stored in the record is less than 32768, then use
35					# that as the length of the record.
36					pass
37				elif textLength == 0x8000:
38					# If the length is 32768, compute the actual length as follows:
39					isLast = i == (len(indices)-1)
40					if isLast:
41						if isExtra:
42							# For the last "extra" record (the very last record of the
43							# table), the length is the difference between the total
44							# length of the TSI1 table and the textOffset of the final
45							# record.
46							nextTextOffset = totalLength
47						else:
48							# For the last "normal" record (the last record just prior
49							# to the record containing the "magic number"), the length
50							# is the difference between the textOffset of the record
51							# following the "magic number" (0xFFFE) record (i.e. the
52							# first "extra" record), and the textOffset of the last
53							# "normal" record.
54							nextTextOffset = indextable.extra_indices[0][2]
55					else:
56						# For all other records with a length of 0x8000, the length is
57						# the difference between the textOffset of the record in
58						# question and the textOffset of the next record.
59						nextTextOffset = indices[i+1][2]
60					assert nextTextOffset >= textOffset, "entries not sorted by offset"
61					if nextTextOffset > totalLength:
62						self.log.warning(
63							"nextTextOffset > totalLength; %r truncated" % name)
64						nextTextOffset = totalLength
65					textLength = nextTextOffset - textOffset
66				else:
67					from fontTools import ttLib
68					raise ttLib.TTLibError(
69						"%r textLength (%d) must not be > 32768" % (name, textLength))
70				text = data[textOffset:textOffset+textLength]
71				assert len(text) == textLength
72				text = tounicode(text, encoding='utf-8')
73				if text:
74					programs[name] = text
75			if isExtra:
76				self.extraPrograms = programs
77			else:
78				self.glyphPrograms = programs
79
80	def compile(self, ttFont):
81		if not hasattr(self, "glyphPrograms"):
82			self.glyphPrograms = {}
83			self.extraPrograms = {}
84		data = b''
85		indextable = ttFont[self.indextable]
86		glyphNames = ttFont.getGlyphOrder()
87
88		indices = []
89		for i in range(len(glyphNames)):
90			if len(data) % 2:
91				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars. Yum.
92			name = glyphNames[i]
93			if name in self.glyphPrograms:
94				text = tobytes(self.glyphPrograms[name], encoding="utf-8")
95			else:
96				text = b""
97			textLength = len(text)
98			if textLength >= 0x8000:
99				textLength = 0x8000
100			indices.append((i, textLength, len(data)))
101			data = data + text
102
103		extra_indices = []
104		codes = sorted(self.extras.items())
105		for i in range(len(codes)):
106			if len(data) % 2:
107				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars.
108			code, name = codes[i]
109			if name in self.extraPrograms:
110				text = tobytes(self.extraPrograms[name], encoding="utf-8")
111			else:
112				text = b""
113			textLength = len(text)
114			if textLength >= 0x8000:
115				textLength = 0x8000
116			extra_indices.append((code, textLength, len(data)))
117			data = data + text
118		indextable.set(indices, extra_indices)
119		return data
120
121	def toXML(self, writer, ttFont):
122		names = sorted(self.glyphPrograms.keys())
123		writer.newline()
124		for name in names:
125			text = self.glyphPrograms[name]
126			if not text:
127				continue
128			writer.begintag("glyphProgram", name=name)
129			writer.newline()
130			writer.write_noindent(text.replace("\r", "\n"))
131			writer.newline()
132			writer.endtag("glyphProgram")
133			writer.newline()
134			writer.newline()
135		extra_names = sorted(self.extraPrograms.keys())
136		for name in extra_names:
137			text = self.extraPrograms[name]
138			if not text:
139				continue
140			writer.begintag("extraProgram", name=name)
141			writer.newline()
142			writer.write_noindent(text.replace("\r", "\n"))
143			writer.newline()
144			writer.endtag("extraProgram")
145			writer.newline()
146			writer.newline()
147
148	def fromXML(self, name, attrs, content, ttFont):
149		if not hasattr(self, "glyphPrograms"):
150			self.glyphPrograms = {}
151			self.extraPrograms = {}
152		lines = strjoin(content).replace("\r", "\n").split("\n")
153		text = '\r'.join(lines[1:-1])
154		if name == "glyphProgram":
155			self.glyphPrograms[attrs["name"]] = text
156		elif name == "extraProgram":
157			self.extraPrograms[attrs["name"]] = text
158