1if not modules then modules = { } end modules ['good-ini'] = {
2 version = 1.000,
3 comment = "companion to font-lib.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
9
10
11local type, next = type, next
12local gmatch, find, topattern = string.gmatch, string.find, string.topattern
13local sortedhash, insert, contains = table.sortedhash, table.insert, table.contains
14
15local fonts = fonts
16
17local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
18local trace_extensions = false trackers.register("fonts.goodies.extensions", function(v) trace_extensions = v end)
19
20local report_goodies = logs.reporter("fonts","goodies")
21
22local allocate = utilities.storage.allocate
23local implement = interfaces.implement
24local findfile = resolvers.findfile
25local formatters = string.formatters
26
27local otf = fonts.handlers.otf
28local afm = fonts.handlers.afm
29local tfm = fonts.handlers.tfm
30
31local registerotffeature = otf.features.register
32local registerafmfeature = afm.features.register
33local registertfmfeature = tfm.features.register
34
35local addotffeature = otf.enhancers.addfeature
36
37local fontgoodies = fonts.goodies or { }
38fonts.goodies = fontgoodies
39
40local data = fontgoodies.data or { }
41fontgoodies.data = data
42
43local list = fontgoodies.list or { }
44fontgoodies.list = list
45
46fontgoodies.suffixes = { "lfg", "lua" }
47
48local contextsetups = fonts.specifiers.contextsetups
49
50function fontgoodies.report(what,trace,goodies)
51 if trace_goodies or trace then
52 local whatever = goodies[what]
53 if whatever then
54 report_goodies("goodie %a found in %a",what,goodies.name)
55 end
56 end
57end
58
59local function locate(filename)
60 local suffixes = fontgoodies.suffixes
61 for i=1,#suffixes do
62 local suffix = suffixes[i]
63 local fullname = findfile(file.addsuffix(filename,suffix))
64 if fullname and fullname ~= "" then
65 return fullname
66 end
67 end
68end
69
70local function loadgoodies(filename)
71 local goodies = data[filename]
72 if goodies ~= nil then
73
74 elseif type(filename) == "string" then
75 local fullname = locate(filename)
76 if not fullname or fullname == "" then
77 report_goodies("goodie file %a is not found (suffixes: % t)",filename,fontgoodies.suffixes)
78 data[filename] = false
79 else
80 goodies = dofile(fullname) or false
81 if not goodies then
82 report_goodies("goodie file %a is invalid",fullname)
83 return nil
84 elseif trace_goodies then
85 report_goodies("goodie file %a is loaded",fullname)
86 end
87 goodies.name = goodies.name or "no name"
88 for i=1,#list do
89 local g = list[i]
90 if trace_goodies then
91 report_goodies("handling goodie %a",g[1])
92 end
93 g[2](goodies)
94 end
95 goodies.initialized = true
96 data[filename] = goodies
97 end
98 end
99 return goodies
100end
101
102function fontgoodies.register(name,fnc,prepend)
103 for i=1,#list do
104 local g = list[i]
105 if g[1] == name then
106 g[2] = fnc
107 return
108 end
109 end
110 local g = { name, fnc }
111 if prepend then
112 insert(list,g,prepend == true and 1 or prepend)
113 else
114 insert(list,g)
115 end
116end
117
118fontgoodies.load = loadgoodies
119
120if implement then
121
122 implement {
123 name = "loadfontgoodies",
124 actions = loadgoodies,
125 arguments = "string",
126 overload = true,
127 }
128
129end
130
131
132
133local function setgoodies(tfmdata,value)
134 local goodies = tfmdata.goodies
135 if not goodies then
136 goodies = { }
137 tfmdata.goodies = goodies
138 end
139 for filename in gmatch(value,"[^, ]+") do
140
141 local ok = loadgoodies(filename)
142 if ok then
143 if trace_goodies then
144 report_goodies("assigning goodie %a",filename)
145 end
146 goodies[#goodies+1] = ok
147 end
148 end
149end
150
151
152
153local function flattenedfeatures(t,tt)
154
155 if not tt then
156 tt = { }
157 end
158 for i=1,#t do
159 local ti = t[i]
160 local ty = type(ti)
161 if ty == "table" then
162 flattenedfeatures(ti,tt)
163 elseif ty == "string" then
164 local set = contextsetups[ti]
165 if set then
166 for k, v in next, set do
167 if k ~= "number" then
168 tt[k] = v or nil
169 end
170 end
171 else
172
173 end
174 elseif tt[ti] == nil then
175 tt[ti] = true
176 end
177 end
178 for k, v in next, t do
179 if type(k) ~= "number" then
180 if type(v) == "table" then
181 flattenedfeatures(v,tt)
182 elseif tt[k] == nil then
183 tt[k] = v
184 end
185 end
186 end
187 return tt
188end
189
190
191
192local function prepare_features(goodies,name,set)
193 if set then
194 local ff = flattenedfeatures(set)
195 local fullname = goodies.name .. "::" .. name
196 local n, s = fonts.specifiers.presetcontext(fullname,"",ff)
197 goodies.featuresets[name] = s
198 if trace_goodies then
199 report_goodies("feature set %a gets number %a and name %a",name,n,fullname)
200 end
201 return n
202 end
203end
204
205fontgoodies.prepare_features = prepare_features
206
207local function initialize(goodies)
208 local featuresets = goodies.featuresets
209 if featuresets then
210 if trace_goodies then
211 report_goodies("checking featuresets in %a",goodies.name)
212 end
213 for name, set in next, featuresets do
214 prepare_features(goodies,name,set)
215 end
216 end
217end
218
219fontgoodies.register("featureset",initialize)
220
221local function setfeatureset(tfmdata,set,features)
222 local goodies = tfmdata.goodies
223 if goodies then
224 local properties = tfmdata.properties
225 local what
226 for i=1,#goodies do
227
228 local g = goodies[i]
229 what = g.featuresets and g.featuresets[set] or what
230 end
231 if what then
232 for feature, value in next, what do
233 if features[feature] == nil then
234 features[feature] = value
235 end
236 end
237 properties.mode = what.mode or properties.mode
238 end
239 end
240end
241
242
243
244function fontgoodies.registerpostprocessor(tfmdata,f,prepend)
245 local postprocessors = tfmdata.postprocessors
246 if not postprocessors then
247 tfmdata.postprocessors = { f }
248 elseif prepend then
249 insert(postprocessors,f,prepend == true and 1 or prepend)
250 else
251 insert(postprocessors,f)
252 end
253end
254
255local function setpostprocessor(tfmdata,processor)
256 local goodies = tfmdata.goodies
257 if goodies and type(processor) == "string" then
258 local found = { }
259 local asked = utilities.parsers.settings_to_array(processor)
260 for i=1,#goodies do
261 local g = goodies[i]
262 local p = g.postprocessors
263 if p then
264 for i=1,#asked do
265 local a = asked[i]
266 local f = p[a]
267 if type(f) == "function" then
268 found[a] = f
269 end
270 end
271 end
272 end
273 local postprocessors = tfmdata.postprocessors or { }
274 for i=1,#asked do
275 local a = asked[i]
276 local f = found[a]
277 if f then
278 postprocessors[#postprocessors+1] = f
279 end
280 end
281 if #postprocessors > 0 then
282 tfmdata.postprocessors = postprocessors
283 end
284 end
285end
286
287local function setextrafeatures(tfmdata)
288 local goodies = tfmdata.goodies
289 if goodies then
290 for i=1,#goodies do
291 local g = goodies[i]
292 local f = g.features
293 if f then
294 local rawdata = tfmdata.shared.rawdata
295 local done = { }
296
297 for i=1,#f do
298 local specification = f[i]
299 local feature = specification.name
300 if feature then
301 addotffeature(rawdata,feature,specification)
302 registerotffeature {
303 name = feature,
304 description = formatters["extra: %s"](feature)
305 }
306 end
307 done[i] = true
308 end
309
310 for feature, specification in sortedhash(f) do
311 if not done[feature] then
312 feature = specification.name or feature
313 specification.name = feature
314 addotffeature(rawdata,feature,specification)
315 registerotffeature {
316 name = feature,
317 description = formatters["extra: %s"](feature)
318 }
319 end
320 end
321 end
322 end
323 end
324end
325
326local function setextensions(tfmdata)
327 local goodies = tfmdata.goodies
328 if goodies then
329 local shared = tfmdata.shared
330 local metadata = shared and shared.rawdata and shared.rawdata.metadata
331 for i=1,#goodies do
332 local g = goodies[i]
333 local e = g.extensions
334 if e then
335 local goodie = g.name or "unknown"
336 local fontname = metadata and metadata.fontname
337 if trace_extensions then
338 report_goodies("checking extensions for font %a",fontname or "unknown")
339 end
340 for i=1,#e do
341 local entry = e[i]
342 local fnames = entry.fonts
343 local tnames = type(fnames)
344 local valid = false
345 if not fontname then
346 valid = true
347 elseif tnames == "table" then
348 if fnames[fontname] then
349 valid = true
350 elseif #fnames > 0 and contains(fnames,fontname) then
351 valid = true
352 end
353 elseif tnames == "string" then
354 fnames = topattern(fnames)
355 valid = find(fontname,fnames) and true
356 else
357 valid = true
358 end
359 if valid then
360 local name = "extension-" .. i
361 if trace_extensions then
362 report_goodies("adding extension %a from %a for font %a",name,goodie,fontname or "unknown")
363 end
364 otf.enhancers.addfeature(tfmdata.shared.rawdata,name,entry)
365 end
366 end
367 end
368 end
369 end
370end
371
372
373
374local goodies_specification = {
375 name = "goodies",
376 description = "goodies on top of built in features",
377 initializers = {
378 position = 1,
379 base = setgoodies,
380 node = setgoodies,
381 }
382}
383
384registerotffeature(goodies_specification)
385registerafmfeature(goodies_specification)
386registertfmfeature(goodies_specification)
387
388
389
390registerotffeature {
391 name = "extrafeatures",
392 description = "extra features",
393 default = true,
394 initializers = {
395 position = 2,
396 base = setextrafeatures,
397 node = setextrafeatures,
398 }
399}
400
401registerotffeature {
402 name = "extensions",
403 description = "extensions to features",
404 default = true,
405 initializers = {
406 position = 2,
407 base = setextensions,
408 node = setextensions,
409 }
410}
411
412registerotffeature {
413 name = "featureset",
414 description = "goodie feature set",
415 initializers = {
416 position = 3,
417 base = setfeatureset,
418 node = setfeatureset,
419 }
420}
421
422registerotffeature {
423 name = "postprocessor",
424 description = "goodie postprocessor",
425 initializers = {
426 base = setpostprocessor,
427 node = setpostprocessor,
428 }
429}
430 |