aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/T_S_I__1.py
blob: 9ae7acd6c1b29f9285668185e1a1f98848a7979e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
tool to store its hinting source data.

TSI1 contains the text of the glyph programs in the form of low-level assembly
code, as well as the 'extra' programs 'fpgm', 'ppgm' (i.e. 'prep'), and 'cvt'.
"""
from fontTools.misc.py23 import strjoin, tobytes, tostr
from . import DefaultTable
from fontTools.misc.loggingTools import LogMixin


class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):

	extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}

	indextable = "TSI0"

	def decompile(self, data, ttFont):
		totalLength = len(data)
		indextable = ttFont[self.indextable]
		for indices, isExtra in zip(
				(indextable.indices, indextable.extra_indices), (False, True)):
			programs = {}
			for i, (glyphID, textLength, textOffset) in enumerate(indices):
				if isExtra:
					name = self.extras[glyphID]
				else:
					name = ttFont.getGlyphName(glyphID)
				if textOffset > totalLength:
					self.log.warning("textOffset > totalLength; %r skipped" % name)
					continue
				if textLength < 0x8000:
					# If the length stored in the record is less than 32768, then use
					# that as the length of the record.
					pass
				elif textLength == 0x8000:
					# If the length is 32768, compute the actual length as follows:
					isLast = i == (len(indices)-1)
					if isLast:
						if isExtra:
							# For the last "extra" record (the very last record of the
							# table), the length is the difference between the total
							# length of the TSI1 table and the textOffset of the final
							# record.
							nextTextOffset = totalLength
						else:
							# For the last "normal" record (the last record just prior
							# to the record containing the "magic number"), the length
							# is the difference between the textOffset of the record
							# following the "magic number" (0xFFFE) record (i.e. the
							# first "extra" record), and the textOffset of the last
							# "normal" record.
							nextTextOffset = indextable.extra_indices[0][2]
					else:
						# For all other records with a length of 0x8000, the length is
						# the difference between the textOffset of the record in
						# question and the textOffset of the next record.
						nextTextOffset = indices[i+1][2]
					assert nextTextOffset >= textOffset, "entries not sorted by offset"
					if nextTextOffset > totalLength:
						self.log.warning(
							"nextTextOffset > totalLength; %r truncated" % name)
						nextTextOffset = totalLength
					textLength = nextTextOffset - textOffset
				else:
					from fontTools import ttLib
					raise ttLib.TTLibError(
						"%r textLength (%d) must not be > 32768" % (name, textLength))
				text = data[textOffset:textOffset+textLength]
				assert len(text) == textLength
				text = tostr(text, encoding='utf-8')
				if text:
					programs[name] = text
			if isExtra:
				self.extraPrograms = programs
			else:
				self.glyphPrograms = programs

	def compile(self, ttFont):
		if not hasattr(self, "glyphPrograms"):
			self.glyphPrograms = {}
			self.extraPrograms = {}
		data = b''
		indextable = ttFont[self.indextable]
		glyphNames = ttFont.getGlyphOrder()

		indices = []
		for i in range(len(glyphNames)):
			if len(data) % 2:
				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars. Yum.
			name = glyphNames[i]
			if name in self.glyphPrograms:
				text = tobytes(self.glyphPrograms[name], encoding="utf-8")
			else:
				text = b""
			textLength = len(text)
			if textLength >= 0x8000:
				textLength = 0x8000
			indices.append((i, textLength, len(data)))
			data = data + text

		extra_indices = []
		codes = sorted(self.extras.items())
		for i in range(len(codes)):
			if len(data) % 2:
				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars.
			code, name = codes[i]
			if name in self.extraPrograms:
				text = tobytes(self.extraPrograms[name], encoding="utf-8")
			else:
				text = b""
			textLength = len(text)
			if textLength >= 0x8000:
				textLength = 0x8000
			extra_indices.append((code, textLength, len(data)))
			data = data + text
		indextable.set(indices, extra_indices)
		return data

	def toXML(self, writer, ttFont):
		names = sorted(self.glyphPrograms.keys())
		writer.newline()
		for name in names:
			text = self.glyphPrograms[name]
			if not text:
				continue
			writer.begintag("glyphProgram", name=name)
			writer.newline()
			writer.write_noindent(text.replace("\r", "\n"))
			writer.newline()
			writer.endtag("glyphProgram")
			writer.newline()
			writer.newline()
		extra_names = sorted(self.extraPrograms.keys())
		for name in extra_names:
			text = self.extraPrograms[name]
			if not text:
				continue
			writer.begintag("extraProgram", name=name)
			writer.newline()
			writer.write_noindent(text.replace("\r", "\n"))
			writer.newline()
			writer.endtag("extraProgram")
			writer.newline()
			writer.newline()

	def fromXML(self, name, attrs, content, ttFont):
		if not hasattr(self, "glyphPrograms"):
			self.glyphPrograms = {}
			self.extraPrograms = {}
		lines = strjoin(content).replace("\r", "\n").split("\n")
		text = '\r'.join(lines[1:-1])
		if name == "glyphProgram":
			self.glyphPrograms[attrs["name"]] = text
		elif name == "extraProgram":
			self.extraPrograms[attrs["name"]] = text