1if not modules then modules = { } end modules ['math-vfu'] = {
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
12
13
14
15
16
17
18
19
20
21
22
23
24
25local type, next, tonumber = type, next, tonumber
26local max = math.max
27local fastcopy, sortedhash = table.copy, table.sortedhash
28
29local fonts, mathematics = fonts, mathematics
30
31local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end)
32local trace_timings = false trackers.register("math.timings", function(v) trace_timings = v end)
33
34local add_optional = false directives.register("math.virtual.optional",function(v) add_optional = v end)
35
36local report_virtual = logs.reporter("fonts","virtual math")
37
38local allocate = utilities.storage.allocate
39local setmetatableindex = table.setmetatableindex
40local formatters = string.formatters
41
42local chardata = characters.data
43
44local mathencodings = allocate()
45fonts.encodings.math = mathencodings
46local vfmath = allocate()
47fonts.handlers.vf.math = vfmath
48
49local helpers = fonts.helpers
50
51local addprivate = helpers.addprivate
52local hasprivate = helpers.hasprivate
53local vfcommands = helpers.commands
54
55local rightcommand = vfcommands.right
56local leftcommand = vfcommands.left
57local downcommand = vfcommands.down
58local upcommand = vfcommands.up
59local push = vfcommands.push
60local pop = vfcommands.pop
61local slotcommand = vfcommands.slot
62local staycommand = vfcommands.stay
63
64local nps = fonts.helpers.newprivateslot
65local ps = fonts.helpers.privateslot
66
67nps("rule middle piece")
68nps("rule right piece")
69nps("rule left piece")
70nps("double rule middle piece")
71nps("double rule right piece")
72nps("double rule left piece")
73nps("arrow left piece")
74nps("arrow right piece")
75nps("double arrow left piece")
76nps("double arrow right piece")
77
78nps("flat rule left piece")
79nps("flat rule middle piece")
80nps("flat rule right piece")
81
82nps("flat double rule left piece")
83nps("flat double rule middle piece")
84nps("flat double rule right piece")
85
86nps("minus rule left piece")
87nps("minus rule middle piece")
88nps("minus rule right piece")
89
90do
91
92
93
94 local function horibar(main,unicode,rule,left,right,normal,force,m,l,r)
95 local characters = main.characters
96 local data = characters[unicode]
97 if force or not data then
98 local height = main.mathparameters.defaultrulethickness or 4*65536/10
99 local f_rule = rule and formatters["M-HORIBAR-M-%H"](rule)
100 local p_rule = rule and hasprivate(main,f_rule)
101 local ndata = normal and characters[normal]
102 if rule and left and right and normal then
103 local ldata = characters[l or left]
104 local mdata = characters[m or rule]
105 local rdata = characters[r or right]
106 local lwidth = ldata.width or 0
107 local mwidth = mdata.width or 0
108 local rwidth = rdata.width or 0
109 local nwidth = ndata.width or 0
110 local down = (mdata.height / 2) - height
111if unicode == normal then
112 height = ndata.height
113 down = 0
114end
115 local f_left = left and formatters["M-HORIBAR-L-%H"](left)
116 local f_right = right and formatters["M-HORIBAR-R-%H"](right)
117 local p_left = left and hasprivate(main,f_left)
118 local p_right = right and hasprivate(main,f_right)
119
120 if not characters[p_rule] then
121 p_rule = addprivate(main,f_rule,{
122 height = height,
123 width = .95*mwidth,
124
125 commands = {
126 push,
127 leftcommand[.025*mwidth],
128 downcommand[down],
129 slotcommand[0][m or rule],
130 pop,
131 },
132 })
133 end
134 if not characters[p_left] then
135 p_left = addprivate(main,f_left,{
136 height = height,
137 width = .95*lwidth,
138
139 commands = {
140 push,
141 leftcommand[.025*lwidth],
142 downcommand[down],
143 slotcommand[0][l or left],
144 pop,
145 },
146 })
147 end
148 if not characters[p_right] then
149 p_right = addprivate(main,f_right,{
150 height = height,
151 width = .95*rwidth,
152
153 commands = {
154 push,
155 leftcommand[.025*rwidth],
156 downcommand[down],
157 slotcommand[0][r or right],
158 pop,
159 },
160 })
161 end
162if unicode ~= normal then
163 data = {
164 unicode = unicode,
165 height = height,
166 width = nwidth,
167 commands = {
168 downcommand[down],
169 slotcommand[0][normal]
170 },
171 }
172 characters[unicode] = data
173end
174 data.parts = {
175 { glyph = p_left, ["end"] = 0.4*lwidth },
176 { glyph = p_rule, extender = 1, ["start"] = mwidth, ["end"] = mwidth },
177 { glyph = p_right, ["start"] = 0.6*rwidth },
178 }
179 else
180 local width = main.parameters.quad/2 or 4*65536
181 if not characters[p_rule] then
182 if unicode == normal then
183 p_rule = addprivate(main,f_rule,{
184 height = ndata.height,
185 width = width,
186 commands = {
187 push,
188 upcommand[(ndata.height - height)/2],
189 { "rule", height, width },
190 pop
191 },
192 })
193 else
194 p_rule = addprivate(main,f_rule,{
195 height = height,
196 width = width,
197 commands = {
198 push,
199 { "rule", height, width },
200 pop
201 },
202 })
203 end
204 end
205if unicode ~= normal then
206 data = {
207 unicode = unicode,
208 height = height,
209 width = width,
210 commands = {
211 slotcommand[0][p_rule]
212 }
213 }
214 characters[unicode] = data
215end
216 data.parts = {
217 { glyph = p_rule, ["start"] = width/2, ["end"] = width/2 },
218 { glyph = p_rule, extender = 1, ["start"] = width/2, ["end"] = width/2 },
219 }
220 end
221 data.keepvirtual = true
222 data.partsorientation = "horizontal"
223 end
224 end
225
226
227
228
229 local function rootbar(main,unicode,rule,right,normal)
230 local characters = main.characters
231 if not characters[unicode] then
232 local height = main.mathparameters.defaultrulethickness or 4*65536/10
233 if rule and right and normal then
234 local mdata = characters[rule]
235 local rdata = characters[right]
236 local ndata = characters[normal]
237 local mwidth = mdata.width or 0
238 local rwidth = rdata.width or 0
239 local nwidth = ndata.width or 0
240 local down = (mdata.height / 2) - height
241
242 local f_rule = rule and formatters["M-ROOTBAR-M-%H"](rule)
243 local f_right = right and formatters["M-ROOTBAR-R-%H"](right)
244 local p_rule = rule and hasprivate(main,f_rule)
245 local p_right = right and hasprivate(main,f_right)
246
247 if not p_rule then
248 p_rule = addprivate(main,f_rule,{
249 height = height,
250 width = .95*mwidth,
251 commands = {
252 push,
253 leftcommand[.05*mwidth],
254 downcommand[down],
255 slotcommand[0][rule],
256 pop,
257 },
258 })
259 end
260 if right and not p_right then
261 p_right = addprivate(main,p_right,{
262 height = height,
263 width = .95*rwidth,
264 commands = {
265 push,
266 leftcommand[.05*rwidth],
267 downcommand[down],
268 slotcommand[0][right],
269 pop,
270 },
271 })
272 end
273 characters[unicode] = {
274 keepvirtual = true,
275 partsorientation = "horizontal",
276 height = height,
277 width = rwidth,
278 commands = {
279 slotcommand[0][p_right],
280 },
281 parts = {
282 { glyph = p_rule, extender = 1, ["start"] = mwidth, ["end"] = 0.9*mwidth },
283 { glyph = p_right, ["start"] = 0.6*rwidth },
284 }
285 }
286 end
287 end
288 end
289
290 local function parent(main,unicode,first,rule,last,where)
291 local characters = main.characters
292 local chardata = characters[unicode]
293 if characters[unicode] then
294 local template = characters[first]
295 if template then
296 local crule = characters[rule]
297 local xheight = main.mathparameters.xheight
298 local rheight = 0
299 local rdepth = 0
300
301
302
303
304 local width = template.width / 4
305 local height = template.height
306 local depth = template.depth
307 rheight = where == "top" and height or 3*height
308 rdepth = where == "top" and 2*height or 0
309 characters[rule] = {
310 height = rheight,
311 depth = rdepth,
312 width = width,
313 commands = { push, { "rule", height, width }, pop },
314 }
315
316 characters[first].depth = rdepth
317 characters[last] .depth = rdepth
318 if where == "top" then
319 while true do
320 chardata.height = chardata.height - xheight
321 chardata.depth = 0
322 chardata.yoffset = -xheight
323 local next = chardata.next
324 if next then
325 unicode = next
326 chardata = characters[unicode]
327 else
328 break
329 end
330 end
331 else
332 while true do
333 chardata.height = 0
334 local next = chardata.next
335 if next then
336 unicode = next
337 chardata = characters[unicode]
338 else
339 break
340 end
341 end
342 end
343 chardata.keepvirtual = true
344 chardata.partsorientation = "horizontal"
345 chardata.parts = {
346 { glyph = first },
347 { glyph = rule, extender = 1, start = width/2, ["end"] = width/2 },
348 { glyph = last },
349 }
350 end
351 end
352 end
353
354 local function brace(main,unicode,first,rule,left,right,rule,last,where)
355 local characters = main.characters
356 local chardata = characters[unicode]
357 if chardata then
358 local template = characters[first]
359 if template then
360 local xheight = main.mathparameters.xheight
361 local width = template.width / 4
362 local height = template.height
363 local depth = template.depth
364 local rheight = 3*height
365 local rdepth = 2*height
366 characters[rule] = {
367 height = rheight,
368 depth = rdepth,
369 width = width,
370 commands = { push, { "rule", height, width }, pop },
371 }
372
373 if where == "top" then
374 while true do
375 chardata.height = chardata.height - xheight
376 chardata.depth = 0
377 chardata.yoffset = -xheight
378 local next = chardata.next
379 if next then
380 unicode = next
381 chardata = characters[unicode]
382 else
383 break
384 end
385 end
386 else
387 while true do
388 chardata.height = 0
389 local next = chardata.next
390 if next then
391 unicode = next
392 chardata = characters[unicode]
393 else
394 break
395 end
396 end
397 end
398 chardata.keepvirtual = true
399 chardata.partsorientation = "horizontal"
400 chardata.parts = {
401 { glyph = first },
402 { glyph = rule, extender = 1, start = width/2, ["end"] = width/2 },
403 { glyph = left },
404 { glyph = right },
405 { glyph = rule, extender = 1, start = width/2, ["end"] = width/2 },
406 { glyph = last },
407 }
408 end
409 end
410 end
411
412 local function dots(main,unicode)
413 local characters = main.characters
414 local c = characters[0x002E]
415 if c then
416 local w = c.width
417 local h = c.height
418 local d = c.depth
419 local size = main.parameters.size
420 local mu = size/18
421 local right3mu = rightcommand[3*mu]
422 local right1mu = rightcommand[1*mu]
423 local up1size = upcommand[.1*size]
424 local up4size = upcommand[.4*size]
425 local up7size = upcommand[.7*size]
426 local right2muw = rightcommand[2*mu + w]
427 local slot = slotcommand[0][0x002E]
428 if unicode == 0x22EF then
429 local c = characters[0x022C5]
430 if c then
431 local width = c.width
432 local height = c.height
433 local depth = c.depth
434 local slot = slotcommand[0][0x022C5]
435
436
437 characters[unicode] = {
438 width = 3*width + 2*3*mu,
439 height = height,
440 depth = depth,
441 commands = {
442 slot, right3mu, slot, right3mu, slot,
443
444
445 }
446 }
447 end
448 elseif unicode == 0x22EE then
449 characters[unicode] = {
450 width = w,
451 height = h+0.8*size,
452 depth = 0,
453 commands = {
454
455 push, slot, pop, up4size, push, slot, pop, up4size, slot,
456 }
457 }
458 elseif unicode == 0x22F1 then
459 characters[unicode] = {
460 width = 3*w + 6*size/18,
461 height = h+0.7*size,
462 depth = 0,
463 commands = {
464
465 right1mu,
466 push, up7size, slot, pop,
467 right2muw,
468 push, up4size, slot, pop,
469 right2muw,
470 push, up1size, slot, pop,
471 right1mu,
472
473 }
474 }
475 elseif unicode == 0x22F0 then
476 characters[unicode] = {
477 width = 3*w + 6*size/18,
478 height = h+0.7*size,
479 depth = 0,
480 commands = {
481
482 right1mu,
483 push, up1size, slot, pop,
484 right2muw,
485 push, up4size, slot, pop,
486 right2muw,
487 push, up7size, slot, pop,
488 right1mu,
489
490 }
491 }
492 else
493 characters[unicode] = {
494 width = 3*w + 2*3*mu,
495 height = h,
496 depth = d,
497 commands = {
498
499 slot, right3mu, slot, right3mu, slot,
500 }
501 }
502 end
503 end
504 end
505
506 local function jointwo(main,unicode,u1,d12,u2)
507 local characters = main.characters
508 local c1 = characters[u1]
509 local c2 = characters[u2]
510 if c1 and c2 then
511 local w1 = c1.width
512 local w2 = c2.width
513 local width
514 if d12 == false then
515 d12 = 0
516 width = w2
517 elseif d12 < 0 then
518 d12 = d12 * w2
519 width = w2
520 else
521 d12 = d12 * main.parameters.size/18
522 width = w1 + w2 - d12
523 end
524 characters[unicode] = {
525 unicode = unicode,
526 width = width,
527 height = max(c1.height or 0, c2.height or 0),
528 depth = max(c1.depth or 0, c2.depth or 0),
529keepvirtual = true,
530 commands = {
531
532
533 slotcommand[0][u1],
534
535 d12 ~= 0 and leftcommand[d12] or false,
536 slotcommand[0][u2],
537
538 },
539 }
540 end
541 end
542
543 local function overlaytwo(main,unicode,u1,factor,u2)
544 local characters = main.characters
545 local c1 = characters[u1]
546 local c2 = characters[u2]
547 if c1 and c2 then
548 local width = c2.width
549 characters[unicode] = {
550 width = width,
551 height = max(c1.height or 0, c2.height or 0),
552 depth = max(c1.depth or 0, c2.depth or 0),
553 commands = {
554 push,
555 slotcommand[0][u2],
556 pop,
557 factor ~= 0 and rightcommand[factor*width] or false,
558 slotcommand[0][u1],
559 },
560 }
561 end
562 end
563
564 local function jointhree(main,unicode,u1,d12,u2,d23,u3)
565 local characters = main.characters
566 local c1 = characters[u1]
567 local c2 = characters[u2]
568 local c3 = characters[u3]
569 if c1 and c2 and c3 then
570 local w1 = c1.width
571 local w2 = c2.width
572 local w3 = c3.width
573 local ds = main.parameters.size/18
574 d12 = d12 * ds
575 d23 = d23 * ds
576 characters[unicode] = {
577 unicode = unicode,
578 width = w1 + w2 + w3 - d12 - d23,
579 height = max(c1.height or 0, c2.height or 0, c3.height or 0),
580 depth = max(c1.depth or 0, c2.depth or 0, c3.depth or 0),
581 commands = {
582
583 slotcommand[0][u1],
584
585 d12 ~= 0 and leftcommand[d12] or false,
586
587 slotcommand[0][u2],
588
589 d23 ~= 0 and leftcommand[d23] or false,
590
591 slotcommand[0][u3],
592
593 }
594 }
595 end
596 end
597
598 local function stack(main,unicode,u1,d12,u2)
599 local characters = main.characters
600 local c1 = characters[u1]
601 if not c1 then
602 return
603 end
604 local c2 = characters[u2]
605 if not c2 then
606 return
607 end
608 local w1 = c1.width or 0
609 local h1 = c1.height or 0
610 local d1 = c1.depth or 0
611 local w2 = c2.width or 0
612 local h2 = c2.height or 0
613 local d2 = c2.depth or 0
614 local mu = main.parameters.size/18
615 characters[unicode] = {
616 width = w1,
617 height = h1 + h2 + d12*mu,
618 depth = d1,
619 commands = {
620 slotcommand[0][u1],
621 leftcommand[w1/2 + w2/2],
622 downcommand[-h1 + d2 -d12*mu],
623 slotcommand[0][u2],
624 }
625 }
626 end
627
628 local function repeated(main,unicode,u,n,fraction)
629 local characters = main.characters
630 local c = characters[u]
631 if c then
632 if n == 1 then
633
634 else
635 local width = c.width
636 local italic = fraction*width
637 local tc = slotcommand[0][u]
638 local tr = leftcommand[italic]
639 local commands = { }
640 for i=1,n-1 do
641 commands[#commands+1] = tc
642 commands[#commands+1] = tr
643 end
644 commands[#commands+1] = tc
645 local next = c.next
646 if next then
647 local p = addprivate(main,formatters["M-R-%H"](next))
648 repeated(main,p,next,n,fraction)
649 next = p
650 end
651 characters[unicode] = {
652 width = width + (n-1)*(width-italic),
653 height = c.height,
654 depth = c.depth,
655 italic = italic,
656 commands = commands,
657 keepvirtual = true,
658 next = next,
659 }
660 end
661 end
662 end
663
664 local function extension(main,unicode,first,middle,last,ffactor,mfactor,lfactor)
665 local characters = main.characters
666 local chardata = characters[unicode]
667 if chardata then
668 local fw = first and characters[first]
669 local mw = middle and characters[middle]
670 local lw = last and characters[last]
671 if fw and lw then
672 fw = fw.width ; if fw == 0 then fw = 1 end
673 lw = lw.width ; if lw == 0 then lw = 1 end
674 if middle == "left" then
675 chardata.parts = {
676 { extender = 0, glyph = first, ["end"] = fw/2, start = 0, advance = fw },
677 { extender = 1, glyph = last, ["end"] = lw/2, start = lw/2, advance = lw },
678 }
679 elseif middle == "right" then
680 chardata.parts = {
681 { extender = 1, glyph = first, ["end"] = fw/2, start = fw/2, advance = fw },
682 { extender = 0, glyph = last, ["end"] = lw/2, start = 0, advance = lw },
683 }
684 elseif mw then
685 mw = mw.width ; if mw == 0 then mw = 1 end
686 chardata.parts = {
687 { extender = 0, glyph = first, ["end"] = fw/2, start = 0, advance = (ffactor or 1)*fw },
688 { extender = 1, glyph = middle, ["end"] = mw/2, start = mw/2, advance = (mfactor or 1)*mw },
689 { extender = 0, glyph = last, ["end"] = 0, start = lw/2, advance = (lfactor or 1)*lw },
690 }
691 end
692 chardata.keepvirtual = true
693 chardata.partsorientation = "horizontal"
694 end
695 end
696 end
697
698 vfmath.builders = {
699 horibar = horibar,
700 rootbar = rootbar,
701 parent = parent,
702 brace = brace,
703 dots = dots,
704 jointwo = jointwo,
705 overlaytwo = overlaytwo,
706 jointhree = jointhree,
707 stack = stack,
708 repeated = repeated,
709 extension = extension,
710 }
711
712
713
714end
715
716local unique = 0
717
718local reported = { }
719local reverse = { }
720
721setmetatableindex(reverse, function(t,name)
722 if trace_virtual then
723 report_virtual("initializing math vector %a",name)
724 end
725 local m = mathencodings[name]
726 local r = { }
727 for u, i in next, m do
728 r[i] = u
729 end
730 reverse[name] = r
731 return r
732end)
733
734
735
736local function copy_glyph(main,target,original,unicode,slot)
737 local olddata = original[unicode]
738 if olddata then
739 local newdata = {
740 width = olddata.width,
741 height = olddata.height,
742 depth = olddata.depth,
743 italic = olddata.italic,
744 topanchor = olddata.topanchor,
745 kerns = olddata.kerns,
746 mathkerns = olddata.mathkerns,
747 tounicode = olddata.tounicode,
748
749 commands = { slotcommand[slot][unicode] },
750 }
751 local glyphdata = newdata
752 local nextglyph = olddata.next
753 while nextglyph do
754 local oldnextdata = original[nextglyph]
755 if oldnextdata then
756 local newnextdata = {
757 width = oldnextdata.width,
758 height = oldnextdata.height,
759 depth = oldnextdata.depth,
760 italic = oldnextdata.italic,
761 topanchor = oldnextdata.topanchor,
762 kerns = olddata.kerns,
763 mathkerns = olddata.mathkerns,
764 tounicode = olddata.tounicode,
765 smaller = olddata.smaller,
766 commands = { slotcommand[slot][nextglyph] },
767 }
768 local newnextglyph = addprivate(main,formatters["M-N-%H"](nextglyph),newnextdata)
769 newdata.next = newnextglyph
770 local nextnextglyph = oldnextdata.next
771 if nextnextglyph == nextglyph then
772 break
773 else
774 olddata = oldnextdata
775 newdata = newnextdata
776 nextglyph = nextnextglyph
777 end
778 else
779 break
780 end
781 end
782 local oldparts = olddata.parts
783 if oldparts then
784 newparts = fastcopy(oldparts)
785 newdata.parts = newparts
786 newdata.partsorientation = olddata.partsorientation
787 newdata.partsitalic = olddata.partsitalic
788 for i=1,#newparts do
789 local newpart = newparts[i]
790 local oldglyph = newpart.glyph
791 local olddata = original[oldglyph]
792 local newdata = {
793 width = olddata.width,
794 height = olddata.height,
795 depth = olddata.depth,
796 tounicode = olddata.tounicode,
797 commands = { slotcommand[slot][oldglyph] },
798 }
799 newpart.glyph = addprivate(main,formatters["M-P-%H"](oldglyph),newdata)
800 end
801 end
802 local smaller = olddata.smaller
803 if smaller then
804 local smallerdata = copy_glyph(main,target,original,smaller,slot)
805 glyphdata.smaller = addprivate(main,formatters["M-S-%H"](smaller),smallerdata)
806 end
807 return glyphdata
808 end
809end
810
811vfmath.copy_glyph = copy_glyph
812
813
814
815
816
817
818local noitalics = true
819
820
821
822
823
824
825
826
827
828
829local function virtualize(s,uni,fci,skewchar,move,mathparameters,unicode,parameters)
830 if fci then
831 local kerns = fci.kerns
832 local width = fci.width
833 local height = fci.height
834 local depth = fci.depth
835 local italic = fci.italic
836 local advance = width
837 local bottomright
838 local topanchor
839 local yoffset
840 if kerns and skewchar then
841 local k = kerns[skewchar]
842 if k then
843 topanchor = width/2 + k
844 end
845 end
846 if italic and noitalics then
847 width = width + italic
848 bottomright = - italic
849 italic = nil
850 end
851 if move then
852 local axis = move * mathparameters.axisheight
853 local half = (height + depth ) / 2
854 yoffset = depth - (half - axis)
855 height = half + axis
856 depth = half - axis
857 end
858
859 local next = fci.next
860 return {
861 advance = advance,
862 width = width,
863 height = height,
864 depth = depth,
865 italic = italic,
866 bottomright = bottomright,
867 topanchor = topanchor,
868 yoffset = yoffset,
869 commands = { slotcommand[s][uni] },
870
871 next = fci.next,
872 parts = fci.parts,
873 partsorientation = fci.partsorientation,
874 partsitalic = fci.partsitalic,
875 unicode = unicode,
876 name = fci.name,
877 }
878 else
879
880 end
881end
882
883function vfmath.define(specification,virtual,goodies)
884 local name = specification.name
885 local size = specification.size
886 local loaded = { }
887 local fontlist = { }
888 local names = { }
889 local main = nil
890 local start = (trace_virtual or trace_timings) and os.clock()
891 local okset = { }
892 local n = 0
893 local f_extra = formatters["virtual.extra.%05X"]
894 local setlist = virtual.recipe or virtual
895 for s=1,#setlist do
896 local ss = setlist[s]
897 local ssname = ss.name
898 if add_optional and ss.optional then
899 if trace_virtual then
900 report_virtual("loading font %a subfont %s with name %a at %p is skipped",name,s,ssname,size)
901 end
902 else
903 if ss.features then
904 ssname = ssname .. "*" .. ss.features
905 end
906 if ss.main then
907 main = s
908 end
909 local alreadyloaded = names[ssname]
910
911
912 local f, id
913 if alreadyloaded then
914 f, id = alreadyloaded.f, alreadyloaded.id
915 if trace_virtual then
916 report_virtual("loading font %a subfont %s with name %a is reused",name,s,ssname)
917 end
918 else
919 f, id = fonts.constructors.readanddefine(ssname,size)
920 names[ssname] = {
921
922 f = f,
923 id = id,
924 fontname = ssname,
925 }
926 end
927 if not f or id == 0 then
928 report_virtual("loading font %a subfont %s with name %a at %p is skipped, not found",name,s,ssname,size)
929 else
930 n = n + 1
931 okset[n] = ss
932 loaded[n] = f
933 fontlist[n] = {
934 id = id,
935 size = size,
936 fontname = ssname,
937 }
938 if trace_virtual then
939 report_virtual("loading font %a subfont %s with name %a at %p as id %s using encoding %a",name,s,ssname,size,id,ss.vector)
940 end
941 end
942 end
943 end
944
945 local parent = loaded[1] or { }
946 local characters = { }
947 local parameters = { }
948 local mathparameters = { }
949 local descriptions = { }
950 local metadata = { }
951 local properties = {
952 hasitalics = true,
953 hasmath = true,
954 }
955 if not goodies then
956 goodies = { }
957 end
958 local main = {
959 metadata = metadata,
960 properties = properties,
961 characters = characters,
962 descriptions = descriptions,
963 parameters = parameters,
964 mathparameters = mathparameters,
965 fonts = fontlist,
966 goodies = goodies,
967
968 fullname = properties.fullname,
969 nomath = false,
970 }
971
972 for key, value in next, parent do
973 if type(value) ~= "table" then
974 main[key] = value
975 end
976 end
977
978 if parent.characters then
979 for unicode, character in next, parent.characters do
980 characters[unicode] = character
981 end
982 else
983 report_virtual("font %a has no characters",name)
984 end
985
986 if parent.parameters then
987 for key, value in next, parent.parameters do
988 parameters[key] = value
989 end
990 else
991 report_virtual("font %a has no parameters",name)
992 end
993
994 local description = { name = "<unset>" }
995 setmetatableindex(descriptions,function() return description end)
996
997 if parent.properties then
998 setmetatableindex(properties,parent.properties)
999 end
1000
1001 if parent.goodies then
1002 setmetatableindex(goodies,parent.goodies)
1003 end
1004
1005 local fullname = properties.fullname
1006 if fullname then
1007 unique = unique + 1
1008 properties.fullname = fullname .. "-" .. unique
1009 end
1010
1011 if not parameters.xheight then
1012 parameters.xheight = 0
1013 end
1014
1015 local already_reported = false
1016 local parameters_done = false
1017 local offset = 0
1018
1019 for s=1,n do
1020 local ss = okset[s]
1021 local fs = loaded[s]
1022 if not fs then
1023
1024 elseif add_optional and ss.optional then
1025
1026 else
1027 local newparameters = fs.parameters
1028 local newmathparameters = fs.mathparameters and ss.parameters ~= false
1029 if newmathparameters then
1030 if not parameters_done or ss.parameters then
1031 mathparameters = newmathparameters
1032 parameters_done = true
1033 end
1034 elseif not newparameters then
1035 report_virtual("no parameters set in font %a",name)
1036 elseif ss.extension then
1037 mathparameters.xheight = newparameters.xheight or 0
1038 mathparameters.defaultrulethickness = newparameters[ 8] or 0
1039 mathparameters.bigopspacing1 = newparameters[ 9] or 0
1040 mathparameters.bigopspacing2 = newparameters[10] or 0
1041 mathparameters.bigopspacing3 = newparameters[11] or 0
1042 mathparameters.bigopspacing4 = newparameters[12] or 0
1043 mathparameters.bigopspacing5 = newparameters[13] or 0
1044
1045 elseif ss.parameters then
1046 mathparameters.xheight = newparameters.xheight
1047 or mathparameters.xheight
1048 or fs.xheight or 0
1049 mathparameters.num1 = newparameters[ 8] or 0
1050 mathparameters.num2 = newparameters[ 9] or 0
1051 mathparameters.num3 = newparameters[10] or 0
1052 mathparameters.denom1 = newparameters[11] or 0
1053 mathparameters.denom2 = newparameters[12] or 0
1054 mathparameters.sup1 = newparameters[13] or 0
1055 mathparameters.sup2 = newparameters[14] or 0
1056 mathparameters.sup3 = newparameters[15] or 0
1057 mathparameters.sub1 = newparameters[16] or 0
1058 mathparameters.sub2 = newparameters[17] or 0
1059 mathparameters.supdrop = newparameters[18] or 0
1060 mathparameters.subdrop = newparameters[19] or 0
1061 mathparameters.delim1 = newparameters[20] or 0
1062 mathparameters.delim2 = newparameters[21] or 0
1063 mathparameters.axisheight = newparameters[22] or 0
1064
1065 end
1066
1067
1068
1069 if ss.overlay then
1070
1071 local fc = fs.characters
1072 local first = ss.first
1073 if first then
1074 local last = ss.last or first
1075 for unicode = first, last do
1076 characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
1077 end
1078 else
1079 for unicode, data in next, fc do
1080 characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
1081 end
1082 end
1083 else
1084 local vectorname = ss.vector
1085 if vectorname then
1086 local vector = mathencodings[vectorname]
1087 local isextension = ss.extension
1088 if vector then
1089 local fc = fs.characters
1090 local fd = fs.descriptions
1091 local fp = fs.parameters
1092 local fontname = fs.properties.name or "unknown"
1093 local skewchar = ss.skewchar
1094 local backmap = ss.backmap
1095 local badones = ss.badones
1096 local ignore = ss.ignore
1097 local done = { }
1098 local extras = { }
1099 if backmap == false then
1100
1101 elseif not backmap then
1102 backmap = { }
1103 for unicode, character in next, fc do
1104 backmap[character.order or character.index or unicode] = unicode
1105 end
1106 ss.backmap = backmap
1107 end
1108 for unicode, index in sortedhash(vector) do
1109 local uni = backmap and backmap[index] or index
1110 local fci = fc[uni]
1111 if not fci then
1112 local rf = reported[fontname]
1113 if not rf then rf = { } reported[fontname] = rf end
1114 local rv = rf[vectorname]
1115 if not rv then rv = { } rf[vectorname] = rv end
1116 local ru = rv[unicode]
1117 if not ru then
1118 if trace_virtual then
1119 local d = chardata[unicode].description
1120 if index then
1121 report_virtual("character %C has no index %H in vector %a for font %a (%S)",unicode,index,vectorname,fontname,d)
1122 else
1123 report_virtual("character %C has no entry in vector %a for font %a (%S)",unicode,vectorname,fontname,d)
1124 end
1125 elseif not already_reported then
1126 report_virtual("the mapping is incomplete for %a at %p",name,size)
1127 already_reported = true
1128 end
1129 rv[unicode] = true
1130 end
1131 else
1132 local name = fci.name or ""
1133 if ignore and ignore[name] then
1134
1135 else
1136 local u = mathematics.gaps[unicode] or unicode
1137 local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[name]),mathparameters,u,fp)
1138 done[uni] = t
1139 characters[unicode] = t
1140 fci.unicode = u
1141 end
1142 end
1143 end
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 if isextension then
1155 local extension = mathencodings["large-to-small"]
1156 for uni, fci in sortedhash(fc) do
1157 local name = fci.name or ""
1158 if ignore and ignore[name] then
1159
1160 elseif not done[uni] then
1161 local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[name]),mathparameters,nil,fp)
1162 local o = addprivate(main,f_extra(offset))
1163 extras[uni] = o
1164 characters[o] = t
1165 done[uni] = t
1166 offset = offset + 1
1167 end
1168 end
1169 for uni, fci in sortedhash(done) do
1170 local next = fci.next
1171 if next then
1172 fci.next = extras[backmap and backmap[next] or next]
1173 end
1174 local parts = fci.parts
1175 if parts then
1176 local p = table.copy(parts)
1177 for i=1,#p do
1178 local part = p[i]
1179 local glyph = part.glyph
1180 part.glyph = extras[backmap and backmap[glyph] or glyph] or glyph
1181 end
1182 fci.keepvirtual = true
1183 fci.parts = p
1184 fci.partsorientation = "vertical"
1185 fci.partsitalic = fci.partsitalic or fci.italic
1186 end
1187 end
1188 for unicode, index in sortedhash(extension) do
1189 local fci = characters[unicode]
1190 if fci then
1191 fci.next = extras[backmap[index] or index]
1192 end
1193 end
1194 local extension = mathencodings["large-to-small-private"]
1195 for unicode, index in sortedhash(extension) do
1196 if not characters[unicode] then
1197 local uni = backmap and backmap[index] or index
1198 local fci = fc[uni]
1199 characters[unicode] = virtualize(s,uni,fci,skewchar,false,mathparameters,unicode,fp)
1200 end
1201 end
1202 end
1203 else
1204 report_virtual("error in loading %a, problematic vector %a",name,vectorname)
1205 end
1206 end
1207 end
1208
1209 end
1210 end
1211
1212 main.mathparameters = mathparameters
1213 fontlist[#fontlist+1] = {
1214 id = 0,
1215 size = size,
1216 fontname = name,
1217 }
1218
1219 local virtualtweaks = virtual.tweaks
1220 if virtualtweaks then
1221
1222 local mathtweaks = mathematics.tweaks
1223 local knowntweaks = {
1224 addmissing = function(main,specification)
1225 local action = specification.action
1226 if action then
1227 action(main,specification)
1228 end
1229 end
1230 }
1231
1232 for i=1,#virtualtweaks do
1233 local specification = virtualtweaks[i]
1234 local tweak = specification.tweak
1235 local action = knowntweaks[tweak]
1236 if action then
1237 action(main,specification)
1238 else
1239 action = mathtweaks[tweak]
1240 if action then
1241 action(main,main,specification)
1242 end
1243 end
1244 end
1245 end
1246
1247 mathematics.addfallbacks(main)
1248
1249 main.properties.math_is_scaled = true
1250 fonts.constructors.assignmathparameters(main,main)
1251
1252 mathematics.initializeparameters(main,main,"noscale")
1253 main.mathconstants = main.mathparameters
1254 main.MathConstants = main.mathconstants
1255 main.nomath = false
1256
1257 if trace_virtual or trace_timings then
1258 report_virtual("loading and virtualizing font %a at size %p took %0.3f seconds",name,size,os.clock()-start)
1259 end
1260
1261 return main
1262end
1263
1264function mathematics.makefont(name,set,goodies)
1265 fonts.definers.methods.variants[name] = function(specification)
1266 return vfmath.define(specification,set,goodies)
1267 end
1268end
1269
1270
1271
1272function vfmath.setletters(font_encoding, name, uppercase, lowercase)
1273 local enc = font_encoding[name]
1274 for i = 0,25 do
1275 enc[uppercase+i] = i + 0x41
1276 enc[lowercase+i] = i + 0x61
1277 end
1278end
1279
1280function vfmath.setdigits(font_encoding, name, digits)
1281 local enc = font_encoding[name]
1282 for i = 0,9 do
1283 enc[digits+i] = i + 0x30
1284 end
1285end
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394 |