1if not modules then modules = { } end modules ['math-act'] = {
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
17local type, next, tonumber = type, next, tonumber
18local fastcopy, copytable, insert, remove, concat = table.fastcopy, table.copy, table.insert, table.remove, table.concat
19local formatters = string.formatters
20local byte = string.byte
21local max = math.max
22local setmetatableindex, sortedkeys, sortedhash = table.setmetatableindex, table.sortedkeys, table.sortedhash
23local lpegmatch = lpeg.match
24
25local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
26local trace_collecting = false trackers.register("math.collecting", function(v) trace_collecting = v end)
27local trace_tweaking = false trackers.register("math.tweaks", function(v) trace_tweaking = v end)
28
29local report_math = logs.reporter("mathematics","initializing")
30local report_mathtweak = logs.reporter("mathematics","tweak")
31
32local getfontoffamily = tex.getfontoffamily
33local texget = tex.get
34local fontcharacters = fonts.hashes.characters
35local chardata = characters.data
36local extensibles = mathematics.extensibles
37
38local context = context
39local commands = commands
40local mathematics = mathematics
41local texsetdimen = tex.setdimen
42local texisdimen = tex.isdimen
43local abs = math.abs
44
45local blocks = characters.blocks
46local stepper = utilities.parsers.stepper
47
48local helpers = fonts.helpers
49local prependcommands = helpers.prependcommands
50
51local vfcommands = helpers.commands
52local upcommand = vfcommands.up
53local downcommand = vfcommands.down
54local rightcommand = vfcommands.right
55local leftcommand = vfcommands.left
56local slotcommand = vfcommands.slot
57local charcommand = vfcommands.char
58local push = vfcommands.push
59local pop = vfcommands.pop
60
61local sequencers = utilities.sequencers
62local appendgroup = sequencers.appendgroup
63local appendaction = sequencers.appendaction
64
65local fontchars = fonts.hashes.characters
66local fontproperties = fonts.hashes.properties
67
68local mathgaps = mathematics.gaps
69
70local d_scratchleftoffset = texisdimen("scratchleftoffset")
71local d_scratchrightoffset = texisdimen("scratchrightoffset")
72
73local use_math_goodies = true directives.register("math.nogoodies", function(v) use_math_goodies = not v end)
74local checkitalics = false trackers .register("math.checkitalics", function(v) checkitalics = v end)
75
76local function registerdone(done,unicode)
77 if not trace_tweaking then
78 done = true
79 elseif done then
80 done[unicode] = true
81 else
82 done = { [unicode] = true }
83 end
84 return done
85end
86
87local mathfontparameteractions = sequencers.new {
88 name = "mathparameters",
89 arguments = "target,original",
90}
91
92appendgroup("mathparameters","before")
93appendgroup("mathparameters","system")
94appendgroup("mathparameters","after" )
95
96function fonts.constructors.assignmathparameters(original,target)
97 local runner = mathfontparameteractions.runner
98 if runner then
99 runner(original,target)
100 end
101end
102
103
104
105local undefined <const> = 0x3FFFFFFF
106
107function mathematics.initializeparameters(target,original,nodimensions)
108 local mathparameters = original.mathparameters
109 if mathparameters and next(mathparameters) then
110 if nodimensions ~= "noscale" then
111 mathparameters = mathematics.dimensions(mathparameters)
112 end
113
114
115 if not mathparameters.SubscriptShiftDownWithSuperscript then mathparameters.SubscriptShiftDownWithSuperscript = mathparameters.SubscriptShiftDown * 1.5 end
116
117
118
119
120 if not mathparameters.PrimeRaisePercent then mathparameters.PrimeRaisePercent = 0 end
121 if not mathparameters.PrimeRaiseComposedPercent then mathparameters.PrimeRaiseComposedPercent = 0 end
122 if not mathparameters.PrimeShiftUp then mathparameters.PrimeShiftUp = mathparameters.SuperscriptShiftUp end
123 if not mathparameters.PrimeBaselineDropMax then mathparameters.PrimeBaselineDropMax = mathparameters.SuperscriptBaselineDropMax end
124 if not mathparameters.PrimeShiftUpCramped then mathparameters.PrimeShiftUpCramped = mathparameters.SuperscriptShiftUpCramped end
125 if not mathparameters.PrimeSpaceAfter then mathparameters.PrimeSpaceAfter = 0 end
126 if not mathparameters.PrimeWidthPercent then mathparameters.PrimeWidthPercent = 50 end
127 if not mathparameters.SpaceBeforeScript then mathparameters.SpaceBeforeScript = mathparameters.SpaceAfterScript end
128 if not mathparameters.NoLimitSupFactor then mathparameters.NoLimitSupFactor = 0 end
129 if not mathparameters.NoLimitSubFactor then mathparameters.NoLimitSubFactor = 0 end
130 if not mathparameters.AccentTopShiftUp then mathparameters.AccentTopShiftUp = 0 end
131 if not mathparameters.AccentBottomShiftDown then mathparameters.AccentBottomShiftDown = 0 end
132 if not mathparameters.FlattenedAccentTopShiftUp then mathparameters.AccentTopShiftUp = 0 end
133 if not mathparameters.FlattenedAccentBottomShiftDown then mathparameters.AccentBottomShiftDown = 0 end
134 if not mathparameters.AccentBaseDepth then mathparameters.AccentBaseDepth = 0 end
135 if not mathparameters.AccentFlattenedBaseDepth then mathparameters.AccentFlattenedBaseDepth = 0 end
136 if not mathparameters.AccentTopOvershoot then mathparameters.AccentTopOvershoot = 0 end
137 if not mathparameters.AccentBottomOvershoot then mathparameters.AccentBottomOvershoot = 0 end
138 if not mathparameters.AccentSuperscriptDrop then mathparameters.AccentSuperscriptDrop = 0 end
139 if not mathparameters.AccentSuperscriptPercent then mathparameters.AccentSuperscriptPercent = 0 end
140 if not mathparameters.AccentExtendMargin then mathparameters.AccentExtendMargin = 50 end
141 if not mathparameters.DelimiterPercent then mathparameters.DelimiterPercent = 100 end
142 if not mathparameters.DelimiterShortfall then mathparameters.DelimiterShortfall = 0 end
143 if not mathparameters.DelimiterDisplayPercent then mathparameters.DelimiterDisplayPercent = 100 end
144 if not mathparameters.DelimiterDisplayShortfall then mathparameters.DelimiterDisplayShortfall = 0 end
145 if not mathparameters.RadicalKernAfterExtensible then mathparameters.RadicalKernAfterExtensible = 0 end
146 if not mathparameters.RadicalKernBeforeExtensible then mathparameters.RadicalKernBeforeExtensible = 0 end
147 if not mathparameters.SuperscriptSnap then mathparameters.SuperscriptSnap = 750 end
148 if not mathparameters.SubscriptSnap then mathparameters.SubscriptSnap = 250 end
149
150
151
152
153
154
155
156
157 target.mathparameters = mathparameters
158 end
159end
160
161sequencers.appendaction("mathparameters","system","mathematics.initializeparameters")
162
163local how = {
164
165
166 ScriptPercentScaleDown = "unscaled",
167 ScriptScriptPercentScaleDown = "unscaled",
168 RadicalDegreeBottomRaisePercent = "unscaled",
169 NoLimitSupFactor = "unscaled",
170 NoLimitSubFactor = "unscaled",
171 PrimeRaisePercent = "unscaled",
172 PrimeRaiseComposedPercent = "unscaled",
173 PrimeWidthPercent = "unscaled",
174 AccentTopOvershoot = "unscaled",
175 AccentBottomOvershoot = "unscaled",
176 AccentSuperscriptPercent = "unscaled",
177 DelimiterPercent = "unscaled",
178 DelimiterDisplayPercent = "unscaled",
179
180 RadicalRuleThickness = "vertical",
181 OverbarRuleThickness = "vertical",
182 FractionRuleThickness = "vertical",
183 UnderbarRuleThickness = "vertical",
184}
185
186local function scaleparameters(mathparameters,parameters)
187 if mathparameters and next(mathparameters) and parameters then
188 local factor = parameters.factor
189 local hfactor = parameters.hfactor
190 local vfactor = parameters.vfactor
191 for name, value in next, mathparameters do
192 local h = how[name]
193 if h == "unscaled" then
194
195 elseif h == "horizontal" then
196 value = value * hfactor
197 elseif h == "vertical" then
198 value = value * vfactor
199 else
200 value = value * factor
201 end
202 mathparameters[name] = value
203 end
204 end
205end
206
207function mathematics.scaleparameters(target,original)
208 if not target.properties.math_is_scaled then
209 scaleparameters(target.mathparameters,target.parameters)
210 target.properties.math_is_scaled = true
211 end
212end
213
214
215
216
217
218
219
220
221
222
223
224
225
226function mathematics.overloadparameters(target,original)
227 if use_math_goodies then
228 local mathparameters = target.mathparameters
229 if mathparameters and next(mathparameters) then
230 local goodies = target.goodies
231 if goodies then
232 for i=1,#goodies do
233 local goodie = goodies[i]
234 local mathematics = goodie.mathematics
235 if mathematics then
236 local parameters = mathematics.parameters
237 local bigslots = mathematics.bigslots or mathematics.bigs
238 if parameters then
239 if trace_defining then
240 report_math("overloading math parameters in %a @ %p",target.properties.fullname,target.parameters.size)
241 end
242 for name, value in next, parameters do
243 local tvalue = type(value)
244 local oldvalue = mathparameters[name]
245 local newvalue = oldvalue
246 if tvalue == "number" then
247 newvalue = value
248 elseif tvalue == "string" then
249
250 elseif tvalue == "function" then
251 newvalue = value(oldvalue,target,original)
252 elseif not tvalue then
253 newvalue = nil
254 end
255 if trace_defining and oldvalue ~= newvalue then
256 report_math("overloading math parameter %a: %S => %S",name,oldvalue or 0,newvalue)
257 end
258 mathparameters[name] = newvalue
259 end
260 for name, value in next, parameters do
261 local tvalue = type(value)
262 if tvalue == "string" then
263 local newvalue = mathparameters[value]
264 if not newvalue then
265 local code = loadstring("return " .. value,"","t",mathparameters)
266 if type(code) == "function" then
267 local okay, v = pcall(code)
268 if okay then
269 newvalue = v
270 end
271 end
272 end
273 if newvalue then
274
275 mathparameters[name] = newvalue
276 elseif trace_defining then
277 report_math("ignoring math parameter %a: %S",name,value)
278 end
279 end
280 end
281 end
282 if bigslots then
283 target.bigslots = bigslots
284 end
285 end
286 end
287 end
288 end
289 end
290end
291
292
293
294local datasets = { }
295local mathtweaks = { datasets = datasets }
296mathematics.tweaks = mathtweaks
297
298
299
300local f_u = formatters["%U"]
301
302local function unicodecharlist(t)
303 local r = { }
304 local n = 0
305 for u in sortedhash(t) do
306 n = n + 1 ; r[n] = f_u(u)
307 end
308 return concat(r," ")
309end
310
311local function report_tweak(fmt,target,original,...)
312 if fmt then
313 local metadata = (original and original.shared.rawdata.metadata) or
314 (target and target .shared.rawdata.metadata)
315 local parameters = target.parameters
316 if parameters then
317 report_mathtweak(
318 "%a, size %P, math size %i, %s",
319 metadata and metadata.fontname or "unknown",
320 parameters.size or 655360,
321 parameters.mathsize or 1,
322 formatters[fmt](...)
323 )
324 else
325 print("something is wrong")
326 end
327 else
328 report_mathtweak("")
329 end
330end
331
332local function feedback_tweak(tweak,target,original,done)
333 if not done or (type(done) == "table" and not next(done)) then
334if trace_tweaking then
335 report_tweak("no need for %a",target,original,tweak)
336end
337 elseif trace_tweaking then
338 report_tweak("tweak %a applied to: %s",target,original,tweak,unicodecharlist(done))
339 end
340end
341
342mathtweaks.subsets = {
343 acenorsuvxz = { 0x1D44E, 0x1D450, 0x1D452, 0x1D45B, 0x1D45C, 0x1D45F, 0x1D460, 0x1D462, 0x1D463, 0x1D465, 0x1D467 },
344 bhklt = { 0x1D44F, 0x1D455, 0x1D458, 0x1D459, 0x1D461 },
345 d = { 0x1D451 },
346 f = { 0x1D453 },
347 gjqy = { 0x1D454, 0x1D457, 0x1D45E, 0x1D466 },
348 i = { 0x1D456 },
349 mw = { 0x1D45A, 0x1D464 },
350 p = { 0x1D45D },
351 letterlike = { 0x02202 },
352 dotless = { 0x00049, 0x0004A, 0x00131, 0x00237, 0x1D6A4, 0x1D6A5 },
353 integrals = { 0x0222B, 0x0222C, 0x0222D, 0x0222E, 0x0222F, 0x02230, 0x02231, 0x02232, 0x02233, 0x02A0B, 0x02A0C, 0x02A0D, 0x02A0E, 0x02A0F, 0x02A10, 0x02A11, 0x02A12, 0x02A13, 0x02A14, 0x02A15, 0x02A16, 0x02A17, 0x02A18, 0x02A19, 0x02A1A, 0x02A1B, 0x02A1C, 0x02320, 0x02321 },
354 horizontalfences = { 0x0203E, 0x023B4, 0x023B5, 0x023DC, 0x023DD, 0x023DE, 0x023DF, 0x023E0, 0x023E1 },
355}
356
357local function getalso(target,original)
358 local also = target.tweakalso
359 if not also then
360 also = { }
361
362 for k, v in next, target.characters do
363 local u = v.unicode
364 if u and k ~= u then
365 local a = also[u]
366 if a then
367 a[#a+1] = k
368 else
369 also[u] = { k }
370 end
371 end
372 end
373 target.tweakalso = also
374 end
375 return also
376end
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396local detail do
397
398 local splitter = lpeg.tsplitat(".")
399 local private = fonts.helpers.privateslot
400
401 detail = function(characters,k)
402 if type(k) == "string" then
403 local t = lpegmatch(splitter,k)
404 local n = #t
405 if n > 0 then
406 local slot = t[1]
407 local base = tonumber(slot) or tonumber(slot,16) or private(slot)
408 if base then
409 local c = characters[base]
410 if c and n > 1 then
411 local list = t[2]
412 if list == "parts" then
413 local nxt = c.next
414 while nxt do
415 c = characters[nxt]
416 nxt = c.next
417 end
418 c = c.parts
419 if c then
420 local index = t[3]
421 if index == "*" then
422 return t
423 else
424 if index == "top" then
425 index = #c
426 elseif index == "bottom" then
427 index = 1
428 else
429 index = tonumber(index)
430 end
431 if index then
432 c = c[index]
433 if c then
434 return c.glyph
435 end
436 end
437 end
438 end
439 elseif list == "variants" then
440 local index = t[3]
441 if index == "*" then
442 local t = { }
443 local nxt = c.next
444 while nxt do
445 t[#t+1] = nxt
446 c = characters[nxt]
447 nxt = c.next
448 end
449 return t
450 else
451 index = tonumber(index)
452 if index then
453 local nxt = c.next
454 while nxt and index > 1 do
455 c = characters[nxt]
456 nxt = c.next
457 index = index - 1
458 end
459 return nxt
460 end
461 end
462 elseif list == "flataccent" then
463 return c.flataccent
464 end
465 end
466 end
467 end
468 else
469 return k
470 end
471 end
472
473end
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553do
554
555 local stepper = utilities.parsers.stepper
556 local count = 0
557 local toeffect = fonts.toeffect
558 local privateslot = fonts.helpers.privateslot
559
560 local function adapt(list,target,original,targetcharacters,originalcharacters,k,v,compact,n)
561 k = mathgaps[k] or k
562 local character = targetcharacters[k]
563 if character then
564
565 local t = type(v)
566 if t == "number" then
567 v = list[v]
568 t = type(v)
569 end
570 if t == "table" and next(v) then
571
572 local axis = tonumber(v.axis)
573 if axis then
574 axis = target.mathparameters.AxisHeight * axis
575 end
576
577 local factor = v.factor
578 if factor then
579 local m = v
580 v = setmetatableindex({
581 width = factor,
582 height = factor,
583 depth = factor,
584 squeeze = factor,
585 extend = factor,
586 }, v)
587 end
588 local originalslot = v.original
589 if not originalslot then
590 local delta = v.delta
591 if delta then
592 originalslot = k + delta
593 end
594 end
595 if originalslot then
596 originalslot = mathgaps[originalslot] or originalslot
597 local data = targetcharacters[originalslot]
598 if data then
599 data = copytable(data)
600 data.unicode = originalslot
601 targetcharacters[k] = data
602 character = data
603 else
604 report_mathtweak("no slot %U",originalslot)
605 return
606 end
607 end
608
609 local width = character.width
610 local height = character.height
611 local depth = character.depth
612 local italic = character.italic
613 local topanchor = character.topanchor
614 local bottomanchor = character.bottomanchor
615
616 local widthfactor = v.width
617 local heightfactor = v.height
618 local depthfactor = v.depth
619 local italicfactor = v.italic
620 local anchorfactor = v.anchor
621 local advancefactor = v.advance
622 local xoffsetfactor = v.xoffset
623 local yoffsetfactor = v.yoffset
624 local scalefactor = v.scale
625 local total = (height or 0) + (depth or 0)
626 if scalefactor ~= 1 then
627 character.scale = scalefactor
628 end
629 if width and width ~= 0 then
630 if advancefactor then
631 character.advance = advancefactor * width
632 else
633 character.advance = character.advance or width
634 end
635 if widthfactor then
636 character.width = widthfactor * width
637 end
638 if xoffsetfactor then
639 character.xoffset = xoffsetfactor * width
640 end
641 end
642 if height and height ~= 0 then
643 if heightfactor then
644 character.height = heightfactor * height
645 end
646 end
647 if depth and depthfactor then
648 character.depth = depthfactor * depth
649 end
650 if yoffsetfactor then
651 character.yoffset = yoffsetfactor * total
652 end
653
654 if axis then
655 character.height = (character.height or 0) - axis
656 character.depth = (character.depth or 0) + axis
657 character.yoffset = (character.yoffset or 0) + axis
658 end
659
660 if italicfactor then
661 if italic then
662 character.italic = italicfactor * italic
663 elseif width and italicfactor ~= 1 then
664 character.italic = italicfactor * width
665 end
666 end
667 if anchorfactor then
668 character.topanchor = anchorfactor * (topanchor or width)
669 end
670
671
672
673
674 local line = v.wline
675 if line then
676 local parameters = target.parameters
677 v.line = parameters.hfactor * line / parameters.units
678 end
679
680 character.effect = toeffect(v)
681
682 v.line = line
683
684 if trace_tweaking then
685 report_tweak("adapting dimensions of %U ",target,original,k)
686 end
687
688 local originaldata = originalcharacters[k]
689 local smaller = originaldata and originaldata.smaller
690 if compact and smaller and smaller ~= k then
691 adapt(list,target,original,targetcharacters,originalcharacters,smaller,v,compact,n+1)
692 end
693 count = count + 1
694 else
695 report_mathtweak("invalid dimension entry %U",k)
696 end
697
698 if v.all then
699 local nxt = character.next
700 if nxt then
701 adapt(list,target,original,targetcharacters,originalcharacters,nxt,v,compact,n)
702 else
703 local parts = character.parts
704 if parts then
705 for i=1,#parts do
706 adapt(list,target,original,targetcharacters,originalcharacters,parts[i],v,compact,n)
707 end
708 end
709 end
710 end
711
712 else
713 report_tweak("no character %U",target,original,k)
714 end
715 end
716
717 function mathtweaks.dimensions(target,original,parameters)
718 local list = parameters.list
719 if list then
720 local targetcharacters = target.characters
721 local originalcharacters = original.characters
722 local compact = target.parameters.textscale and true or false
723 count = 0
724 for k, v in sortedhash(list) do
725 local t = type(k)
726 if t == "number" then
727 adapt(list,target,original,targetcharacters,originalcharacters,k,v,compact,1)
728 elseif t == "string" then
729 local d = privateslot(k) or detail(targetcharacters,k)
730 local t = type(d)
731 if t == "table" then
732 for i=1,#d do
733 adapt(list,target,original,targetcharacters,originalcharacters,d[i],v,compact,1)
734 end
735 elseif t == "number" then
736 adapt(list,target,original,targetcharacters,originalcharacters,d,v,compact,1)
737 elseif d then
738
739 else
740 local r = blocks[k]
741 if r then
742 local done = false
743 for i=r.first,r.last do
744 adapt(list,target,original,targetcharacters,originalcharacters,i,v,compact,1)
745 end
746 else
747 stepper(k,function(n)
748 adapt(list,target,original,targetcharacters,originalcharacters,n,v,compact,1)
749 end)
750 end
751 end
752
753
754
755
756 end
757 end
758 if trace_tweaking and count > 0 then
759 report_mathtweak("%i dimensions adapted",count)
760 end
761 end
762 end
763
764end
765
766do
767
768 function mathtweaks.message(target,original,parameters)
769 report_mathtweak(parameters.text or "no message")
770 end
771
772 function mathtweaks.showinfo(target,original,parameters)
773 local mathparameters = target.mathparameters
774 for k, v in sortedhash(mathparameters) do
775 report_mathtweak("%s : %s",k,v)
776 end
777 end
778
779end
780
781do
782
783 function mathtweaks.wipevariants(target,original,parameters)
784 local list = parameters.list
785 if list then
786 local targetcharacters = target.characters
787
788 local count = 0
789
790 local done = false
791 for k, v in sortedhash(list) do
792 local ori = targetcharacters[k]
793 local nxt = ori.next
794 local cnt = v
795 if nxt then
796 local prt = nil
797 local pro = nil
798 local lst = { }
799 while nxt do
800 local chr = targetcharacters[nxt]
801 lst[#lst+1] = chr
802 nxt = chr.next
803 if not nxt then
804 prt = chr.parts
805 pro = chr.partsorientation
806 break
807 end
808 end
809 if prt then
810 count = count + 1
811 if cnt ~= "*" then
812 if #lst < cnt then
813 cnt = #lst
814 end
815 ori = lst[cnt]
816 end
817 ori.parts = prt
818 ori.partsorientation = pro
819 end
820 done = registerdone(done,k)
821 end
822 end
823 feedback_tweak("wipevariants",target,original,done)
824 end
825 end
826
827end
828
829do
830
831
832
833 local function find(targetcharacters,slot,n)
834 local chr = targetcharacters[slot]
835 while chr do
836 slot = chr.next
837 if not slot then
838 break
839 elseif n == 1 then
840 return slot
841 end
842 n = n - 1
843 chr = targetcharacters[slot]
844 end
845 return nil
846 end
847
848 function mathtweaks.fixvariants(target,original,parameters)
849 local list = parameters.list
850 if list then
851 local targetcharacters = target.characters
852 local done = false
853 for k, v in sortedhash(list) do
854 local tmp = v.template
855 local idx = v.index
856 local dy = v.yoffset or 0
857 if tmp and idx then
858 local olduni = find(targetcharacters,k,idx)
859 local tmpuni = find(targetcharacters,tmp,idx)
860 local axis = target.mathparameters.AxisHeight
861 if axis then
862 while olduni and tmpuni do
863 local oldchr = targetcharacters[olduni]
864 local tmpchr = targetcharacters[tmpuni]
865 local oldht = oldchr.height or 0
866 local olddp = oldchr.depth or 0
867 local tmpht = tmpchr.height or 0
868 local tmpdp = tmpchr.depth or 0
869 local scale = (tmpht + tmpdp) / (oldht + olddp)
870 local total = (oldht - olddp) * scale
871 local yoffset = axis - total/2
872 oldchr.effect = { squeeze = scale }
873 oldchr.height = scale * oldht + yoffset
874 oldchr.depth = scale * olddp - yoffset
875 oldchr.yoffset = yoffset
876 olduni = oldchr.next
877 tmpuni = tmpchr.next
878 end
879 done = registerdone(done,k)
880 end
881 end
882 end
883 feedback_tweak("addvariants",target,original,done)
884 end
885 end
886
887end
888
889do
890
891 function mathtweaks.replace(target,original,parameters)
892 local list = parameters.list
893 if list then
894 local targetcharacters = target.characters
895 local originalcharacters = original.characters
896 local unicodes = original.resources.unicodes
897 if unicodes then
898 local count = 0
899 for k, v in sortedhash(list) do
900 if type(v) == "string" then
901 v = unicodes[v]
902 end
903 if type(v) == "number" then
904 targetcharacters[mathgaps[k] or k] = targetcharacters[mathgaps[v] or v]
905 count = count + 1
906 end
907 end
908 if trace_tweaking and count > 0 then
909 report_tweak("%i permanent replacements",target,original,count)
910 end
911 end
912 end
913 end
914
915 function mathtweaks.substitute(target,original,parameters)
916 local list = parameters.list
917 if list then
918 local targetcharacters = target.characters
919 local originalcharacters = original.characters
920 local getsubstitution = fonts.handlers.otf.getsubstitution
921 local count = 0
922 for k, v in next, list do
923 local sub = getsubstitution(original,k,v,true)
924 if sub then
925 targetcharacters[mathgaps[k] or k] = targetcharacters[mathgaps[sub] or sub]
926 count = count + 1
927 end
928 end
929 if trace_tweaking and count > 0 then
930 report_tweak("%i permanent substitutions",target,original,count)
931 end
932 end
933 end
934
935end
936
937do
938
939
940
941 function mathtweaks.kernpairs(target,original,parameters)
942 local list = parameters.list
943 if list then
944 local targetcharacters = target.characters
945 local originalcharacters = original.characters
946 local done = false
947
948 local function add(v,n)
949 local chardata = targetcharacters[mathgaps[n] or n]
950 if chardata then
951 local width = chardata.width
952 if width then
953 local kerns = chardata.kerns or { }
954 for kk, vv in next, v do
955
956 stepper(kk,function(nn)
957 local t = mathgaps[nn] or nn
958 if t then
959 kerns[t] = vv * width
960 done = registerdone(done,t)
961 end
962 end)
963 end
964 chardata.kerns = kerns
965 end
966 end
967 end
968
969 for k, v in next, list do
970 stepper(k,function(n)
971 add(v,n)
972 end)
973 end
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993 feedback_tweak("kernpairs",target,original,done)
994 end
995 end
996
997end
998
999do
1000
1001 local list = {
1002 { 0x2032, 1 },
1003 { 0x2033, 2, 0x2032 },
1004 { 0x2034, 3, 0x2032 },
1005 { 0x2057, 4, 0x2032 },
1006 { 0x2035, 1 },
1007 { 0x2036, 2, 0x2035 },
1008 { 0x2037, 3, 0x2035 },
1009 }
1010
1011 datasets.fixprimes = list
1012
1013 function mathtweaks.fixprimes(target,original,parameters)
1014 local targetcharacters = target.characters
1015 local factor = parameters.factor or 1
1016 local fake = tonumber(parameters.fake)
1017 for i=1,#list do
1018 local entry = list[i]
1019 local unicode = entry[1]
1020 local count = entry[2]
1021 local used = fonts.handlers.otf.getsubstitution(target,unicode,"ssty",true,"math","dflt") or unicode
1022 local data = targetcharacters[used]
1023 if data then
1024 targetcharacters[unicode] = data
1025 local oldheight = data.height or 0
1026 local newheight = factor * oldheight
1027 data.yoffset = newheight - (oldheight or 0)
1028 data.height = newheight
1029 data.smaller = nil
1030 elseif not fake then
1031 report_tweak("missing %i prime %U",target,original,count,unicode)
1032 end
1033 end
1034 if fake then
1035 for i=1,#list do
1036 local entry = list[i]
1037 local count = entry[2]
1038 if count > 1 then
1039 local unicode = entry[1]
1040 local original = entry[3]
1041 local data = targetcharacters[original]
1042 if data then
1043 local oldwidth = data.width
1044 local xoffset = fake * oldwidth
1045 local newwidth = oldwidth + (count - 1) * xoffset
1046 targetcharacters[unicode] = {
1047 width = newwidth,
1048 height = data.height,
1049 unicode = unicode,
1050 commands = {
1051 { "offset", 0, 0, original },
1052 { "offset", xoffset, 0, original },
1053 count > 2 and { "offset", 2 * xoffset, 0, original } or nil,
1054 count > 3 and { "offset", 3 * xoffset, 0, original } or nil,
1055 },
1056 }
1057 end
1058 end
1059 end
1060 end
1061 end
1062
1063end
1064
1065do
1066
1067 local nps = fonts.helpers.newprivateslot
1068
1069 local privates = {
1070 [0x2212] = nps("unary minus"),
1071 [0x002B] = nps("unary plus"),
1072 [0x00B1] = nps("unary plus minus"),
1073 [0x2213] = nps("unary minus plus"),
1074 }
1075
1076
1077
1078 local predefined = {
1079 ["unary minus"] = {
1080 original = 0x2212,
1081 extend = .5,
1082 width = .5,
1083 unicode = 0x002D,
1084 },
1085 ["unary plus"] = {
1086 original = 0x002B,
1087 extend = .5,
1088 squeeze = .5,
1089 width = .5,
1090 height = .5,
1091 yoffset = .2,
1092 mode = 2,
1093 wline = .5,
1094 unicode = 0x002B,
1095 },
1096 ["unary plus minus"] = {
1097 original = 0x00B1,
1098 extend = .5,
1099 squeeze = .5,
1100 width = .5,
1101 height = .5,
1102 yoffset = .2,
1103 mode = 2,
1104 wline = .5,
1105 },
1106 ["unary minus plus"] = {
1107 original = 0x2213,
1108 extend = .5,
1109 squeeze = .5,
1110 width = .5,
1111 height = .5,
1112 yoffset = .2,
1113 mode = 2,
1114 wline = .5,
1115 },
1116 }
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129 function mathtweaks.addprivates(target,original,parameters)
1130 local list = parameters.list or predefined
1131 if list then
1132 local targetcharacters = target.characters
1133 local targetparameters = target.parameters
1134 local originalcharacters = original.characters
1135 local processedprivates = { }
1136 for name, v in sortedhash(list) do
1137 if type(v) == "table" then
1138 local preset = v.preset
1139 if preset then
1140 local p = predefined[preset]
1141 if p then
1142 v = table.combine(p,v)
1143 p.preset = nil
1144 else
1145 goto next
1146 end
1147 end
1148 local charslot = v.original
1149 if charslot then
1150 local chardata = targetcharacters[charslot]
1151 if chardata then
1152 local clonedata = copytable(chardata)
1153 local cloneslot = nps(name)
1154 local unicode = v.unicode or clonedata.unicode
1155 clonedata.uncode = unicode
1156 targetcharacters[cloneslot] = clonedata
1157 if trace_tweaking then
1158 report_tweak("cloning %a from %C into %U with tounicode %U",target,original,name,charslot,cloneslot,unicode)
1159 end
1160 end
1161 processedprivates[name] = v
1162 end
1163 ::next::
1164 end
1165 end
1166 mathtweaks.dimensions(target,original,{
1167 tweak = parameters.tweak,
1168 list = processedprivates,
1169 })
1170 end
1171 end
1172
1173end
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224do
1225
1226 function mathtweaks.simplifykerns(target,original,parameters)
1227 local characters = target.characters
1228 local done = false
1229
1230 for u, v in next, characters do
1231 local mathkerns = v.mathkerns
1232 if mathkerns then
1233 local k = mathkerns.topleft
1234 if k then
1235 k = k[#k].kern
1236 if k then
1237 v.topleft = k
1238 end
1239 end
1240 local k = mathkerns.topright
1241 if k then
1242 k = k[#k].kern
1243 if k then
1244 v.topright = k
1245 end
1246 end
1247 local k = mathkerns.bottomleft
1248 if k then
1249 k = k[1].kern
1250 if k then
1251 v.bottomleft = k
1252 end
1253 end
1254 local k = mathkerns.bottomright
1255 if k then
1256 k = k[1].kern
1257 if k then
1258 v.bottomright = k
1259 end
1260 end
1261 v.mathkerns = nil
1262 done = registerdone(done,u)
1263 end
1264 end
1265 feedback_tweak("simplifykerns",target,original,done)
1266 end
1267
1268end
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285do
1286
1287 local function wipe(whatever,target,original,parameters,field,move,integrals)
1288 local targetcharacters = target.characters
1289 local targetdescriptions = target.descriptions
1290 local factor = target.parameters.factor
1291 local correct = parameters.correct
1292 local done = false
1293 local function getllx(u)
1294 local d = targetdescriptions[u]
1295 if d then
1296 local b = d.boundingbox
1297 if b then
1298 local llx = b[1]
1299 if llx < 0 then
1300 return - llx
1301 end
1302 end
1303 end
1304 return false
1305 end
1306 local function step(s)
1307 while s do
1308 local u = mathgaps[s] or s
1309 local c = targetcharacters[u]
1310 if c then
1311 if field == "topanchor" then
1312 if c.topanchor then
1313 c.topanchor = nil
1314 else
1315 goto smaller
1316 end
1317 else
1318 local okay = false
1319 local italic = c.italic
1320 if move and not c.advance then
1321 local width = c.width or 0
1322 c.advance = width
1323 if correct then
1324 local llx = getllx(u)
1325 if llx then
1326 local topanchor = c.topanchor
1327 llx = llx * factor
1328 width = width + llx
1329 c.xoffset = llx
1330 if topanchor then
1331 c.topanchor = topanchor + llx
1332 end
1333
1334
1335
1336 okay = true
1337 end
1338 end
1339 if italic and italic ~= 0 then
1340 c.width = width + italic
1341 c.bottomright = - italic
1342 okay = true
1343 else
1344 c.width = width
1345 end
1346c.bottomanchor = width/2
1347 end
1348 if italic then
1349 c.italic = nil
1350 okay = true
1351 end
1352 if okay then
1353 done = registerdone(done,u)
1354 else
1355 goto smaller
1356 end
1357 end
1358 goto smaller
1359 ::smaller::
1360 s = c.smaller
1361 ::variants::
1362
1363 else
1364 break
1365 end
1366 end
1367 end
1368 local list = parameters.list
1369 if list == "letters" or parameters.letters then
1370 local chardata = characters.data
1371
1372 for k, v in next, targetcharacters do
1373 if v.italic then
1374 local d = chardata[v.unicode]
1375 local c = d and d.category
1376 if c == "ll" or c == "lu" then
1377 step(k)
1378 end
1379 end
1380 end
1381 goto done
1382 end
1383 if not list or list == "all" or list == true or parameters.all then
1384 list = sortedkeys(targetcharacters)
1385 elseif type(list) == "string" then
1386 list = { list }
1387 end
1388 for i=1,#list do
1389 local l = list[i]
1390 local t = type(l)
1391 if not l then
1392
1393 elseif t == "table" then
1394 for i=1,#l do
1395 step(l[i])
1396 end
1397 elseif t == "number" then
1398 step(l)
1399 else
1400 local r = blocks[l]
1401 if r then
1402 for i=r.first,r.last do
1403 step(i)
1404 end
1405 else
1406 stepper(l,step)
1407 end
1408 end
1409 end
1410 ::done::
1411 feedback_tweak(whatever,target,original,done)
1412 end
1413
1414 function mathtweaks.wipeanchors(target,original,parameters)
1415 wipe("wipeanchors",target,original,parameters,"topanchor")
1416 end
1417
1418 function mathtweaks.wipeitalics(target,original,parameters)
1419 if not checkitalics then
1420 wipe("wipeitalics",target,original,parameters,"italic")
1421 end
1422 end
1423
1424 function mathtweaks.moveitalics(target,original,parameters)
1425 wipe("moveitalics",target,original,parameters,"italic",true)
1426 end
1427
1428
1429
1430
1431
1432end
1433
1434do
1435
1436 function mathtweaks.topanchors(target,original,parameters)
1437 local characters = target.characters
1438 local list = parameters.list
1439 if list then
1440 local done = false
1441 for u, v in sortedhash(list) do
1442 local c = characters[k]
1443 if c then
1444 local w = c.width
1445 if w and w ~= 0 then
1446 c.topanchor = v * w
1447 done = registerdone(done,u)
1448 end
1449 end
1450 end
1451 feedback_tweak("topanchors",target,original,done)
1452 end
1453 end
1454
1455 function mathtweaks.movelimits(target,original,parameters)
1456 local characters = target.characters
1457 local list = parameters.list
1458 if list then
1459 local factor = parameters.factor or 1
1460 local also = getalso(target,original)
1461 local done = { }
1462 local function relocate(u,factor)
1463 if done[u] then
1464 return
1465 end
1466 done[u] = true
1467 local c = characters[u]
1468 if c then
1469 local italic = c.italic
1470 if italic then
1471 if italic ~= 0 then
1472 local width = c.width or 0
1473 local half = (italic/2) * factor
1474
1475 c.topanchor = width + half
1476 c.bottomanchor = width - half
1477
1478 c.bottomright = - italic * (parameters.icfactor or 1)
1479 if trace_tweaking then
1480
1481 end
1482 end
1483 c.italic = nil
1484 end
1485 local s = c.smaller
1486 if s then
1487 relocate(s,factor)
1488 end
1489 local n = c.next
1490 if n then
1491 relocate(n,factor)
1492 end
1493
1494
1495 local parts = c.parts
1496 local italic = c.partsitalic
1497 if parts and italic then
1498 if italic ~= 0 then
1499 local tchar = characters[parts[#parts].glyph]
1500 local bchar = characters[parts[1].glyph]
1501 local width = tchar.width or 0
1502 local half = (italic/2) * factor
1503 tchar.topanchor = width + half
1504 bchar.bottomanchor = width - half
1505 bchar.bottomright = - italic
1506 if trace_tweaking then
1507
1508 end
1509 tchar.italic = nil
1510 bchar.italic = nil
1511 end
1512 c.vitalic = nil
1513 end
1514 if also then
1515 local a = also[u]
1516 if a then
1517 for i=1,#a do
1518 relocate(a[i],factor)
1519 end
1520 end
1521 end
1522 end
1523 end
1524 if #list > 0 then
1525 for i=1,#list do
1526 relocate(list[i],factor)
1527 end
1528 else
1529 for k, v in sortedhash(list) do
1530 relocate(k,tonumber(v) or factor)
1531 end
1532 end
1533 feedback_tweak("movelimits",target,original,done)
1534 end
1535 end
1536
1537end
1538
1539do
1540
1541
1542
1543 function mathtweaks.kerns(target,original,parameters)
1544 local kerns = parameters.list
1545 if kerns then
1546 local characters = target.characters
1547 local done = false
1548 local function setone(uc,data)
1549 local function set(unicode)
1550 unicode = mathgaps[unicode] or unicode
1551 local chardata = characters[unicode]
1552 if chardata then
1553 local width = chardata.width or 0
1554 local k = data.topleft ; if k and k ~= 0 then chardata.topleft = k * width end
1555 local k = data.topright ; if k and k ~= 0 then chardata.topright = k * width end
1556 local k = data.bottomleft ; if k and k ~= 0 then chardata.bottomleft = k * width end
1557 local k = data.bottomright ; if k and k ~= 0 then chardata.bottomright = k * width end
1558 done = registerdone(done,unicode)
1559 end
1560 end
1561 local unicode = detail(characters,uc)
1562 if type(unicode) == "table" then
1563 for i=1,#unicode do
1564 set(unicode[i])
1565 end
1566 elseif unicode then
1567 set(unicode)
1568 end
1569 end
1570 for unicode, data in next, kerns do
1571 setone(unicode,data)
1572
1573 end
1574 feedback_tweak("kerns",target,original,done)
1575 end
1576 end
1577
1578end
1579
1580do
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608 function mathtweaks.margins(target,original,parameters)
1609 local kerns = parameters.list
1610 if kerns then
1611 local characters = target.characters
1612 local done = false
1613 local function setone(uc,data)
1614 local function set(unicode)
1615 unicode = mathgaps[unicode] or unicode
1616 local chardata = characters[unicode]
1617 if chardata then
1618 local width = chardata.width or 0
1619 local k = data.left ; if k and k ~= 0 then chardata.leftmargin = k * width end
1620 local k = data.right ; if k and k ~= 0 then chardata.rightmargin = k * width end
1621 local k = data.top ; if k and k ~= 0 then chardata.topmargin = k * width end
1622 local k = data.bottom ; if k and k ~= 0 then chardata.bottommargin = k * width end
1623 done = registerdone(done,unicode)
1624 end
1625 end
1626 local unicode = detail(characters,uc)
1627 if type(unicode) == "table" then
1628 for i=1,#unicode do
1629 set(unicode[i])
1630 end
1631 elseif unicode then
1632 set(unicode)
1633 end
1634 end
1635 for unicode, data in next, kerns do
1636 setone(unicode,data)
1637
1638 end
1639 feedback_tweak("margins",target,original,done)
1640 end
1641 end
1642
1643
1644end
1645
1646do
1647
1648
1649
1650
1651
1652 local function scale(t,width,total)
1653 local r = { }
1654 for i=1,#t do
1655 local ti = t[i]
1656 local kern = ti.kern
1657 local height = ti.height
1658 if kern then
1659 kern = width * kern
1660 end
1661 if height then
1662 height = total * height
1663 end
1664 r[i] = {
1665 kern = kern or 0,
1666 height = height or 0,
1667 }
1668 end
1669 return r
1670 end
1671
1672 function mathtweaks.staircase(target,original,parameters)
1673 local kerns = parameters.list
1674 if kerns then
1675 local characters = target.characters
1676 local function kernone(unicode,data)
1677 local chardata = characters[mathgaps[unicode] or unicode]
1678 local total = (chardata.height or 0) + (chardata.depth or 0)
1679 local width = chardata.width or 0
1680 if data then
1681 local tl = data.topleft ; if tl then tl = scale(tl,width,total) end
1682 local tr = data.topright ; if tr then tr = scale(tr,width,total) end
1683 local bl = data.bottomleft ; if bl then bl = scale(bl,width,total) end
1684 local br = data.bottomright ; if br then br = scale(br,width,total) end
1685 chardata.mathkerns = {
1686 topleft = tl,
1687 ropright = tr,
1688 bottomleft = bl,
1689 bottomright = br,
1690 }
1691 else
1692 chardata.mathkerns = nil
1693 end
1694 end
1695 for unicode, data in next, kerns do
1696 kernone(unicode,data)
1697
1698 end
1699 end
1700 end
1701
1702end
1703
1704do
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723 local nps = fonts.helpers.newprivateslot
1724
1725 local umbracepiece = nps("um brace piece")
1726 local lmbracepiece = nps("lm brace piece")
1727 local cmbracepiece = nps("cm brace piece")
1728
1729 local ulbracepiece = nps("ul brace piece")
1730 local urbracepiece = nps("ur brace piece")
1731 local llbracepiece = nps("ll brace piece")
1732 local lrbracepiece = nps("lr brace piece")
1733
1734 local over = { factor = "over" }
1735 local under = { factor = "under" }
1736
1737 local candidates = {
1738 over = {
1739 [0x203E] = over,
1740 [0x23DE] = over,
1741 [0x23DC] = over,
1742 [0x23B4] = over,
1743 },
1744 under = {
1745 [0x23DF] = under,
1746 [0x23DD] = under,
1747 [0x23B5] = under,
1748 },
1749 accent = {
1750 [0x0300] = over,
1751 [0x0308] = over,
1752 [0x0304] = over,
1753 [0x0305] = over,
1754 [0x0301] = over,
1755 [0x0302] = over,
1756 [0x030C] = over,
1757 [0x0306] = over,
1758 [0x0307] = over,
1759 [0x030A] = over,
1760 [0x0303] = over,
1761 [0x20DB] = over,
1762 },
1763 }
1764
1765 datasets.accentdimensions = candidates
1766
1767 local function adapt(c,factor,baseheight,basedepth)
1768 if not c.tweaked then
1769 local height = c.height or 0
1770 local depth = c.depth or 0
1771 local yoffset = 0
1772 if factor == "over" then
1773 local h = height - baseheight
1774 yoffset = h - height
1775 height = h
1776 depth = depth - baseheight
1777 elseif factor == "under" then
1778 local d = depth - basedepth
1779 yoffset = depth - d
1780 depth = d
1781 height = height - baseheight
1782 elseif height > 0 then
1783 local h = tonumber(factor) * height
1784 yoffset = h - height
1785 height = h
1786 elseif depth > 0 then
1787 local d = tonumber(factor) * depth
1788 yoffset = depth - d
1789 depth = d
1790 end
1791 c.yoffset = yoffset ~= 0 and yoffset or nil
1792 c.height = height > 0 and height or nil
1793 c.depth = depth > 0 and depth or nil
1794 c.tweaked = true
1795 end
1796 end
1797
1798 local function process(target,original,characters,list,baseheight,basedepth)
1799 if list then
1800 for k, v in sortedhash(list) do
1801 local c = characters[k]
1802 if c and not c.yoffset then
1803 local factor = v.factor
1804 if factor then
1805 adapt(c,factor,baseheight,basedepth)
1806 local nc = c.next
1807 local nv = 0
1808 local ns = 0
1809 while nc do
1810 local c = characters[nc]
1811 if c then
1812 adapt(c,factor,baseheight,basedepth)
1813 nv = nv + 1
1814 nc = c.next
1815 if not nc then
1816 local hv = c.parts
1817 if hv then
1818 for i=1,#hv do
1819 local c = characters[hv[i].glyph]
1820 if c then
1821 adapt(c,factor,baseheight,basedepth)
1822 ns = ns + 1
1823 end
1824 end
1825 end
1826 break
1827 end
1828 else
1829 break
1830 end
1831 end
1832 if trace_tweaking then
1833 report_tweak("adapting extensible (%i sizes, %i parts) %U",target,original,k,nv,ns)
1834 end
1835 end
1836 end
1837 end
1838 end
1839 end
1840
1841 function mathtweaks.accentdimensions(target,original,parameters)
1842 local list = parameters.list or { "over", "under" }
1843 if list then
1844 local characters = target.characters
1845 local baseheight = target.mathparameters.AccentBaseHeight or 0
1846 local basedepth = target.mathparameters.AccentBaseDepth or 0
1847 for k, v in sortedhash(list) do
1848 local t = type(v)
1849 if t == "string" then
1850 v = candidates[v]
1851 t = type(v)
1852 end
1853 if t == "table" then
1854 process(target,original,characters,v,baseheight,basedepth)
1855 end
1856 end
1857 end
1858 end
1859
1860end
1861
1862do
1863
1864 local addprivate = fonts.helpers.addprivate
1865 local privateslot = fonts.helpers.privateslot
1866 local newprivateslot = fonts.helpers.newprivateslot
1867
1868
1869
1870 function mathtweaks.addrules(target,original,parameters)
1871 local characters = target.characters
1872 local thickness = target.mathparameters.OverbarRuleThickness
1873 local width = target.parameters.emwidth / 3
1874 local step = width / 2
1875 local quarter = thickness / 4
1876 local half = thickness / 2
1877 local double = thickness * 2
1878 local done = false
1879 characters[0x203E] = {
1880 width = width,
1881 height = half,
1882 depth = half,
1883 yoffset = - half,
1884 unicode = 0x203E,
1885 commands = { { "rule", thickness, width } },
1886 parts = {
1887 { advance = width, ["end"] = step, glyph = 0x203E, start = 0 },
1888 { advance = width, ["end"] = 0, glyph = 0x203E, start = step, extender = 1 },
1889 },
1890 partsorientation = "horizontal",
1891 }
1892 local function build(target,leftarrow,rightarrow)
1893 if leftarrow and rightarrow then
1894
1895 local left = leftarrow.parts
1896 local right = rightarrow.parts
1897 if left and right then
1898 local leftline = right[1].glyph
1899 local rightline = left[#left].glyph
1900 local leftdata = characters[leftline]
1901 local rightdata = characters[rightline]
1902 local leftwidth = leftdata.width
1903 local rightwidth = rightdata.width
1904 local result = characters[target]
1905 if not result or result.width == 0 then
1906 result = {
1907 height = leftdata.height,
1908 depth = leftdata.depth,
1909 width = 0.9*leftwidth + rightwidth,
1910 unicode = target,
1911 commands = {
1912 slotcommand[0][leftline],
1913 leftcommand[0.1*leftwidth],
1914 slotcommand[0][rightline],
1915 },
1916 }
1917 characters[target] = result
1918 end
1919 result.parts = {
1920 { advance = leftwidth, glyph = leftline, ["end"] = .9*leftwidth, start = 0 },
1921 { advance = rightwidth, glyph = rightline, ["end"] = .1*leftwidth, start = .9*rightwidth, extender = 1 },
1922 }
1923 result.partsorientation = "horizontal"
1924 done = registerdone(done,target)
1925 end
1926 end
1927 end
1928 build(0x305,characters[0x20D6],characters[0x20D7])
1929 build(0x332,characters[0x20EE],characters[0x20EF])
1930
1931
1932
1933 if not characters[0x23B4] then
1934 local depth = 0
1935 local height = 5 * thickness
1936 local tpiece = addprivate(target,"bracket-piece-top",{
1937 width = thickness,
1938 height = height,
1939 depth = depth,
1940 commands = { upcommand[thickness], { "rule", 4 * thickness, thickness } },
1941 })
1942 local mpiece = addprivate(target,"bracket-piece-top-middle",{
1943 width = width,
1944 height = height,
1945 depth = depth,
1946 commands = { upcommand[4*thickness], { "rule", thickness, width } },
1947 })
1948 characters[0x23B4] = {
1949 width = double + width,
1950 height = height,
1951 depth = depth,
1952 unicode = 0x23B4,
1953extensible = false,
1954 commands = {
1955 slotcommand[0][tpiece],
1956 slotcommand[0][mpiece],
1957 slotcommand[0][tpiece],
1958 },
1959 parts = {
1960 { advance = thickness, glyph = tpiece, ["end"] = 0, start = half },
1961 { advance = width, glyph = mpiece, ["end"] = step, start = step, extender = 1 },
1962 { advance = thickness, glyph = tpiece, ["end"] = half, start = 0 },
1963 },
1964 partsorientation = "horizontal",
1965 }
1966 done = registerdone(done,0x23B4)
1967 end
1968 if not characters[0x23B5] then
1969 local depth = 0
1970 local height = 5 * thickness
1971 local bpiece = addprivate(target,"bracket-piece-bottom",{
1972 width = thickness,
1973 height = height,
1974 depth = depth,
1975 yoffset = depth,
1976 commands = { { "rule", 4 * thickness, thickness } },
1977 })
1978 local mpiece = addprivate(target,"bracket-piece-bottom-middle",{
1979 width = width,
1980 height = height,
1981 depth = depth,
1982 commands = { { "rule", thickness, width } },
1983 })
1984 characters[0x23B5] = {
1985 width = double + width,
1986 height = height,
1987 depth = depth,
1988 unicode = 0x23B5,
1989extensible = false,
1990 commands = {
1991 slotcommand[0][bpiece],
1992 slotcommand[0][mpiece],
1993 slotcommand[0][bpiece],
1994 },
1995 parts = {
1996 { advance = thickness, glyph = bpiece, ["end"] = 0, start = half },
1997 { advance = width, glyph = mpiece, ["end"] = step, start = step, extender = 1 },
1998 { advance = thickness, glyph = bpiece, ["end"] = half, start = 0 },
1999 },
2000 partsorientation = "horizontal",
2001 }
2002 done = registerdone(done,0x23B5)
2003 end
2004
2005 feedback_tweak("rules",target,original,done)
2006 end
2007
2008
2009
2010 local rbe = newprivateslot("radical bar extender")
2011 local fbe = newprivateslot("fraction bar extender")
2012
2013 local frp = {
2014 newprivateslot("flat rule left piece"),
2015 newprivateslot("flat rule middle piece"),
2016 newprivateslot("flat rule right piece"),
2017 }
2018
2019 local rrp = {
2020 newprivateslot("radical rule left piece"),
2021 newprivateslot("radical rule middle piece"),
2022 newprivateslot("radical rule right piece"),
2023 }
2024
2025 local mrp = {
2026 newprivateslot("minus rule left piece"),
2027 newprivateslot("minus rule middle piece"),
2028 newprivateslot("minus rule right piece"),
2029 }
2030
2031 local forceextensible_tag = tex.charactertagcodes.forceextensible
2032
2033 local function useminus(target,unicode,characters,parameters,skipfirst,what,tounicode)
2034 local minus = characters[0x2212]
2035 local parts = minus.parts
2036 if parameters == true then
2037 parameters = { }
2038 end
2039 if parts then
2040 minus.tag = forceextensible_tag
2041 what = copytable(what)
2042 parts = copytable(parts)
2043 local xscale = parameters.xscale or 1
2044 local yscale = parameters.yscale or 1
2045 local mwidth = minus.width
2046 local mheight = minus.height
2047 local height = (parameters.height or 1) * mheight
2048 local yshift = (parameters.yoffset or 0) * mheight
2049 local loverlap = parameters.leftoverlap or 0
2050 local roverlap = parameters.rightoverlap or 0
2051 local loffset = parameters.leftoffset or 0
2052 local roffset = parameters.rightoffset or 0
2053 if skipfirst then
2054 remove(parts,1)
2055 remove(what,1)
2056 end
2057 height = height / 2
2058 yshift = yshift + height
2059 for i=1,#parts do
2060 local part = parts[i]
2061 local glyph = part.glyph
2062 local gdata = characters[glyph]
2063 local width = gdata.width
2064 local xshift = 0
2065 if i == 1 and loverlap ~= 0 then
2066 xshift = loverlap * width
2067 width = width - xshift
2068 elseif i == #parts and roverlap ~= 0 then
2069 width = width - roverlap * width
2070 end
2071 characters[what[i]] = {
2072 height = height,
2073 depth = height,
2074 width = width,
2075 advance = gdata.width,
2076 unicode = 0xFFFD,
2077 commands = {
2078 leftcommand[xshift],
2079 downcommand[yshift],
2080 { "slot", 0, glyph, xscale, yscale },
2081 },
2082 }
2083
2084 if part["start"] >= width then
2085 part["start"] = width
2086 end
2087 if part["end"] >= width then
2088 part["end"] = width
2089 end
2090 part.advance = width
2091 part.glyph = what[i]
2092 end
2093 characters[what[1]].unicode = unicode
2094 xshift = loffset * mwidth + loverlap * mwidth
2095 width = mwidth - xshift - roffset * mwidth - roverlap * mwidth
2096 characters[unicode] = {
2097
2098 height = height,
2099 depth = height,
2100 width = width,
2101 tag = forceextensible_tag,
2102
2103 commands = {
2104 leftcommand[xshift],
2105 downcommand[yshift],
2106 { "slot", 0, 0x2212, xscale, yscale },
2107 },
2108 unicode = tounicode or unicode,
2109
2110 parts = parts,
2111 partsorientation = "horizontal",
2112 }
2113 end
2114 end
2115
2116
2117
2118 local function checkminus(target,unicode,characters,parameters,skipfirst,what,tounicode)
2119 local minus = characters[unicode]
2120 local parts = minus.parts
2121 if parameters == true then
2122 parameters = { }
2123 end
2124 local p_normal = 0
2125 local p_flat = 0
2126 local mwidth = minus.width
2127 local height = minus.height
2128 local depth = minus.depth
2129 local loffset = parameters.leftoffset or 0
2130 local roffset = parameters.rightoffset or 0
2131 local lshift = mwidth * loffset
2132 local rshift = mwidth * roffset
2133 local width = mwidth - lshift - rshift
2134 if parts then
2135
2136 if lshift ~= 0 or width ~= mwidth then
2137 parts = copytable(parts)
2138 for i=1,#parts do
2139 local part = parts[i]
2140 local glyph = part.glyph
2141 local gdata = characters[glyph]
2142 local width = gdata.width
2143 local advance = part.advance
2144 local lshift = 0
2145 if i == 1 and left ~= 0 then
2146 lshift = loffset * width
2147 width = width - lshift
2148 advance = advance - lshift
2149 elseif i == #parts and roffset ~= 0 then
2150 width = width - rshift
2151 advance = advance - rshift
2152 end
2153 characters[what[i]] = {
2154 height = height,
2155 depth = depth,
2156 width = width,
2157 commands = {
2158 leftcommand[lshift],
2159 slotcommand[0][glyph],
2160
2161 },
2162 }
2163 part.glyph = what[i]
2164 part.advance = advance
2165 end
2166 minus.parts = parts
2167 minus.partsorientation = "horizontal"
2168
2169 end
2170 else
2171 local f_normal = formatters["M-NORMAL-%H"](unicode)
2172
2173 p_normal = addprivate(target,f_normal,{
2174 height = height,
2175 width = width,
2176 commands = {
2177 push,
2178 leftcommand[lshift],
2179 slotcommand[0][unicode],
2180 pop,
2181
2182 },
2183 })
2184
2185 local step = .8*width
2186 minus.parts = {
2187 { extender = 0, glyph = p_normal, ["end"] = step, start = 0, advance = width },
2188 { extender = 1, glyph = p_normal, ["end"] = step, start = step, advance = width },
2189 { extender = 0, glyph = p_normal, ["end"] = 0, start = step, advance = width },
2190 }
2191 minus.partsorientation = "horizontal"
2192 end
2193 minus.unicode = tounicode or unicode
2194 end
2195
2196 function mathtweaks.replacerules(target,original,parameters)
2197 local characters = target.characters
2198 local minus = parameters.minus
2199 local fraction = parameters.fraction
2200 local radical = parameters.radical
2201 local stacker = parameters.stacker
2202 if minus then
2203 checkminus(target,0x2212,characters,minus,false,mrp)
2204 end
2205 if fraction then
2206 useminus(target,fbe,characters,fraction,false,frp,0x2044)
2207 end
2208 if radical then
2209if not characters[rbe] then
2210 local skipfirst = true
2211 if radical.skipfirst == false then
2212 skipfirst = false
2213 end
2214 useminus(target,rbe,characters,radical,skipfirst,rrp,0x2061)
2215end
2216 end
2217 if stacker then
2218 useminus(target,0x203E,characters,stacker,false,frp)
2219 end
2220 end
2221
2222 local force = false experiments.register("math.arrows", function(v) force = v end)
2223
2224 local function tighten(target,unicode,left,right,squeeze,yoffset)
2225 local name = formatters["math tightened %U %.3N %.3N %.3N %.3N"](unicode,left,right,squeeze,yoffset)
2226 local slot = privateslot(target,name)
2227 if not slot then
2228 local characters = target.characters
2229 local data = copytable(characters[unicode])
2230 local width = data.width
2231 data.advance = width
2232 data.width = width * (1-left-right)
2233 data.xoffset = width * -left
2234 if squeeze ~= 1 then
2235 data.effect = { squeeze = squeeze }
2236 end
2237 if yoffset ~= 0 then
2238 data.yoffset = (data.height or 0) * yoffset
2239 end
2240 slot = addprivate(target,name,data)
2241 end
2242 return slot
2243 end
2244
2245 local function create(target,unicode,list,overloads)
2246 local characters = target.characters
2247 local chardata = characters[unicode]
2248 if chardata then
2249 local endpoint = unicode
2250 while chardata.next do
2251 chardata = characters[chardata.next]
2252 end
2253 if chardata and (force or overloads[unicode] == false or not chardata.parts) then
2254 if not list then
2255
2256
2257 else
2258 local overload = overloads[unicode]
2259 local parts = { }
2260 for i=1,#list do
2261 local part = list[i]
2262 local glyph = part.glyph or unicode
2263 local check = overloads[glyph]
2264 local left = (check and check.left ) or part.left or 0
2265 local right = (check and check.right ) or part.right or 0
2266 local squeeze = check and check.squeeze or 1
2267 local yoffset = check and check.yoffset or 0
2268 if left~= 0 or right ~= 0 or squeeze ~= 1 or yoffset ~= 0 then
2269 glyph = tighten(target,glyph,left,right,squeeze,yoffset)
2270 end
2271 local width = characters[glyph].width
2272 local step = width/2
2273 if part.extensible then
2274 parts[#parts+1] = {
2275 advance = width,
2276 glyph = glyph,
2277 ["end"] = step,
2278 start = step,
2279 extender = 1,
2280 }
2281 else
2282 parts[#parts+1] = {
2283 advance = width,
2284 glyph = glyph,
2285 ["end"] = 0,
2286 start = step,
2287 }
2288 end
2289 end
2290 if #parts == #list then
2291 chardata.parts = parts
2292 chardata.partsorientation = "horizontal"
2293 end
2294 end
2295 end
2296 end
2297 end
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317 local function initialize(left, right, slack)
2318
2319
2320 local single = { glyph = 0x2212, left = slack, right = slack, extensible = true }
2321 local double = { glyph = 0x003D, left = slack, right = slack, extensible = true }
2322 local triple = { glyph = 0x2261, left = slack, right = slack, extensible = true }
2323
2324 local slackslack = { left = slack, right = slack }
2325 local leftslack = { left = left, right = slack }
2326 local slackright = { left = slack, right = right }
2327
2328 local centered = false
2329 local singleright = { single, slackright }
2330 local leftsingle = { leftslack, single }
2331 return {
2332
2333 [0x002D] = { { left = slack, right = slack, glyph = 0x2212 }, single },
2334
2335
2336 [0x2190] = leftsingle,
2337 [0x219E] = leftsingle,
2338 [0x21BC] = leftsingle,
2339 [0x21BD] = leftsingle,
2340
2341 [0x2192] = singleright,
2342 [0x21A0] = singleright,
2343 [0x21C0] = singleright,
2344 [0x21C1] = singleright,
2345
2346 [0x003D] = { slackslack, double },
2347 [0x2261] = { slackslack, triple },
2348 [0x27F8] = { leftslack, double },
2349 [0x27F9] = { double, slackright },
2350
2351 [0x21A9] = centered,
2352 [0x21AA] = centered,
2353 [0x21CB] = centered,
2354 [0x21CC] = centered,
2355 [0x21C4] = centered,
2356 [0x21C6] = centered,
2357 [0x21A6] = centered,
2358
2359 [0x203E] = { slackslack, { left = slack, right = slack, extensible = true } },
2360
2361 [0x27F7] = { { glyph = 0x2190, left = left, right = slack }, single, { glyph = 0x2192, left = slack, right = right } },
2362 [0x27FA] = { { glyph = 0x27F8, left = left, right = slack }, double, { glyph = 0x27F9, left = slack, right = right } },
2363 }
2364 end
2365
2366 datasets.addarrows = { }
2367
2368 function mathtweaks.addarrows(target,original,parameters)
2369 local overloads = parameters.list or { }
2370 local left = parameters.left or 0.05
2371 local right = parameters.right or 0.05
2372 local slack = parameters.slack or 0.1
2373 local arrows = initialize(left,right,slack)
2374
2375 for unicode, list in sortedhash(arrows) do
2376 create(target,unicode,list,overloads)
2377 end
2378 datasets.addarrows = sortedkeys(arrows)
2379 if trace_tweaking then
2380 report_tweak("arrows added",target,original)
2381 end
2382
2383 end
2384
2385end
2386
2387
2388
2389do
2390
2391 function mathtweaks.addparts(target,original,parameters)
2392 local characters = target.characters
2393 local list = parameters.list
2394 if list then
2395 for unicode, data in sortedhash(list) do
2396 local template = data.template
2397 if template then
2398 local source = characters[template]
2399 local target = characters[unicode]
2400 if source and target then
2401 local sequence = data.sequence
2402 if sequence then
2403
2404 local parts = source.parts
2405 if parts then
2406 local p = { }
2407 for i=1,#sequence do
2408 local step = sequence[i]
2409 local glyph = step.glyph
2410 if glyph == "first" or glyph == "last" then
2411 local g = glyph == "first" and 1 or #parts
2412 local c = fastcopy(parts[g])
2413 local f = step.factor
2414 if f then
2415 c["end"] = f * (c["end"] or 0)
2416 c.start = f * (c.start or 0)
2417 end
2418 p[#p+1] = c
2419 else
2420 local c = characters[glyph]
2421 if c then
2422 p[#p+1] = {
2423 glyph = glyph,
2424 advance = c.width,
2425 start = 0,
2426 ["end"] = 0,
2427 }
2428 end
2429 end
2430 end
2431 if #p > 0 then
2432 target.parts = p
2433 if not data.horizontal then
2434 target.partsorientation = "vertical"
2435 end
2436 end
2437 end
2438 end
2439 end
2440 end
2441 end
2442 end
2443 end
2444
2445end
2446
2447function mathtweaks.action(target,original,parameters)
2448 local action = parameters.action
2449 if type(action) == "function" then
2450 action(target,original,parameters)
2451 end
2452end
2453
2454do
2455
2456 local list = {
2457 { 0x00A0, "s", 1 },
2458 { 0x2000, "q", 1/2 },
2459 { 0x2001, "q", 1 },
2460 { 0x2002, "q", 1/2 },
2461 { 0x2003, "q", 1 },
2462 { 0x2004, "q", 1/3 },
2463 { 0x2005, "q", 1/4 },
2464 { 0x2006, "q", 1/6 },
2465 { 0x2007, "c", byte('0') },
2466 { 0x2008, "c", byte('.') },
2467 { 0x2009, "q", 1/8 },
2468 { 0x200A, "q", 1/8 },
2469 { 0x200B, "q", 0 },
2470 { 0x202F, "q", 1/8 },
2471 { 0x205F, "s", 1/2 },
2472 }
2473
2474 datasets.checkspacing = list
2475
2476 function mathtweaks.checkspacing(target,original,parameters)
2477 local characters = target.characters
2478 local parameters = target.parameters
2479 for i=1,#list do
2480 local entry = list[i]
2481 local unicode = entry[1]
2482 local data = characters[unicode]
2483 if not data then
2484 local method = entry[2]
2485 local fraction = entry[3]
2486 local width = 0
2487 local height = 0
2488
2489 if method == "c" then
2490 local template = characters[fraction]
2491 width = template.width
2492 height = template.height
2493
2494 elseif method == "s" then
2495 width = fraction * parameters.space
2496 height = 0
2497
2498 else
2499 width = fraction * parameters.quad
2500 height = 0
2501
2502 end
2503 if trace_tweaking then
2504 report_tweak("setting width of %U to %p",target,original,unicode,width)
2505 end
2506 characters[unicode] = {
2507 width = width,
2508
2509 height = height,
2510
2511 unicode = unicode,
2512 commands = {
2513
2514 },
2515 }
2516 end
2517 end
2518 end
2519
2520end
2521
2522do
2523
2524
2525
2526
2527 local nps = fonts.helpers.newprivateslot
2528
2529 local radicalbarextender = nps("radical bar extender")
2530
2531 local list = {
2532 0x221A,
2533 }
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563 local function fix(target,original,characters,unicode)
2564 local data = characters[unicode]
2565 if data then
2566 local height = data.height or 0
2567 local depth = data.depth or 0
2568 if depth > height then
2569 if trace_tweaking then
2570 report_tweak("swapping height and depth of radical %U",target,original,unicode)
2571 end
2572 if data.rorrim then
2573
2574 else
2575 data.height = (data.height or 0) + (data.depth or 0)
2576 data.yoffset = data.depth
2577 data.depth = 0
2578 end
2579 end
2580 local smaller = data.smaller
2581 if smaller then
2582 fix(target,original,characters,smaller)
2583 end
2584
2585
2586
2587
2588 local next = data.next
2589 if next then
2590 fix(target,original,characters,next)
2591 else
2592
2593
2594
2595
2596
2597 end
2598 end
2599 end
2600
2601 function mathtweaks.fixradicals(target,original,parameters)
2602 local characters = target.characters
2603 for i=1,#list do
2604 local unicode = list[i]
2605 fix(target,original,characters,unicode)
2606 end
2607 end
2608
2609 local function fix(target,original,characters,u,l)
2610 local data = characters[u]
2611 if data then
2612
2613 data.innerlocation = l.location == "right" and "right" or "left"
2614 data.innerxoffset = (l.hfactor or 1) * (data.width or 0)
2615 data.inneryoffset = (l.vfactor or 1) * ((data.height or 0) + (data.depth or 0))
2616 end
2617 end
2618
2619 function mathtweaks.radicaldegreeanchors(target,original,parameters)
2620 local list = parameters.list
2621 if list then
2622 local characters = target.characters
2623 for unicode, l in sortedhash(list) do
2624 local u = detail(characters,unicode) or unicode
2625 if type(u) == "table" then
2626 for i=1,#u do
2627 fix(target,original,characters,u[i],l)
2628 end
2629 else
2630 fix(target,original,characters,u,l)
2631 end
2632 end
2633 end
2634 end
2635
2636 local function fix(target,original,characters,u,l)
2637 local data = characters[u]
2638 if data then
2639 data.leftmargin = (l.left or 1) * (data.width or 0)
2640 data.rightmargin = (l.right or 1) * (data.width or 0)
2641 end
2642 end
2643
2644 function mathtweaks.radicalbodymargins(target,original,parameters)
2645 local list = parameters.list
2646 if list then
2647 local characters = target.characters
2648 for unicode, l in sortedhash(list) do
2649 local u = detail(characters,unicode) or unicode
2650 if type(u) == "table" then
2651 for i=1,#u do
2652 fix(target,original,characters,u[i],l)
2653 end
2654 else
2655 fix(target,original,characters,u,l)
2656 end
2657 end
2658 end
2659 end
2660
2661end
2662
2663do
2664
2665 local done = nil
2666
2667 local function fix(target,original,characters,unicode,axis)
2668 if done[unicode] then
2669 return
2670 end
2671 done[unicode] = true
2672 local data = characters[unicode]
2673 if data then
2674 local height = data.height or 0
2675 local depth = data.depth or 0
2676 if trace_tweaking then
2677 report_tweak("swapping height and depth of %U",target,original,unicode)
2678 end
2679 local half = (height + depth)/2
2680 if data.rorrim then
2681
2682 else
2683 data.yoffset = depth - (half - axis)
2684 end
2685 height = half + axis
2686 depth = half - axis
2687 data.height = height
2688 data.depth = depth
2689 local smaller = data.smaller
2690 if smaller then
2691 fix(target,original,characters,smaller,axis)
2692 end
2693 local mirror = data.mirror
2694 if mirror then
2695 fix(target,original,characters,mirror,axis)
2696 end
2697 local next = data.next
2698 if next then
2699 fix(target,original,characters,next,axis)
2700 end
2701 end
2702 end
2703
2704 function mathtweaks.fixoldschool(target,original,parameters)
2705 local characters = target.characters
2706 local list = mathtweaks.subsets.integrals
2707 local also = getalso(target,original)
2708 local axis = target.mathparameters.AxisHeight
2709 done = { }
2710 for i=1,#list do
2711 local unicode = list[i]
2712 fix(target,original,characters,unicode,axis)
2713 end
2714 if also then
2715 local a = also[u]
2716 if a then
2717 for i=1,#a do
2718 fix(target,original,characters,a[i],axis)
2719 end
2720 end
2721 end
2722 done = nil
2723 end
2724
2725
2726
2727
2728
2729 function mathtweaks.fixintegrals(target,original,parameters)
2730 local characters = target.characters
2731 local integral = characters[0x222B]
2732 if integral and not integral.parts then
2733 local top = characters[0x2320]
2734 local mid = characters[0x23AE]
2735 local bot = characters[0x2321]
2736 if top and mid and bot then
2737 top = top.height
2738 mid = mid.height
2739 bot = bot.height
2740 integral.partsitalic = integral.italic
2741 integral.parts = {
2742 { advance = bot, ["end"] = bot/3, glyph = 0x2321, start = bot/3 },
2743 { advance = mid, ["end"] = mid/2, glyph = 0x23AE, start = mid/2, extender = 1 },
2744 { advance = top, ["end"] = top/3, glyph = 0x2320, start = top/3 },
2745 }
2746 integral.partsorientation = "vertical"
2747 if trace_tweaking then
2748 report_tweak("fixing the integral extensible",target,original)
2749 end
2750 end
2751 else
2752 report_tweak("no need to fix the integral extensible",target,original)
2753 end
2754 end
2755
2756end
2757
2758do
2759
2760 local list = { 0x2061, 0x2062, 0x2063, 0x2064 }
2761
2762 datasets.wipecues = list
2763
2764 function mathtweaks.wipecues(target,original,parameters)
2765 local characters = target.characters
2766 local tobewiped = parameters.list or list
2767 local done = false
2768 for i=1,#tobewiped do
2769 local unicode = tobewiped[i]
2770 characters[unicode] = {
2771 width = 0,
2772 height = 0,
2773 depth = 0,
2774 unicode = unicode,
2775 }
2776 done = registerdone(done,unicode)
2777 end
2778 feedback_tweak("wipecues",target,original,done)
2779 end
2780
2781end
2782
2783do
2784
2785 local mapping = {
2786 [0x002F] = 0x2044,
2787 }
2788
2789 datasets.fixslashes = mapping
2790
2791 function mathtweaks.fixslashes(target,original,parameters)
2792 local characters = target.characters
2793
2794 for normal, weird in sortedhash(mapping) do
2795 local normalone = characters[normal]
2796 local weirdone = characters[weird]
2797 if normalone and weirdone and not normalone.next then
2798 normalone.next = weirdone.next
2799
2800 end
2801 weirdone = copytable(normalone)
2802 characters[weird] = weirdone
2803 weirdone.unicode = weird
2804 end
2805
2806 if trace_tweaking then
2807 report_tweak("slashes fixed",target,original)
2808 end
2809 end
2810
2811end
2812
2813do
2814
2815 local nps = fonts.helpers.newprivateslot
2816
2817 local mapping = {
2818 [0x0300] = { 0x0060, false },
2819 [0x0308] = { 0x00A8, false },
2820 [0x0304] = { 0x00AF, false },
2821 [0x0301] = { 0x00B4, false },
2822 [0x0302] = { 0x02C6, true },
2823 [0x030C] = { 0x02C7, true },
2824 [0x0306] = { 0x02D8, false },
2825 [0x0307] = { 0x02D9, false },
2826 [0x030A] = { 0x02DA, false },
2827 [0x0303] = { 0x02DC, true },
2828 [0x20DB] = { 0x20DB, false },
2829
2830 }
2831
2832 datasets.fixaccents = mapping
2833 datasets.extendaccents = mapping
2834 datasets.flattenaccents = mapping
2835 datasets.copyaccents = mapping
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847local cdata = characters.data
2848
2849 function mathtweaks.fixaccents(target,original,parameters)
2850 local characters = target.characters
2851 local done = false
2852 for stretching, entry in sortedhash(mapping) do
2853 local alias = entry[1]
2854 local stretchingdata = characters[stretching]
2855 if stretchingdata and stretchingdata.width == 0 then
2856if false then
2857 local b = target.descriptions[stretching].boundingbox
2858 if b then
2859 local llx = b[1] * target.parameters.hfactor
2860 local urx = b[3] * target.parameters.hfactor
2861 width = urx - llx
2862 stretchingdata.width = width
2863 stretchingdata.xoffset = - llx
2864 stretchingdata.advance = urx
2865 stretchingdata.topanchor = width/2
2866 stretchingdata.bottomanchor = width/2
2867 end
2868else
2869 local topanchor = stretchingdata.topanchor or 0
2870 local width = -topanchor
2871 topanchor = width/2
2872 stretchingdata.width = width
2873 stretchingdata.advance = 0
2874 stretchingdata.topanchor = topanchor
2875 stretchingdata.commands = { rightcommand[width + topanchor], charcommand[stretching] }
2876end
2877 done = registerdone(done,stretching)
2878 end
2879 end
2880 feedback_tweak("fixaccents",target,original,done)
2881 end
2882
2883 function mathtweaks.checkaccents(target,original,parameters)
2884 local characters = target.characters
2885 local done = false
2886 local factor = target.parameters.hfactor
2887 for unicode, data in sortedhash(characters) do
2888 local width = data.width
2889 if width == 0 then
2890 local d = chardata[data.unicode or unicode]
2891 local c = d and d.category
2892 if c == "mn" or c == "sk" or c == "lm" then
2893 local b = target.descriptions[unicode]
2894 if b then
2895 b = b.boundingbox
2896 end
2897 if b then
2898 local topanchor = data.topanchor or 0
2899 local llx = b[1] * factor
2900 local urx = b[3] * factor
2901
2902data.advance = data.width
2903if true then
2904
2905 width = 2 * (topanchor - llx)
2906
2907
2908
2909
2910
2911data.xoffset = -llx
2912else
2913 width = urx - llx
2914 data.commands = {
2915 leftcommand[llx],
2916 slotcommand[0][unicode]
2917 }
2918end
2919 data.width = width
2920 data.topanchor = width/2
2921 data.bottomanchor = width/2
2922 end
2923 done = registerdone(done,unicode)
2924 end
2925 end
2926 end
2927 feedback_tweak("checkaccents",target,original,done)
2928 end
2929
2930
2931
2932 function mathtweaks.extendaccents(target,original,parameters)
2933 local characters = target.characters
2934 local all = parameters.all
2935 local count = tonumber(all)
2936 local done = false
2937 for stretching, entry in sortedhash(mapping) do
2938 local extend = entry[2]
2939 if extend then
2940 local last = characters[stretching]
2941 local cnt = 1
2942 local okay = false
2943 while last do
2944 if all or (count and cnt > count) then
2945 last.extensible = true
2946 local flataccent = last.flataccent
2947 if flataccent then
2948 characters[flataccent].extensible = true
2949 okay = true
2950 end
2951 end
2952 local n = last.next
2953 if n then
2954 last = characters[n]
2955 else
2956 last.extensible = true
2957 local flataccent = last.flataccent
2958 if flataccent then
2959 characters[flataccent].extensible = true
2960 okay = true
2961 end
2962 break
2963 end
2964 cnt = cnt + 1
2965 end
2966 if okay then
2967 done = registerdone(done,stretching)
2968 end
2969 end
2970 end
2971 feedback_tweak("extendaccents",target,original,done)
2972 end
2973
2974
2975
2976
2977
2978
2979 local f_flat = formatters["flat accent %05X"]
2980
2981 function mathtweaks.flattenaccents(target,original,parameters)
2982 local characters = target.characters
2983 local force = parameters.force
2984 local squeeze = parameters.squeeze or 0.85
2985 local ofactor = parameters.offset or (squeeze/8.5)
2986 local hfactor = parameters.height or 0.95
2987 local done = false
2988 for stretching, entry in sortedhash(mapping) do
2989 local code = stretching
2990 local last = characters[stretching]
2991 while last do
2992 if force or not last.flataccent then
2993 local slot = nps(f_flat(code))
2994 local height = last.height or 0
2995
2996
2997characters[slot] = {
2998 width = last.width,
2999 depth = last.depth,
3000 height = last.height * hfactor,
3001 topanchor = last.topanchor,
3002 bottomanchor = last.bottomanchor,
3003 commands = { { "offset", 0, ofactor * height, code, 1, squeeze } },
3004
3005
3006
3007 unicode = last.unicode,
3008}
3009
3010
3011
3012
3013 last.flataccent = slot
3014 done = registerdone(done,stretching)
3015 end
3016 code = last.next
3017 if code then
3018 last = characters[code]
3019 else
3020 break
3021 end
3022 end
3023 end
3024 feedback_tweak("flattenaccents",target,original,done)
3025 end
3026
3027 function mathtweaks.copyaccents(target,original,parameters)
3028 local characters = target.characters
3029 local done = false
3030 for stretching, entry in sortedhash(mapping) do
3031 local alias = entry[1]
3032 if alias ~= stretching then
3033 local stretchingdata = characters[stretching]
3034 if stretchingdata then
3035
3036 characters[alias] = {
3037 width = stretchingdata.width,
3038 height = stretchingdata.height,
3039 depth = stretchingdata.depth,
3040 next = stretchingdata.next,
3041
3042 commands = stretchingdata.commands or { charcommand[stretching] },
3043 topanchor = stretchingdata.topanchor,
3044
3045 unicode = alias,
3046 }
3047 done = registerdone(done,stretching)
3048 end
3049 end
3050 end
3051 feedback_tweak("copyaccents",target,original,done)
3052 end
3053
3054 function mathtweaks.keepbases(target,original,parameters)
3055 local characters = target.characters
3056 local done = false
3057 local list = parameters.list
3058 if list == "default" then
3059 list = sortedkeys(mapping)
3060 end
3061 if list and #list > 0 then
3062 for i=1,#list do
3063 local unicode = list[i]
3064 local chardata = characters[unicode]
3065 if chardata then
3066 chardata.keepbase = true
3067 done = registerdone(done,unicode)
3068 end
3069 end
3070 else
3071
3072 end
3073 feedback_tweak("keepbases",target,original,done)
3074 end
3075
3076end
3077
3078do
3079
3080 function mathtweaks.inspect(target,original,parameters)
3081 local characters = target.characters
3082 local slot = parameters.slot or parameters.unicode
3083 if slot then
3084
3085 report_math(formatters["%C data:"](slot))
3086 inspect(characters[slot])
3087 end
3088 end
3089
3090end
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128do
3129
3130 local function jointwo(characters,force,unicode,ds,u1,d12,u2)
3131 if force or not characters[unicode] then
3132 local c1 = characters[u1]
3133 local c2 = characters[u2]
3134 if c1 and c2 then
3135 local w1 = c1.width
3136 local w2 = c2.width
3137 local width
3138 if d12 == false then
3139 d12 = 0
3140 width = w2
3141 elseif d12 < 0 then
3142 d12 = d12 * w2
3143 width = w2
3144 else
3145 d12 = d12 * ds
3146 width = w1 + w2 - d12
3147 end
3148 characters[unicode] = {
3149 unicode = unicode,
3150 width = width,
3151 height = max(c1.height or 0, c2.height or 0),
3152 depth = max(c1.depth or 0, c2.depth or 0),
3153keepvirtual = true,
3154 commands = {
3155
3156
3157 slotcommand[0][u1],
3158
3159 d12 ~= 0 and leftcommand[d12] or false,
3160 slotcommand[0][u2],
3161
3162 },
3163 }
3164 end
3165 end
3166 end
3167
3168 local function jointhree(characters,force,unicode,ds,u1,d12,u2,d23,u3)
3169 if force or not characters[unicode] then
3170 local c1 = characters[u1]
3171 local c2 = characters[u2]
3172 local c3 = characters[u3]
3173 if c1 and c2 and c3 then
3174 local w1 = c1.width
3175 local w2 = c2.width
3176 local w3 = c3.width
3177 d12 = d12 * ds
3178 d23 = d23 * ds
3179 characters[unicode] = {
3180 unicode = unicode,
3181 width = w1 + w2 + w3 - d12 - d23,
3182 height = max(c1.height or 0, c2.height or 0, c3.height or 0),
3183 depth = max(c1.depth or 0, c2.depth or 0, c3.depth or 0),
3184 commands = {
3185 slotcommand[0][u1],
3186 d12 ~= 0 and leftcommand[d12] or false,
3187 slotcommand[0][u2],
3188 d23 ~= 0 and leftcommand[d23] or false,
3189 slotcommand[0][u3],
3190 }
3191 }
3192 end
3193 end
3194 end
3195
3196 function mathtweaks.addequals(target,original,parameters)
3197 local characters = target.characters
3198 local step = target.parameters.size/18
3199 local force = parameters.force
3200force = true
3201 jointwo (characters,force,0x2254,step,0x03A,0,0x03D)
3202 jointhree(characters,force,0x2A74,step,0x03A,0,0x03A,0,0x03D)
3203 jointwo (characters,force,0x2A75,step,0x03D,0,0x03D)
3204 jointhree(characters,force,0x2A76,step,0x03D,0,0x03D,0,0x03D)
3205 end
3206
3207end
3208
3209do
3210
3211
3212
3213
3214
3215 local nps = fonts.helpers.newprivateslot
3216
3217 local radical <const> = 0x0221A
3218 local actuarianrightlong <const> = nps("delimited right annuity long")
3219 local actuarianrightshort <const> = nps("delimited right annuity short")
3220 local actuarianleftlong <const> = nps("delimited left annuity long" )
3221 local actuarianleftshort <const> = nps("delimited left annuity short" )
3222 local placehold <const> = nps("delimited ghost annuity")
3223
3224 local actuarianbottomrightlong <const> = nps("delimited bottom right annuity long")
3225 local actuarianbottomrightshort <const> = nps("delimited bottom right annuity short")
3226 local actuarianbottomleftlong <const> = nps("delimited bottom left annuity long" )
3227 local actuarianbottomleftshort <const> = nps("delimited bottom left annuity short" )
3228
3229 function mathtweaks.addactuarian(target,original,parameters)
3230 local characters = target.characters
3231 local parameters = target.parameters
3232 local linewidth = target.MathConstants.RadicalRuleThickness
3233 local basechar = characters[radical]
3234 local baseheight = (basechar.height or 0)/2
3235 local basedepth = (basechar.depth or 0)/2
3236 local basetotal = baseheight + basedepth
3237 local used = baseheight
3238
3239 characters[placehold] = {
3240 width = 2*linewidth,
3241 height = baseheight,
3242 depth = basedepth,
3243 unicode = actuarian,
3244 callback = "devirtualize",
3245 commands = {
3246 rightcommand[linewidth],
3247 downcommand[basedepth],
3248 { "rule", basetotal, 0 },
3249 },
3250 }
3251
3252 characters[0x020E7] = {
3253 width = 6*linewidth,
3254 height = baseheight,
3255 depth = basedepth,
3256 unicode = actuarian,
3257 callback = "devirtualize",
3258 commands = {
3259 upcommand[baseheight-4*linewidth],
3260 { "rule", linewidth, 4*linewidth },
3261 downcommand[basetotal/2-linewidth],
3262 { "rule", basetotal/2, linewidth },
3263 },
3264 }
3265
3266 characters[actuarianrightlong] = {
3267 width = 2*linewidth,
3268 height = baseheight,
3269 depth = basedepth,
3270 unicode = actuarian,
3271 callback = "devirtualize",
3272 commands = {
3273 downcommand[basedepth],
3274 { "rule", basetotal, linewidth },
3275 },
3276 parts = {
3277 {
3278 advance = basetotal,
3279 ["end"] = used,
3280 glyph = actuarianrightlong,
3281 start = 0,
3282 },
3283 {
3284 advance = basetotal,
3285 ["end"] = 0,
3286 extender = 1,
3287 glyph = actuarianrightlong,
3288 start = used,
3289 },
3290 }
3291 }
3292 characters[actuarianbottomrightlong] =
3293 characters[actuarianrightlong]
3294 characters[actuarianrightshort] = {
3295 width = 2*linewidth,
3296 height = baseheight,
3297 depth = basedepth,
3298 unicode = actuarian,
3299 callback = "devirtualize",
3300 commands = {
3301 upcommand[baseheight-4*linewidth],
3302 { "rule", 4*linewidth, linewidth },
3303 },
3304 parts = {
3305 {
3306 advance = basetotal,
3307 ["end"] = used,
3308 glyph = actuarianrightshort,
3309 start = 0,
3310 },
3311 {
3312 advance = basetotal,
3313 ["end"] = 0,
3314 extender = 1,
3315 glyph = actuarianrightshort,
3316 start = used,
3317 },
3318 }
3319 }
3320 characters[actuarianbottomrightshort] = {
3321 width = 2*linewidth,
3322 height = linewidth,
3323 depth = basedepth,
3324 unicode = actuarian,
3325 callback = "devirtualize",
3326 commands = {
3327 downcommand[basedepth],
3328 { "rule", 4*linewidth, linewidth },
3329 },
3330 parts = {
3331 {
3332 advance = basetotal,
3333 ["end"] = used,
3334 glyph = actuarianrightshort,
3335 start = 0,
3336 },
3337 {
3338 advance = basetotal,
3339 ["end"] = 0,
3340 extender = 1,
3341 glyph = actuarianrightshort,
3342 start = used,
3343 },
3344 }
3345 }
3346
3347 characters[actuarianleftlong] = {
3348 width = 2*linewidth,
3349 height = baseheight,
3350 depth = basedepth,
3351 unicode = actuarian,
3352 callback = "devirtualize",
3353 commands = {
3354 rightcommand[linewidth],
3355 downcommand[basedepth],
3356 { "rule", basetotal, linewidth },
3357 },
3358 parts = {
3359 {
3360 advance = basetotal,
3361 ["end"] = used,
3362 extender = 1,
3363 glyph = placehold,
3364 start = 0,
3365 },
3366 {
3367 advance = basetotal,
3368 ["end"] = 0,
3369 glyph = actuarianleftlong,
3370 start = used,
3371 },
3372 }
3373 }
3374 characters[actuarianbottomleftlong] =
3375 characters[actuarianleftlong]
3376 characters[actuarianleftshort] = {
3377 width = 2*linewidth,
3378 height = baseheight,
3379 depth = basedepth,
3380 unicode = actuarian,
3381 callback = "devirtualize",
3382 commands = {
3383 rightcommand[linewidth],
3384 upcommand[baseheight-4*linewidth],
3385 { "rule", 4*linewidth, linewidth },
3386 },
3387 parts = {
3388 {
3389 advance = basetotal,
3390 ["end"] = used,
3391 extender = 1,
3392 glyph = placehold,
3393 start = 0,
3394 },
3395 {
3396 advance = basetotal,
3397 ["end"] = 0,
3398 glyph = actuarianleftshort,
3399 start = used,
3400 },
3401 }
3402 }
3403 characters[actuarianbottomleftshort] = {
3404 width = 2*linewidth,
3405
3406 height = linewidth,
3407 depth = basedepth,
3408 unicode = actuarian,
3409 callback = "devirtualize",
3410 commands = {
3411 rightcommand[linewidth],
3412 downcommand[basedepth],
3413 { "rule", 4*linewidth, linewidth },
3414 },
3415 parts = {
3416 {
3417 advance = basetotal,
3418 ["end"] = used,
3419 extender = 1,
3420 glyph = placehold,
3421 start = 0,
3422 },
3423 {
3424 advance = basetotal,
3425 ["end"] = 0,
3426 glyph = actuarianleftshort,
3427 start = used,
3428 },
3429 }
3430 }
3431
3432 if trace_tweaking then
3433 report_tweak("actuarian %U added",target,original,actuarian)
3434 end
3435 end
3436
3437end
3438
3439do
3440
3441
3442
3443
3444 local nps = fonts.helpers.newprivateslot
3445
3446 local list = {
3447
3448 { 0x0308, nps("delimited right ddot"), nps("delimited ghost ddot") },
3449 { 0x0304, nps("delimited right bar"), nps("delimited ghost bar") },
3450
3451 { 0x0302, nps("delimited right hat"), nps("delimited ghost hat") },
3452 { 0x030C, nps("delimited right check"), nps("delimited ghost check") },
3453 { 0x0306, nps("delimited right breve"), nps("delimited ghost breve") },
3454 { 0x0307, nps("delimited right dot"), nps("delimited ghost dot") },
3455 { 0x030A, nps("delimited right ring"), nps("delimited ghost ring") },
3456 { 0x0303, nps("delimited right tilde"), nps("delimited ghost tilde") },
3457 { 0x20DB, nps("delimited right dddot"), nps("delimited ghost dddot") },
3458
3459 { 0x2032, nps("delimited right prime"), nps("delimited ghost prime"), false, 1 },
3460 { 0x2033, nps("delimited right dprime"), nps("delimited ghost dprime"), false, 1 },
3461 { 0x2034, nps("delimited right tprime"), nps("delimited ghost tprime"), false, 1 },
3462 { 0x2057, nps("delimited right qprime"), nps("delimited ghost qprime"), false, 1 },
3463 { 0x2035, nps("delimited right rprime"), nps("delimited ghost rprime"), false, 1 },
3464 { 0x2036, nps("delimited right drprime"), nps("delimited ghost rdprime"), false, 1 },
3465 { 0x2037, nps("delimited right dtprime"), nps("delimited ghost rtprime"), false, 1 },
3466
3467 { 0x231C, nps("delimited left upper corner"), nps("delimited ghost upper corner") },
3468 { 0x231D, nps("delimited right upper corner"), nps("delimited ghost upper corner") },
3469 { 0x231E, nps("delimited left lower corner"), nps("delimited ghost lower corner"), true },
3470 { 0x231F, nps("delimited right lower corner"), nps("delimited ghost lower corner"), true },
3471
3472
3473
3474 { 0x2020, nps("delimited right dagger"), nps("delimited ghost dagger") },
3475 { 0x2021, nps("delimited right ddagger"), nps("delimited ghost ddagger") },
3476 { 0x2217, nps("delimited right ast"), nps("delimited ghost ast") },
3477 { 0x22C6, nps("delimited right star"), nps("delimited ghost star") },
3478
3479 { 0x2020, nps("delimited right dagger 1"), nps("delimited ghost dagger 1"), false, 1 },
3480 { 0x2021, nps("delimited right ddagger 1"), nps("delimited ghost ddagger 1"), false, 1 },
3481 { 0x2217, nps("delimited right ast 1"), nps("delimited ghost ast 1"), false, 1 },
3482 { 0x22C6, nps("delimited right star 1"), nps("delimited ghost star 1"), false, 1 },
3483 }
3484
3485 function mathtweaks.addfourier(target,original,parameters)
3486 local characters = target.characters
3487 for i=1,#list do
3488 local entry = list[i]
3489 local basecode = entry[1]
3490 local fouriercode = entry[2]
3491 local movecode = entry[3]
3492 local reverse = entry[4]
3493 local size = entry[5] or 0
3494 local basechar = characters[basecode]
3495 local compactscale = 1
3496
3497 if basechar and target.properties.compactmath and size > 0 then
3498 compactscale = target.parameters[size > 1 and "scriptscriptscale" or "scriptscale"] / 1000
3499 for i=1,size do
3500 basecode = basechar.smaller or basecode
3501 basechar = characters[basecode]
3502 end
3503 end
3504 if basechar then
3505 local scale = (parameters.scale or 1) * compactscale
3506 local variant = parameters.variant
3507 if variant then
3508 for i=1,variant do
3509 local okay = basechar.next
3510 if okay then
3511 basecode = okay
3512 basechar = characters[basecode]
3513 else
3514 break
3515 end
3516 end
3517 end
3518 local baseheight = scale * (basechar.height or 0)
3519 local basedepth = scale * (basechar.depth or 0)
3520 local basewidth = scale * (basechar.width or 0)
3521 local used = baseheight/2
3522 local total = baseheight + basedepth
3523 if reverse then
3524 used = total / 2
3525 end
3526 characters[movecode] = {
3527 width = basewidth,
3528 height = used,
3529 unicode = 0xFFFD,
3530 commands = {
3531 downcommand[used],
3532 { "rule", used, 0 },
3533 },
3534 }
3535 local parts = {
3536 {
3537 advance = used,
3538 ["end"] = used,
3539 extender = 1,
3540 glyph = movecode,
3541 start = used,
3542 },
3543 {
3544 advance = total,
3545 ["end"] = 0,
3546 glyph = fouriercode,
3547 start = total,
3548 },
3549 }
3550 if reverse then
3551 parts[1], parts[2] = parts[2], parts[1]
3552 end
3553 characters[fouriercode] = {
3554 width = basewidth,
3555 height = baseheight,
3556 depth = basedepth,
3557 unicode = basecode,
3558 commands = {
3559 scale == 1 and charcommand[basecode] or { "slot", 0, basecode, scale, scale },
3560 },
3561 partsorientation = "vertical",
3562 parts = parts,
3563
3564
3565 }
3566 if trace_tweaking then
3567 report_tweak("fourier %U added using %U",target,original,basecode,fouriercode)
3568 end
3569 end
3570 end
3571 end
3572
3573end
3574
3575do
3576
3577
3578
3579
3580
3581 local single <const> = 0x007C
3582 local double <const> = 0x2016
3583 local triple <const> = 0x2980
3584
3585 local function variantlist(unicode,chardata,total,used)
3586 chardata.varianttemplate = 0x0028
3587
3588 chardata.parts = {
3589 {
3590 advance = total,
3591 ["end"] = used,
3592 glyph = unicode,
3593 start = 0,
3594 },
3595 {
3596 advance = total,
3597
3598
3599 ["end"] = 4*used/5,
3600 extender = 1,
3601 glyph = unicode,
3602 start = used,
3603 },
3604 }
3605 chardata.partsorientation = "vertical"
3606 end
3607
3608
3609
3610
3611
3612 function mathtweaks.addbars(target,original,parameters)
3613 local characters = target.characters
3614 local template = single
3615 local basechar = characters[template]
3616 local tempchar = basechar
3617 local check = parameters.check
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630 local width = tempchar.width
3631 local height = tempchar.height
3632 local depth = tempchar.depth
3633 local advance = (parameters.advance or 1/10) * width
3634 local used = 1.2*height
3635 local total = height + depth
3636
3637 if not check or not basechar.parts then
3638 variantlist(template,basechar,total,used)
3639 end
3640 if not check or not characters[double] then
3641 basechar = {
3642 unicode = double,
3643 width = 2*width - 1*advance,
3644 height = height,
3645 depth = depth,
3646
3647 commands = {
3648 charcommand[template],
3649 leftcommand[advance],
3650 charcommand[template],
3651 },
3652 }
3653 characters[double] = basechar
3654 else
3655 basechar = characters[double]
3656 end
3657 if not check or not basechar.parts then
3658 variantlist(double,basechar,total,used)
3659 end
3660 if not check or not characters[triple] then
3661 basechar = {
3662 unicode = triple,
3663 width = 3*width - 2*advance,
3664 height = height,
3665 depth = depth,
3666
3667 commands = {
3668 charcommand[template],
3669 leftcommand[advance],
3670 charcommand[template],
3671 leftcommand[advance],
3672 charcommand[template],
3673 },
3674 }
3675 characters[triple] = basechar
3676 else
3677 basechar = characters[triple]
3678 end
3679 if not check or not basechar.parts then
3680 variantlist(triple,basechar,total,used)
3681 end
3682
3683 if trace_tweaking then
3684 report_tweak("single, double and triple bars added",target,original)
3685 end
3686 end
3687
3688end
3689
3690do
3691
3692
3693
3694 local snormal <const> = 0x002E
3695 local sraised <const> = 0x22C5
3696
3697 local tnormal <const> = 0x2026
3698 local traised <const> = 0x22EF
3699
3700 function mathtweaks.fixellipses(target,original,parameters)
3701 local characters = target.characters
3702 local function fix(normal,raised)
3703 local normaldata = characters[normal]
3704 if normaldata then
3705 local raiseddata = copytable(normaldata)
3706 characters[raised] = raiseddata
3707 raiseddata.unicode = raised
3708 local height = raiseddata.height
3709 local yoffset = (parameters.yoffset or 2) * height
3710 raiseddata.yoffset = yoffset
3711 raiseddata.height = height + yoffset
3712 if trace_tweaking then
3713 report_tweak("taking %U from %U",target,original,raised,normal)
3714 end
3715 end
3716 end
3717 fix(snormal,sraised)
3718 fix(tnormal,traised)
3719 end
3720
3721end
3722
3723do
3724
3725
3726
3727
3728 local list = {
3729 { 0x207A, 0x002B, true },
3730 { 0x207B, 0x2212, true },
3731 { 0x208A, 0x002B, false },
3732 { 0x208B, 0x2212, false },
3733 }
3734
3735 datasets.addscripts = list
3736
3737 local function add(target,original,characters,unicode,template,super,baseheight,scale)
3738 if not characters[unicode] then
3739 local origdata = characters[template]
3740 if origdata then
3741 local width = scale * (origdata.width or 0)
3742 local height = scale * (origdata.height or 0)
3743 local depth = scale * (origdata.depth or 0)
3744 local half = - (height + depth) / 2
3745 local offset = super and baseheight/2 or -baseheight/4
3746 characters[unicode] = {
3747 width = width,
3748 height = height + offset,
3749 depth = depth - offset,
3750 unicode = unicode,
3751 commands = {
3752 { "offset", 0, offset, template, scale, scale }
3753 },
3754 }
3755 if trace_tweaking then
3756 report_tweak("adding script %U scaled %0.3f",target,original,unicode,scale)
3757 end
3758
3759 end
3760 end
3761 end
3762
3763 function mathtweaks.addscripts(target,original,parameters)
3764 local characters = target.characters
3765 local baseheight = target.mathparameters.AccentBaseHeight
3766 local scaledown = parameters.scale or target.mathparameters.ScriptScriptPercentScaleDown / 100
3767 for i=1,#list do
3768 local entry = list[i]
3769 if entry then
3770 add(target,original,characters,entry[1],entry[2],entry[3],baseheight,scaledown)
3771 end
3772 end
3773 end
3774
3775end
3776
3777do
3778
3779 function mathtweaks.sortvariants(target,original,parameters)
3780 local list = parameters.list
3781 if list then
3782 local characters = target.characters
3783 local horizontal = parameters.orientation == "horizontal"
3784 for i=1,#list do
3785 local u = list[i]
3786 local c = characters[u]
3787 if c then
3788 local t = { }
3789 while true do
3790 local n = c.next
3791 if n then
3792 c = characters[n]
3793 end
3794 if c and not c.parts then
3795 if horizontal then
3796 t[c.width or 0] = n
3797 else
3798 t[(c.height or 0) + (c.depth or 0)] = n
3799 end
3800 else
3801 break
3802 end
3803 end
3804 local c = characters[u]
3805 for k, v in sortedhash(t) do
3806 c.next = v
3807 c = characters[v]
3808 end
3809 end
3810 end
3811 end
3812 end
3813
3814end
3815
3816do
3817
3818
3819
3820
3821
3822
3823
3824
3825 local mirrors = {
3826
3827 [0x0002F] = true,
3828 [0x0005C] = true,
3829 [0x000F7] = true,
3830 [0x02044] = true,
3831 [0x02215] = true,
3832
3833 [0x02032] = true,
3834 [0x02033] = true,
3835 [0x02034] = true,
3836 [0x02057] = true,
3837 [0x02035] = true,
3838 [0x02036] = true,
3839 [0x02037] = true,
3840
3841 [0x0221A] = true,
3842 [0x0221B] = true,
3843 [0x0221C] = true,
3844 [0x0221D] = true,
3845
3846 [0x0222B] = true,
3847 [0x0222C] = true,
3848 [0x0222D] = true,
3849 [0x0222E] = true,
3850 [0x0222F] = true,
3851 [0x02230] = true,
3852 [0x02231] = true,
3853 [0x02232] = true,
3854 [0x02233] = true,
3855
3856 [0x02A0A] = true,
3857 [0x02A0B] = true,
3858 [0x02A0C] = true,
3859 [0x02A0D] = true,
3860 [0x02A0E] = true,
3861
3862 [0x02140] = true,
3863 [0x02201] = true,
3864 [0x02202] = true,
3865 [0x02203] = true,
3866 [0x02204] = true,
3867 [0x02211] = true,
3868
3869 [0x02239] = true,
3870 [0x0225F] = true,
3871 [0x0228C] = true,
3872 [0x022A7] = true,
3873 [0x022AA] = true,
3874 [0x022AC] = true,
3875 [0x022AD] = true,
3876 [0x022AE] = true,
3877 [0x022AF] = true,
3878 [0x022F5] = true,
3879 [0x022F8] = true,
3880 [0x022F9] = true,
3881 [0x022FF] = true,
3882 [0x02320] = true,
3883 [0x02321] = true,
3884 [0x027C0] = true,
3885 [0x029DC] = true,
3886 [0x029F4] = true,
3887
3888 [0x02A0F] = true,
3889 [0x02A10] = true,
3890 [0x02A11] = true,
3891 [0x02A12] = true,
3892 [0x02A13] = true,
3893 [0x02A14] = true,
3894 [0x02A15] = true,
3895 [0x02A16] = true,
3896 [0x02A17] = true,
3897 [0x02A18] = true,
3898 [0x02A19] = true,
3899 [0x02A1A] = true,
3900 [0x02A1B] = true,
3901 [0x02A1C] = true,
3902 [0x02A20] = true,
3903
3904 [0x02A74] = true,
3905 [0x02AA3] = true,
3906 [0x02AE2] = true,
3907 [0x02AE6] = true,
3908 [0x1D715] = true,
3909 }
3910
3911 local new = fonts.helpers.newprivateslot
3912
3913 local function add(target,original,characters,unicode,what)
3914 local data = characters[unicode]
3915 if data then
3916 if not data.mirror then
3917 local slot = new("mirror."..unicode)
3918 local mirror = copytable(data)
3919 data.mirror = slot
3920 mirror.rorrim = unicode
3921 mirror.commands = {
3922 { "offset", data.width, 0, unicode, -1, 1 }
3923 }
3924 if trace_tweaking then
3925 report_tweak("adding mirror %U (%s)",target,original,unicode,what)
3926 end
3927 characters[slot] = mirror
3928 elseif trace_tweaking then
3929 report_tweak("skipping mirror %U (%s)",target,original,unicode,what)
3930 end
3931 local parts = data.parts
3932 if parts then
3933 for i=1,#parts do
3934 add(target,original,characters,parts[i],"hpart")
3935 end
3936 end
3937 local smaller = data.smaller
3938 if smaller then
3939 add(target,original,characters,"smaller")
3940 end
3941 local next = data.next
3942 if next then
3943 if next == unicode then
3944 report_tweak("skipping cyclic %U (%s)",target,original,unicode,"next")
3945 else
3946 add(target,original,characters,next,"next")
3947 end
3948 end
3949 end
3950 end
3951
3952
3953
3954 function mathtweaks.addmirrors(target,original,parameters)
3955 local characters = target.characters
3956
3957 for unicode, detail in next, characters do
3958 local data = chardata[unicode]
3959 if data and data.mirror then
3960 add(target,original,characters,unicode,"mirror")
3961 end
3962 end
3963 for unicode, detail in sortedhash(mirrors) do
3964 if characters[unicode] then
3965 add(target,original,characters,unicode,"character")
3966 elseif trace_tweaking then
3967 report_tweak("ignoring mirror %U (%s)",target,original,unicode,what)
3968 end
3969 end
3970 end
3971
3972end
3973
3974do
3975
3976 local reported = { }
3977
3978 function mathtweaks.version(target,original,parameters)
3979 local metadata = original.shared.rawdata.metadata
3980 if metadata then
3981 local version = string.strip(metadata.version or "")
3982 if version then
3983 local expected = parameters.expected
3984 local fontname = metadata.fontname or false
3985 local message = parameters.message
3986
3987 if version ~= expected and not reported[fontname] then
3988 report_tweak("version %a found, version %a expected",target,original,version,expected)
3989 elseif trace_tweaking then
3990 report_tweak("version %a found",target,original,version)
3991 end
3992 if message and message ~= "" and not reported[fontname] then
3993 report_tweak()
3994 report_tweak("%s",target,original,message)
3995 report_tweak()
3996 end
3997 reported[fontname] = true
3998 target.tweakversion = version
3999 end
4000 end
4001 end
4002
4003end
4004
4005do
4006
4007 function mathtweaks.parameters(target,original,parameters)
4008 local newparameters = parameters.list
4009 local oldparameters = target.mathparameters
4010 if newparameters and oldparameters then
4011 newparameters = copytable(newparameters)
4012 scaleparameters(newparameters,target.parameters)
4013 for name, newvalue in next, newparameters do
4014 oldparameters[name] = newvalue
4015 end
4016 end
4017 end
4018
4019 function mathtweaks.bigslots(target,original,parameters)
4020 local list = parameters.list
4021 if list then
4022 target.bigslots = list
4023 end
4024 end
4025
4026end
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042do
4043
4044 function mathtweaks.setoptions(target,original,parameters)
4045 local setlist = parameters.set or parameters.list
4046 local resetlist = parameters.reset
4047 if setlist or resetlist then
4048 local properties = target.properties
4049 local codes = tex.mathcontrolcodes
4050 local oldcontrol = texget("mathfontcontrol")
4051 local newcontrol = oldcontrol
4052
4053 if resetlist then
4054 for i=1,#resetlist do
4055 local v = tonumber(codes[resetlist[i]])
4056 if v then
4057 newcontrol = newcontrol & (not v)
4058 end
4059 end
4060 end
4061 if setlist then
4062 for i=1,#setlist do
4063 local v = tonumber(codes[setlist[i]])
4064 if v then
4065 newcontrol = newcontrol | v
4066 end
4067 end
4068 end
4069 newcontrol = newcontrol | codes.usefontcontrol
4070 properties.mathcontrol = newcontrol
4071 target.mathcontrol = newcontrol
4072 if trace_tweaking then
4073 report_tweak("forcing math font options 0x%08X instead of 0x%08X",target,original,newcontrol,oldcontrol)
4074 end
4075 end
4076 end
4077
4078end
4079
4080do
4081
4082 function mathtweaks.setovershoots(target,original,parameters)
4083 local list = parameters.list
4084 if list then
4085 local characters = target.characters
4086 local emwidth = target.parameters.quad
4087 local done = false
4088 for i=1,#list do
4089 local entry = list[i]
4090 local target = entry.target
4091 local top = entry.topovershoot
4092 local quad = entry.quad
4093 if target and top then
4094 local range = blocks[target]
4095 if range then
4096 if quad then
4097 quad = emwidth
4098 end
4099 for r = range.first, range.last do
4100 local unicode = mathgaps[r] or r
4101 local data = characters[unicode]
4102 if data then
4103 data.topovershoot = top * (quad or data.width or 0)
4104 done = registerdone(done,r)
4105 end
4106 end
4107 end
4108 end
4109 end
4110 feedback_tweak("setovershoots",target,original,done)
4111 end
4112 end
4113
4114
4115
4116 local efindex = 0
4117 local effects = setmetatableindex (function (t,k)
4118 efindex = efindex + 1
4119 local v = "tweakreplacealphabets" .. efindex
4120 local e = fonts.specifiers.presetcontext(v,"",k)
4121
4122 t[k] = v
4123 return v
4124 end)
4125
4126 function mathtweaks.replacealphabets(target,original,parameters)
4127 local list = parameters.list
4128 if list then
4129 local features = target.specification.features.normal
4130 local definedfont = fonts.definers.internal
4131 local copiedglyph = fonts.handlers.vf.math.copy_glyph
4132
4133 local getsubstitution = fonts.handlers.otf.getsubstitution
4134 local fontdata = fonts.hashes.identifiers
4135
4136 local fonts = target.fonts
4137 local size = target.size
4138 local characters = target.characters
4139 if not fonts then
4140 fonts = { }
4141 target.fonts = fonts
4142 end
4143 if #fonts == 0 then
4144 fonts[1] = { id = 0, size = size }
4145 end
4146 for i=1,#list do
4147 local entry = list[i]
4148 local filename = entry.filename or parameters.filename
4149 local feature = entry.feature
4150 local thesource = entry.source
4151 local thetarget = entry.target or thesource
4152 local theunicode = entry.unicode
4153 local unicodes = entry.unicodes
4154 local keep = (entry.keep == true) or (parameters.keep == true)
4155 if (thesource and thetarget) or unicodes then
4156 local function getrange(t)
4157 return (type(t) == "table" and t) or (type(t) == "string" and blocks[t])
4158 end
4159 local sourcerange = thesource and getrange(thesource)
4160 local targetrange = thetarget and getrange(thetarget)
4161 local unicoderange = theunicode and getrange(theunicode)
4162 local firsttarget = targetrange and targetrange.first
4163 local firstsource = sourcerange and sourcerange.first
4164 local lastsource = sourcerange and sourcerange.last or firstsource
4165 if (firstsource and firsttarget) or unicodes then
4166 local offset = unicodes and 0 or (firsttarget - firstsource)
4167 if filename then
4168 local rscale = entry.rscale or 1
4169 size = size * rscale
4170
4171 local fullname = filename
4172 local effect = features.effect
4173 if effect then
4174 fullname = fullname .. "*" .. effects["effect={"..effect.."}"]
4175 end
4176 local id = definedfont {
4177 name = fullname,
4178 size = size,
4179 }
4180 if id <= 0 then
4181 report_math("font %a doesn't exist",fullname)
4182 else
4183 local chars = fontchars[id]
4184 local dropin = fontdata[id]
4185 local index = false
4186 for i=1,#fonts do
4187 local f = fonts[i]
4188 if f.id == id and f.size == size then
4189 index = i
4190 break
4191 end
4192 end
4193 if not index then
4194 index = #fonts + 1
4195 fonts[index] = { id = id, size = size }
4196 end
4197
4198 local function use(sourceunicode,targetunicode)
4199 if keep and characters[targetunicode] then
4200
4201 else
4202 if feature then
4203 sourceunicode = getsubstitution(dropin,sourceunicode,feature,true,"math","dflt") or sourceunicode
4204 end
4205 characters[targetunicode] = copiedglyph(target,characters,chars,sourceunicode,index)
4206 end
4207 end
4208 if unicodes then
4209 for i=1,#unicodes do
4210 local unicode = unicodes[i]
4211 if chars[unicode] then
4212 use(unicode,unicode)
4213 end
4214 end
4215 else
4216 for s=firstsource,lastsource do
4217 local t = s + offset
4218 local sourceunicode = mathgaps[s] or s
4219 if chars[sourceunicode] then
4220 use(sourceunicode,mathgaps[t] or t)
4221 end
4222 end
4223 end
4224
4225 local inherit = entry.inherit
4226 if inherit then
4227 local mathparameters = target.mathparameters
4228 local dropparameters = fontdata[id].mathparameters
4229 if dropparameters then
4230 for name in sortedhash(inherit) do
4231 local value = dropparameters[name]
4232 if value then
4233 mathparameters[name] = value
4234 end
4235 end
4236 end
4237 end
4238 end
4239 elseif feature then
4240 local function use(sourceunicode,targetunicode)
4241 local variant = getsubstitution(original,sourceunicode,feature,true,"math","dflt")
4242 local data = characters[variant]
4243 if data then
4244 characters[targetunicode] = copytable(data)
4245 end
4246 end
4247 if unicodes then
4248 for i=1,#unicodes do
4249 local unicode = unicodes[i]
4250 use(unicode,unicode)
4251 end
4252 else
4253 for s=firstsource,lastsource do
4254 local t = s + offset
4255 use(mathgaps[s] or s,mathgaps[t] or t)
4256 end
4257 end
4258 else
4259
4260 if unicodes then
4261
4262 else
4263 local firstcode = unicoderange and unicoderange.first
4264 local descriptions = original.descriptions
4265 for s=firstsource,lastsource do
4266 local t = s + offset
4267 local sourceunicode = mathgaps[s] or s
4268 local targetunicode = mathgaps[t] or t
4269 if sourceunicode ~= targetunicode then
4270 local data = characters[sourceunicode]
4271 if data then
4272 if firstcode then
4273 local d = descriptions[sourceunicode]
4274 if d then
4275 d.unicode = firstcode
4276 end
4277 data.unicode = firstcode
4278 firstcode = firstcode + 1
4279 end
4280 characters[targetunicode] = copytable(data)
4281 end
4282 end
4283 end
4284 end
4285 end
4286 end
4287 end
4288 end
4289 end
4290 end
4291
4292 function mathtweaks.fallbacks(target,original,parameters)
4293 local fallbacks = target.specification.fallbacks
4294 if fallbacks then
4295 local definitions = fonts.collections.definitions[fallbacks]
4296 if definitions then
4297 local list = { }
4298 for i=1,#definitions do
4299 local definition = definitions[i]
4300
4301
4302 local first = definition.start
4303 local last = definition.stop
4304 local offset = definition.offset or first
4305 list[#list+1] = {
4306 filename = definition.font,
4307 rscale = definition.rscale or 1,
4308 source = { first = first, last = last },
4309 target = { first = offset, last = offset + (last - first) },
4310 }
4311 end
4312 mathtweaks.replacealphabets(target,original,{
4313 tweak = "replacealphabets",
4314 list = list,
4315 } )
4316 end
4317 end
4318 end
4319
4320end
4321
4322local apply_tweaks = true directives.register("math.applytweaks", function(v) apply_tweaks = v end)
4323local applied_tweaks = 0
4324
4325local function tweaklist(target,original,tweaks)
4326 if type(tweaks) == "table" then
4327 for i=1,#tweaks do
4328 local tweak = tweaks[i]
4329 if type(tweak) == "table" then
4330 local action = mathtweaks[tweak.tweak or ""]
4331 if action then
4332 local feature = tweak.feature
4333 local features = target.specification.features.normal
4334 if feature == nil or features[feature] then
4335 local version = tweak.version
4336 if version and version ~= target.tweakversion then
4337 report_math("skipping tweak %a version %a",tweak.tweak,version)
4338 elseif original then
4339 action(target,original,tweak)
4340 else
4341 action(target,tweak)
4342 end
4343 end
4344 end
4345 end
4346 end
4347 end
4348end
4349
4350function mathtweaks.tweaks(target,original,parameters)
4351 tweaklist(target,original,parameters.list)
4352end
4353
4354local function applytweaks(when,target,original)
4355 if apply_tweaks then
4356 local goodies = original.goodies
4357 if goodies then
4358 local tweaked = target.tweaked or { }
4359 if tweaked[when] then
4360 if trace_defining then
4361 report_math("tweaking math of %a @ %p (%s: %s)",target.properties.fullname,target.parameters.size,when,"done")
4362 end
4363 else
4364 for i=1,#goodies do
4365 local goodie = goodies[i]
4366 local mathematics = goodie.mathematics
4367 local tweaks = mathematics and mathematics.tweaks
4368 if type(tweaks) == "table" then
4369 statistics.starttiming(mathtweaks)
4370 applied_tweaks = applied_tweaks + 1
4371 tweaks = tweaks[when]
4372 if trace_defining then
4373 report_math("tweaking math of %a @ %p (%s: %s)",target.properties.fullname,target.parameters.size,when,"okay")
4374 end
4375 tweaklist(target,original,tweaks)
4376 end
4377 statistics.stoptiming(mathtweaks)
4378 end
4379 tweaked[when] = true
4380 target.tweaked = tweaked
4381 end
4382 end
4383 else
4384 report_math("not tweaking math of %a @ %p (%s)",target.properties.fullname,target.parameters.size,when)
4385 end
4386end
4387
4388local function tweakable(target)
4389 local mathparameters = target.mathparameters
4390
4391
4392
4393 return mathparameters
4394end
4395
4396function mathematics.tweakbeforecopyingfont(target,original)
4397 if use_math_goodies and tweakable(target) then
4398 applytweaks("beforecopying",target,original)
4399 end
4400end
4401
4402function mathematics.tweakaftercopyingfont(target,original)
4403 if use_math_goodies and tweakable(target) then
4404 applytweaks("aftercopying",target,original)
4405 target.properties.hasitalics = false
4406 end
4407end
4408
4409statistics.register("math tweaking time",function()
4410 if applied_tweaks > 0 then
4411 return string.format("%s seconds, %s math goodie tables", statistics.elapsedtime(mathtweaks),applied_tweaks)
4412 end
4413end)
4414
4415do
4416
4417 local defaults = {
4418 {
4419 source = "uppercasescript",
4420 target = "uppercasecalligraphic",
4421 },
4422 {
4423 source = "lowercasescript",
4424 target = "lowercasecalligraphic",
4425 },
4426 {
4427 source = "uppercaseboldscript",
4428 target = "uppercaseboldcalligraphic",
4429 },
4430 {
4431 source = "lowercaseboldscript",
4432 target = "lowercaseboldcalligraphic",
4433 },
4434 }
4435
4436 local reported = table.setmetatableindex("table")
4437
4438 function mathematics.checkaftercopyingfont(target,original)
4439 if tweakable(target) then
4440 local chardata = characters.data
4441 local characters = target.characters
4442
4443 for i=1,#defaults do
4444
4445 local default = defaults[i]
4446 local block = blocks[default.target]
4447 local first = block.first
4448 local last = block.last
4449 if not characters[mathgaps[first] or last] then
4450 mathtweaks.replacealphabets(target,original,{
4451 tweak = "replacealphabets",
4452 list = { default }
4453 })
4454 end
4455 end
4456
4457 local addvariant = mathematics.addvariant
4458 local function register(old,new)
4459 for i, cold in next, old do
4460 local cnew = new[i]
4461 addvariant(target,cold,cold,0xFE00)
4462 addvariant(target,cnew,cnew,0xFE01)
4463 addvariant(target,cnew,cold,0xFE00)
4464 addvariant(target,cold,cnew,0xFE01)
4465 end
4466 end
4467 local sr = mathematics.alphabets.sr.tf
4468 local ca = mathematics.alphabets.ca.tf
4469 register(sr.ucletters,ca.ucletters)
4470 register(sr.lcletters,ca.lcletters)
4471
4472 if checkitalics then
4473 local italics = 0
4474 local metadata = original.shared.rawdata.metadata
4475 local fontname = metadata and metadata.fontname or false
4476
4477 for k, v in next, characters do
4478 local italic = v.italic
4479 if italic then
4480 local unicode = v.unicode
4481 if unicode and not reported[fontname][unicode] then
4482 local data = chardata[unicode]
4483 local description = data.description or ""
4484 local category = data.category or "--"
4485 report_tweak("italics: %C %p %s %s",target,original,k,italic,category,description)
4486 reported[fontname][unicode] = true
4487 end
4488 italics = italics + 1
4489 end
4490 end
4491 if italics > 0 then
4492 report_tweak("still has %i italics",target,original,italics)
4493 goto NEXTSTEP
4494 end
4495 end
4496 ::NEXTSTEP::
4497
4498 end
4499 end
4500
4501end
4502
4503function mathematics.beforepassingfonttotex(target)
4504 if tweakable(target) then
4505 applytweaks("beforepassing",target,target)
4506 end
4507end
4508
4509sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
4510sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
4511
4512
4513
4514sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
4515sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
4516sequencers.appendaction("aftercopyingcharacters", "system","mathematics.checkaftercopyingfont")
4517sequencers.appendaction("beforepassingfonttotex", "system","mathematics.beforepassingfonttotex")
4518
4519
4520
4521
4522
4523
4524
4525local e_left = extensibles.left
4526local e_right = extensibles.right
4527local e_horizontal = extensibles.horizontal
4528local e_mixed = extensibles.mixed
4529local e_unknown = extensibles.unknown
4530
4531local unknown = { e_unknown, false, false }
4532
4533
4534
4535local function extensiblecode(font,unicode)
4536 local characters = fontcharacters[font]
4537 local character = characters[unicode]
4538 if not character then
4539 return unknown
4540 end
4541 local first = character.next
4542 local code = unicode
4543 local next = first
4544 while next do
4545 code = next
4546 character = characters[next]
4547 next = character.next
4548 end
4549 local char = chardata[unicode]
4550 if not char then
4551 return unknown
4552 end
4553 if character.parts then
4554 local m = char.mathextensible
4555 local e = m and extensibles[m]
4556 return e and { e, code, character } or unknown
4557 elseif first then
4558
4559 local m = char.mathextensible or char.mathstretch
4560 local e = m and extensibles[m]
4561 return e and { e, code, character } or unknown
4562 else
4563 return unknown
4564 end
4565end
4566
4567setmetatableindex(extensibles,function(extensibles,font)
4568 local codes = { }
4569 setmetatableindex(codes, function(codes,unicode)
4570 local status = extensiblecode(font,unicode)
4571 codes[unicode] = status
4572 return status
4573 end)
4574 extensibles[font] = codes
4575 return codes
4576end)
4577
4578local function extensiblecode(family,unicode)
4579 return extensibles[getfontoffamily(family or 0)][unicode][1]
4580end
4581
4582
4583
4584
4585
4586
4587
4588local function horizontalcode(family,unicode)
4589 local font = getfontoffamily(family or 0)
4590 local data = extensibles[font][unicode]
4591 local kind = data[1]
4592 local loffset = 0
4593 local roffset = 0
4594 if kind == e_left then
4595 local charlist = data[3].parts
4596 if charlist then
4597 local left = charlist[1]
4598 loffset = abs((left["start"] or 0) - (left["end"] or 0))
4599 end
4600 elseif kind == e_right then
4601 local charlist = data[3].parts
4602 if charlist then
4603 local right = charlist[#charlist]
4604 roffset = abs((right["start"] or 0) - (right["end"] or 0))
4605 end
4606 elseif kind == e_horizontal then
4607 local charlist = data[3].parts
4608 if charlist then
4609 local left = charlist[1]
4610 local right = charlist[#charlist]
4611 loffset = abs((left ["start"] or 0) - (left ["end"] or 0))
4612 roffset = abs((right["start"] or 0) - (right["end"] or 0))
4613 end
4614 end
4615 return kind, loffset, roffset
4616end
4617
4618mathematics.extensiblecode = extensiblecode
4619mathematics.horizontalcode = horizontalcode
4620
4621interfaces.implement {
4622 name = "extensiblecode",
4623 arguments = "2 integers",
4624 actions = { extensiblecode, context }
4625}
4626
4627interfaces.implement {
4628 name = "horizontalcode",
4629 arguments = "2 integers",
4630 actions = function(family,unicode)
4631 local kind, loffset, roffset = horizontalcode(family,unicode)
4632 texsetdimen(d_scratchleftoffset, loffset)
4633 texsetdimen(d_scratchrightoffset,roffset)
4634 context(kind)
4635 end
4636}
4637
4638function mathematics.variantcode(unicode,variant)
4639 local data = fontcharacters[getfontoffamily(texget("fam"))]
4640 local char = data and data[unicode]
4641 if char then
4642 for i=1,variant do
4643 local next = char.next
4644 if next then
4645 unicode = next
4646 char = data[next]
4647 else
4648 break
4649 end
4650 end
4651 end
4652 return unicode
4653end
4654
4655function mathematics.variantcount(unicode)
4656 local data = fontcharacters[getfontoffamily(texget("fam"))]
4657 local char = data and data[unicode]
4658 local count = 0
4659 if char then
4660 while true do
4661 local next = char.next
4662 if next then
4663 count = count + 1
4664 char = data[next]
4665 else
4666 break
4667 end
4668 end
4669 end
4670 return count
4671end
4672
4673interfaces.implement {
4674 name = "mathvariantcode",
4675 public = true,
4676 arguments = "2 integers",
4677 actions = { mathematics.variantcode, context },
4678}
4679
4680interfaces.implement {
4681 name = "mathvariantcount",
4682 public = true,
4683 arguments = "integer",
4684 actions = { mathematics.variantcount, context },
4685}
4686 |