1if not modules then modules = { } end modules ['font-otd'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to font-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10local type = type
11local match = string.match
12local sequenced = table.sequenced
13
14local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end)
15local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end)
16
17local report_otf = logs.reporter("fonts","otf loading")
18local report_process = logs.reporter("fonts","otf process")
19
20local allocate = utilities.storage.allocate
21
22local fonts = fonts
23local otf = fonts.handlers.otf
24local hashes = fonts.hashes
25local definers = fonts.definers
26local constructors = fonts.constructors
27local specifiers = fonts.specifiers
28
29local fontidentifiers = hashes.identifiers
30local fontresources = hashes.resources
31local fontproperties = hashes.properties
32local fontdynamics = hashes.dynamics
33
34local contextsetups = specifiers.contextsetups
35local contextnumbers = specifiers.contextnumbers
36local contextmerged = specifiers.contextmerged
37
38local setmetatableindex = table.setmetatableindex
39
40local a_to_script = { }
41local a_to_language = { }
42
43
44
45function otf.setdynamics(font,attribute)
46
47 local features = contextsetups[attribute]
48 if features then
49 local dynamics = fontdynamics[font]
50 dynamic = contextmerged[attribute] or 0
51 local script, language
52 if dynamic == 2 then
53 language = features.language or fontproperties[font].language or "dflt"
54 script = features.script or fontproperties[font].script or "dflt"
55 else
56 language = features.language or "dflt"
57 script = features.script or "dflt"
58 end
59 if script == "auto" then
60
61 script = definers.checkedscript(fontidentifiers[font],fontresources[font],features)
62 end
63 local ds = dynamics[script]
64
65 if not ds then
66 ds = { }
67 dynamics[script] = ds
68 end
69 local dsl = ds[language]
70
71 if not dsl then
72 dsl = { }
73 ds[language] = dsl
74 end
75 local dsla = dsl[attribute]
76 if not dsla then
77 local tfmdata = fontidentifiers[font]
78 a_to_script [attribute] = script
79 a_to_language[attribute] = language
80
81 local properties = tfmdata.properties
82 local shared = tfmdata.shared
83 local s_script = properties.script
84 local s_language = properties.language
85 local s_mode = properties.mode
86 local s_features = shared.features
87 properties.mode = "node"
88 properties.language = language
89 properties.script = script
90 properties.dynamics = true
91 shared.features = { }
92
93 local set = constructors.checkedfeatures("otf",features)
94 set.mode = "node"
95 dsla = otf.setfeatures(tfmdata,set)
96 if trace_dynamics then
97 report_otf("setting dynamics %s: attribute %a, script %a, language %a, set %a",contextnumbers[attribute],attribute,script,language,set)
98 end
99
100 properties.script = s_script
101 properties.language = s_language
102 properties.mode = s_mode
103 shared.features = s_features
104
105 dynamics[script][language][attribute] = dsla
106 elseif trace_dynamics then
107
108 end
109 return dsla
110 end
111end
112
113function otf.scriptandlanguage(tfmdata,attr)
114 local properties = tfmdata.properties
115 if attr and attr > 0 then
116 return a_to_script[attr] or properties.script or "dflt", a_to_language[attr] or properties.language or "dflt"
117 else
118 return properties.script or "dflt", properties.language or "dflt"
119 end
120end
121
122
123
124local autofeatures = fonts.analyzers.features
125local featuretypes = otf.tables.featuretypes
126local defaultscript = otf.features.checkeddefaultscript
127local defaultlanguage = otf.features.checkeddefaultlanguage
128
129local resolved = { }
130local wildcard = "*"
131
132
133
134
135
136local P, C, Cc, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.match
137
138local pattern = P("always") * (P(-1) * Cc(true) + P(":") * C((1-P(-1))^1))
139
140local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage)
141 local features = sequence.features
142 if features then
143 local order = sequence.order
144 if order then
145 local featuretype = featuretypes[sequence.type or "unknown"]
146 local lookupdone = false
147 for i=1,#order do
148 local kind = order[i]
149 local e_e
150 local a_e = a_enabled and a_enabled[kind]
151 if a_e ~= nil then
152 e_e = a_e
153 else
154 e_e = s_enabled and s_enabled[kind]
155 end
156 if e_e then
157 local usedattribute, usedscript, usedlanguage, usedlookup
158 local valid = type(e_e) == "string" and lpegmatch(pattern,e_e)
159 if valid then
160
161 usedattribute = autofeatures[kind] or false
162 usedlanguage = "*"
163 usedscript = "*"
164 usedlookup = { valid, usedattribute, sequence, kind }
165 else
166
167 local scripts = features[kind]
168 local languages = scripts[script] or scripts[wildcard]
169 if not languages and autoscript then
170 langages = defaultscript(featuretype,autoscript,scripts)
171 end
172 if languages then
173
174
175
176 if languages[language] then
177 valid = e_e
178 elseif languages[wildcard] then
179 valid = e_e
180 elseif autolanguage and defaultlanguage(featuretype,autolanguage,languages) then
181 valid = e_e
182 end
183 end
184 if valid then
185 usedattribute = autofeatures[kind] or false
186 usedlanguage = script
187 usedscript = language
188 usedlookup = { valid, usedattribute, sequence, kind }
189 end
190 end
191 if not usedlookup then
192
193 elseif lookupdone then
194 if trace_applied then
195 report_process(
196 "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a, nofsteps %a, lookup already set by %a",
197 font,attr or 0,dynamic,kind,usedscript,usedlanguage,sequence.name,valid,sequence.nofsteps,ra[#ra][4])
198 end
199 else
200 ra[#ra+1] = usedlookup
201 if trace_applied then
202 report_process(
203 "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a, nofsteps %a",
204 font,attr or 0,dynamic,kind,usedscript,usedlanguage,sequence.name,valid,sequence.nofsteps)
205 else
206 return
207 end
208 lookupdone = true
209 end
210 end
211 end
212 end
213 end
214end
215
216
217
218function otf.dataset(tfmdata,font,attr)
219
220 local script, language, s_enabled, a_enabled, dynamic
221
222 if attr and attr ~= 0 then
223 dynamic = contextmerged[attr] or 0
224
225 local features = contextsetups[attr]
226 a_enabled = features
227 if dynamic == 1 then
228
229 language = features.language or "dflt"
230 script = features.script or "dflt"
231 elseif dynamic == 2 then
232
233 local properties = tfmdata.properties
234 s_enabled = tfmdata.shared.features
235 language = features.language or properties.language or "dflt"
236 script = features.script or properties.script or "dflt"
237 else
238
239 local properties = tfmdata.properties
240 language = properties.language or "dflt"
241 script = properties.script or "dflt"
242 end
243 else
244 local properties = tfmdata.properties
245 language = properties.language or "dflt"
246 script = properties.script or "dflt"
247 s_enabled = tfmdata.shared.features
248 dynamic = 0
249 end
250
251 local res = resolved[font]
252 if not res then
253 res = { }
254 resolved[font] = res
255 end
256 local rs = res[script]
257 if not rs then
258 rs = { }
259 res[script] = rs
260 end
261 local rl = rs[language]
262 if not rl then
263 rl = { }
264 rs[language] = rl
265 end
266 local ra = rl[attr]
267 if ra == nil then
268 ra = {
269
270 }
271 rl[attr] = ra
272 local sequences = tfmdata.shared.reorderedsequences or tfmdata.resources.sequences
273 if sequences then
274 local autoscript = (s_enabled and s_enabled.autoscript ) or (a_enabled and a_enabled.autoscript )
275 local autolanguage = (s_enabled and s_enabled.autolanguage) or (a_enabled and a_enabled.autolanguage)
276 for s=1,#sequences do
277
278 initialize(sequences[s],script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage)
279 end
280 end
281 end
282 return ra
283
284end
285 |