| """ 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 __future__ import print_function, division, absolute_import |
| from fontTools.misc.py23 import * |
| 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 = tounicode(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 |