1if not modules then modules = { } end modules ['font-imp-spacekerns'] = {
2 version = 1.001,
3 comment = "companion to font-ini.mkiv and hand-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9if not context then return end
10
11
12
13local type, next = type, next
14local insert, setmetatableindex = table.insert, table.setmetatableindex
15
16local fonts = fonts
17local otf = fonts.handlers.otf
18local fontdata = fonts.hashes.identifiers
19local fontfeatures = fonts.hashes.features
20local otffeatures = fonts.constructors.features.otf
21local registerotffeature = otffeatures.register
22local handlers = otf.handlers
23local setspacekerns = nodes.injections.setspacekerns
24
25function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr)
26 local features = fontfeatures[font]
27 local enabled = features and features.spacekern
28 if enabled then
29 setspacekerns(font,sequence)
30 end
31 return head, enabled
32end
33
34local function hasspacekerns(data)
35 local resources = data.resources
36 local sequences = resources.sequences
37 local validgpos = resources.features.gpos
38 if validgpos and sequences then
39 for i=1,#sequences do
40 local sequence = sequences[i]
41 local steps = sequence.steps
42 if steps then
43 local kind = sequence.type
44 if kind == "gpos_pair" then
45 for i=1,#steps do
46 local step = steps[i]
47 local coverage = step.coverage
48 local rules = step.rules
49
50
51
52
53
54
55
56 if coverage and not rules then
57 local format = step.format
58 if format == "move" or format == "kern" then
59 local kerns = coverage[32]
60 if kerns then
61 return true
62 end
63 for k, v in next, coverage do
64 if v[32] then
65 return true
66 end
67 end
68 elseif format == "pair" then
69 local kerns = coverage[32]
70 if kerns then
71 for k, v in next, kerns do
72 local one = v[1]
73 if one and one ~= true then
74 return true
75 end
76 end
77 end
78 for k, v in next, coverage do
79 local kern = v[32]
80 if kern then
81 local one = kern[1]
82 if one and one ~= true then
83 return true
84 end
85 end
86 end
87 end
88 end
89 end
90 end
91 end
92 end
93 end
94 return false
95end
96
97otf.readers.registerextender {
98 name = "spacekerns",
99 action = function(data)
100 data.properties.hasspacekerns = hasspacekerns(data)
101 end
102}
103
104local function newdata(t,k)
105 local v = {
106 left = { },
107 right = { },
108 last = 0,
109 feat = nil,
110 }
111 t[k] = v
112 return v
113end
114
115local function spaceinitializer(tfmdata,value)
116 local resources = tfmdata.resources
117 local spacekerns = resources and resources.spacekerns
118 if value and spacekerns == nil then
119 local rawdata = tfmdata.shared and tfmdata.shared.rawdata
120 local properties = rawdata.properties
121 if properties and properties.hasspacekerns then
122 local sequences = resources.sequences
123 local validgpos = resources.features.gpos
124 if validgpos and sequences then
125 local data = setmetatableindex(newdata)
126 for i=1,#sequences do
127 local sequence = sequences[i]
128 local steps = sequence.steps
129 if steps then
130
131
132
133 for tag, kern in next, sequence.features do
134
135 local d = data[tag]
136 local left = d.left
137 local right = d.right
138 local last = d.last
139 local feat = d.feat
140
141 local kind = sequence.type
142 if kind == "gpos_pair" then
143 if feat then
144 for script, languages in next, kern do
145 local f = feat[script]
146 if f then
147 for l in next, languages do
148 f[l] = true
149 end
150 else
151 feat[script] = languages
152 end
153 end
154 else
155 feat = kern
156 d.feat = feat
157 end
158 for i=1,#steps do
159 local step = steps[i]
160 local coverage = step.coverage
161 local rules = step.rules
162
163
164
165
166
167
168
169 if coverage and not rules then
170 local format = step.format
171 if format == "move" or format == "kern" then
172 local kerns = coverage[32]
173 if kerns then
174 for k, v in next, kerns do
175 right[k] = v
176 end
177 end
178 for k, v in next, coverage do
179 local kern = v[32]
180 if kern then
181 left[k] = kern
182 end
183 end
184 elseif format == "pair" then
185 local kerns = coverage[32]
186 if kerns then
187 for k, v in next, kerns do
188 local one = v[1]
189 if one and one ~= true then
190 right[k] = one[3]
191 end
192 end
193 end
194 for k, v in next, coverage do
195 local kern = v[32]
196 if kern then
197 local one = kern[1]
198 if one and one ~= true then
199 left[k] = one[3]
200 end
201 end
202 end
203 end
204 end
205 end
206 last = i
207 end
208d.last = last
209 end
210 end
211 end
212
213 for tag, d in next, data do
214 local left = d.left
215 local right = d.right
216 left = next(left) and left or false
217 right = next(right) and right or false
218 if left or right then
219
220 local last = d.last
221 local feat = d.feat
222
223 if last > 0 then
224 local triggersequence = {
225
226 features = { [tag] = feat or { dflt = { dflt = true, } } },
227 flags = noflags,
228 name = "trigger_space_kerns",
229 order = { tag },
230 type = "trigger_space_kerns",
231 left = left,
232 right = right,
233 }
234 insert(sequences,last,triggersequence)
235 d.last = d.last + 1
236 spacekerns = true
237 end
238 end
239
240 end
241 end
242 end
243 if not spacekerns then
244 spacekerns = false
245 end
246 resources.spacekerns = spacekerns
247 end
248 return spacekerns
249end
250
251registerotffeature {
252 name = "spacekern",
253 description = "space kern injection",
254 default = true,
255 initializers = {
256 node = spaceinitializer,
257 },
258}
259 |