text: from fontTools.misc.py23 import *
  from fontTools.misc import sstruct
  from fontTools.misc.fixedTools import floatToFixedToStr
  from fontTools.misc.textTools import safeEval
  from itertools import *
  from . import DefaultTable
  from . import grUtils
  from array import array
  from functools import reduce
  import struct, operator, warnings, re, sys
  
  Silf_hdr_format = '''
      >
      version:            16.16F
  '''
  
  Silf_hdr_format_3 = '''
      >
      version:            16.16F
      compilerVersion:    L
      numSilf:            H
                          x
                          x
  '''
  
  Silf_part1_format_v3 = '''
      >
      ruleVersion:        16.16F
      passOffset:         H
      pseudosOffset:      H
  '''
  
  Silf_part1_format = '''
      >
      maxGlyphID:         H
      extraAscent:        h
      extraDescent:       h
      numPasses:          B
      iSubst:             B
      iPos:               B
      iJust:              B
      iBidi:              B
      flags:              B
      maxPreContext:      B
      maxPostContext:     B
      attrPseudo:         B
      attrBreakWeight:    B
      attrDirectionality: B
      attrMirroring:      B
      attrSkipPasses:     B
      numJLevels:         B
  '''
  
  Silf_justify_format = '''
      >
      attrStretch:        B
      attrShrink:         B
      attrStep:           B
      attrWeight:         B
      runto:              B
                          x
                          x
                          x
  '''
  
  Silf_part2_format = '''
      >
      numLigComp:         H
      numUserDefn:        B
      maxCompPerLig:      B
      direction:          B
      attCollisions:      B
                          x
                          x
                          x
      numCritFeatures:    B
  '''
  
  Silf_pseudomap_format = '''
      >
      unicode:            L
      nPseudo:            H
  '''
  
  Silf_pseudomap_format_h = '''
      >
      unicode:            H
      nPseudo:            H
  '''
  
  Silf_classmap_format = '''
      >
      numClass:           H
      numLinear:          H
  '''
  
  Silf_lookupclass_format = '''
      >
      numIDs:             H
      searchRange:        H
      entrySelector:      H
      rangeShift:         H
  '''
  
  Silf_lookuppair_format = '''
      >
      glyphId:            H
      index:              H
  '''
  
  Silf_pass_format = '''
      >
      flags:              B
      maxRuleLoop:        B
      maxRuleContext:     B
      maxBackup:          B
      numRules:           H
      fsmOffset:          H
      pcCode:             L
      rcCode:             L
      aCode:              L
      oDebug:             L
      numRows:            H
      numTransitional:    H
      numSuccess:         H
      numColumns:         H
  '''
  
  aCode_info = (
      ("NOP", 0),
      ("PUSH_BYTE", "b"),
      ("PUSH_BYTE_U", "B"),
      ("PUSH_SHORT", ">h"),
      ("PUSH_SHORT_U", ">H"),
      ("PUSH_LONG", ">L"),
      ("ADD", 0),
      ("SUB", 0),
      ("MUL", 0),
      ("DIV", 0),
      ("MIN", 0),
      ("MAX", 0),
      ("NEG", 0),
      ("TRUNC8", 0),
      ("TRUNC16", 0),
      ("COND", 0),
      ("AND", 0),         # x10
      ("OR", 0),
      ("NOT", 0),
      ("EQUAL", 0),
      ("NOT_EQ", 0),
      ("LESS", 0),
      ("GTR", 0),
      ("LESS_EQ", 0),
      ("GTR_EQ", 0),
      ("NEXT", 0),
      ("NEXT_N", "b"),
      ("COPY_NEXT", 0),
      ("PUT_GLYPH_8BIT_OBS", "B"),
      ("PUT_SUBS_8BIT_OBS", "bBB"),
      ("PUT_COPY", "b"),
      ("INSERT", 0),
      ("DELETE", 0),      # x20
      ("ASSOC", -1),
      ("CNTXT_ITEM", "bB"),
      ("ATTR_SET", "B"),
      ("ATTR_ADD", "B"),
      ("ATTR_SUB", "B"),
      ("ATTR_SET_SLOT", "B"),
      ("IATTR_SET_SLOT", "BB"),
      ("PUSH_SLOT_ATTR", "Bb"),
      ("PUSH_GLYPH_ATTR_OBS", "Bb"),
      ("PUSH_GLYPH_METRIC", "Bbb"),
      ("PUSH_FEAT", "Bb"),
      ("PUSH_ATT_TO_GATTR_OBS", "Bb"),
      ("PUSH_ATT_TO_GLYPH_METRIC", "Bbb"),
      ("PUSH_ISLOT_ATTR", "Bbb"),
      ("PUSH_IGLYPH_ATTR", "Bbb"),
      ("POP_RET", 0),     # x30
      ("RET_ZERO", 0),
      ("RET_TRUE", 0),
      ("IATTR_SET", "BB"),
      ("IATTR_ADD", "BB"),
      ("IATTR_SUB", "BB"),
      ("PUSH_PROC_STATE", "B"),
      ("PUSH_VERSION", 0),
      ("PUT_SUBS", ">bHH"),
      ("PUT_SUBS2", 0),
      ("PUT_SUBS3", 0),
      ("PUT_GLYPH", ">H"),
      ("PUSH_GLYPH_ATTR", ">Hb"),
      ("PUSH_ATT_TO_GLYPH_ATTR", ">Hb"),
      ("BITOR", 0),
      ("BITAND", 0),
      ("BITNOT", 0),      # x40
      ("BITSET", ">HH"),
      ("SET_FEAT", "Bb")
  )
  aCode_map = dict([(x[0], (i, x[1])) for i,x in enumerate(aCode_info)])
  
  def disassemble(aCode):
      codelen = len(aCode)
      pc = 0
      res = []
      while pc < codelen:
          opcode = byteord(aCode[pc:pc+1])
          if opcode > len(aCode_info):
              instr = aCode_info[0]
          else:
              instr = aCode_info[opcode]
          pc += 1
          if instr[1] != 0 and pc >= codelen : return res
          if instr[1] == -1:
              count = byteord(aCode[pc])
              fmt = "%dB" % count
              pc += 1
          elif instr[1] == 0:
              fmt = ""
          else :
              fmt = instr[1]
          if fmt == "":
              res.append(instr[0])
              continue
          parms = struct.unpack_from(fmt, aCode[pc:])
          res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")")
          pc += struct.calcsize(fmt)
      return res
  
  instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?")
  def assemble(instrs):
      res = b""
      for inst in instrs:
          m = instre.match(inst)
          if not m or not m.group(1) in aCode_map:
              continue
          opcode, parmfmt = aCode_map[m.group(1)]
          res += struct.pack("B", opcode)
          if m.group(2):
              if parmfmt == 0:
                  continue
              parms = [int(x) for x in re.split(r",\s*", m.group(2))]
              if parmfmt == -1:
                  l = len(parms)
                  res += struct.pack(("%dB" % (l+1)), l, *parms)
              else:
                  res += struct.pack(parmfmt, *parms)
      return res
  
  def writecode(tag, writer, instrs):
      writer.begintag(tag)
      writer.newline()
      for l in disassemble(instrs):
          writer.write(l)
          writer.newline()
      writer.endtag(tag)
      writer.newline()
  
  def readcode(content):
      res = []
      for e in content_string(content).split('\n'):
          e = e.strip()
          if not len(e): continue
          res.append(e)
      return assemble(res)
      
  attrs_info=('flags', 'extraAscent', 'extraDescent', 'maxGlyphID',
              'numLigComp', 'numUserDefn', 'maxCompPerLig', 'direction', 'lbGID')
  attrs_passindexes = ('iSubst', 'iPos', 'iJust', 'iBidi')
  attrs_contexts = ('maxPreContext', 'maxPostContext')
  attrs_attributes = ('attrPseudo', 'attrBreakWeight', 'attrDirectionality',
                      'attrMirroring', 'attrSkipPasses', 'attCollisions')
  pass_attrs_info = ('flags', 'maxRuleLoop', 'maxRuleContext', 'maxBackup',
              'minRulePreContext', 'maxRulePreContext', 'collisionThreshold')
  pass_attrs_fsm = ('numRows', 'numTransitional', 'numSuccess', 'numColumns')
  
  def writesimple(tag, self, writer, *attrkeys):
      attrs = dict([(k, getattr(self, k)) for k in attrkeys])
      writer.simpletag(tag, **attrs)
      writer.newline()
  
  def getSimple(self, attrs, *attr_list):
      for k in attr_list:
          if k in attrs:
              setattr(self, k, int(safeEval(attrs[k])))
  
  def content_string(contents):
      res = ""
      for element in contents:
          if isinstance(element, tuple): continue
          res += element
      return res.strip()
  
  def wrapline(writer, dat, length=80):
      currline = ""
      for d in dat:
          if len(currline) > length:
              writer.write(currline[:-1])
              writer.newline()
              currline = ""
          currline += d + " "
      if len(currline):
          writer.write(currline[:-1])
          writer.newline()
  
  class _Object() :
      pass
  
  class table_S__i_l_f(DefaultTable.DefaultTable):
      '''Silf table support'''
  
      def __init__(self, tag=None):
          DefaultTable.DefaultTable.__init__(self, tag)
          self.silfs = []
  
      def decompile(self, data, ttFont):
          sstruct.unpack2(Silf_hdr_format, data, self)
          self.version = float(floatToFixedToStr(self.version, precisionBits=16))
          if self.version >= 5.0:
              (data, self.scheme) = grUtils.decompress(data)
              sstruct.unpack2(Silf_hdr_format_3, data, self)
              base = sstruct.calcsize(Silf_hdr_format_3)
          elif self.version < 3.0:
              self.numSilf = struct.unpack('>H', data[4:6])
              self.scheme = 0
              self.compilerVersion = 0
              base = 8
          else:
              self.scheme = 0
              sstruct.unpack2(Silf_hdr_format_3, data, self)
              base = sstruct.calcsize(Silf_hdr_format_3)
  
          silfoffsets = struct.unpack_from(('>%dL' % self.numSilf), data[base:])
          for offset in silfoffsets:
              s = Silf()
              self.silfs.append(s)
              s.decompile(data[offset:], ttFont, self.version)
  
      def compile(self, ttFont):
          self.numSilf = len(self.silfs)
          if self.version < 3.0:
              hdr = sstruct.pack(Silf_hdr_format, self)
              hdr += struct.pack(">HH", self.numSilf, 0)
          else:
              hdr = sstruct.pack(Silf_hdr_format_3, self)
          offset = len(hdr) + 4 * self.numSilf
          data = b""
          for s in self.silfs:
              hdr += struct.pack(">L", offset)
              subdata = s.compile(ttFont, self.version)
              offset += len(subdata)
              data += subdata
          if self.version >= 5.0:
              return grUtils.compress(self.scheme, hdr+data)
          return hdr+data
  
      def toXML(self, writer, ttFont):
          writer.comment('Attributes starting with _ are informative only')
          writer.newline()
          writer.simpletag('version', version=self.version,
              compilerVersion=self.compilerVersion, compressionScheme=self.scheme)
          writer.newline()
          for s in self.silfs:
              writer.begintag('silf')
              writer.newline()
              s.toXML(writer, ttFont, self.version)
              writer.endtag('silf')
              writer.newline()
  
      def fromXML(self, name, attrs, content, ttFont):
          if name == 'version':
              self.scheme=int(safeEval(attrs['compressionScheme']))
              self.version = float(safeEval(attrs['version']))
              self.compilerVersion = int(safeEval(attrs['compilerVersion']))
              return
          if name == 'silf':
              s = Silf()
              self.silfs.append(s)
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, attrs, subcontent = element
                  s.fromXML(tag, attrs, subcontent, ttFont, self.version)
  
  class Silf(object):
      '''A particular Silf subtable'''
  
      def __init__(self):
          self.passes = []
          self.scriptTags = []
          self.critFeatures = []
          self.jLevels = []
          self.pMap = {}
  
      def decompile(self, data, ttFont, version=2.0):
          if version >= 3.0 :
              _, data = sstruct.unpack2(Silf_part1_format_v3, data, self)
              self.ruleVersion = float(floatToFixedToStr(self.ruleVersion, precisionBits=16))
          _, data = sstruct.unpack2(Silf_part1_format, data, self)
          for jlevel in range(self.numJLevels):
              j, data = sstruct.unpack2(Silf_justify_format, data, _Object())
              self.jLevels.append(j)
          _, data = sstruct.unpack2(Silf_part2_format, data, self)
          if self.numCritFeatures:
              self.critFeatures = struct.unpack_from(('>%dH' % self.numCritFeatures), data)
          data = data[self.numCritFeatures * 2 + 1:]
          (numScriptTag,) = struct.unpack_from('B', data)
          if numScriptTag:
              self.scriptTags = [struct.unpack("4s", data[x:x+4])[0].decode("ascii") for x in range(1, 1 + 4 * numScriptTag, 4)]
          data = data[1 + 4 * numScriptTag:]
          (self.lbGID,) = struct.unpack('>H', data[:2])
          if self.numPasses:
              self.oPasses = struct.unpack(('>%dL' % (self.numPasses+1)), data[2:6+4*self.numPasses])
          data = data[6 + 4 * self.numPasses:]
          (numPseudo,) = struct.unpack(">H", data[:2])
          for i in range(numPseudo):
              if version >= 3.0:
                  pseudo = sstruct.unpack(Silf_pseudomap_format, data[8+6*i:14+6*i], _Object())
              else:
                  pseudo = sstruct.unpack(Silf_pseudomap_format_h, data[8+4*i:12+4*i], _Object())
              self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo)
          data = data[8 + 6 * numPseudo:]
          currpos = (sstruct.calcsize(Silf_part1_format)
                      + sstruct.calcsize(Silf_justify_format) * self.numJLevels
                      + sstruct.calcsize(Silf_part2_format) + 2 * self.numCritFeatures
                      + 1 + 1 + 4 * numScriptTag + 6 + 4 * self.numPasses + 8 + 6 * numPseudo)
          if version >= 3.0:
              currpos += sstruct.calcsize(Silf_part1_format_v3)
          self.classes = Classes()
          self.classes.decompile(data, ttFont, version)
          for i in range(self.numPasses):
              p = Pass()
              self.passes.append(p)
              p.decompile(data[self.oPasses[i]-currpos:self.oPasses[i+1]-currpos],
                          ttFont, version)
  
      def compile(self, ttFont, version=2.0):
          self.numPasses = len(self.passes)
          self.numJLevels = len(self.jLevels)
          self.numCritFeatures = len(self.critFeatures)
          numPseudo = len(self.pMap)
          data = b""
          if version >= 3.0:
              hdroffset = sstruct.calcsize(Silf_part1_format_v3)
          else:
              hdroffset = 0
          data += sstruct.pack(Silf_part1_format, self)
          for j in self.jLevels:
              data += sstruct.pack(Silf_justify_format, j)
          data += sstruct.pack(Silf_part2_format, self)
          if self.numCritFeatures:
              data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures)
          data += struct.pack("BB", 0, len(self.scriptTags))
          if len(self.scriptTags):
              tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags]
              data += b"".join(tdata)
          data += struct.pack(">H", self.lbGID)
          self.passOffset = len(data)
  
          data1 = grUtils.bininfo(numPseudo, 6)
          currpos = hdroffset + len(data) + 4 * (self.numPasses + 1)
          self.pseudosOffset = currpos + len(data1)
          for u, p in sorted(self.pMap.items()):
              data1 += struct.pack((">LH" if version >= 3.0 else ">HH"),
                                  u, ttFont.getGlyphID(p))
          data1 += self.classes.compile(ttFont, version)
          currpos += len(data1)
          data2 = b""
          datao = b""
          for i, p in enumerate(self.passes):
              base = currpos + len(data2)
              datao += struct.pack(">L", base)
              data2 += p.compile(ttFont, base, version)
          datao += struct.pack(">L", currpos + len(data2))
  
          if version >= 3.0:
              data3 = sstruct.pack(Silf_part1_format_v3, self)
          else:
              data3 = b""
          return data3 + data + datao + data1 + data2
  
  
      def toXML(self, writer, ttFont, version=2.0):
          if version >= 3.0:
              writer.simpletag('version', ruleVersion=self.ruleVersion)
              writer.newline()
          writesimple('info', self, writer, *attrs_info)
          writesimple('passindexes', self, writer, *attrs_passindexes)
          writesimple('contexts', self, writer, *attrs_contexts)
          writesimple('attributes', self, writer, *attrs_attributes)
          if len(self.jLevels):
              writer.begintag('justifications')
              writer.newline()
              jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format)
              for i, j in enumerate(self.jLevels):
                  attrs = dict([(k, getattr(j, k)) for k in jnames])
                  writer.simpletag('justify', **attrs)
                  writer.newline()
              writer.endtag('justifications')
              writer.newline()
          if len(self.critFeatures):
              writer.begintag('critFeatures')
              writer.newline()
              writer.write(" ".join(map(str, self.critFeatures)))
              writer.newline()
              writer.endtag('critFeatures')
              writer.newline()
          if len(self.scriptTags):
              writer.begintag('scriptTags')
              writer.newline()
              writer.write(" ".join(self.scriptTags))
              writer.newline()
              writer.endtag('scriptTags')
              writer.newline()
          if self.pMap:
              writer.begintag('pseudoMap')
              writer.newline()
              for k, v in sorted(self.pMap.items()):
                  writer.simpletag('pseudo', unicode=hex(k), pseudo=v)
                  writer.newline()
              writer.endtag('pseudoMap')
              writer.newline()
          self.classes.toXML(writer, ttFont, version)
          if len(self.passes):
              writer.begintag('passes')
              writer.newline()
              for i, p in enumerate(self.passes):
                  writer.begintag('pass', _index=i)
                  writer.newline()
                  p.toXML(writer, ttFont, version)
                  writer.endtag('pass')
                  writer.newline()
              writer.endtag('passes')
              writer.newline()
  
      def fromXML(self, name, attrs, content, ttFont, version=2.0):
          if name == 'version':
              self.ruleVersion = float(safeEval(attrs.get('ruleVersion', "0")))
          if name == 'info':
              getSimple(self, attrs, *attrs_info)
          elif name == 'passindexes':
              getSimple(self, attrs, *attrs_passindexes)
          elif name == 'contexts':
              getSimple(self, attrs, *attrs_contexts)
          elif name == 'attributes':
              getSimple(self, attrs, *attrs_attributes)
          elif name == 'justifications':
              for element in content:
                  if not isinstance(element, tuple): continue
                  (tag, attrs, subcontent) = element
                  if tag == 'justify':
                      j = _Object()
                      for k, v in attrs.items():
                          setattr(j, k, int(v))
                      self.jLevels.append(j)
          elif name == 'critFeatures':
              self.critFeatures = []
              element = content_string(content)
              self.critFeatures.extend(map(int, element.split()))
          elif name == 'scriptTags':
              self.scriptTags = []
              element = content_string(content)
              for n in element.split():
                  self.scriptTags.append(n)
          elif name == 'pseudoMap':
              self.pMap = {}
              for element in content:
                  if not isinstance(element, tuple): continue
                  (tag, attrs, subcontent) = element
                  if tag == 'pseudo':
                      k = int(attrs['unicode'], 16)
                      v = attrs['pseudo']
                  self.pMap[k] = v
          elif name == 'classes':
              self.classes = Classes()
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, attrs, subcontent = element
                  self.classes.fromXML(tag, attrs, subcontent, ttFont, version)
          elif name == 'passes':
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, attrs, subcontent = element
                  if tag == 'pass':
                      p = Pass()
                      for e in subcontent:
                          if not isinstance(e, tuple): continue
                          p.fromXML(e[0], e[1], e[2], ttFont, version)
                      self.passes.append(p)
  
  
  class Classes(object):
  
      def __init__(self):
          self.linear = []
          self.nonLinear = []
  
      def decompile(self, data, ttFont, version=2.0):
          sstruct.unpack2(Silf_classmap_format, data, self)
          if version >= 4.0 :
              oClasses = struct.unpack((">%dL" % (self.numClass+1)),
                                          data[4:8+4*self.numClass])
          else:
              oClasses = struct.unpack((">%dH" % (self.numClass+1)),
                                          data[4:6+2*self.numClass])
          for s,e in zip(oClasses[:self.numLinear], oClasses[1:self.numLinear+1]):
              self.linear.append(ttFont.getGlyphName(x) for x in
                                     struct.unpack((">%dH" % ((e-s)/2)), data[s:e]))
          for s,e in zip(oClasses[self.numLinear:self.numClass],
                          oClasses[self.numLinear+1:self.numClass+1]):
              nonLinids = [struct.unpack(">HH", data[x:x+4]) for x in range(s+8, e, 4)]
              nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids])
              self.nonLinear.append(nonLin)
  
      def compile(self, ttFont, version=2.0):
          data = b""
          oClasses = []
          if version >= 4.0:
              offset = 8 + 4 * (len(self.linear) + len(self.nonLinear))
          else:
              offset = 6 + 2 * (len(self.linear) + len(self.nonLinear))
          for l in self.linear:
              oClasses.append(len(data) + offset)
              gs = [ttFont.getGlyphID(x) for x in l]
              data += struct.pack((">%dH" % len(l)), *gs)
          for l in self.nonLinear:
              oClasses.append(len(data) + offset)
              gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()]
              data += grUtils.bininfo(len(gs))
              data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)])
          oClasses.append(len(data) + offset)
          self.numClass = len(oClasses) - 1
          self.numLinear = len(self.linear)
          return sstruct.pack(Silf_classmap_format, self) + \
                 struct.pack(((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)),
                              *oClasses) + data
  
      def toXML(self, writer, ttFont, version=2.0):
          writer.begintag('classes')
          writer.newline()
          writer.begintag('linearClasses')
          writer.newline()
          for i,l in enumerate(self.linear):
              writer.begintag('linear', _index=i)
              writer.newline()
              wrapline(writer, l)
              writer.endtag('linear')
              writer.newline()
          writer.endtag('linearClasses')
          writer.newline()
          writer.begintag('nonLinearClasses')
          writer.newline()
          for i, l in enumerate(self.nonLinear):
              writer.begintag('nonLinear', _index=i + self.numLinear)
              writer.newline()
              for inp, ind in l.items():
                  writer.simpletag('map', glyph=inp, index=ind)
                  writer.newline()
              writer.endtag('nonLinear')
              writer.newline()
          writer.endtag('nonLinearClasses')
          writer.newline()
          writer.endtag('classes')
          writer.newline()
  
      def fromXML(self, name, attrs, content, ttFont, version=2.0):
          if name == 'linearClasses':
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, attrs, subcontent = element
                  if tag == 'linear':
                      l = content_string(subcontent).split()
                      self.linear.append(l)
          elif name == 'nonLinearClasses':
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, attrs, subcontent = element
                  if tag =='nonLinear':
                      l = {}
                      for e in subcontent:
                          if not isinstance(e, tuple): continue
                          tag, attrs, subsubcontent = e
                          if tag == 'map':
                              l[attrs['glyph']] = int(safeEval(attrs['index']))
                      self.nonLinear.append(l)
  
  class Pass(object):
  
      def __init__(self):
          self.colMap = {}
          self.rules = []
          self.rulePreContexts = []
          self.ruleSortKeys = []
          self.ruleConstraints = []
          self.passConstraints = b""
          self.actions = []
          self.stateTrans = []
          self.startStates = []
  
      def decompile(self, data, ttFont, version=2.0):
          _, data = sstruct.unpack2(Silf_pass_format, data, self)
          (numRange, _, _, _) = struct.unpack(">4H", data[:8])
          data = data[8:]
          for i in range(numRange):
              (first, last, col) = struct.unpack(">3H", data[6*i:6*i+6])
              for g in range(first, last+1):
                  self.colMap[ttFont.getGlyphName(g)] = col
          data = data[6*numRange:]
          oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data)
          data = data[2+2*self.numSuccess:]
          rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data)
          self.rules = [rules[s:e] for (s,e) in zip(oRuleMap, oRuleMap[1:])]
          data = data[2*oRuleMap[-1]:]
          (self.minRulePreContext, self.maxRulePreContext) = struct.unpack('BB', data[:2])
          numStartStates = self.maxRulePreContext - self.minRulePreContext + 1
          self.startStates = struct.unpack((">%dH" % numStartStates),
                                          data[2:2 + numStartStates * 2])
          data = data[2+numStartStates*2:]
          self.ruleSortKeys = struct.unpack((">%dH" % self.numRules), data[:2 * self.numRules])
          data = data[2*self.numRules:]
          self.rulePreContexts = struct.unpack(("%dB" % self.numRules), data[:self.numRules])
          data = data[self.numRules:]
          (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3])
          oConstraints = list(struct.unpack((">%dH" % (self.numRules + 1)),
                                          data[3:5 + self.numRules * 2]))
          data = data[5 + self.numRules * 2:]
          oActions = list(struct.unpack((">%dH" % (self.numRules + 1)),
                                          data[:2 + self.numRules * 2]))
          data = data[2 * self.numRules + 2:]
          for i in range(self.numTransitional):
              a = array("H", data[i*self.numColumns*2:(i+1)*self.numColumns*2])
              if sys.byteorder != "big": a.byteswap()
              self.stateTrans.append(a)
          data = data[self.numTransitional * self.numColumns * 2 + 1:]
          self.passConstraints = data[:pConstraint]
          data = data[pConstraint:]
          for i in range(len(oConstraints)-2,-1,-1):
              if oConstraints[i] == 0 :
                  oConstraints[i] = oConstraints[i+1]
          self.ruleConstraints = [(data[s:e] if (e-s > 1) else b"") for (s,e) in zip(oConstraints, oConstraints[1:])]
          data = data[oConstraints[-1]:]
          self.actions = [(data[s:e] if (e-s > 1) else "") for (s,e) in zip(oActions, oActions[1:])]
          data = data[oActions[-1]:]
          # not using debug
  
      def compile(self, ttFont, base, version=2.0):
          # build it all up backwards
          oActions = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.actions + [b""], (0, []))[1]
          oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [b""], (1, []))[1]
          constraintCode = b"\000" + b"".join(self.ruleConstraints)
          transes = []
          for t in self.stateTrans:
              if sys.byteorder != "big": t.byteswap()
              transes.append(t.tobytes())
              if sys.byteorder != "big": t.byteswap()
          if not len(transes):
              self.startStates = [0]
          oRuleMap = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.rules+[[]], (0, []))[1]
          passRanges = []
          gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()])
          for e in grUtils.entries(gidcolmap, sameval = True):
              if e[1]:
                  passRanges.append((e[0], e[0]+e[1]-1, e[2][0]))
          self.numRules = len(self.actions)
          self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6
                      + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2
                      + 2 * len(self.startStates) + 3 * self.numRules + 3
                      + 4 * self.numRules + 4)
          self.pcCode = self.fsmOffset + 2*self.numTransitional*self.numColumns + 1 + base
          self.rcCode = self.pcCode + len(self.passConstraints)
          self.aCode = self.rcCode + len(constraintCode)
          self.oDebug = 0
          # now generate output
          data = sstruct.pack(Silf_pass_format, self)
          data += grUtils.bininfo(len(passRanges), 6)
          data += b"".join(struct.pack(">3H", *p) for p in passRanges)
          data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap)
          flatrules = reduce(lambda a,x: a+x, self.rules, [])
          data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules)
          data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext)
          data += struct.pack((">%dH" % len(self.startStates)), *self.startStates)
          data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys)
          data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts)
          data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints))
          data += struct.pack((">%dH" % (self.numRules+1)), *oConstraints)
          data += struct.pack((">%dH" % (self.numRules+1)), *oActions)
          return data + b"".join(transes) + struct.pack("B", 0) + \
                  self.passConstraints + constraintCode + b"".join(self.actions)
  
      def toXML(self, writer, ttFont, version=2.0):
          writesimple('info', self, writer, *pass_attrs_info)
          writesimple('fsminfo', self, writer, *pass_attrs_fsm)
          writer.begintag('colmap')
          writer.newline()
          wrapline(writer, ["{}={}".format(*x) for x in sorted(self.colMap.items(),
                                          key=lambda x:ttFont.getGlyphID(x[0]))])
          writer.endtag('colmap')
          writer.newline()
          writer.begintag('staterulemap')
          writer.newline()
          for i, r in enumerate(self.rules):
              writer.simpletag('state', number = self.numRows - self.numSuccess + i,
                                  rules = " ".join(map(str, r)))
              writer.newline()
          writer.endtag('staterulemap')
          writer.newline()
          writer.begintag('rules')
          writer.newline()
          for i in range(len(self.actions)):
              writer.begintag('rule', index=i, precontext=self.rulePreContexts[i],
                              sortkey=self.ruleSortKeys[i])
              writer.newline()
              if len(self.ruleConstraints[i]):
                  writecode('constraint', writer, self.ruleConstraints[i])
              writecode('action', writer, self.actions[i])
              writer.endtag('rule')
              writer.newline()
          writer.endtag('rules')
          writer.newline()
          if len(self.passConstraints):
              writecode('passConstraint', writer, self.passConstraints)
          if len(self.stateTrans):
              writer.begintag('fsm')
              writer.newline()
              writer.begintag('starts')
              writer.write(" ".join(map(str, self.startStates)))
              writer.endtag('starts')
              writer.newline()
              for i, s in enumerate(self.stateTrans):
                  writer.begintag('row', _i=i)
                  # no newlines here
                  writer.write(" ".join(map(str, s)))
                  writer.endtag('row')
                  writer.newline()
              writer.endtag('fsm')
              writer.newline()
  
      def fromXML(self, name, attrs, content, ttFont, version=2.0):
          if name == 'info':
              getSimple(self, attrs, *pass_attrs_info)
          elif name == 'fsminfo':
              getSimple(self, attrs, *pass_attrs_fsm)
          elif name == 'colmap':
              e = content_string(content)
              for w in e.split():
                  x = w.split('=')
                  if len(x) != 2 or x[0] == '' or x[1] == '': continue
                  self.colMap[x[0]] = int(x[1])
          elif name == 'staterulemap':
              for e in content:
                  if not isinstance(e, tuple): continue
                  tag, a, c = e
                  if tag == 'state':
                      self.rules.append([int(x) for x in a['rules'].split(" ")])
          elif name == 'rules':
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, a, c = element
                  if tag != 'rule': continue
                  self.rulePreContexts.append(int(a['precontext']))
                  self.ruleSortKeys.append(int(a['sortkey']))
                  con = b""
                  act = b""
                  for e in c:
                      if not isinstance(e, tuple): continue
                      tag, a, subc = e
                      if tag == 'constraint':
                          con = readcode(subc)
                      elif tag == 'action':
                          act = readcode(subc)
                  self.actions.append(act)
                  self.ruleConstraints.append(con)
          elif name == 'passConstraint':
              self.passConstraints = readcode(content)
          elif name == 'fsm':
              for element in content:
                  if not isinstance(element, tuple): continue
                  tag, a, c = element
                  if tag == 'row':
                      s = array('H')
                      e = content_string(c)
                      s.extend(map(int, e.split()))
                      self.stateTrans.append(s)
                  elif tag == 'starts':
                      s = []
                      e = content_string(c)
                      s.extend(map(int, e.split()))
                      self.startStates = s
  
  