""" 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