1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
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
|