1if not modules then modules = { } end modules ['math-noa'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to math-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27local next, tonumber = next, tonumber
28local utfchar, utfbyte = utf.char, utf.byte
29local formatters, gmatch = string.formatters, string.gmatch
30local sortedhash = table.sortedhash
31local insert, remove = table.insert, table.remove
32local div, round = math.div, math.round
33local bor, band = bit32.bor, bit32.band
34
35local fonts = fonts
36local nodes = nodes
37local node = node
38local mathematics = mathematics
39local context = context
40
41local otf = fonts.handlers.otf
42local otffeatures = fonts.constructors.features.otf
43local registerotffeature = otffeatures.register
44
45local privateattribute = attributes.private
46local registertracker = trackers.register
47local registerdirective = directives.register
48local logreporter = logs.reporter
49local setmetatableindex = table.setmetatableindex
50
51local colortracers = nodes.tracers.colors
52
53local trace_remapping = false registertracker("math.remapping", function(v) trace_remapping = v end)
54local trace_processing = false registertracker("math.processing", function(v) trace_processing = v end)
55local trace_analyzing = false registertracker("math.analyzing", function(v) trace_analyzing = v end)
56local trace_normalizing = false registertracker("math.normalizing", function(v) trace_normalizing = v end)
57local trace_collapsing = false registertracker("math.collapsing", function(v) trace_collapsing = v end)
58local trace_fixing = false registertracker("math.fixing", function(v) trace_fixing = v end)
59local trace_patching = false registertracker("math.patching", function(v) trace_patching = v end)
60local trace_goodies = false registertracker("math.goodies", function(v) trace_goodies = v end)
61local trace_variants = false registertracker("math.variants", function(v) trace_variants = v end)
62local trace_alternates = false registertracker("math.alternates", function(v) trace_alternates = v end)
63local trace_italics = false registertracker("math.italics", function(v) trace_italics = v end)
64local trace_kernpairs = false registertracker("math.kernpairs", function(v) trace_kernpairs = v end)
65local trace_domains = false registertracker("math.domains", function(v) trace_domains = v end)
66local trace_families = false registertracker("math.families", function(v) trace_families = v end)
67local trace_fences = false registertracker("math.fences", function(v) trace_fences = v end)
68local trace_unstacking = false registertracker("math.unstack", function(v) trace_unstacking = v end)
69
70local check_coverage = true registerdirective("math.checkcoverage", function(v) check_coverage = v end)
71
72local report_processing = logreporter("mathematics","processing")
73local report_remapping = logreporter("mathematics","remapping")
74local report_normalizing = logreporter("mathematics","normalizing")
75local report_collapsing = logreporter("mathematics","collapsing")
76local report_fixing = logreporter("mathematics","fixing")
77local report_patching = logreporter("mathematics","patching")
78local report_goodies = logreporter("mathematics","goodies")
79local report_variants = logreporter("mathematics","variants")
80local report_alternates = logreporter("mathematics","alternates")
81local report_italics = logreporter("mathematics","italics")
82local report_kernpairs = logreporter("mathematics","kernpairs")
83local report_domains = logreporter("mathematics","domains")
84local report_families = logreporter("mathematics","families")
85local report_fences = logreporter("mathematics","fences")
86local report_unstacking = logreporter("mathematics","unstack")
87
88local a_mathrendering = privateattribute("mathrendering")
89local a_exportstatus = privateattribute("exportstatus")
90
91local nuts = nodes.nuts
92local nodepool = nuts.pool
93local tonut = nuts.tonut
94local nutstring = nuts.tostring
95
96local setfield = nuts.setfield
97local setlink = nuts.setlink
98local setlist = nuts.setlist
99local setnext = nuts.setnext
100local setprev = nuts.setprev
101local setchar = nuts.setchar
102local setfam = nuts.setfam
103local setsubtype = nuts.setsubtype
104local setattr = nuts.setattr
105local setattrlist = nuts.setattrlist
106local setwidth = nuts.setwidth
107local setheight = nuts.setheight
108local setdepth = nuts.setdepth
109
110local getfield = nuts.getfield
111local getnext = nuts.getnext
112local getprev = nuts.getprev
113local getboth = nuts.getboth
114local getid = nuts.getid
115local getsubtype = nuts.getsubtype
116local getchar = nuts.getchar
117local getfont = nuts.getfont
118local getfam = nuts.getfam
119local getattr = nuts.getattr
120local getlist = nuts.getlist
121local getwidth = nuts.getwidth
122local getheight = nuts.getheight
123local getdepth = nuts.getdepth
124
125local getnucleus = nuts.getnucleus
126local getsub = nuts.getsub
127local getsup = nuts.getsup
128local getsubpre = nuts.getsubpre
129local getsuppre = nuts.getsuppre
130
131local setnucleus = nuts.setnucleus
132local setsub = nuts.setsub
133local setsup = nuts.setsup
134local setsubpre = nuts.setsubpre
135local setsuppre = nuts.setsuppre
136
137local flushnode = nuts.flush
138local copy_node = nuts.copy
139local slide_nodes = nuts.slide
140local set_visual = nuts.setvisual
141
142local mlisttohlist = nuts.mlisttohlist
143
144local new_kern = nodepool.kern
145local new_submlist = nodepool.submlist
146local new_noad = nodepool.noad
147local new_delimiter = nodepool.delimiter
148local new_fence = nodepool.fence
149
150local fonthashes = fonts.hashes
151local fontdata = fonthashes.identifiers
152local fontcharacters = fonthashes.characters
153local fontitalics = fonthashes.italics
154
155local variables = interfaces.variables
156local texsetattribute = tex.setattribute
157local texgetattribute = tex.getattribute
158local getfontoffamily = tex.getfontoffamily
159local unsetvalue = attributes.unsetvalue
160local implement = interfaces.implement
161
162local v_reset = variables.reset
163
164local chardata = characters.data
165
166noads = noads or { }
167local noads = noads
168
169noads.processors = noads.processors or { }
170local processors = noads.processors
171
172noads.handlers = noads.handlers or { }
173local handlers = noads.handlers
174
175local tasks = nodes.tasks
176local enableaction = tasks.enableaction
177local setaction = tasks.setaction
178
179local nodecodes = nodes.nodecodes
180local noadcodes = nodes.noadcodes
181local fencecodes = nodes.fencecodes
182
183local ordnoad_code = noadcodes.ord
184local opdisplaylimitsnoad_code = noadcodes.opdisplaylimits
185local oplimitsnoad_code = noadcodes.oplimits
186local opnolimitsnoad_code = noadcodes.opnolimits
187local binnoad_code = noadcodes.bin
188local relnoad_code = noadcodes.rel
189local opennoad_code = noadcodes.open
190local closenoad_code = noadcodes.close
191local punctnoad_code = noadcodes.punct
192local innernoad_code = noadcodes.inner
193local undernoad_code = noadcodes.under
194local overnoad_code = noadcodes.over
195local vcenternoad_code = noadcodes.vcenter
196local ordlimitsnoad_code = noadcodes.ordlimits or oplimitsnoad_code
197
198local noad_code = nodecodes.noad
199local accent_code = nodecodes.accent
200local radical_code = nodecodes.radical
201local fraction_code = nodecodes.fraction
202local subbox_code = nodecodes.subbox
203local submlist_code = nodecodes.submlist
204local mathchar_code = nodecodes.mathchar
205local mathtextchar_code = nodecodes.mathtextchar
206local delimiter_code = nodecodes.delimiter
207
208
209local math_choice = nodecodes.choice
210local fence_code = nodecodes.fence
211
212local leftfence_code = fencecodes.left
213local middlefence_code = fencecodes.middle
214local rightfence_code = fencecodes.right
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229local function process(start,what,n,parent)
230
231 if n then
232 n = n + 1
233 else
234 n = 0
235 end
236
237 local initial = start
238
239 slide_nodes(start)
240
241 while start do
242 local id = getid(start)
243 if trace_processing then
244 if id == noad_code then
245 report_processing("%w%S, class %a",n*2,nutstring(start),noadcodes[getsubtype(start)])
246 elseif id == mathchar_code then
247 local char = getchar(start)
248 local font = getfont(start)
249 local fam = getfam(start)
250 report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,nutstring(start),fam,font,char,char)
251 else
252 report_processing("%w%S",n*2,nutstring(start))
253 end
254 end
255 local proc = what[id]
256 if proc then
257
258 local done, newstart, newinitial = proc(start,what,n,parent)
259 if newinitial then
260 initial = newinitial
261 if newstart then
262 start = newstart
263
264 else
265
266 break
267 end
268 else
269 if newstart then
270 start = newstart
271
272 else
273
274 end
275 end
276 elseif id == noad_code then
277
278 local noad = getnucleus(start) if noad then process(noad,what,n,start) end
279 noad = getsup (start) if noad then process(noad,what,n,start) end
280 noad = getsub (start) if noad then process(noad,what,n,start) end
281 if getsubpre then
282 noad = getsuppre (start) if noad then process(noad,what,n,start) end
283 noad = getsubpre (start) if noad then process(noad,what,n,start) end
284 end
285 elseif id == mathchar_code or id == mathtextchar_code or id == delimiter_code then
286 break
287 elseif id == subbox_code or id == submlist_code then
288 local noad = getlist(start) if noad then process(noad,what,n,start) end
289 elseif id == fraction_code then
290 local noad = getfield(start,"num") if noad then process(noad,what,n,start) end
291 noad = getfield(start,"denom") if noad then process(noad,what,n,start) end
292 noad = getfield(start,"left") if noad then process(noad,what,n,start) end
293 noad = getfield(start,"right") if noad then process(noad,what,n,start) end
294 elseif id == math_choice then
295 local noad = getfield(start,"display") if noad then process(noad,what,n,start) end
296 noad = getfield(start,"text") if noad then process(noad,what,n,start) end
297 noad = getfield(start,"script") if noad then process(noad,what,n,start) end
298 noad = getfield(start,"scriptscript") if noad then process(noad,what,n,start) end
299 elseif id == fence_code then
300 local noad = getfield(start,"delim") if noad then process(noad,what,n,start) end
301 elseif id == radical_code then
302 local noad = getnucleus(start) if noad then process(noad,what,n,start) end
303 noad = getsup (start) if noad then process(noad,what,n,start) end
304 noad = getsub (start) if noad then process(noad,what,n,start) end
305 if getsubpre then
306 noad = getsuppre (start) if noad then process(noad,what,n,start) end
307 noad = getsubpre (start) if noad then process(noad,what,n,start) end
308 end
309 noad = getfield(start,"left") if noad then process(noad,what,n,start) end
310 noad = getfield(start,"degree") if noad then process(noad,what,n,start) end
311 elseif id == accent_code then
312 local noad = getnucleus(start) if noad then process(noad,what,n,start) end
313 noad = getsup (start) if noad then process(noad,what,n,start) end
314 noad = getsub (start) if noad then process(noad,what,n,start) end
315 if getsubpre then
316 noad = getsuppre (start) if noad then process(noad,what,n,start) end
317 noad = getsubpre (start) if noad then process(noad,what,n,start) end
318 end
319 noad = getfield(start,"accent") if noad then process(noad,what,n,start) end
320 noad = getfield(start,"bot_accent") if noad then process(noad,what,n,start) end
321
322
323
324
325
326
327 end
328 start = getnext(start)
329 end
330 if not parent then
331 return initial
332 end
333end
334
335local function processnested(current,what,n)
336 local noad = nil
337 local id = getid(current)
338 if id == noad_code then
339 noad = getnucleus(current) if noad then process(noad,what,n,current) end
340 noad = getsup (current) if noad then process(noad,what,n,current) end
341 noad = getsub (current) if noad then process(noad,what,n,current) end
342 if getsubpre then
343 noad = getsuppre (current) if noad then process(noad,what,n,current) end
344 noad = getsubpre (current) if noad then process(noad,what,n,current) end
345 end
346 elseif id == subbox_code or id == submlist_code then
347 noad = getlist(current) if noad then process(noad,what,n,current) end
348 elseif id == fraction_code then
349 noad = getfield(current,"num") if noad then process(noad,what,n,current) end
350 noad = getfield(current,"denom") if noad then process(noad,what,n,current) end
351 noad = getfield(current,"left") if noad then process(noad,what,n,current) end
352 noad = getfield(current,"right") if noad then process(noad,what,n,current) end
353 elseif id == math_choice then
354 noad = getfield(current,"display") if noad then process(noad,what,n,current) end
355 noad = getfield(current,"text") if noad then process(noad,what,n,current) end
356 noad = getfield(current,"script") if noad then process(noad,what,n,current) end
357 noad = getfield(current,"scriptscript") if noad then process(noad,what,n,current) end
358 elseif id == fence_code then
359 noad = getfield(current,"delim") if noad then process(noad,what,n,current) end
360 elseif id == radical_code then
361 noad = getnucleus(current) if noad then process(noad,what,n,current) end
362 noad = getsup (current) if noad then process(noad,what,n,current) end
363 noad = getsub (current) if noad then process(noad,what,n,current) end
364 if getsubpre then
365 noad = getsuppre (current) if noad then process(noad,what,n,current) end
366 noad = getsubpre (current) if noad then process(noad,what,n,current) end
367 end
368 noad = getfield(current,"left") if noad then process(noad,what,n,current) end
369 noad = getfield(current,"degree") if noad then process(noad,what,n,current) end
370 elseif id == accent_code then
371 noad = getnucleus(current) if noad then process(noad,what,n,current) end
372 noad = getsup (current) if noad then process(noad,what,n,current) end
373 noad = getsub (current) if noad then process(noad,what,n,current) end
374 if getsubpre then
375 noad = getsuppre (current) if noad then process(noad,what,n,current) end
376 noad = getsubpre (current) if noad then process(noad,what,n,current) end
377 end
378 noad = getfield(current,"accent") if noad then process(noad,what,n,current) end
379 noad = getfield(current,"bot_accent") if noad then process(noad,what,n,current) end
380 end
381end
382
383local function processstep(current,process,n,id)
384 local noad = nil
385 local id = id or getid(current)
386 if id == noad_code then
387 noad = getnucleus(current) if noad then process(noad,n,current) end
388 noad = getsup (current) if noad then process(noad,n,current) end
389 noad = getsub (current) if noad then process(noad,n,current) end
390 if getsubpre then
391 noad = getsuppre (current) if noad then process(noad,n,current) end
392 noad = getsubpre (current) if noad then process(noad,n,current) end
393 end
394 elseif id == subbox_code or id == submlist_code then
395 noad = getlist(current) if noad then process(noad,n,current) end
396 elseif id == fraction_code then
397 noad = getfield(current,"num") if noad then process(noad,n,current) end
398 noad = getfield(current,"denom") if noad then process(noad,n,current) end
399 noad = getfield(current,"left") if noad then process(noad,n,current) end
400 noad = getfield(current,"right") if noad then process(noad,n,current) end
401 elseif id == math_choice then
402 noad = getfield(current,"display") if noad then process(noad,n,current) end
403 noad = getfield(current,"text") if noad then process(noad,n,current) end
404 noad = getfield(current,"script") if noad then process(noad,n,current) end
405 noad = getfield(current,"scriptscript") if noad then process(noad,n,current) end
406 elseif id == fence_code then
407 noad = getfield(current,"delim") if noad then process(noad,n,current) end
408 elseif id == radical_code then
409 noad = getnucleus(current) if noad then process(noad,n,current) end
410 noad = getsup (current) if noad then process(noad,n,current) end
411 noad = getsub (current) if noad then process(noad,n,current) end
412 if getsubpre then
413 noad = getsuppre (current) if noad then process(noad,n,current) end
414 noad = getsubpre (current) if noad then process(noad,n,current) end
415 end
416 noad = getfield(current,"left") if noad then process(noad,n,current) end
417 noad = getfield(current,"degree") if noad then process(noad,n,current) end
418 elseif id == accent_code then
419 noad = getnucleus(current) if noad then process(noad,n,current) end
420 noad = getsup (current) if noad then process(noad,n,current) end
421 noad = getsub (current) if noad then process(noad,n,current) end
422 if getsubpre then
423 noad = getsuppre (current) if noad then process(noad,n,current) end
424 noad = getsubpre (current) if noad then process(noad,n,current) end
425 end
426 noad = getfield(current,"accent") if noad then process(noad,n,current) end
427 noad = getfield(current,"bot_accent") if noad then process(noad,n,current) end
428 end
429end
430
431local function processnoads(head,actions,banner)
432 if trace_processing then
433 report_processing("start %a",banner)
434 head = process(head,actions)
435 report_processing("stop %a",banner)
436 else
437 head = process(head,actions)
438 end
439 return head
440end
441
442noads.process = processnoads
443noads.processnested = processnested
444noads.processouter = process
445
446
447
448local unknowns = { }
449local checked = { }
450local tracked = false trackers.register("fonts.missing", function(v) tracked = v end)
451local cached = setmetatableindex("table")
452
453local function errorchar(font,char)
454 local done = unknowns[char]
455 if done then
456 unknowns[char] = done + 1
457 else
458 unknowns[char] = 1
459 end
460 if tracked then
461
462
463 local fake = cached[font][char]
464 if fake then
465 return fake
466 else
467 local kind, fake = fonts.checkers.placeholder(font,char)
468 if not fake or kind ~= "char" then
469 fake = 0x3F
470 end
471 cached[font][char] = fake
472 return fake
473 end
474 else
475
476
477
478 if not checked[char] then
479 if trace_normalizing then
480 report_normalizing("character %C is not available",char)
481 end
482 checked[char] = true
483 end
484 return 0x3F
485 end
486end
487
488
489
490
491
492
493
494
495do
496
497 local families = { }
498 local a_mathfamily = privateattribute("mathfamily")
499 local boldmap = mathematics.boldmap
500
501 local familymap = { [0] =
502 "regular",
503 "regular",
504 "regular",
505 "bold",
506 "bold",
507 "bold",
508 "pseudobold",
509 "pseudobold",
510 "pseudobold",
511 }
512
513 families[fraction_code] = function(pointer,what,n,parent)
514 local a = getattr(pointer,a_mathfamily)
515 if a and a >= 0 then
516 if a > 0 then
517 setattr(pointer,a_mathfamily,0)
518 if a > 5 then
519 a = a - 3
520 end
521 end
522 setfam(pointer,a)
523 end
524 processnested(pointer,families,n+1)
525 end
526
527 families[noad_code] = function(pointer,what,n,parent)
528 local a = getattr(pointer,a_mathfamily)
529 if a and a >= 0 then
530 if a > 0 then
531 setattr(pointer,a_mathfamily,0)
532 if a > 5 then
533 a = a - 3
534 end
535 end
536 setfam(pointer,a)
537 end
538 processnested(pointer,families,n+1)
539 end
540
541 families[mathchar_code] = function(pointer)
542 if getfam(pointer) == 0 then
543 local a = getattr(pointer,a_mathfamily)
544 if a and a > 0 then
545 setattr(pointer,a_mathfamily,0)
546 if a > 5 then
547 local char = getchar(pointer)
548 local bold = boldmap[char]
549 local newa = a - 3
550 if not bold then
551 if trace_families then
552 report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
553 end
554 setfam(pointer,newa)
555 elseif not fontcharacters[getfontoffamily(newa)][bold] then
556 if trace_families then
557 report_families("no bold character for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
558 end
559 if newa > 3 then
560 setfam(pointer,newa-3)
561 end
562 else
563 setattr(pointer,a_exportstatus,char)
564 setchar(pointer,bold)
565 if trace_families then
566 report_families("replacing %C by bold %C, family %s with remap %s becomes %s with remap %s",char,bold,a,familymap[a],newa,familymap[newa])
567 end
568 setfam(pointer,newa)
569 end
570 else
571 local char = getchar(pointer)
572 if not fontcharacters[getfontoffamily(a)][char] then
573 if trace_families then
574 report_families("no bold replacement for %C",char)
575 end
576 else
577 if trace_families then
578 report_families("family of %C becomes %s with remap %s",char,a,familymap[a])
579 end
580 setfam(pointer,a)
581 end
582 end
583 end
584 end
585 end
586 families[delimiter_code] = function(pointer)
587 if getfield(pointer,"small_fam") == 0 then
588 local a = getattr(pointer,a_mathfamily)
589 if a and a > 0 then
590 setattr(pointer,a_mathfamily,0)
591 if a > 5 then
592
593 a = a - 3
594 end
595 local char = getfield(pointer,"small_char")
596 local okay = fontcharacters[getfontoffamily(a)][char]
597 if okay then
598 setfield(pointer,"small_fam",a)
599 elseif a > 2 then
600 setfield(pointer,"small_fam",a-3)
601 end
602 local char = getfield(pointer,"large_char")
603 local okay = fontcharacters[getfontoffamily(a)][char]
604 if okay then
605 setfield(pointer,"large_fam",a)
606 elseif a > 2 then
607 setfield(pointer,"large_fam",a-3)
608 end
609 else
610 setfield(pointer,"small_fam",0)
611 setfield(pointer,"large_fam",0)
612 end
613 end
614 end
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640 families[mathtextchar_code] = families[mathchar_code]
641
642 function handlers.families(head,style,penalties)
643 processnoads(head,families,"families")
644 return true
645 end
646
647end
648
649
650
651do
652
653 local a_mathalphabet = privateattribute("mathalphabet")
654 local a_mathgreek = privateattribute("mathgreek")
655
656 local relocate = { }
657
658 local remapalphabets = mathematics.remapalphabets
659 local fallbackstyleattr = mathematics.fallbackstyleattr
660 local setnodecolor = colortracers.set
661
662 local function report_remap(tag,id,old,new,extra)
663 report_remapping("remapping %s in font (%s,%s) from %C to %C%s",
664 tag,id,fontdata[id].properties.fontname or "",old,new,extra)
665 end
666
667 local function checked(pointer)
668 local char = getchar(pointer)
669 local font = getfont(pointer)
670 local data = fontcharacters[font]
671 if not data[char] then
672 local specials = characters.data[char].specials
673 if specials and (specials[1] == "char" or specials[1] == "font") then
674 local newchar = specials[#specials]
675 if trace_remapping then
676 report_remap("fallback",font,char,newchar)
677 end
678 if trace_analyzing then
679 setnodecolor(pointer,"font:isol")
680 end
681 setattr(pointer,a_exportstatus,char)
682 setchar(pointer,newchar)
683 return true
684 end
685 end
686 end
687
688 relocate[mathchar_code] = function(pointer)
689 local g = getattr(pointer,a_mathgreek) or 0
690 local a = getattr(pointer,a_mathalphabet) or 0
691 local char = getchar(pointer)
692 local font = getfont(pointer)
693 local characters = fontcharacters[font]
694 if a > 0 or g > 0 then
695 if a > 0 then
696 setattr(pointer,a_mathgreek,0)
697 end
698 if g > 0 then
699 setattr(pointer,a_mathalphabet,0)
700 end
701 local newchar = remapalphabets(char,a,g)
702 if newchar then
703 local newchardata = characters[newchar]
704 if newchardata then
705 if trace_remapping then
706 report_remap("char",font,char,newchar,newchardata.commands and " (virtual)" or "")
707 end
708 if trace_analyzing then
709 setnodecolor(pointer,"font:isol")
710 end
711 setchar(pointer,newchar)
712 return true
713 else
714 local fallback = fallbackstyleattr(a)
715 if fallback then
716 local newchar = remapalphabets(char,fallback,g)
717 if newchar then
718 if characters[newchar] then
719 if trace_remapping then
720 report_remap("char",font,char,newchar," (fallback remapping used)")
721 end
722 if trace_analyzing then
723 setnodecolor(pointer,"font:isol")
724 end
725 setchar(pointer,newchar)
726 return true
727 elseif trace_remapping then
728 report_remap("char",font,char,newchar," fails (no fallback character)")
729 end
730 elseif trace_remapping then
731 report_remap("char",font,char,newchar," fails (no fallback remap character)")
732 end
733 elseif trace_remapping then
734 report_remap("char",font,char,newchar," fails (no fallback style)")
735 end
736 end
737 elseif trace_remapping then
738 local chardata = characters[char]
739 if chardata and chardata.commands then
740 report_remap("char",font,char,char," (virtual)")
741 end
742 end
743 end
744 if not characters[char] then
745 setchar(pointer,errorchar(font,char))
746 end
747 if trace_analyzing then
748 setnodecolor(pointer,"font:medi")
749 end
750 if check_coverage then
751 return checked(pointer)
752 end
753 end
754
755 relocate[mathtextchar_code] = function(pointer)
756 if trace_analyzing then
757 setnodecolor(pointer,"font:init")
758 end
759 end
760
761 relocate[delimiter_code] = function(pointer)
762 if trace_analyzing then
763 setnodecolor(pointer,"font:fina")
764 end
765 end
766
767 function handlers.relocate(head,style,penalties)
768 processnoads(head,relocate,"relocate")
769 return true
770 end
771
772end
773
774
775
776do
777
778 local render = { }
779
780 local rendersets = mathematics.renderings.numbers or { }
781
782 render[mathchar_code] = function(pointer)
783 local attr = getattr(pointer,a_mathrendering)
784 if attr and attr > 0 then
785 local char = getchar(pointer)
786 local renderset = rendersets[attr]
787 if renderset then
788 local newchar = renderset[char]
789 if newchar then
790 local font = getfont(pointer)
791 local characters = fontcharacters[font]
792 if characters and characters[newchar] then
793 setchar(pointer,newchar)
794 setattr(pointer,a_exportstatus,char)
795 end
796 end
797 end
798 end
799 end
800
801 function handlers.render(head,style,penalties)
802 processnoads(head,render,"render")
803 return true
804 end
805
806end
807
808
809
810
811
812
813
814
815
816
817
818do
819
820 local a_mathsize = privateattribute("mathsize")
821 local resize = { }
822
823 resize[fence_code] = function(pointer)
824 local subtype = getsubtype(pointer)
825 if subtype == leftfence_code or subtype == rightfence_code then
826 local a = getattr(pointer,a_mathsize)
827 if a and a > 0 then
828 local method = div(a,100)
829 local size = a % 100
830 setattr(pointer,a_mathsize,0)
831 local delimiter = getfield(pointer,"delim")
832 local chr = getchar(delimiter)
833 if chr > 0 then
834 local fam = getfam(delimiter)
835 local id = getfontoffamily(fam)
836 if id > 0 then
837 local data = fontdata[id]
838 local char = mathematics.big(data,chr,size,method)
839 local ht = getheight(pointer)
840 local dp = getdepth(pointer)
841 if ht == 1 or dp == 1 then
842 local chardata = data.characters[char]
843 if ht == 1 then
844 setheight(pointer,chardata.height)
845 end
846 if dp == 1 then
847 setdepth(pointer,chardata.depth)
848 end
849 end
850 if trace_fences then
851 report_fences("replacing %C by %C using method %a and size %a",chr,char,method,size)
852 end
853 setchar(delimiter,char)
854 end
855 end
856 end
857 end
858 end
859
860 function handlers.resize(head,style,penalties)
861 processnoads(head,resize,"resize")
862 return true
863 end
864
865end
866
867
868
869do
870
871 local a_autofence = privateattribute("mathautofence")
872 local autofences = { }
873 local dummyfencechar = 0x2E
874
875 local function makefence(what,char)
876 local d = new_delimiter()
877 local f = new_fence()
878 if char then
879 local sym = getnucleus(char)
880 local chr = getchar(sym)
881 local fam = getfam(sym)
882 if chr == dummyfencechar then
883 chr = 0
884 end
885 setchar(d,chr)
886 setfam(d,fam)
887 flushnode(sym)
888 end
889 setattrlist(d,char)
890 setattrlist(f,char)
891 setsubtype(f,what)
892 setfield(f,"delim",d)
893 setfield(f,"class",-1)
894 return f
895 end
896
897 local function show(where,pointer)
898 print("")
899 local i = 0
900 for n in nuts.traverse(pointer) do
901 i = i + 1
902 print(i,where,nuts.tonode(n))
903 end
904 print("")
905 end
906
907 local function makelist(middle,noad,f_o,o_next,c_prev,f_c)
908
909
910
911
912
913
914
915
916
917 local list = new_submlist()
918 setsubtype(noad,innernoad_code)
919 setnucleus(noad,list)
920 setlist(list,f_o)
921 setlink(f_o,o_next)
922 setlink(c_prev,f_c)
923
924 if middle and next(middle) then
925 local prev = f_o
926 local current = o_next
927 while current ~= f_c do
928 local midl = middle[current]
929 local next = getnext(current)
930 if midl then
931 local fence = makefence(middlefence_code,current)
932 setnucleus(current)
933 flushnode(current)
934 middle[current] = nil
935
936 setlink(prev,fence,next)
937 prev = fence
938 else
939 prev = current
940 end
941 current = next
942 end
943 end
944 return noad
945 end
946
947
948
949 local function convert_both(open,close,middle)
950 local o_next = getnext(open)
951 if o_next == close then
952 return close
953 else
954 local c_prev, c_next = getboth(close)
955 local f_o = makefence(leftfence_code,open)
956 local f_c = makefence(rightfence_code,close)
957 makelist(middle,open,f_o,o_next,c_prev,f_c)
958 setnucleus(close)
959 flushnode(close)
960
961 setlink(open,c_next)
962 return open
963 end
964 end
965
966 local function convert_open(open,last,middle)
967 local f_o = makefence(leftfence_code,open)
968 local f_c = makefence(rightfence_code)
969 local o_next = getnext(open)
970 makelist(middle,open,f_o,o_next,last,nil)
971
972 setlink(open,l_next)
973 return open
974 end
975
976 local function convert_close(first,close,middle)
977 local f_o = makefence(leftfence_code)
978 local f_c = makefence(rightfence_code,close)
979 local c_prev = getprev(close)
980 local f_next = getnext(first)
981 makelist(middle, close, f_o,f_next,c_prev,f_c)
982
983 if c_prev ~= first then
984 setlink(first,close)
985 end
986 return close
987 end
988
989 local stacks = setmetatableindex("table")
990
991
992
993 local function processfences(pointer,n,parent)
994 local current = pointer
995 local last = pointer
996 local start = pointer
997 local done = false
998 local initial = pointer
999 local stack = nil
1000 local middle = nil
1001 while current do
1002
1003 local id = getid(current)
1004 if id == noad_code then
1005 local a = getattr(current,a_autofence)
1006 if a and a > 0 then
1007 local stack = stacks[n]
1008 setattr(current,a_autofence,0)
1009 local level = #stack
1010 if a == 1 then
1011 if trace_fences then
1012 report_fences("%2i: level %i, handling %s, action %s",n,level,"open","open")
1013 end
1014 insert(stack,current)
1015 elseif a == 2 then
1016 local open = remove(stack)
1017 if open then
1018 if trace_fences then
1019 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","both")
1020 end
1021 current = convert_both(open,current,middle)
1022 elseif current == start then
1023 if trace_fences then
1024 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","skip")
1025 end
1026 else
1027 if trace_fences then
1028 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","close")
1029 end
1030 current = convert_close(initial,current,middle)
1031 if not parent then
1032 initial = current
1033 end
1034 end
1035 elseif a == 3 then
1036 if trace_fences then
1037 report_fences("%2i: level %i, handling %s, action %s",n,level,"middle","middle")
1038 end
1039 if middle then
1040 middle[current] = last
1041 else
1042 middle = { [current] = last }
1043 end
1044 elseif a == 4 then
1045 if not stack or #stack == 0 then
1046 if trace_fences then
1047 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","open")
1048 end
1049 insert(stack,current)
1050 else
1051 local open = remove(stack)
1052 if open then
1053 if trace_fences then
1054 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","both")
1055 end
1056 current = convert_both(open,current,middle)
1057 elseif current == start then
1058 if trace_fences then
1059 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","skip")
1060 end
1061 else
1062 if trace_fences then
1063 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","close")
1064 end
1065 current = convert_close(initial,current,middle)
1066 if not parent then
1067 initial = current
1068 end
1069 end
1070 end
1071 end
1072 done = true
1073 else
1074 processstep(current,processfences,n+1,id)
1075 end
1076 else
1077
1078 processstep(current,processfences,n,id)
1079 end
1080
1081 last = current
1082 current = getnext(current)
1083 end
1084 if done then
1085 local stack = stacks[n]
1086 local s = #stack
1087 if s > 0 then
1088 for i=1,s do
1089 local open = remove(stack)
1090 if trace_fences then
1091 report_fences("%2i: level %i, handling %s, action %s",n,#stack,"flush","open")
1092 end
1093 last = convert_open(open,last,middle)
1094 end
1095
1096 end
1097 end
1098 end
1099
1100
1101
1102
1103 local enabled = false
1104
1105 implement {
1106 name = "enableautofences",
1107 onlyonce = true,
1108 actions = function()
1109 enableaction("math","noads.handlers.autofences")
1110 enabled = true
1111 end
1112 }
1113
1114 function handlers.autofences(head,style,penalties)
1115 if enabled then
1116
1117 processfences(head,1)
1118
1119 end
1120 end
1121
1122end
1123
1124
1125
1126do
1127
1128 local unscript = { } noads.processors.unscript = unscript
1129 local superscripts = characters.superscripts
1130 local subscripts = characters.subscripts
1131 local fractions = characters.fractions
1132 local replaced = { }
1133
1134 local function replace(pointer,what,n,parent)
1135 pointer = parent
1136 local next = getnext(pointer)
1137 local start_super, stop_super, start_sub, stop_sub
1138 local mode = "unset"
1139 while next and getid(next) == noad_code do
1140 local nextnucleus = getnucleus(next)
1141 if nextnucleus and getid(nextnucleus) == mathchar_code and not getsub(next) and not getsup(next) then
1142 local char = getchar(nextnucleus)
1143 local s = superscripts[char]
1144 if s then
1145 if not start_super then
1146 start_super = next
1147 mode = "super"
1148 elseif mode == "sub" then
1149 break
1150 end
1151 stop_super = next
1152 next = getnext(next)
1153 setchar(nextnucleus,s)
1154 replaced[char] = (replaced[char] or 0) + 1
1155 if trace_normalizing then
1156 report_normalizing("superscript %C becomes %C",char,s)
1157 end
1158 else
1159 local s = subscripts[char]
1160 if s then
1161 if not start_sub then
1162 start_sub = next
1163 mode = "sub"
1164 elseif mode == "super" then
1165 break
1166 end
1167 stop_sub = next
1168 next = getnext(next)
1169 setchar(nextnucleus,s)
1170 replaced[char] = (replaced[char] or 0) + 1
1171 if trace_normalizing then
1172 report_normalizing("subscript %C becomes %C",char,s)
1173 end
1174 else
1175 break
1176 end
1177 end
1178 else
1179 break
1180 end
1181 end
1182 if start_super then
1183 if start_super == stop_super then
1184 setsup(pointer,getnucleus(start_super))
1185 else
1186 local list = new_submlist()
1187 setlist(list,start_super)
1188 setsup(pointer,list)
1189 end
1190 if mode == "super" then
1191 setnext(pointer,getnext(stop_super))
1192 end
1193 setnext(stop_super)
1194 end
1195 if start_sub then
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208 if start_sub == stop_sub then
1209 setsub(pointer,getnucleus(start_sub))
1210 else
1211 local list = new_submlist()
1212 setlist(list,start_sub)
1213 setsub(pointer,list)
1214 end
1215 if mode == "sub" then
1216 setnext(pointer,getnext(stop_sub))
1217 end
1218 setnext(stop_sub)
1219 end
1220
1221 end
1222
1223 unscript[mathchar_code] = replace
1224
1225 function handlers.unscript(head,style,penalties)
1226 processnoads(head,unscript,"unscript")
1227 return true
1228 end
1229
1230end
1231
1232do
1233
1234 local unstack = { } noads.processors.unstack = unstack
1235 local enabled = false
1236 local a_unstack = privateattribute("mathunstack")
1237
1238 unstack[noad_code] = function(pointer)
1239 if getattr(pointer,a_unstack) then
1240 local sup = getsup(pointer)
1241 local sub = getsub(pointer)
1242 if sup and sub then
1243
1244
1245
1246 local nxt = getnext(pointer)
1247 local new = new_noad(pointer)
1248 setnucleus(new,new_submlist())
1249 setsub(pointer)
1250 setsub(new,sub)
1251 setlink(pointer,new,nxt)
1252 end
1253 end
1254 end
1255
1256 function handlers.unstack(head,style,penalties)
1257 if enabled then
1258 processnoads(head,unstack,"unstack")
1259 return true
1260 end
1261 end
1262
1263 implement {
1264 name = "enablescriptunstacking",
1265 onlyonce = true,
1266 actions = function()
1267 enableaction("math","noads.handlers.unstack")
1268 enabled = true
1269 end
1270 }
1271
1272end
1273
1274do
1275
1276 local function collected(list)
1277 if list and next(list) then
1278 local n, t = 0, { }
1279 for k, v in sortedhash(list) do
1280 n = n + 1
1281 t[n] = formatters["%C"](k)
1282 end
1283 return formatters["% t (n=%s)"](t,n)
1284 end
1285 end
1286
1287 statistics.register("math script replacements", function()
1288 return collected(replaced)
1289 end)
1290
1291 statistics.register("unknown math characters", function()
1292 return collected(unknowns)
1293 end)
1294
1295end
1296
1297
1298
1299
1300
1301
1302
1303
1304do
1305
1306 local last = 0
1307
1308 local known = setmetatableindex(function(t,k)
1309 local v = bor(0,2^last)
1310 t[k] = v
1311 last = last + 1
1312 return v
1313 end)
1314
1315 local defaults = {
1316 dotless = { feature = 'dtls', value = 1, comment = "Mathematical Dotless Forms" },
1317
1318 }
1319
1320 local function initializemathalternates(tfmdata)
1321 local goodies = tfmdata.goodies
1322 local autolist = defaults
1323
1324 local function setthem(newalternates)
1325 local resources = tfmdata.resources
1326 local mathalternates = resources.mathalternates
1327 local alternates, attributes, registered, presets
1328 if mathalternates then
1329 alternates = mathalternates.alternates
1330 attributes = mathalternates.attributes
1331 registered = mathalternates.registered
1332 else
1333 alternates, attributes, registered = { }, { }, { }
1334 mathalternates = {
1335 attributes = attributes,
1336 alternates = alternates,
1337 registered = registered,
1338 presets = { },
1339 resets = { },
1340 hashes = setmetatableindex("table")
1341 }
1342 resources.mathalternates = mathalternates
1343 end
1344
1345 for name, data in sortedhash(newalternates) do
1346 if alternates[name] then
1347
1348 else
1349 local attr = known[name]
1350 attributes[attr] = data
1351 alternates[name] = attr
1352 registered[#registered+1] = attr
1353 end
1354 end
1355 end
1356
1357 if goodies then
1358 local done = { }
1359 for i=1,#goodies do
1360
1361
1362 local mathgoodies = goodies[i].mathematics
1363 local alternates = mathgoodies and mathgoodies.alternates
1364 if alternates then
1365 if trace_goodies then
1366 report_goodies("loading alternates for font %a",tfmdata.properties.name)
1367 end
1368 for k, v in next, autolist do
1369 if not alternates[k] then
1370 alternates[k] = v
1371 end
1372 end
1373 setthem(alternates)
1374 return
1375 end
1376 end
1377 end
1378
1379 if trace_goodies then
1380 report_goodies("loading default alternates for font %a",tfmdata.properties.name)
1381 end
1382 setthem(autolist)
1383
1384 end
1385
1386 registerotffeature {
1387 name = "mathalternates",
1388 description = "additional math alternative shapes",
1389 initializers = {
1390 base = initializemathalternates,
1391 node = initializemathalternates,
1392 }
1393 }
1394
1395
1396
1397
1398
1399 local a_mathalternate = privateattribute("mathalternate")
1400 local alternate = { }
1401 local fontdata = fonts.hashes.identifiers
1402 local fontresources = fonts.hashes.resources
1403
1404 local function getalternate(fam,tag,current)
1405 local resources = fontresources[getfontoffamily(fam)]
1406 local attribute = unsetvalue
1407 if resources then
1408 local mathalternates = resources.mathalternates
1409 if mathalternates then
1410 local presets = mathalternates.presets
1411 if presets then
1412 local resets = mathalternates.resets
1413 attribute = presets[tag]
1414 if not attribute then
1415 attribute = 0
1416 local alternates = mathalternates.alternates
1417 for s in gmatch(tag,"[^, ]+") do
1418 if s == v_reset then
1419 resets[tag] = true
1420 current = unsetvalue
1421 else
1422 local a = alternates[s]
1423 if a then
1424 attribute = bor(attribute,a)
1425 end
1426 end
1427 end
1428 if attribute == 0 then
1429 attribute = unsetvalue
1430 end
1431 presets[tag] = attribute
1432 elseif resets[tag] then
1433 current = unsetvalue
1434 end
1435 end
1436 end
1437 end
1438 if attribute > 0 and current and current > 0 then
1439 return bor(current,attribute)
1440 else
1441 return attribute
1442 end
1443 end
1444
1445 local function presetalternate(fam,tag)
1446 texsetattribute(a_mathalternate,getalternate(fam,tag))
1447 end
1448
1449 implement {
1450 name = "presetmathalternate",
1451 actions = presetalternate,
1452 arguments = { "integer", "string" }
1453 }
1454
1455 local function setalternate(fam,tag)
1456 local a = texgetattribute(a_mathalternate)
1457 local v = getalternate(fam,tag,a)
1458 texsetattribute(a_mathalternate,v)
1459 end
1460
1461 implement {
1462 name = "setmathalternate",
1463 actions = setalternate,
1464 arguments = { "integer", "string" }
1465 }
1466
1467 alternate[mathchar_code] = function(pointer)
1468 local a = getattr(pointer,a_mathalternate)
1469 if a and a > 0 then
1470 setattr(pointer,a_mathalternate,0)
1471 local fontid = getfont(pointer)
1472 local resources = fontresources[fontid]
1473 if resources then
1474 local mathalternates = resources.mathalternates
1475 if mathalternates then
1476 local attributes = mathalternates.attributes
1477 local registered = mathalternates.registered
1478 local hashes = mathalternates.hashes
1479 for i=1,#registered do
1480 local r = registered[i]
1481 if band(a,r) ~= 0 then
1482 local char = getchar(pointer)
1483 local alt = hashes[i][char]
1484 if alt == nil then
1485 local what = attributes[r]
1486 alt = otf.getalternate(fontdata[fontid],char,what.feature,what.value) or false
1487 if alt == char then
1488 alt = false
1489 end
1490 hashes[i][char] = alt
1491 end
1492 if alt then
1493 if trace_alternates then
1494 local what = attributes[r]
1495 report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U",
1496 tostring(what.feature),tostring(what.value),getchar(pointer),alt)
1497 end
1498 setchar(pointer,alt)
1499 break
1500 end
1501 end
1502 end
1503 end
1504 end
1505 end
1506 end
1507
1508 function handlers.alternates(head,style,penalties)
1509 processnoads(head,alternate,"alternate")
1510 return true
1511 end
1512
1513end
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530do
1531
1532 local a_mathitalics = privateattribute("mathitalics")
1533
1534 local italics = { }
1535 local default_factor = 1/20
1536
1537 local setcolor = colortracers.set
1538 local resetcolor = colortracers.reset
1539 local italic_kern = new_kern
1540
1541 local c_positive_d = "trace:dg"
1542 local c_negative_d = "trace:dr"
1543
1544 local function insert_kern(current,kern)
1545 local sub = new_submlist()
1546 local noad = new_noad()
1547 setlist(sub,kern)
1548 setnext(kern,noad)
1549 setnucleus(noad,current)
1550 return sub
1551 end
1552
1553 registertracker("math.italics.visualize", function(v)
1554 if v then
1555 italic_kern = function(k)
1556 local n = new_kern(k)
1557 set_visual(n,"italic")
1558 return n
1559 end
1560 else
1561 italic_kern = new_kern
1562 end
1563 end)
1564
1565 local function getcorrection(method,font,char)
1566
1567 local visual = chardata[char].visual
1568
1569 if method == 1 then
1570
1571 local italics = fontitalics[font]
1572 if italics then
1573 local character = fontcharacters[font][char]
1574 if character then
1575 local correction = character.italic
1576 if correction and correction ~= 0 then
1577 return correction, visual
1578 end
1579 end
1580 end
1581 elseif method == 2 then
1582
1583 local character = fontcharacters[font][char]
1584 if character then
1585 local correction = character.italic
1586 if correction and correction ~= 0 then
1587 return correction, visual
1588 end
1589 end
1590 elseif method == 3 then
1591
1592 if visual == "it" or visual == "bi" then
1593 local character = fontcharacters[font][char]
1594 if character then
1595 local correction = character.italic
1596 if correction and correction ~= 0 then
1597 return correction, visual
1598 end
1599 end
1600 end
1601 elseif method == 4 then
1602
1603 local italics = fontitalics[font]
1604 if italics and (visual == "it" or visual == "bi") then
1605 local character = fontcharacters[font][char]
1606 if character then
1607 local correction = character.italic
1608 if correction and correction ~= 0 then
1609 return correction, visual
1610 end
1611 end
1612 end
1613 end
1614
1615 end
1616
1617 italics[mathchar_code] = function(pointer,what,n,parent)
1618 local method = getattr(pointer,a_mathitalics)
1619 if method and method > 0 and method < 100 then
1620 local char = getchar(pointer)
1621 local font = getfont(pointer)
1622 local correction, visual = getcorrection(method,font,char)
1623 if correction and correction ~= 0 then
1624 local next_noad = getnext(parent)
1625 if not next_noad then
1626 if n == 1 then
1627
1628 if trace_italics then
1629 report_italics("method %a, flagging italic correction %p between %C and end math",method,correction,char)
1630 end
1631 if correction > 0 then
1632 correction = correction + 100
1633 else
1634 correction = correction - 100
1635 end
1636 correction = round(correction)
1637 setattr(pointer,a_mathitalics,correction)
1638 setattr(parent,a_mathitalics,correction)
1639 return
1640 end
1641 end
1642 end
1643 end
1644 setattr(pointer,a_mathitalics,unsetvalue)
1645 end
1646
1647 function handlers.italics(head,style,penalties)
1648 processnoads(head,italics,"italics")
1649 return true
1650 end
1651
1652 local enable = function()
1653 enableaction("math", "noads.handlers.italics")
1654 if trace_italics then
1655 report_italics("enabling math italics")
1656 end
1657
1658 typesetters.italics.enablemath()
1659 enable = false
1660 end
1661
1662
1663
1664 function mathematics.setitalics(name)
1665 if enable then
1666 enable()
1667 end
1668 texsetattribute(a_mathitalics,name and name ~= v_reset and tonumber(name) or unsetvalue)
1669 end
1670
1671 function mathematics.getitalics(name)
1672 if enable then
1673 enable()
1674 end
1675 context(name and name ~= v_reset and tonumber(name) or unsetvalue)
1676 end
1677
1678 function mathematics.resetitalics()
1679 texsetattribute(a_mathitalics,unsetvalue)
1680 end
1681
1682 implement {
1683 name = "initializemathitalics",
1684 actions = enable,
1685 onlyonce = true,
1686 }
1687
1688 implement {
1689 name = "setmathitalics",
1690 actions = mathematics.setitalics,
1691 arguments = "string",
1692 }
1693
1694 implement {
1695 name = "getmathitalics",
1696 actions = mathematics.getitalics,
1697 arguments = "string",
1698 }
1699
1700 implement {
1701 name = "resetmathitalics",
1702 actions = mathematics.resetitalics
1703 }
1704
1705end
1706
1707do
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719 local a_kernpairs = privateattribute("mathkernpairs")
1720 local kernpairs = { }
1721
1722 local function enable()
1723 enableaction("math", "noads.handlers.kernpairs")
1724 if trace_kernpairs then
1725 report_kernpairs("enabling math kern pairs")
1726 end
1727 enable = false
1728 end
1729
1730 implement {
1731 name = "initializemathkernpairs",
1732 actions = enable,
1733 onlyonce = true,
1734 }
1735
1736 local hash = setmetatableindex(function(t,font)
1737 local g = fontdata[font].goodies
1738 local m = g and g[1] and g[1].mathematics
1739 local k = m and m.kernpairs
1740 t[font] = k
1741 return k
1742 end)
1743
1744
1745
1746 kernpairs[mathchar_code] = function(pointer,what,n,parent)
1747 if getattr(pointer,a_kernpairs) == 1 then
1748 local font = getfont(pointer)
1749 local list = hash[font]
1750 if list then
1751 local first = getchar(pointer)
1752 local found = list[first]
1753 if found then
1754 local next = getnext(parent)
1755 if next and getid(next) == noad_code then
1756 pointer = getnucleus(next)
1757 if pointer then
1758 if getfont(pointer) == font then
1759 local second = getchar(pointer)
1760 local kern = found[second]
1761 if kern then
1762 kern = kern * fonts.hashes.parameters[font].hfactor
1763 if trace_kernpairs then
1764 report_kernpairs("adding %p kerning between %C and %C",kern,first,second)
1765 end
1766 setlink(parent,new_kern(kern),getnext(parent))
1767 end
1768 end
1769 end
1770 end
1771 end
1772 end
1773 end
1774 end
1775
1776 function handlers.kernpairs(head,style,penalties)
1777 processnoads(head,kernpairs,"kernpairs")
1778 return true
1779 end
1780
1781end
1782
1783
1784
1785do
1786
1787
1788
1789 local a_mathcollapsing = privateattribute("mathcollapsing")
1790 local collapse = { }
1791 local mathlists = characters.mathlists
1792 local validpair = {
1793 [ordnoad_code] = true,
1794 [opdisplaylimitsnoad_code] = true,
1795 [oplimitsnoad_code] = true,
1796 [opnolimitsnoad_code] = true,
1797 [binnoad_code] = true,
1798 [relnoad_code] = true,
1799 [opennoad_code] = true,
1800 [closenoad_code] = true,
1801 [punctnoad_code] = true,
1802 [innernoad_code] = false,
1803 [undernoad_code] = false,
1804 [overnoad_code] = false,
1805 [vcenternoad_code] = false,
1806 [ordlimitsnoad_code] = true,
1807 }
1808
1809 local reported = setmetatableindex("table")
1810
1811 collapse[mathchar_code] = function(pointer,what,n,parent)
1812
1813 if parent and mathlists[getchar(pointer)] then
1814 local found, last, lucleus, lsup, lsub, category
1815 local tree = mathlists
1816 local current = parent
1817 while current and validpair[getsubtype(current)] do
1818 local nucleus = getnucleus(current)
1819 local sub = getsub(current)
1820 local sup = getsup(current)
1821 local char = getchar(nucleus)
1822 if char then
1823 local match = tree[char]
1824 if match then
1825 local method = getattr(current,a_mathcollapsing)
1826 if method and method > 0 and method <= 3 then
1827 local specials = match.specials
1828 local mathlist = match.mathlist
1829 local ligature
1830 if method == 1 then
1831 ligature = specials
1832 elseif method == 2 then
1833 ligature = specials or mathlist
1834 else
1835 ligature = mathlist or specials
1836 end
1837 if ligature then
1838 category = mathlist and "mathlist" or "specials"
1839 found = ligature
1840 last = current
1841 lucleus = nucleus
1842 lsup = sup
1843 lsub = sub
1844 end
1845 tree = match
1846 if sub or sup then
1847 break
1848 else
1849 current = getnext(current)
1850 end
1851 else
1852 break
1853 end
1854 else
1855 break
1856 end
1857 else
1858 break
1859 end
1860 end
1861 if found and last and lucleus then
1862 local id = getfont(lucleus)
1863 local characters = fontcharacters[id]
1864 local replace = characters and characters[found]
1865 if not replace then
1866 if not reported[id][found] then
1867 reported[id][found] = true
1868 report_collapsing("%s ligature %C from %s","ignoring",found,category)
1869 end
1870 elseif trace_collapsing then
1871 report_collapsing("%s ligature %C from %s","creating",found,category)
1872 end
1873 setchar(pointer,found)
1874 local l = getnext(last)
1875 local c = getnext(parent)
1876 if lsub then
1877 setsub(parent,lsub)
1878 setsub(last)
1879 end
1880 if lsup then
1881 setsup(parent,lsup)
1882 setsup(last)
1883 end
1884 while c ~= l do
1885 local n = getnext(c)
1886 flushnode(c)
1887 c = n
1888 end
1889 setlink(parent,l)
1890 end
1891 end
1892 end
1893
1894 function noads.handlers.collapse(head,style,penalties)
1895 processnoads(head,collapse,"collapse")
1896 return true
1897 end
1898
1899 local enable = function()
1900 enableaction("math", "noads.handlers.collapse")
1901 if trace_collapsing then
1902 report_collapsing("enabling math collapsing")
1903 end
1904 enable = false
1905 end
1906
1907 implement {
1908 name = "initializemathcollapsing",
1909 actions = enable,
1910 onlyonce = true,
1911 }
1912
1913end
1914
1915do
1916
1917
1918 local fixscripts = { }
1919 local movesub = {
1920
1921 [0x2032] = 0xFE932,
1922 [0x2033] = 0xFE933,
1923 [0x2034] = 0xFE934,
1924 [0x2057] = 0xFE957,
1925
1926 [0x2035] = 0xFE935,
1927 [0x2036] = 0xFE936,
1928 [0x2037] = 0xFE937,
1929 }
1930
1931 mathematics.virtualize(movesub)
1932
1933 local function fixsupscript(parent,current,current_char,new_char)
1934 if new_char ~= current_char and new_char ~= true then
1935 setchar(current,new_char)
1936 if trace_fixing then
1937 report_fixing("fixing subscript, replacing superscript %U by %U",current_char,new_char)
1938 end
1939 else
1940 if trace_fixing then
1941 report_fixing("fixing subscript, superscript %U",current_char)
1942 end
1943 end
1944 setfield(parent,"options",0x08+0x22)
1945 end
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973 local function move_none_none(parent,prev,nuc,oldchar,newchar)
1974 fixsupscript(prev,nuc,oldchar,newchar)
1975 local sub = getsub(parent)
1976 setsup(prev,nuc)
1977 setsub(prev,sub)
1978 local dummy = copy_node(nuc)
1979 setchar(dummy,0)
1980 setnucleus(parent,dummy)
1981 setsub(parent)
1982 end
1983
1984 local function move_none_psub(parent,prev,nuc,oldchar,newchar)
1985 fixsupscript(prev,nuc,oldchar,newchar)
1986 setsup(prev,nuc)
1987 local dummy = copy_node(nuc)
1988 setchar(dummy,0)
1989 setnucleus(parent,dummy)
1990 end
1991
1992 fixscripts[mathchar_code] = function(pointer,what,n,parent,nested)
1993 if parent then
1994 local oldchar = getchar(pointer)
1995 local newchar = movesub[oldchar]
1996 if newchar then
1997 local nuc = getnucleus(parent)
1998 if pointer == nuc then
1999 local sub = getsub(pointer)
2000 local sup = getsup(pointer)
2001 if sub then
2002 if sup then
2003
2004 else
2005
2006 end
2007 elseif sup then
2008
2009 else
2010 local prev = getprev(parent)
2011 if prev and getid(prev) == noad_code then
2012 local psub = getsub(prev)
2013 local psup = getsup(prev)
2014 if psub then
2015 if psup then
2016
2017 else
2018
2019 move_none_psub(parent,prev,nuc,oldchar,newchar)
2020 end
2021 elseif psup then
2022
2023 else
2024
2025 move_none_none(parent,prev,nuc,oldchar,newchar)
2026 end
2027 else
2028
2029 end
2030 end
2031 else
2032
2033 end
2034 end
2035 end
2036 end
2037
2038 function noads.handlers.fixscripts(head,style,penalties)
2039 processnoads(head,fixscripts,"fixscripts")
2040 return true
2041 end
2042
2043end
2044
2045
2046
2047do
2048
2049 local variants = { }
2050 local validvariants = {
2051 [0x2229] = 0xFE00, [0x222A] = 0xFE00,
2052 [0x2268] = 0xFE00, [0x2269] = 0xFE00,
2053 [0x2272] = 0xFE00, [0x2273] = 0xFE00,
2054 [0x228A] = 0xFE00, [0x228B] = 0xFE00,
2055 [0x2293] = 0xFE00, [0x2294] = 0xFE00,
2056 [0x2295] = 0xFE00,
2057 [0x2297] = 0xFE00,
2058 [0x229C] = 0xFE00,
2059 [0x22DA] = 0xFE00, [0x22DB] = 0xFE00,
2060 [0x2A3C] = 0xFE00, [0x2A3D] = 0xFE00,
2061 [0x2A9D] = 0xFE00, [0x2A9E] = 0xFE00,
2062 [0x2AAC] = 0xFE00, [0x2AAD] = 0xFE00,
2063 [0x2ACB] = 0xFE00, [0x2ACC] = 0xFE00,
2064 }
2065
2066 variants[mathchar_code] = function(pointer,what,n,parent)
2067 local char = getchar(pointer)
2068 local selector = validvariants[char]
2069 if selector then
2070 local next = getnext(parent)
2071 if next and getid(next) == noad_code then
2072 local nucleus = getnucleus(next)
2073 if nucleus and getid(nucleus) == mathchar_code and getchar(nucleus) == selector then
2074 local variant
2075 local tfmdata = fontdata[getfont(pointer)]
2076 local mathvariants = tfmdata.resources.variants
2077 if mathvariants then
2078 mathvariants = mathvariants[selector]
2079 if mathvariants then
2080 variant = mathvariants[char]
2081 end
2082 end
2083 if variant then
2084 setchar(pointer,variant)
2085 setattr(pointer,a_exportstatus,char)
2086 if trace_variants then
2087 report_variants("variant (%U,%U) replaced by %U",char,selector,variant)
2088 end
2089 else
2090 if trace_variants then
2091 report_variants("no variant (%U,%U)",char,selector)
2092 end
2093 end
2094 setprev(next,pointer)
2095 setnext(parent,getnext(next))
2096 flushnode(next)
2097 end
2098 end
2099 end
2100 end
2101
2102 function handlers.variants(head,style,penalties)
2103 processnoads(head,variants,"unicode variant")
2104 return true
2105 end
2106
2107end
2108
2109
2110
2111do
2112
2113 local classes = { }
2114 local colors = {
2115 [relnoad_code] = "trace:dr",
2116 [ordnoad_code] = "trace:db",
2117 [binnoad_code] = "trace:dg",
2118 [opennoad_code] = "trace:dm",
2119 [closenoad_code] = "trace:dm",
2120 [punctnoad_code] = "trace:dc",
2121
2122
2123
2124
2125
2126
2127
2128
2129 }
2130
2131 local setcolor = colortracers.set
2132 local resetcolor = colortracers.reset
2133
2134 classes[mathchar_code] = function(pointer,what,n,parent)
2135 local color = colors[getsubtype(parent)]
2136 if color then
2137 setcolor(pointer,color)
2138 else
2139 resetcolor(pointer)
2140 end
2141 end
2142
2143 function handlers.classes(head,style,penalties)
2144 processnoads(head,classes,"classes")
2145 return true
2146 end
2147
2148 registertracker("math.classes",function(v)
2149 setaction("math","noads.handlers.classes",v)
2150 end)
2151
2152end
2153
2154
2155
2156do
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166 local domains = { }
2167 local categories = { }
2168 local numbers = { }
2169 local a_mathdomain = privateattribute("mathdomain")
2170 mathematics.domains = categories
2171 local permitted = {
2172 ordinary = ordnoad_code,
2173 binary = binnoad_code,
2174 relation = relnoad_code,
2175 punctuation = punctnoad_code,
2176 inner = innernoad_code,
2177 }
2178
2179 function mathematics.registerdomain(data)
2180 local name = data.name
2181 if not name then
2182 return
2183 end
2184 local attr = #numbers + 1
2185 categories[name] = data
2186 numbers[attr] = data
2187 data.attribute = attr
2188
2189 return attr
2190 end
2191
2192 local enable
2193
2194 enable = function()
2195 enableaction("math", "noads.handlers.domains")
2196 if trace_domains then
2197 report_domains("enabling math domains")
2198 end
2199 enable = false
2200 end
2201
2202 function mathematics.setdomain(name)
2203 if enable then
2204 enable()
2205 end
2206 local data = name and name ~= v_reset and categories[name]
2207 texsetattribute(a_mathdomain,data and data.attribute or unsetvalue)
2208 end
2209
2210 function mathematics.getdomain(name)
2211 if enable then
2212 enable()
2213 end
2214 local data = name and name ~= v_reset and categories[name]
2215 context(data and data.attribute or unsetvalue)
2216 end
2217
2218 implement {
2219 name = "initializemathdomain",
2220 actions = enable,
2221 onlyonce = true,
2222 }
2223
2224 implement {
2225 name = "setmathdomain",
2226 arguments = "string",
2227 actions = mathematics.setdomain,
2228 }
2229
2230 implement {
2231 name = "getmathdomain",
2232 arguments = "string",
2233 actions = mathematics.getdomain,
2234 }
2235
2236 local function makehash(data)
2237 local hash = { }
2238 local parents = data.parents
2239 if parents then
2240 local function merge(name)
2241 if name then
2242 local c = categories[name]
2243 if c then
2244 local hash = c.hash
2245 if not hash then
2246 hash = makehash(c)
2247 end
2248 for k, v in next, hash do
2249 hash[k] = v
2250 end
2251 end
2252 end
2253 end
2254 if type(parents) == "string" then
2255 merge(parents)
2256 elseif type(parents) == "table" then
2257 for i=1,#parents do
2258 merge(parents[i])
2259 end
2260 end
2261 end
2262 local characters = data.characters
2263 if characters then
2264 for k, v in next, characters do
2265
2266 local cls = v.class
2267 if cls then
2268 v.code = permitted[cls]
2269 else
2270
2271 end
2272 hash[k] = v
2273 end
2274 end
2275 data.hash = hash
2276 return hash
2277 end
2278
2279 domains[mathchar_code] = function(pointer,what,n,parent)
2280 local attr = getattr(pointer,a_mathdomain)
2281 if attr then
2282 local domain = numbers[attr]
2283 if domain then
2284 local hash = domain.hash
2285 if not hash then
2286 hash = makehash(domain)
2287 end
2288 local char = getchar(pointer)
2289 local okay = hash[char]
2290 if okay then
2291 local chr = okay.char
2292 local cls = okay.code
2293 if chr and chr ~= char then
2294 setchar(pointer,chr)
2295 end
2296 if cls and cls ~= getsubtype(parent) then
2297 setsubtype(parent,cls)
2298 end
2299 end
2300 end
2301 end
2302 end
2303
2304 function handlers.domains(head,style,penalties)
2305 processnoads(head,domains,"domains")
2306 return true
2307 end
2308
2309end
2310
2311
2312
2313function handlers.showtree(head,style,penalties)
2314 inspect(nodes.totree(tonut(head)))
2315end
2316
2317registertracker("math.showtree",function(v)
2318 setaction("math","noads.handlers.showtree",v)
2319end)
2320
2321
2322
2323do
2324
2325 local applyvisuals = nuts.applyvisuals
2326 local visual = false
2327
2328 function handlers.makeup(head)
2329 applyvisuals(head,visual)
2330 end
2331
2332 registertracker("math.makeup",function(v)
2333 visual = v
2334 setaction("math","noads.handlers.makeup",v)
2335 end)
2336
2337end
2338
2339
2340
2341do
2342
2343 local force_penalties = false
2344
2345
2346
2347
2348
2349 function builders.kernel.mlisttohlist(head,style,penalties)
2350 return mlisttohlist(head,style,force_penalties or penalties)
2351 end
2352
2353 implement {
2354 name = "setmathpenalties",
2355 arguments = "integer",
2356 actions = function(p)
2357 force_penalties = p > 0
2358 end,
2359 }
2360
2361end
2362
2363local actions = tasks.actions("math")
2364
2365local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
2366
2367function processors.mlisttohlist(head,style,penalties)
2368 starttiming(noads)
2369 head = actions(head,style,penalties)
2370 stoptiming(noads)
2371 return head
2372end
2373
2374callbacks.register('mlist_to_hlist',processors.mlisttohlist,"preprocessing math list")
2375
2376
2377
2378statistics.register("math processing time", function()
2379 return statistics.elapsedseconds(noads)
2380end)
2381 |