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, tohash = table.copy, table.sortedhash, table.tohash
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 bottomanchor = olddata.bottomanchor,
746 kerns = olddata.kerns,
747 mathkerns = olddata.mathkerns,
748 tounicode = olddata.tounicode,
749 unicode = olddata.unicode,
750
751 commands = { slotcommand[slot][unicode] },
752 }
753 local glyphdata = newdata
754 local nextglyph = olddata.next
755 while nextglyph do
756 local oldnextdata = original[nextglyph]
757 if oldnextdata then
758 local newnextdata = {
759 width = oldnextdata.width,
760 height = oldnextdata.height,
761 depth = oldnextdata.depth,
762 italic = oldnextdata.italic,
763 topanchor = oldnextdata.topanchor,
764 bottomanchor = oldnextdata.bottomanchor,
765 kerns = olddata.kerns,
766 mathkerns = olddata.mathkerns,
767 tounicode = olddata.tounicode,
768 unicode = olddata.unicode,
769 smaller = olddata.smaller,
770 commands = { slotcommand[slot][nextglyph] },
771 }
772 local newnextglyph = addprivate(main,formatters["M-N-%H"](nextglyph),newnextdata)
773 newdata.next = newnextglyph
774 local nextnextglyph = oldnextdata.next
775 if nextnextglyph == nextglyph then
776 break
777 else
778 olddata = oldnextdata
779 newdata = newnextdata
780 nextglyph = nextnextglyph
781 end
782 else
783 break
784 end
785 end
786 local oldparts = olddata.parts
787 if oldparts then
788 newparts = fastcopy(oldparts)
789 newdata.parts = newparts
790 newdata.partsorientation = olddata.partsorientation
791 newdata.partsitalic = olddata.partsitalic
792 for i=1,#newparts do
793 local newpart = newparts[i]
794 local oldglyph = newpart.glyph
795 local olddata = original[oldglyph]
796 local newdata = {
797 width = olddata.width,
798 height = olddata.height,
799 depth = olddata.depth,
800 tounicode = olddata.tounicode,
801 unicode = olddata.unicode,
802 commands = { slotcommand[slot][oldglyph] },
803 }
804 newpart.glyph = addprivate(main,formatters["M-P-%H"](oldglyph),newdata)
805 end
806 end
807 local smaller = olddata.smaller
808 if smaller then
809 local smallerdata = copy_glyph(main,target,original,smaller,slot)
810 glyphdata.smaller = addprivate(main,formatters["M-S-%H"](smaller),smallerdata)
811 end
812 return glyphdata
813 end
814end
815
816vfmath.copy_glyph = copy_glyph
817
818
819
820
821
822
823local noitalics = true
824
825
826
827
828
829
830
831
832
833
834local integrals = setmetatableindex(function(t,k)
835 integrals = tohash(mathematics.tweaks.subsets.integrals)
836 return integrals[k]
837end)
838
839local function virtualize(s,uni,fci,skewchar,move,mathparameters,unicode,parameters,characters)
840 if fci then
841 local kerns = fci.kerns
842 local width = fci.width
843 local height = fci.height
844 local depth = fci.depth
845 local italic = fci.italic
846 local topanchor = fci.topanchor
847 local bottomanchor = fci.bottomanchor
848 local bottomright = fci.bottomright
849 local advance = width
850 local nextone = fci.next
851 local yoffset
852 if kerns and skewchar then
853 local k = kerns[skewchar]
854 if k then
855 topanchor = width/2 + k
856 end
857 end
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884 if move then
885 local axis = move * mathparameters.axisheight
886 local half = (height + depth ) / 2
887 yoffset = depth - (half - axis)
888 height = half + axis
889 depth = half - axis
890 end
891
892 return {
893 advance = advance,
894 width = width,
895 height = height,
896 depth = depth,
897 italic = italic,
898 bottomright = bottomright,
899 topanchor = topanchor,
900 bottomanchor = bottomanchor,
901 yoffset = yoffset,
902 commands = { slotcommand[s][uni] },
903
904 next = nextone,
905 parts = fci.parts,
906 partsorientation = fci.partsorientation,
907 partsitalic = fci.partsitalic,
908 unicode = unicode,
909 name = fci.name,
910
911 }
912 else
913
914 end
915end
916
917function vfmath.define(specification,virtual,goodies)
918 local name = specification.name
919 local size = specification.size
920 local loaded = { }
921 local fontlist = { }
922 local names = { }
923 local main = nil
924 local start = (trace_virtual or trace_timings) and os.clock()
925 local okset = { }
926 local n = 0
927 local f_extra = formatters["virtual.extra.%05X"]
928 local setlist = virtual.recipe or virtual
929 for s=1,#setlist do
930 local ss = setlist[s]
931 local ssname = ss.name
932 if add_optional and ss.optional then
933 if trace_virtual then
934 report_virtual("loading font %a subfont %s with name %a at %p is skipped",name,s,ssname,size)
935 end
936 else
937 if ss.features then
938 ssname = ssname .. "*" .. ss.features
939 end
940 if ss.main then
941 main = s
942 end
943 local alreadyloaded = names[ssname]
944
945
946 local f, id
947 if alreadyloaded then
948 f, id = alreadyloaded.f, alreadyloaded.id
949 if trace_virtual then
950 report_virtual("loading font %a subfont %s with name %a is reused",name,s,ssname)
951 end
952 else
953 f, id = fonts.constructors.readanddefine(ssname,size)
954 names[ssname] = {
955
956 f = f,
957 id = id,
958 fontname = ssname,
959 }
960 end
961 if not f or id == 0 then
962 report_virtual("loading font %a subfont %s with name %a at %p is skipped, not found",name,s,ssname,size)
963 else
964 n = n + 1
965 okset[n] = ss
966 loaded[n] = f
967 fontlist[n] = {
968 id = id,
969 size = size,
970 fontname = ssname,
971 }
972 if trace_virtual then
973 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)
974 end
975 end
976 end
977 end
978
979 local parent = loaded[1] or { }
980 local characters = { }
981 local parameters = { }
982 local mathparameters = { }
983 local descriptions = { }
984 local metadata = { }
985 local properties = {
986 hasitalics = true,
987 hasmath = true,
988 }
989 if not goodies then
990 goodies = { }
991 end
992 local main = {
993 metadata = metadata,
994 properties = properties,
995 characters = characters,
996 descriptions = descriptions,
997 parameters = parameters,
998 mathparameters = mathparameters,
999 fonts = fontlist,
1000 goodies = goodies,
1001
1002 fullname = properties.fullname,
1003 nomath = false,
1004 }
1005
1006 for key, value in next, parent do
1007 if type(value) ~= "table" then
1008 main[key] = value
1009 end
1010 end
1011
1012 if parent.characters then
1013 for unicode, character in next, parent.characters do
1014 characters[unicode] = character
1015 end
1016 else
1017 report_virtual("font %a has no characters",name)
1018 end
1019
1020 if parent.parameters then
1021 for key, value in next, parent.parameters do
1022 parameters[key] = value
1023 end
1024 else
1025 report_virtual("font %a has no parameters",name)
1026 end
1027
1028 local description = { name = "<unset>" }
1029 setmetatableindex(descriptions,function() return description end)
1030
1031 if parent.properties then
1032 setmetatableindex(properties,parent.properties)
1033 end
1034
1035 if parent.goodies then
1036 setmetatableindex(goodies,parent.goodies)
1037 end
1038
1039 local fullname = properties.fullname
1040 if fullname then
1041 unique = unique + 1
1042 properties.fullname = fullname .. "-" .. unique
1043 end
1044
1045 if not parameters.xheight then
1046 parameters.xheight = 0
1047 end
1048
1049 local already_reported = false
1050 local parameters_done = false
1051 local offset = 0
1052
1053 for s=1,n do
1054 local ss = okset[s]
1055 local fs = loaded[s]
1056 if not fs then
1057
1058 elseif add_optional and ss.optional then
1059
1060 else
1061 local newparameters = fs.parameters
1062 local newmathparameters = fs.mathparameters and ss.parameters ~= false
1063 if newmathparameters then
1064 if not parameters_done or ss.parameters then
1065 mathparameters = newmathparameters
1066 parameters_done = true
1067 end
1068 elseif not newparameters then
1069 report_virtual("no parameters set in font %a",name)
1070 elseif ss.extension then
1071 mathparameters.xheight = newparameters.xheight or 0
1072 mathparameters.defaultrulethickness = newparameters[ 8] or 0
1073 mathparameters.bigopspacing1 = newparameters[ 9] or 0
1074 mathparameters.bigopspacing2 = newparameters[10] or 0
1075 mathparameters.bigopspacing3 = newparameters[11] or 0
1076 mathparameters.bigopspacing4 = newparameters[12] or 0
1077 mathparameters.bigopspacing5 = newparameters[13] or 0
1078
1079 elseif ss.parameters then
1080 mathparameters.xheight = newparameters.xheight
1081 or mathparameters.xheight
1082 or fs.xheight or 0
1083 mathparameters.num1 = newparameters[ 8] or 0
1084 mathparameters.num2 = newparameters[ 9] or 0
1085 mathparameters.num3 = newparameters[10] or 0
1086 mathparameters.denom1 = newparameters[11] or 0
1087 mathparameters.denom2 = newparameters[12] or 0
1088 mathparameters.sup1 = newparameters[13] or 0
1089 mathparameters.sup2 = newparameters[14] or 0
1090 mathparameters.sup3 = newparameters[15] or 0
1091 mathparameters.sub1 = newparameters[16] or 0
1092 mathparameters.sub2 = newparameters[17] or 0
1093 mathparameters.supdrop = newparameters[18] or 0
1094 mathparameters.subdrop = newparameters[19] or 0
1095 mathparameters.delim1 = newparameters[20] or 0
1096 mathparameters.delim2 = newparameters[21] or 0
1097 mathparameters.axisheight = newparameters[22] or 0
1098
1099 end
1100
1101
1102
1103 if ss.overlay then
1104
1105 local fc = fs.characters
1106 local first = ss.first
1107 if first then
1108 local last = ss.last or first
1109 for unicode = first, last do
1110 characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
1111 end
1112 else
1113 for unicode, data in next, fc do
1114 characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
1115 end
1116 end
1117 else
1118 local vectorname = ss.vector
1119 if vectorname then
1120 local vector = mathencodings[vectorname]
1121 local isextension = ss.extension
1122 if vector then
1123 local fc = fs.characters
1124 local fd = fs.descriptions
1125 local fp = fs.parameters
1126 local fontname = fs.properties.name or "unknown"
1127 local skewchar = ss.skewchar
1128 local backmap = ss.backmap
1129 local badones = ss.badones
1130 local ignore = ss.ignore
1131 local done = { }
1132 local extras = { }
1133 if backmap == false then
1134
1135 elseif not backmap then
1136 backmap = { }
1137 for unicode, character in next, fc do
1138 backmap[character.order or character.index or unicode] = unicode
1139 end
1140 ss.backmap = backmap
1141 end
1142 for unicode, index in sortedhash(vector) do
1143 local uni = backmap and backmap[index] or index
1144 local fci = fc[uni]
1145 if not fci then
1146 local rf = reported[fontname]
1147 if not rf then rf = { } reported[fontname] = rf end
1148 local rv = rf[vectorname]
1149 if not rv then rv = { } rf[vectorname] = rv end
1150 local ru = rv[unicode]
1151 if not ru then
1152 if trace_virtual then
1153 local d = chardata[unicode].description
1154 if index then
1155 report_virtual("character %C has no index %H in vector %a for font %a (%S)",unicode,index,vectorname,fontname,d)
1156 else
1157 report_virtual("character %C has no entry in vector %a for font %a (%S)",unicode,vectorname,fontname,d)
1158 end
1159 elseif not already_reported then
1160 report_virtual("the mapping is incomplete for %a at %p",name,size)
1161 already_reported = true
1162 end
1163 rv[unicode] = true
1164 end
1165 else
1166 local name = fci.name or ""
1167 if ignore and ignore[name] then
1168
1169 else
1170 local u = mathematics.gaps[unicode] or unicode
1171 local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[name]),mathparameters,u,fp,characters)
1172 done[uni] = t
1173 characters[unicode] = t
1174 fci.unicode = u
1175 end
1176 end
1177 end
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188 if isextension then
1189 local extension = mathencodings["large-to-small"]
1190 for uni, fci in sortedhash(fc) do
1191 local name = fci.name or ""
1192 if ignore and ignore[name] then
1193
1194 elseif not done[uni] then
1195 local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[name]),mathparameters,nil,fp,characters)
1196 local o = addprivate(main,f_extra(offset))
1197 extras[uni] = o
1198 characters[o] = t
1199 done[uni] = t
1200 offset = offset + 1
1201 end
1202 end
1203 for uni, fci in sortedhash(done) do
1204 local next = fci.next
1205 if next then
1206 fci.next = extras[backmap and backmap[next] or next]
1207 end
1208 local parts = fci.parts
1209 if parts then
1210 local p = table.copy(parts)
1211 for i=1,#p do
1212 local part = p[i]
1213 local glyph = part.glyph
1214 part.glyph = extras[backmap and backmap[glyph] or glyph] or glyph
1215 end
1216 fci.keepvirtual = true
1217 fci.parts = p
1218 fci.partsorientation = "vertical"
1219 fci.partsitalic = fci.partsitalic or fci.italic
1220 end
1221 end
1222 for unicode, index in sortedhash(extension) do
1223 local fci = characters[unicode]
1224 if fci then
1225 fci.next = extras[backmap[index] or index]
1226 end
1227 end
1228 local extension = mathencodings["large-to-small-private"]
1229 for unicode, index in sortedhash(extension) do
1230 if not characters[unicode] then
1231 local uni = backmap and backmap[index] or index
1232 local fci = fc[uni]
1233 characters[unicode] = virtualize(s,uni,fci,skewchar,false,mathparameters,unicode,fp,characters)
1234 end
1235 end
1236 end
1237 else
1238 report_virtual("error in loading %a, problematic vector %a",name,vectorname)
1239 end
1240 end
1241 end
1242
1243 end
1244 end
1245 for unicode, data in next, characters do
1246 local n = data.next
1247 while n do
1248 local c = characters[n]
1249 if c then
1250 c.unicode = unicode
1251 n = c.next
1252 else
1253 break
1254 end
1255 end
1256 end
1257 if noitalics then
1258 for unicode, data in next, characters do
1259 local italic = data.italic
1260 if italic and italic ~= 0 then
1261 local width = data.width + italic
1262 if integrals[data.unicode] then
1263 data.topanchor = (width + italic) / 2
1264 data.bottomanchor = (width - italic) / 2
1265
1266 end
1267 data.width = width
1268 data.italic = nil
1269 data.bottomright = - italic
1270 end
1271 end
1272 end
1273
1274 main.mathparameters = mathparameters
1275 fontlist[#fontlist+1] = {
1276 id = 0,
1277 size = size,
1278 fontname = name,
1279 }
1280
1281 local virtualtweaks = virtual.tweaks
1282 if virtualtweaks then
1283
1284 local mathtweaks = mathematics.tweaks
1285 local knowntweaks = {
1286 addmissing = function(main,specification)
1287 local action = specification.action
1288 if action then
1289 action(main,specification)
1290 end
1291 end
1292 }
1293
1294 for i=1,#virtualtweaks do
1295 local specification = virtualtweaks[i]
1296 local tweak = specification.tweak
1297 local action = knowntweaks[tweak]
1298 if action then
1299 action(main,specification)
1300 else
1301 action = mathtweaks[tweak]
1302 if action then
1303 action(main,main,specification)
1304 end
1305 end
1306 end
1307 end
1308
1309 mathematics.addfallbacks(main)
1310
1311 main.properties.math_is_scaled = true
1312 fonts.constructors.assignmathparameters(main,main)
1313
1314 mathematics.initializeparameters(main,main,"noscale")
1315 main.mathconstants = main.mathparameters
1316 main.MathConstants = main.mathconstants
1317 main.nomath = false
1318
1319 if trace_virtual or trace_timings then
1320 report_virtual("loading and virtualizing font %a at size %p took %0.3f seconds",name,size,os.clock()-start)
1321 end
1322
1323 return main
1324end
1325
1326function mathematics.makefont(name,set,goodies)
1327 fonts.definers.methods.variants[name] = function(specification)
1328 return vfmath.define(specification,set,goodies)
1329 end
1330end
1331
1332
1333
1334function vfmath.setletters(font_encoding, name, uppercase, lowercase)
1335 local enc = font_encoding[name]
1336 for i = 0,25 do
1337 enc[uppercase+i] = i + 0x41
1338 enc[lowercase+i] = i + 0x61
1339 end
1340end
1341
1342function vfmath.setdigits(font_encoding, name, digits)
1343 local enc = font_encoding[name]
1344 for i = 0,9 do
1345 enc[digits+i] = i + 0x30
1346 end
1347end
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
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456 |