1if not modules then modules = { } end modules ['good-mth'] = {
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
9local type, next, tonumber, unpack = type, next, tonumber, unpack
10local ceil = math.ceil
11local match = string.match
12
13local fonts = fonts
14
15local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
16local report_goodies = logs.reporter("fonts","goodies")
17
18local registerotffeature = fonts.handlers.otf.features.register
19
20local fontgoodies = fonts.goodies or { }
21
22local fontcharacters = fonts.hashes.characters
23
24local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
25
26local report_math = logs.reporter("mathematics","initializing")
27
28local nuts = nodes.nuts
29
30local setlink = nuts.setlink
31
32local nodepool = nuts.pool
33
34local new_kern = nodepool.kern
35local new_glyph = nodepool.glyph
36local new_hlist = nodepool.hlist
37local new_vlist = nodepool.vlist
38
39local insertnodeafter = nuts.insertafter
40
41local helpers = fonts.helpers
42local upcommand = helpers.commands.up
43local rightcommand = helpers.commands.right
44local charcommand = helpers.commands.char
45local prependcommands = helpers.prependcommands
46
47
48
49
50local function withscriptcode(tfmdata,unicode,data,action)
51 if type(unicode) == "string" then
52 local p, u = match(unicode,"^(.-):(.-)$")
53 if u then
54 u = tonumber(u)
55 if u then
56 local slots = fonts.helpers.mathscriptslots(tfmdata,u)
57 if slots then
58 if p == "*" then
59 action(u,data)
60 if type(slots) == "number" then
61 action(slots,data)
62 else
63 for i=1,#slots do
64 action(slots[i],data)
65 end
66 end
67 else
68 p = tonumber(p)
69 if p then
70 if type(slots) == "number" then
71 action(slots,data)
72 else
73 action(slots[p],data)
74 end
75 end
76 end
77 end
78 end
79 end
80 else
81 action(unicode,data)
82 end
83end
84
85local function finalize(tfmdata,feature,value,...)
86
87 local goodies = tfmdata.goodies
88 if goodies then
89 local virtualized = mathematics.virtualized
90 for i=1,#goodies do
91 local goodie = goodies[i]
92 local mathematics = goodie.mathematics
93 local dimensions = mathematics and mathematics.dimensions
94 if dimensions then
95 if trace_defining then
96 report_math("overloading dimensions in %a @ %p",tfmdata.properties.fullname,tfmdata.parameters.size)
97 end
98 local characters = tfmdata.characters
99 local descriptions = tfmdata.descriptions
100 local parameters = tfmdata.parameters
101 local factor = parameters.factor
102 local hfactor = parameters.hfactor
103 local vfactor = parameters.vfactor
104
105 tfmdata.type = "virtual"
106 tfmdata.properties.virtualized = true
107
108 local function overloadone(unicode,data)
109 local character = characters[unicode]
110 if not character then
111 local c = virtualized[unicode]
112 if c then
113 character = characters[c]
114 end
115 end
116 if character then
117 local width = data.width
118 local height = data.height
119 local depth = data.depth
120 if trace_defining and (width or height or depth) then
121 report_math("overloading dimensions of %C, width %p, height %p, depth %p",
122 unicode,width or 0,height or 0,depth or 0)
123 end
124 if width then character.width = width * hfactor end
125 if height then character.height = height * vfactor end
126 if depth then character.depth = depth * vfactor end
127
128 local xoffset = data.xoffset
129 local yoffset = data.yoffset
130 if xoffset == "llx" then
131 local d = descriptions[unicode]
132 if d then
133 xoffset = - d.boundingbox[1]
134 character.width = character.width + xoffset * hfactor
135 xoffset = rightcommand[xoffset]
136 else
137 xoffset = nil
138 end
139 elseif xoffset and xoffset ~= 0 then
140 xoffset = rightcommand[xoffset * hfactor]
141 else
142 xoffset = nil
143 end
144 if yoffset and yoffset ~= 0 then
145 yoffset = upcommand[yoffset * vfactor]
146 else
147 yoffset = nil
148 end
149 if xoffset or yoffset then
150 local commands = character.commands
151 if commands then
152 prependcommands(commands,yoffset,xoffset)
153 else
154
155 local slot = charcommand[unicode]
156 if xoffset and yoffset then
157 character.commands = { xoffset, yoffset, slot }
158 elseif xoffset then
159 character.commands = { xoffset, slot }
160 else
161 character.commands = { yoffset, slot }
162 end
163 end
164 end
165 elseif trace_defining then
166 report_math("no overloading dimensions of %C, not in font",unicode)
167 end
168 end
169 local function overload(dimensions)
170 for unicode, data in next, dimensions do
171 withscriptcode(tfmdata,unicode,data,overloadone)
172 end
173 end
174 if value == nil then
175 value = { "default" }
176 end
177 if value == "all" or value == true then
178 for name, value in next, dimensions do
179 overload(value)
180 end
181 else
182 if type(value) == "string" then
183 value = utilities.parsers.settings_to_array(value)
184 end
185 if type(value) == "table" then
186 for i=1,#value do
187 local d = dimensions[value[i]]
188 if d then
189 overload(d)
190 end
191 end
192 end
193 end
194 end
195 end
196 end
197end
198
199registerotffeature {
200 name = "mathdimensions",
201 description = "manipulate math dimensions",
202
203 manipulators = {
204 base = finalize,
205 node = finalize,
206 }
207}
208
209local function initialize(goodies)
210 local mathgoodies = goodies.mathematics
211 if mathgoodies then
212 local virtuals = mathgoodies.virtuals
213 local mapfiles = mathgoodies.mapfiles
214 local maplines = mathgoodies.maplines
215 if virtuals then
216 for name, specification in next, virtuals do
217
218 mathematics.makefont(name,specification,goodies)
219 end
220 end
221 if mapfiles then
222 for i=1,#mapfiles do
223 fonts.mappings.loadfile(mapfiles[i])
224 end
225 end
226 if maplines then
227 for i=1,#maplines do
228 fonts.mappings.loadline(maplines[i])
229 end
230 end
231 end
232end
233
234fontgoodies.register("mathematics", initialize)
235
236
237
238local function initialize(tfmdata)
239
240 if tfmdata.mathparameters then
241 local goodies = tfmdata.goodies
242 if goodies then
243 local characters = tfmdata.characters
244 if characters[0x1D44E] then
245
246 for i=1,#goodies do
247 local mathgoodies = goodies[i].mathematics
248 if mathgoodies then
249 local kerns = mathgoodies.kerns
250 if kerns then
251 local function kernone(unicode,data)
252 local chardata = characters[unicode]
253 if chardata and (not chardata.mathkerns or data.force) then
254 chardata.mathkerns = data
255 end
256 end
257 for unicode, data in next, kerns do
258 withscriptcode(tfmdata,unicode,data,kernone)
259 end
260 return
261 end
262 end
263 end
264 else
265 return
266 end
267 end
268 end
269end
270
271registerotffeature {
272 name = "mathkerns",
273 description = "math kerns",
274
275 initializers = {
276 base = initialize,
277 node = initialize,
278 }
279}
280
281
282
283
284
285local function initialize(tfmdata)
286 local goodies = tfmdata.goodies
287 if goodies then
288 local shared = tfmdata.shared
289 for i=1,#goodies do
290 local mathgoodies = goodies[i].mathematics
291 if mathgoodies then
292 local mathitalics = mathgoodies.italics
293 if mathitalics then
294 local properties = tfmdata.properties
295 if properties.setitalics then
296 mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics
297 if mathitalics then
298 if trace_goodies then
299 report_goodies("loading mathitalics for font %a",properties.name)
300 end
301 local corrections = mathitalics.corrections
302 local defaultfactor = mathitalics.defaultfactor
303
304 if corrections then
305 fontgoodies.registerpostprocessor(tfmdata, function(tfmdata)
306
307 local properties = tfmdata.properties
308 local parameters = tfmdata.parameters
309 local characters = tfmdata.characters
310 properties.mathitalic_defaultfactor = defaultfactor
311 properties.mathitalic_defaultvalue = defaultfactor * parameters.quad
312 if trace_goodies then
313 report_goodies("assigning mathitalics for font %a",properties.name)
314 end
315 local quad = parameters.quad
316 local hfactor = parameters.hfactor
317 for k, v in next, corrections do
318 local c = characters[k]
319 if c then
320 if v > -1 and v < 1 then
321 c.italic = v * quad
322 else
323 c.italic = v * hfactor
324 end
325 else
326 report_goodies("invalid mathitalics entry %U for font %a",k,properties.name)
327 end
328 end
329 end)
330 end
331 return
332 end
333 end
334 end
335 end
336 end
337 end
338end
339
340registerotffeature {
341 name = "mathitalics",
342 description = "additional math italic corrections",
343
344 initializers = {
345 base = initialize,
346 node = initialize,
347 }
348}
349
350
351
352local function mathradicalaction(n,h,v,font,mchar,echar)
353 local characters = fontcharacters[font]
354 local mchardata = characters[mchar]
355 local echardata = characters[echar]
356 local ewidth = echardata.width
357 local mwidth = mchardata.width
358 local delta = h - ewidth
359 local glyph = new_glyph(font,echar)
360 local head = glyph
361 if delta > 0 then
362 local count = ceil(delta/mwidth)
363 local kern = (delta - count * mwidth) / count
364 for i=1,count do
365 local k = new_kern(kern)
366 local g = new_glyph(font,mchar)
367 setlink(k,head)
368 setlink(g,k)
369 head = g
370 end
371 end
372 local height = mchardata.height
373 local list = new_hlist(head)
374 local kern = new_kern(height-v)
375 list = setlink(kern,list)
376 local list = new_vlist(kern)
377 insertnodeafter(n,n,list)
378end
379
380local function mathhruleaction(n,h,v,font,bchar,mchar,echar)
381 local characters = fontcharacters[font]
382 local bchardata = characters[bchar]
383 local mchardata = characters[mchar]
384 local echardata = characters[echar]
385 local bwidth = bchardata.width
386 local mwidth = mchardata.width
387 local ewidth = echardata.width
388 local delta = h - ewidth - bwidth
389 local glyph = new_glyph(font,echar)
390 local head = glyph
391 if delta > 0 then
392 local count = ceil(delta/mwidth)
393 local kern = (delta - count * mwidth) / (count+1)
394 for i=1,count do
395 local k = new_kern(kern)
396 local g = new_glyph(font,mchar)
397 setlink(k,head)
398 setlink(g,k)
399 head = g
400 end
401 local k = new_kern(kern)
402 setlink(k,head)
403 head = k
404 end
405 local g = new_glyph(font,bchar)
406 setlink(g,head)
407 head = g
408 local height = mchardata.height
409 local list = new_hlist(head)
410 local kern = new_kern(height-v)
411 list = setlink(kern,list)
412 local list = new_vlist(kern)
413 insertnodeafter(n,n,list)
414end
415
416local function initialize(tfmdata)
417 local goodies = tfmdata.goodies
418 if goodies then
419 local resources = tfmdata.resources
420 local ruledata = { }
421 for i=1,#goodies do
422 local mathematics = goodies[i].mathematics
423 if mathematics then
424 local rules = mathematics.rules
425 if rules then
426 for tag, name in next, rules do
427 ruledata[tag] = name
428 end
429 end
430 end
431 end
432 if next(ruledata) then
433 local characters = tfmdata.characters
434 local unicodes = resources.unicodes
435 if characters and unicodes then
436 local mathruleactions = resources.mathruleactions
437 if not mathruleactions then
438 mathruleactions = { }
439 resources.mathruleactions = mathruleactions
440 end
441
442 local mchar = unicodes[ruledata["radical.extender"] or false]
443 local echar = unicodes[ruledata["radical.end"] or false]
444 if mchar and echar then
445 mathruleactions.radicalaction = function(n,h,v,font)
446 mathradicalaction(n,h,v,font,mchar,echar)
447 end
448 end
449
450 local bchar = unicodes[ruledata["hrule.begin"] or false]
451 local mchar = unicodes[ruledata["hrule.extender"] or false]
452 local echar = unicodes[ruledata["hrule.end"] or false]
453 if bchar and mchar and echar then
454 mathruleactions.hruleaction = function(n,h,v,font)
455 mathhruleaction(n,h,v,font,bchar,mchar,echar)
456 end
457 end
458
459
460 end
461 end
462 end
463end
464
465registerotffeature {
466 name = "mathrules",
467 description = "check math rules",
468 default = true,
469 initializers = {
470 base = initialize,
471 node = initialize,
472 }
473}
474 |