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