# Source code for fontTools.ttLib.tables.T_S_I__1

    """ 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 . import DefaultTable
    from fontTools.misc.loggingTools import LogMixin
    from fontTools.misc.textTools import strjoin, tobytes, tostr

    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