1if not modules then modules = { } end modules ['math-act'] = {
2 version = 1.001,
3 comment = "companion to math-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
9
10
11
12local type, next = type, next
13local fastcopy, insert, remove = table.fastcopy, table.insert, table.remove
14local formatters = string.formatters
15
16local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
17local trace_collecting = false trackers.register("math.collecting", function(v) trace_collecting = v end)
18
19local report_math = logs.reporter("mathematics","initializing")
20
21local context = context
22local commands = commands
23local mathematics = mathematics
24local texsetdimen = tex.setdimen
25local abs = math.abs
26
27local helpers = fonts.helpers
28local upcommand = helpers.commands.up
29local rightcommand = helpers.commands.right
30local charcommand = helpers.commands.char
31local prependcommands = helpers.prependcommands
32
33local sequencers = utilities.sequencers
34local appendgroup = sequencers.appendgroup
35local appendaction = sequencers.appendaction
36
37local fontchars = fonts.hashes.characters
38local fontproperties = fonts.hashes.properties
39
40local mathfontparameteractions = sequencers.new {
41 name = "mathparameters",
42 arguments = "target,original",
43}
44
45appendgroup("mathparameters","before")
46appendgroup("mathparameters","system")
47appendgroup("mathparameters","after" )
48
49function fonts.constructors.assignmathparameters(original,target)
50 local runner = mathfontparameteractions.runner
51 if runner then
52 runner(original,target)
53 end
54end
55
56function mathematics.initializeparameters(target,original)
57 local mathparameters = original.mathparameters
58 if mathparameters and next(mathparameters) then
59 mathparameters = mathematics.dimensions(mathparameters)
60 if not mathparameters.SpaceBeforeScript then
61 mathparameters.SpaceBeforeScript = mathparameters.SpaceAfterScript
62 end
63 target.mathparameters = mathparameters
64 end
65end
66
67sequencers.appendaction("mathparameters","system","mathematics.initializeparameters")
68
69local how = {
70
71
72 ScriptPercentScaleDown = "unscaled",
73 ScriptScriptPercentScaleDown = "unscaled",
74 RadicalDegreeBottomRaisePercent = "unscaled",
75 NoLimitSupFactor = "unscaled",
76 NoLimitSubFactor = "unscaled",
77}
78
79function mathematics.scaleparameters(target,original)
80 if not target.properties.math_is_scaled then
81 local mathparameters = target.mathparameters
82 if mathparameters and next(mathparameters) then
83 local parameters = target.parameters
84 local factor = parameters.factor
85 local hfactor = parameters.hfactor
86 local vfactor = parameters.vfactor
87 for name, value in next, mathparameters do
88 local h = how[name]
89 if h == "unscaled" then
90
91 elseif h == "horizontal" then
92 value = value * hfactor
93 elseif h == "vertical"then
94 value = value * vfactor
95 else
96 value = value * factor
97 end
98 mathparameters[name] = value
99 end
100 end
101 target.properties.math_is_scaled = true
102 end
103end
104
105
106
107function mathematics.checkaccentbaseheight(target,original)
108 local mathparameters = target.mathparameters
109 if mathparameters and mathparameters.AccentBaseHeight == 0 then
110 mathparameters.AccentBaseHeight = target.parameters.x_height
111 end
112end
113
114function mathematics.checkprivateparameters(target,original)
115 local mathparameters = target.mathparameters
116 if mathparameters then
117 local parameters = target.parameters
118 local properties = target.properties
119 if parameters then
120 local size = parameters.size
121 if size then
122 if not mathparameters.FractionDelimiterSize then
123 mathparameters.FractionDelimiterSize = 1.01 * size
124 end
125 if not mathparameters.FractionDelimiterDisplayStyleSize then
126 mathparameters.FractionDelimiterDisplayStyleSize = 2.40 * size
127 end
128 elseif properties then
129 report_math("invalid parameters in font %a",properties.fullname or "?")
130 else
131 report_math("invalid parameters in font")
132 end
133 elseif properties then
134 report_math("no parameters in font %a",properties.fullname or "?")
135 else
136 report_math("no parameters and properties in font")
137 end
138 end
139end
140
141function mathematics.overloadparameters(target,original)
142 local mathparameters = target.mathparameters
143 if mathparameters and next(mathparameters) then
144 local goodies = target.goodies
145 if goodies then
146 for i=1,#goodies do
147 local goodie = goodies[i]
148 local mathematics = goodie.mathematics
149 local parameters = mathematics and mathematics.parameters
150 if parameters then
151 if trace_defining then
152 report_math("overloading math parameters in %a @ %p",target.properties.fullname,target.parameters.size)
153 end
154 for name, value in next, parameters do
155 local tvalue = type(value)
156 if tvalue == "string" then
157 report_math("comment for math parameter %a: %s",name,value)
158 else
159 local oldvalue = mathparameters[name]
160 local newvalue = oldvalue
161 if oldvalue then
162 if tvalue == "number" then
163 newvalue = value
164 elseif tvalue == "function" then
165 newvalue = value(oldvalue,target,original)
166 elseif not tvalue then
167 newvalue = nil
168 end
169 if trace_defining and oldvalue ~= newvalue then
170 report_math("overloading math parameter %a: %S => %S",name,oldvalue,newvalue)
171 end
172 else
173 report_math("invalid math parameter %a",name)
174 end
175 mathparameters[name] = newvalue
176 end
177 end
178 end
179 end
180 end
181 end
182end
183
184local function applytweaks(when,target,original)
185 local goodies = original.goodies
186 if goodies then
187 for i=1,#goodies do
188 local goodie = goodies[i]
189 local mathematics = goodie.mathematics
190 local tweaks = mathematics and mathematics.tweaks
191 if type(tweaks) == "table" then
192 tweaks = tweaks[when]
193 if type(tweaks) == "table" then
194 if trace_defining then
195 report_math("tweaking math of %a @ %p (%s)",target.properties.fullname,target.parameters.size,when)
196 end
197 for i=1,#tweaks do
198 local tweak= tweaks[i]
199 local tvalue = type(tweak)
200 if tvalue == "function" then
201 tweak(target,original)
202 end
203 end
204 end
205 end
206 end
207 end
208end
209
210function mathematics.tweakbeforecopyingfont(target,original)
211 local mathparameters = target.mathparameters
212 if mathparameters then
213 applytweaks("beforecopying",target,original)
214 end
215end
216
217function mathematics.tweakaftercopyingfont(target,original)
218 local mathparameters = target.mathparameters
219 if mathparameters then
220 applytweaks("aftercopying",target,original)
221 end
222end
223
224sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
225sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight")
226sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters")
227sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
228
229sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
230sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
231
232
233
234
235
236
237
238local tweaks = { }
239mathematics.tweaks = tweaks
240
241
242
243local setmetatableindex = table.setmetatableindex
244
245local getfontoffamily = tex.getfontoffamily
246
247local fontcharacters = fonts.hashes.characters
248local extensibles = utilities.storage.allocate()
249fonts.hashes.extensibles = extensibles
250
251local chardata = characters.data
252local extensibles = mathematics.extensibles
253
254
255
256local e_left = extensibles.left
257local e_right = extensibles.right
258local e_horizontal = extensibles.horizontal
259local e_mixed = extensibles.mixed
260local e_unknown = extensibles.unknown
261
262local unknown = { e_unknown, false, false }
263
264local function extensiblecode(font,unicode)
265 local characters = fontcharacters[font]
266 local character = characters[unicode]
267 if not character then
268 return unknown
269 end
270 local first = character.next
271 local code = unicode
272 local next = first
273 while next do
274 code = next
275 character = characters[next]
276 next = character.next
277 end
278 local char = chardata[unicode]
279 if not char then
280 return unknown
281 end
282 if character.horiz_variants then
283 if character.vert_variants then
284 return { e_mixed, code, character }
285 else
286 local m = char.mathextensible
287 local e = m and extensibles[m]
288 return e and { e, code, character } or unknown
289 end
290 elseif character.vert_variants then
291 local m = char.mathextensible
292 local e = m and extensibles[m]
293 return e and { e, code, character } or unknown
294 elseif first then
295
296 local m = char.mathextensible or char.mathstretch
297 local e = m and extensibles[m]
298 return e and { e, code, character } or unknown
299 else
300 return unknown
301 end
302end
303
304setmetatableindex(extensibles,function(extensibles,font)
305 local codes = { }
306 setmetatableindex(codes, function(codes,unicode)
307 local status = extensiblecode(font,unicode)
308 codes[unicode] = status
309 return status
310 end)
311 extensibles[font] = codes
312 return codes
313end)
314
315local function extensiblecode(family,unicode)
316 return extensibles[getfontoffamily(family or 0)][unicode][1]
317end
318
319
320
321
322
323
324
325local function horizontalcode(family,unicode)
326 local font = getfontoffamily(family or 0)
327 local data = extensibles[font][unicode]
328 local kind = data[1]
329 local loffset = 0
330 local roffset = 0
331 if kind == e_left then
332 local charlist = data[3].horiz_variants
333 if charlist then
334 local left = charlist[1]
335 loffset = abs((left["start"] or 0) - (left["end"] or 0))
336 end
337 elseif kind == e_right then
338 local charlist = data[3].horiz_variants
339 if charlist then
340 local right = charlist[#charlist]
341 roffset = abs((right["start"] or 0) - (right["end"] or 0))
342 end
343 elseif kind == e_horizontal then
344 local charlist = data[3].horiz_variants
345 if charlist then
346 local left = charlist[1]
347 local right = charlist[#charlist]
348 loffset = abs((left ["start"] or 0) - (left ["end"] or 0))
349 roffset = abs((right["start"] or 0) - (right["end"] or 0))
350 end
351 end
352 return kind, loffset, roffset
353end
354
355mathematics.extensiblecode = extensiblecode
356mathematics.horizontalcode = horizontalcode
357
358interfaces.implement {
359 name = "extensiblecode",
360 arguments = { "integer", "integer" },
361 actions = { extensiblecode, context }
362}
363
364interfaces.implement {
365 name = "horizontalcode",
366 arguments = { "integer", "integer" },
367 actions = function(family,unicode)
368 local kind, loffset, roffset = horizontalcode(family,unicode)
369 texsetdimen("scratchleftoffset", loffset)
370 texsetdimen("scratchrightoffset",roffset)
371 context(kind)
372 end
373}
374
375
376local stack = { }
377
378function mathematics.registerfallbackid(n,id,name)
379 if trace_collecting then
380 report_math("resolved fallback font %i, name %a, id %a, used %a",
381 n,name,id,fontproperties[id].fontname)
382 end
383 stack[#stack][n] = id
384end
385
386interfaces.implement {
387 name = "registerfontfallbackid",
388 arguments = { "integer", "integer", "string" },
389 actions = mathematics.registerfallbackid,
390}
391
392function mathematics.resolvefallbacks(target,specification,fallbacks)
393 local definitions = fonts.collections.definitions[fallbacks]
394 if definitions then
395 local size = specification.size
396 local list = { }
397 insert(stack,list)
398 context.pushcatcodes("prt")
399 for i=1,#definitions do
400 local definition = definitions[i]
401 local name = definition.font
402 local features = definition.features or ""
403 local size = size * (definition.rscale or 1)
404 context.font_fallbacks_register_math(i,name,features,size)
405 if trace_collecting then
406 report_math("registering fallback font %i, name %a, size %a, features %a",i,name,size,features)
407 end
408 end
409 context.popcatcodes()
410 end
411end
412
413function mathematics.finishfallbacks(target,specification,fallbacks)
414 local list = remove(stack)
415 if list and #list > 0 then
416 local definitions = fonts.collections.definitions[fallbacks]
417 if definitions and #definitions > 0 then
418 if trace_collecting then
419 report_math("adding fallback characters to font %a",specification.hash)
420 end
421 local definedfont = fonts.definers.internal
422 local copiedglyph = fonts.handlers.vf.math.copy_glyph
423 local fonts = target.fonts
424 local size = specification.size
425 local characters = target.characters
426 if not fonts then
427 fonts = { }
428 target.fonts = fonts
429 end
430 if #fonts == 0 then
431 fonts[1] = { id = 0, size = size }
432 end
433 local done = { }
434 for i=1,#definitions do
435 local definition = definitions[i]
436 local name = definition.font
437 local start = definition.start
438 local stop = definition.stop
439 local gaps = definition.gaps
440 local check = definition.check
441 local force = definition.force
442 local rscale = definition.rscale or 1
443 local offset = definition.offset or start
444 local id = list[i]
445 if id then
446 local index = #fonts + 1
447 fonts[index] = { id = id, size = size }
448 local chars = fontchars[id]
449 local function remap(unic,unicode,gap)
450 if check and not chars[unicode] then
451 return
452 end
453 if force or (not done[unic] and not characters[unic]) then
454 if trace_collecting then
455 report_math("replacing math character %C by %C using vector %a and font id %a for %a%s%s",
456 unic,unicode,fallbacks,id,fontproperties[id].fontname,check and ", checked",gap and ", gap plugged")
457 end
458 characters[unic] = copiedglyph(target,characters,chars,unicode,index)
459 done[unic] = true
460 end
461 end
462 local step = offset - start
463 for unicode = start, stop do
464 remap(unicode + step,unicode,false)
465 end
466 if gaps then
467 for unic, unicode in next, gaps do
468 remap(unic,unicode,true)
469 remap(unicode,unicode,true)
470 end
471 end
472 end
473 end
474 elseif trace_collecting then
475 report_math("no fallback characters added to font %a",specification.hash)
476 end
477 end
478end
479 |