| from fontTools.misc import sstruct |
| from fontTools.misc.fixedTools import floatToFixedToStr |
| from fontTools.misc.textTools import safeEval |
| from . import DefaultTable |
| from . import grUtils |
| import struct |
| |
| Feat_hdr_format=''' |
| > |
| version: 16.16F |
| ''' |
| |
| class table_F__e_a_t(DefaultTable.DefaultTable): |
| """The ``Feat`` table is used exclusively by the Graphite shaping engine |
| to store features and possible settings specified in GDL. Graphite features |
| determine what rules are applied to transform a glyph stream. |
| |
| Not to be confused with ``feat``, or the OpenType Layout tables |
| ``GSUB``/``GPOS``.""" |
| |
| def __init__(self, tag=None): |
| DefaultTable.DefaultTable.__init__(self, tag) |
| self.features = {} |
| |
| def decompile(self, data, ttFont): |
| (_, data) = sstruct.unpack2(Feat_hdr_format, data, self) |
| self.version = float(floatToFixedToStr(self.version, precisionBits=16)) |
| numFeats, = struct.unpack('>H', data[:2]) |
| data = data[8:] |
| allfeats = [] |
| maxsetting = 0 |
| for i in range(numFeats): |
| if self.version >= 2.0: |
| (fid, nums, _, offset, flags, lid) = struct.unpack(">LHHLHH", |
| data[16*i:16*(i+1)]) |
| offset = int((offset - 12 - 16 * numFeats) / 4) |
| else: |
| (fid, nums, offset, flags, lid) = struct.unpack(">HHLHH", |
| data[12*i:12*(i+1)]) |
| offset = int((offset - 12 - 12 * numFeats) / 4) |
| allfeats.append((fid, nums, offset, flags, lid)) |
| maxsetting = max(maxsetting, offset + nums) |
| data = data[16*numFeats:] |
| allsettings = [] |
| for i in range(maxsetting): |
| if len(data) >= 4 * (i + 1): |
| (val, lid) = struct.unpack(">HH", data[4*i:4*(i+1)]) |
| allsettings.append((val, lid)) |
| for i,f in enumerate(allfeats): |
| (fid, nums, offset, flags, lid) = f |
| fobj = Feature() |
| fobj.flags = flags |
| fobj.label = lid |
| self.features[grUtils.num2tag(fid)] = fobj |
| fobj.settings = {} |
| fobj.default = None |
| fobj.index = i |
| for i in range(offset, offset + nums): |
| if i >= len(allsettings): continue |
| (vid, vlid) = allsettings[i] |
| fobj.settings[vid] = vlid |
| if fobj.default is None: |
| fobj.default = vid |
| |
| def compile(self, ttFont): |
| fdat = b"" |
| vdat = b"" |
| offset = 0 |
| for f, v in sorted(self.features.items(), key=lambda x:x[1].index): |
| fnum = grUtils.tag2num(f) |
| if self.version >= 2.0: |
| fdat += struct.pack(">LHHLHH", grUtils.tag2num(f), len(v.settings), |
| 0, offset * 4 + 12 + 16 * len(self.features), v.flags, v.label) |
| elif fnum > 65535: # self healing for alphabetic ids |
| self.version = 2.0 |
| return self.compile(ttFont) |
| else: |
| fdat += struct.pack(">HHLHH", grUtils.tag2num(f), len(v.settings), |
| offset * 4 + 12 + 12 * len(self.features), v.flags, v.label) |
| for s, l in sorted(v.settings.items(), key=lambda x:(-1, x[1]) if x[0] == v.default else x): |
| vdat += struct.pack(">HH", s, l) |
| offset += len(v.settings) |
| hdr = sstruct.pack(Feat_hdr_format, self) |
| return hdr + struct.pack('>HHL', len(self.features), 0, 0) + fdat + vdat |
| |
| def toXML(self, writer, ttFont): |
| writer.simpletag('version', version=self.version) |
| writer.newline() |
| for f, v in sorted(self.features.items(), key=lambda x:x[1].index): |
| writer.begintag('feature', fid=f, label=v.label, flags=v.flags, |
| default=(v.default if v.default else 0)) |
| writer.newline() |
| for s, l in sorted(v.settings.items()): |
| writer.simpletag('setting', value=s, label=l) |
| writer.newline() |
| writer.endtag('feature') |
| writer.newline() |
| |
| def fromXML(self, name, attrs, content, ttFont): |
| if name == 'version': |
| self.version = float(safeEval(attrs['version'])) |
| elif name == 'feature': |
| fid = attrs['fid'] |
| fobj = Feature() |
| fobj.flags = int(safeEval(attrs['flags'])) |
| fobj.label = int(safeEval(attrs['label'])) |
| fobj.default = int(safeEval(attrs.get('default','0'))) |
| fobj.index = len(self.features) |
| self.features[fid] = fobj |
| fobj.settings = {} |
| for element in content: |
| if not isinstance(element, tuple): continue |
| tag, a, c = element |
| if tag == 'setting': |
| fobj.settings[int(safeEval(a['value']))] = int(safeEval(a['label'])) |
| |
| class Feature(object): |
| pass |
| |