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
9
10
11
12local next, type = next, type
13
14local trace_fallbacks = false trackers.register("math.fallbacks", function(v) trace_fallbacks = v end)
15
16local report_fallbacks = logs.reporter("math","fallbacks")
17
18local formatters = string.formatters
19local fastcopy = table.fastcopy
20local byte = string.byte
21local sortedhash = table.sortedhash
22local setmetatableindex = table.setmetatableindex
23
24local fallbacks = { }
25mathematics.fallbacks = fallbacks
26
27local helpers = fonts.helpers
28local prependcommands = helpers.prependcommands
29local charcommand = helpers.commands.char
30local leftcommand = helpers.commands.left
31local rightcommand = helpers.commands.right
32local upcommand = helpers.commands.up
33local downcommand = helpers.commands.down
34local dummycommand = helpers.commands.dummy
35local popcommand = helpers.commands.pop
36local pushcommand = helpers.commands.push
37
38local virtualcharacters = { }
39local virtualforced = { }
40
41local hashes = fonts.hashes
42local identifiers = hashes.identifiers
43local lastmathids = hashes.lastmathids
44
45
46
47
48
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)
316 local characters = target.characters
317 local olddata = characters[oldchr]
318
319 if 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)
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)
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 local height = 0.85 * data.target.mathparameters.AccentBaseHeight
526 local newchar = table.copy(oldchar)
527 newchar.yoffset = height - oldchar.height
528 newchar.height = height
529 return newchar
530 elseif not optional then
531 report_fallbacks("missing %U prime in font %a",unicode,data.target.properties.fullname)
532 end
533end
534
535addextra(0xFE932)
536addextra(0xFE933)
537addextra(0xFE934)
538addextra(0xFE957)
539
540addextra(0xFE935)
541addextra(0xFE936)
542addextra(0xFE937)
543
544virtualcharacters[0xFE932] = function(data) return smashed(data,0x02032) end
545virtualcharacters[0xFE933] = function(data) return smashed(data,0x02033) end
546virtualcharacters[0xFE934] = function(data) return smashed(data,0x02034) end
547virtualcharacters[0xFE957] = function(data) return smashed(data,0x02057) end
548
549virtualcharacters[0xFE935] = function(data) return smashed(data,0x02035,true) end
550virtualcharacters[0xFE936] = function(data) return smashed(data,0x02036,true) end
551virtualcharacters[0xFE937] = function(data) return smashed(data,0x02037,true) end
552
553local hack = nil
554
555function mathematics.getridofprime(target,original)
556
557
558 local mathparameters = original.mathparameters
559 if mathparameters and next(mathparameters) then
560 local changed = original.changed
561 if changed then
562 hack = changed[0x02032]
563 changed[0x02032] = nil
564 changed[0x02033] = nil
565 changed[0x02034] = nil
566 changed[0x02057] = nil
567 changed[0x02035] = nil
568 changed[0x02036] = nil
569 changed[0x02037] = nil
570 end
571 end
572end
573
574function mathematics.setridofprime(target,original)
575 local mathparameters = original.mathparameters
576 if mathparameters and next(mathparameters) and original.changed then
577 target.characters[0xFE931] = target.characters[hack or 0x2032]
578 hack = nil
579 end
580end
581
582utilities.sequencers.appendaction("beforecopyingcharacters","system","mathematics.getridofprime")
583utilities.sequencers.appendaction("aftercopyingcharacters", "system","mathematics.setridofprime")
584
585
586
587addextra(0xFE940)
588
589local function actuarian(data)
590 local characters = data.target.characters
591 local parameters = data.target.parameters
592 local basechar = characters[0x0078]
593 local linewidth = parameters.xheight / 10
594 local basewidth = basechar.width
595 local baseheight = basechar.height
596 return {
597
598
599 width = basewidth + 4 * linewidth,
600 height = basechar.height,
601 depth = basechar.depth,
602 unicode = 0x20E7,
603 commands = {
604 rightcommand[2 * linewidth],
605 downcommand[- baseheight - 3 * linewidth],
606 { "rule", linewidth, basewidth + 4 * linewidth },
607 leftcommand[linewidth],
608 downcommand[baseheight + 4 * linewidth],
609 { "rule", baseheight + 5 * linewidth, linewidth },
610 },
611 }
612end
613
614virtualcharacters[0x020E7] = actuarian
615virtualcharacters[0xFE940] = actuarian
616
617local function equals(data,unicode,snippet,advance,n)
618 local characters = data.target.characters
619 local parameters = data.target.parameters
620 local basechar = characters[snippet]
621 local advance = advance * parameters.quad
622 return {
623 unicode = unicode,
624 width = n*basechar.width - (n-1)*advance,
625 height = basechar.height,
626 depth = basechar.depth,
627 commands = {
628 charcommand[snippet],
629 leftcommand[advance],
630 charcommand[snippet],
631 n > 2 and leftcommand[advance] or nil,
632 n > 2 and charcommand[snippet] or nil,
633 },
634 }
635end
636
637virtualcharacters[0x2A75] = function(data) return equals(data,0x2A75,0x003D, 1/5,2) end
638virtualcharacters[0x2A76] = function(data) return equals(data,0x2A76,0x003D, 1/5,3) end
639virtualcharacters[0x2980] = function(data) return equals(data,0x2980,0x007C,-1/8,3) end
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667virtualcharacters[0x305] = function(data)
668 local target = data.target
669 local height = target.parameters.xheight/8
670 local width = target.parameters.emwidth/2
671 local depth = height
672 local used = 0.8 * width
673 return {
674 width = width,
675 height = height,
676 depth = depth,
677 commands = { { "rule", height, width } },
678 horiz_variants = {
679 {
680 advance = width,
681 ["end"] = used,
682 glyph = 0x305,
683 start = 0,
684 },
685 {
686 advance = width,
687 ["end"] = 0,
688 extender = 1,
689 glyph = 0x305,
690 start = used,
691 },
692 }
693 }
694end
695
696local function threedots(data,shift)
697 local characters = data.target.characters
698 local parameters = data.target.parameters
699 local periodchar = characters[0x002E]
700 local pluschar = characters[0x002B]
701 local period = charcommand[0x002E]
702 local periodwd = periodchar.width or 0
703 local periodht = periodchar.height or 0
704 local perioddp = periodchar.depth or 0
705 local offset = 0
706 if shift then
707 local plusht = pluschar.height or 0
708 local plusdp = pluschar.depth or 0
709 local axis = (plusdp + plusht)//2 - plusdp
710 offset = axis - periodht//2
711 periodht = axis + periodht//2
712 end
713 return {
714 width = 3*periodwd,
715 height = periodht,
716 depth = 0,
717 commands = { upcommand[offset], period, period, period }
718 }
719end
720
721virtualcharacters[0x2026] = function(data) return threedots(data,false) end virtualforced[0x2026] = true
722virtualcharacters[0x22EF] = function(data) return threedots(data, true) end virtualforced[0x22EF] = true
723 |