1if not modules then modules = { } end modules ['math-fbk'] = {
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
9local next, type = next, type
10
11local trace_fallbacks = false trackers.register("math.fallbacks", function(v) trace_fallbacks = v end)
12
13local report_fallbacks = logs.reporter("math","fallbacks")
14
15local formatters = string.formatters
16local fastcopy = table.fastcopy
17local byte = string.byte
18local sortedhash = table.sortedhash
19
20local fallbacks = { }
21mathematics.fallbacks = fallbacks
22
23local helpers = fonts.helpers
24local prependcommands = helpers.prependcommands
25local charcommand = helpers.commands.char
26local leftcommand = helpers.commands.left
27local rightcommand = helpers.commands.right
28local upcommand = helpers.commands.up
29local downcommand = helpers.commands.down
30local dummycommand = helpers.commands.dummy
31local popcommand = helpers.commands.pop
32local pushcommand = helpers.commands.push
33
34local virtualcharacters = { }
35local virtualforced = { }
36
37local hashes = fonts.hashes
38local identifiers = hashes.identifiers
39local lastmathids = hashes.lastmathids
40
41
42
43
44
45
46local scripscriptdelayed = { }
47local scriptdelayed = { }
48
49function fallbacks.apply(target,original)
50 local mathparameters = target.mathparameters
51 if not mathparameters or not next(mathparameters) then
52 return
53 end
54
55
56 local parameters = target.parameters
57 local properties = target.properties
58 local mathsize = parameters.mathsize
59 if mathsize < 1 or mathsize > 3 then
60 return
61 end
62 local characters = target.characters
63 local size = parameters.size
64 local usedfonts = target.fonts
65 local compactmath = properties.compactmath
66 if not usedfonts then
67 usedfonts = { { id = 0 } }
68 target.fonts = usedfonts
69 end
70
71 local textid, scriptid, scriptscriptid
72 local textindex, scriptindex, scriptscriptindex
73 local textdata, scriptdata, scriptscriptdata
74 if mathsize == 3 then
75
76 textid = 0
77 scriptid = 0
78 scriptscriptid = 0
79 elseif mathsize == 2 then
80
81 textid = 0
82 scriptid = lastmathids[3] or 0
83 scriptscriptid = lastmathids[3] or 0
84 else
85
86 textid = 0
87 scriptid = lastmathids[2] or 0
88 scriptscriptid = lastmathids[3] or 0
89 end
90 if textid and textid ~= 0 then
91 textindex = #usedfonts + 1
92 textdata = target
93 usedfonts[textindex] = { id = textid }
94 else
95 textdata = target
96 end
97 if compactmath then
98 scriptid = textid
99 scriptscriptid = textid
100 end
101 if scriptid and scriptid ~= 0 then
102 scriptindex = #usedfonts + 1
103 scriptdata = identifiers[scriptid]
104 usedfonts[scriptindex] = { id = scriptid }
105 else
106 scriptindex = textindex
107 scriptdata = textdata
108 end
109 if scriptscriptid and scriptscriptid ~= 0 then
110 scriptscriptindex = #usedfonts + 1
111 scriptscriptdata = identifiers[scriptscriptid]
112 usedfonts[scriptscriptindex] = { id = scriptscriptid }
113 else
114 scriptscriptindex = scriptindex
115 scriptscriptdata = scriptdata
116 end
117
118 local data = {
119 textdata = textdata,
120 scriptdata = scriptdata,
121 scriptscriptdata = scriptscriptdata,
122 textindex = textindex,
123 scriptindex = scriptindex,
124 scriptscriptindex = scriptscriptindex,
125 textid = textid,
126 scriptid = scriptid,
127 scriptscriptid = scriptscriptid,
128 characters = characters,
129 unicode = k,
130 target = target,
131 original = original,
132 size = size,
133 mathsize = mathsize,
134 }
135 target.mathrelation = data
136
137 local fullname = trace_fallbacks and target.properties.fullname
138
139 for k, v in sortedhash(virtualcharacters) do
140 if not characters[k] or virtualforced[k] then
141 local tv = type(v)
142 local cd = nil
143 if tv == "table" then
144 cd = v
145 elseif tv == "number" then
146 cd = characters[v]
147 elseif tv == "function" then
148 cd = v(data)
149 end
150 if cd then
151 characters[k] = cd
152 else
153
154 end
155 if trace_fallbacks and characters[k] then
156 report_fallbacks("extending math font %a with %U",fullname,k)
157 end
158 end
159 end
160 data.unicode = nil
161end
162
163utilities.sequencers.appendaction("aftercopyingcharacters","system","mathematics.fallbacks.apply")
164
165function fallbacks.install(unicode,value)
166 virtualcharacters[unicode] = value
167end
168
169
170
171local function reference(index,char)
172 if index then
173 return { "slot", index, char }
174 else
175 return charcommand[char]
176 end
177end
178
179local function raised(data,replacement,down)
180 local character = data.scriptdata.characters[replacement]
181 if character then
182 local size = data.size
183 return {
184 width = character.width,
185 height = character.height,
186 depth = character.depth,
187 commands = {
188 down and downcommand[size/4] or upcommand[size/2],
189 reference(data.scriptindex,replacement)
190 }
191 }
192 end
193end
194
195
196
197
198
199
200virtualcharacters[0x207A] = function(data) return raised(data,0x002B) end
201virtualcharacters[0x207B] = function(data) return raised(data,0x2212) end
202virtualcharacters[0x208A] = function(data) return raised(data,0x002B,true) end
203virtualcharacters[0x208B] = function(data) return raised(data,0x2212,true) end
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236local addextra = mathematics.extras.add
237
238addextra(0xFE350)
239addextra(0xFE351)
240addextra(0xFE352)
241
242local leftarrow = charcommand[0x2190]
243local relbar = charcommand[0x2212]
244local rightarrow = charcommand[0x2192]
245
246virtualcharacters[0xFE350] = function(data)
247
248 local charone = data.characters[0x2190]
249 local chartwo = data.characters[0x2212]
250 if charone and chartwo then
251 local size = data.size/2
252 return {
253 width = chartwo.width,
254 height = size,
255 depth = size,
256 commands = {
257 pushcommand,
258 downcommand[size/2],
259 leftarrow,
260 popcommand,
261 upcommand[size/2],
262 relbar,
263 }
264 }
265 end
266end
267
268virtualcharacters[0xFE351] = function(data)
269
270 local char = data.characters[0x2212]
271 if char then
272 local size = data.size/2
273 return {
274 width = char.width,
275 height = size,
276 depth = size,
277 commands = {
278 pushcommand,
279 downcommand[size/2],
280 relbar,
281 popcommand,
282 upcommand[size/2],
283 relbar,
284 }
285 }
286 end
287end
288
289virtualcharacters[0xFE352] = function(data)
290
291 local charone = data.characters[0x2192]
292 local chartwo = data.characters[0x2212]
293 if charone and chartwo then
294 local size = data.size/2
295 return {
296 width = chartwo.width,
297 height = size,
298 depth = size,
299 commands = {
300 pushcommand,
301 downcommand[size/2],
302 relbar,
303 popcommand,
304 rightcommand[chartwo.width - charone.width],
305 upcommand[size/2],
306 rightarrow,
307 }
308 }
309 end
310end
311
312
313
314local function accent_to_extensible(target,newchr,original,oldchr,height,depth,swap,offset,unicode)
315 local characters = target.characters
316 local olddata = characters[oldchr]
317
318 if olddata and not olddata.commands then
319 local addprivate = fonts.helpers.addprivate
320 if swap then
321 swap = characters[swap]
322 height = swap.depth or 0
323 depth = 0
324 else
325 height = height or 0
326 depth = depth or 0
327 end
328 local oldheight = olddata.height or 0
329 local correction = swap and
330 downcommand[oldheight - height]
331 or downcommand[oldheight + (offset or 0)]
332 local newdata = {
333 commands = { correction, charcommand[oldchr] },
334 width = olddata.width,
335 height = height,
336 depth = depth,
337 unicode = unicode,
338 }
339 local glyphdata = newdata
340 local nextglyph = olddata.next
341 while nextglyph do
342 local oldnextdata = characters[nextglyph]
343 if oldnextdata then
344 local newnextdata = {
345 commands = { correction, charcommand[nextglyph] },
346 width = oldnextdata.width,
347 height = height,
348 depth = depth,
349 }
350 local newnextglyph = addprivate(target,formatters["M-N-%H"](nextglyph),newnextdata)
351 newdata.next = newnextglyph
352 local nextnextglyph = oldnextdata.next
353 if nextnextglyph == nextglyph then
354 break
355 else
356 olddata = oldnextdata
357 newdata = newnextdata
358 nextglyph = nextnextglyph
359 end
360 else
361 report_fallbacks("error in fallback: no valid next, slot %X",nextglyph)
362 break
363 end
364 end
365 local hv = olddata.horiz_variants
366 if hv then
367 hv = fastcopy(hv)
368 newdata.horiz_variants = hv
369 for i=1,#hv do
370 local hvi = hv[i]
371 local oldglyph = hvi.glyph
372 local olddata = characters[oldglyph]
373 if olddata then
374 local newdata = {
375 commands = { correction, charcommand[oldglyph] },
376 width = olddata.width,
377 height = height,
378 depth = depth,
379 }
380 hvi.glyph = addprivate(target,formatters["M-H-%H"](oldglyph),newdata)
381 else
382 report_fallbacks("error in fallback: no valid horiz_variants, slot %X, index %i",oldglyph,i)
383 end
384 end
385 end
386 return glyphdata, true
387 else
388 return olddata, false
389 end
390end
391
392virtualcharacters[0x203E] = function(data)
393 local target = data.target
394 local height = 0
395 local depth = 0
396
397
398
399
400
401 height = target.parameters.xheight/4
402 depth = height
403
404 return accent_to_extensible(target,0x203E,data.original,0x0305,height,depth,nil,nil,0x203E)
405end
406
407
408
409
410virtualcharacters[0xFE33E] = function(data)
411 local target = data.target
412 local height = 0
413 local depth = target.parameters.xheight/4
414 return accent_to_extensible(target,0xFE33E,data.original,0x0305,height,depth,nil,nil,0x203E)
415end
416
417virtualcharacters[0xFE33F] = function(data)
418 local target = data.target
419 local height = target.parameters.xheight/8
420 local depth = height
421 return accent_to_extensible(target,0xFE33F,data.original,0x0305,height,depth,nil,nil,0x203E)
422end
423
424
425
426local c_zero = byte('0')
427local c_period = byte('.')
428
429local function spacefraction(data,fraction)
430 local width = fraction * data.target.parameters.space
431 return {
432 width = width,
433 commands = { rightcommand[width] }
434 }
435end
436
437local function charfraction(data,char)
438 local width = data.target.characters[char].width
439 return {
440 width = width,
441 commands = { rightcommand[width] }
442 }
443end
444
445local function quadfraction(data,fraction)
446 local width = fraction * data.target.parameters.quad
447 return {
448 width = width,
449 commands = { rightcommand[width] }
450 }
451end
452
453virtualcharacters[0x00A0] = function(data) return spacefraction(data,1) end
454virtualcharacters[0x2000] = function(data) return quadfraction (data,1/2) end
455virtualcharacters[0x2001] = function(data) return quadfraction (data,1) end
456virtualcharacters[0x2002] = function(data) return quadfraction (data,1/2) end
457virtualcharacters[0x2003] = function(data) return quadfraction (data,1) end
458virtualcharacters[0x2004] = function(data) return quadfraction (data,1/3) end
459virtualcharacters[0x2005] = function(data) return quadfraction (data,1/4) end
460virtualcharacters[0x2006] = function(data) return quadfraction (data,1/6) end
461virtualcharacters[0x2007] = function(data) return charfraction (data,c_zero) end
462virtualcharacters[0x2008] = function(data) return charfraction (data,c_period) end
463virtualcharacters[0x2009] = function(data) return quadfraction (data,1/8) end
464virtualcharacters[0x200A] = function(data) return quadfraction (data,1/8) end
465virtualcharacters[0x200B] = function(data) return quadfraction (data,0) end
466virtualcharacters[0x202F] = function(data) return quadfraction (data,1/8) end
467virtualcharacters[0x205F] = function(data) return spacefraction(data,1/2) end
468
469
470
471local function smashed(data,unicode,swap,private)
472 local target = data.target
473 local original = data.original
474 local chardata = target.characters[unicode]
475 if chardata and chardata.height > target.parameters.xheight then
476 return accent_to_extensible(target,private,original,unicode,0,0,swap,nil,unicode)
477 else
478 return original.characters[unicode]
479 end
480end
481
482addextra(0xFE3DE)
483addextra(0xFE3DC)
484addextra(0xFE3B4)
485
486virtualcharacters[0xFE3DE] = function(data) return smashed(data,0x23DE,0x23DF,0xFE3DE) end
487virtualcharacters[0xFE3DC] = function(data) return smashed(data,0x23DC,0x23DD,0xFE3DC) end
488virtualcharacters[0xFE3B4] = function(data) return smashed(data,0x23B4,0x23B5,0xFE3B4) end
489
490addextra(0xFE3DF)
491addextra(0xFE3DD)
492addextra(0xFE3B5)
493
494virtualcharacters[0xFE3DF] = function(data) local c = data.target.characters[0x23DF] if c then c.unicode = 0x23DF return c end end
495virtualcharacters[0xFE3DD] = function(data) local c = data.target.characters[0x23DD] if c then c.unicode = 0x23DD return c end end
496virtualcharacters[0xFE3B5] = function(data) local c = data.target.characters[0x23B5] if c then c.unicode = 0x23B5 return c end end
497
498
499
500addextra(0xFE302)
501addextra(0xFE303)
502
503local function smashed(data,unicode,private)
504 local target = data.target
505 local height = target.parameters.xheight / 2
506 local c, done = accent_to_extensible(target,private,data.original,unicode,height,0,nil,-height,unicode)
507 if done then
508 c.top_accent = nil
509 end
510 return c
511end
512
513virtualcharacters[0xFE302] = function(data) return smashed(data,0x0302,0xFE302) end
514virtualcharacters[0xFE303] = function(data) return smashed(data,0x0303,0xFE303) end
515
516
517
518
519
520
521local function smashed(data,unicode,optional)
522 local oldchar = data.characters[unicode]
523 if oldchar then
524
525 local height = 0.85 * data.target.mathparameters.AccentBaseHeight
526 local shift = oldchar.height - height
527 local newchar = {
528 commands = {
529 downcommand[shift],
530 charcommand[unicode],
531 },
532 height = height,
533 width = oldchar.width,
534 }
535 return newchar
536 elseif not optional then
537 report_fallbacks("missing %U prime in font %a",unicode,data.target.properties.fullname)
538 end
539end
540
541addextra(0xFE932)
542addextra(0xFE933)
543addextra(0xFE934)
544addextra(0xFE957)
545
546addextra(0xFE935)
547addextra(0xFE936)
548addextra(0xFE937)
549
550virtualcharacters[0xFE932] = function(data) return smashed(data,0x02032) end
551virtualcharacters[0xFE933] = function(data) return smashed(data,0x02033) end
552virtualcharacters[0xFE934] = function(data) return smashed(data,0x02034) end
553virtualcharacters[0xFE957] = function(data) return smashed(data,0x02057) end
554
555virtualcharacters[0xFE935] = function(data) return smashed(data,0x02035,true) end
556virtualcharacters[0xFE936] = function(data) return smashed(data,0x02036,true) end
557virtualcharacters[0xFE937] = function(data) return smashed(data,0x02037,true) end
558
559local hack = nil
560
561function mathematics.getridofprime(target,original)
562
563
564 local mathparameters = original.mathparameters
565 if mathparameters and next(mathparameters) then
566 local changed = original.changed
567 if changed then
568 hack = changed[0x02032]
569 changed[0x02032] = nil
570 changed[0x02033] = nil
571 changed[0x02034] = nil
572 changed[0x02057] = nil
573 changed[0x02035] = nil
574 changed[0x02036] = nil
575 changed[0x02037] = nil
576 end
577 end
578end
579
580function mathematics.setridofprime(target,original)
581 local mathparameters = original.mathparameters
582 if mathparameters and next(mathparameters) and original.changed then
583 target.characters[0xFE931] = target.characters[hack or 0x2032]
584 hack = nil
585 end
586end
587
588utilities.sequencers.appendaction("beforecopyingcharacters","system","mathematics.getridofprime")
589utilities.sequencers.appendaction("aftercopyingcharacters", "system","mathematics.setridofprime")
590
591
592
593addextra(0xFE940)
594
595local function actuarian(data)
596 local characters = data.target.characters
597 local parameters = data.target.parameters
598 local basechar = characters[0x0078]
599 local linewidth = parameters.xheight / 10
600 local basewidth = basechar.width
601 local baseheight = basechar.height
602 return {
603
604
605 width = basewidth + 4 * linewidth,
606 height = basechar.height,
607 depth = basechar.depth,
608 unicode = 0x20E7,
609 commands = {
610 rightcommand[2 * linewidth],
611 downcommand[- baseheight - 3 * linewidth],
612 { "rule", linewidth, basewidth + 4 * linewidth },
613 leftcommand[linewidth],
614 downcommand[baseheight + 4 * linewidth],
615 { "rule", baseheight + 5 * linewidth, linewidth },
616 },
617 }
618end
619
620virtualcharacters[0x020E7] = actuarian
621virtualcharacters[0xFE940] = actuarian
622
623local function equals(data,unicode,snippet,advance,n)
624 local characters = data.target.characters
625 local parameters = data.target.parameters
626 local basechar = characters[snippet]
627 local advance = advance * parameters.quad
628 return {
629 unicode = unicode,
630 width = n*basechar.width - (n-1)*advance,
631 height = basechar.height,
632 depth = basechar.depth,
633 commands = {
634 charcommand[snippet],
635 leftcommand[advance],
636 charcommand[snippet],
637 n > 2 and leftcommand[advance] or nil,
638 n > 2 and charcommand[snippet] or nil,
639 },
640 }
641end
642
643virtualcharacters[0x2A75] = function(data) return equals(data,0x2A75,0x003D, 1/5,2) end
644virtualcharacters[0x2A76] = function(data) return equals(data,0x2A76,0x003D, 1/5,3) end
645virtualcharacters[0x2980] = function(data) return equals(data,0x2980,0x007C,-1/8,3) end
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673virtualcharacters[0x305] = function(data)
674 local target = data.target
675 local height = target.parameters.xheight/8
676 local width = target.parameters.emwidth/2
677 local depth = height
678 local used = 0.8 * width
679 return {
680 width = width,
681 height = height,
682 depth = depth,
683 commands = { { "rule", height, width } },
684 horiz_variants = {
685 {
686 advance = width,
687 ["end"] = used,
688 glyph = 0x305,
689 start = 0,
690 },
691 {
692 advance = width,
693 ["end"] = 0,
694 extender = 1,
695 glyph = 0x305,
696 start = used,
697 },
698 }
699 }
700end
701
702local function threedots(data,shift)
703 local characters = data.target.characters
704 local parameters = data.target.parameters
705 local periodchar = characters[0x002E]
706 local pluschar = characters[0x002B]
707 local period = charcommand[0x002E]
708 local periodwd = periodchar.width or 0
709 local periodht = periodchar.height or 0
710 local perioddp = periodchar.depth or 0
711 local offset = 0
712 if shift then
713 local plusht = pluschar.height or 0
714 local plusdp = pluschar.depth or 0
715 local axis = (plusdp + plusht)//2 - plusdp
716 offset = axis - periodht//2
717 periodht = axis + periodht//2
718 end
719 return {
720 width = 3*periodwd,
721 height = periodht,
722 depth = 0,
723 commands = { upcommand[offset], period, period, period }
724 }
725end
726
727virtualcharacters[0x2026] = function(data) return threedots(data,false) end virtualforced[0x2026] = true
728virtualcharacters[0x22EF] = function(data) return threedots(data, true) end virtualforced[0x22EF] = true
729 |