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
27
28
29
30
31
32local next, tonumber = next, tonumber
33local formatters, gmatch, match = string.formatters, string.gmatch, string.match
34local insert, remove, concat, sortedhash = table.insert, table.remove, table.concat, table.sortedhash
35local div, round = math.div, math.round
36
37local fonts = fonts
38local nodes = nodes
39local node = node
40local mathematics = mathematics
41
42local privateattribute = attributes.private
43local registertracker = trackers.register
44local registerdirective = directives.register
45local logreporter = logs.reporter
46local setmetatableindex = table.setmetatableindex
47
48local texgetmode = tex.getmode
49local mathmode_code = tex.modelevels.math
50
51local colortracers = nodes.tracers.colors
52
53
54
55local trace_remapping = false registertracker("math.remapping", function(v) trace_remapping = v end)
56local trace_processing = false registertracker("math.processing", function(v) trace_processing = v end)
57local trace_analyzing = false registertracker("math.analyzing", function(v) trace_analyzing = v end)
58local trace_normalizing = false registertracker("math.normalizing", function(v) trace_normalizing = v end)
59local trace_collapsing = false registertracker("math.collapsing", function(v) trace_collapsing = v end)
60local trace_goodies = false registertracker("math.goodies", function(v) trace_goodies = v end)
61
62local check_coverage = true registerdirective("math.checkcoverage", function(v) check_coverage = v end)
63local use_math_goodies = true registerdirective("math.nogoodies", function(v) use_math_goodies = not v end)
64
65local report_processing = logreporter("mathematics","processing")
66local report_remapping = logreporter("mathematics","remapping")
67local report_normalizing = logreporter("mathematics","normalizing")
68local report_collapsing = logreporter("mathematics","collapsing")
69local report_goodies = logreporter("mathematics","goodies")
70
71local a_mathrendering <const> = privateattribute("mathrendering")
72local a_exportstatus <const> = privateattribute("exportstatus")
73
74local nuts = nodes.nuts
75local nodepool = nuts.pool
76local tonut = nuts.tonut
77local nutstring = nuts.tostring
78
79local setfield = nuts.setfield
80local setlink = nuts.setlink
81local setlist = nuts.setlist
82local setnext = nuts.setnext
83local setprev = nuts.setprev
84local setchar = nuts.setchar
85local setfam = nuts.setfam
86local setsubtype = nuts.setsubtype
87local setattr = nuts.setattr
88local setattrlist = nuts.setattrlist
89local setwidth = nuts.setwidth
90local setheight = nuts.setheight
91local setdepth = nuts.setdepth
92local setdelimiter = nuts.setdelimiter
93local setclass = nuts.setclass
94
95local getfield = nuts.getfield
96local getnext = nuts.getnext
97local getprev = nuts.getprev
98local getboth = nuts.getboth
99local isnext = nuts.isnext
100local isprev = nuts.isprev
101local isboth = nuts.isboth
102local getid = nuts.getid
103local getsubtype = nuts.getsubtype
104local getchar = nuts.getchar
105local getfont = nuts.getfont
106local getfam = nuts.getfam
107local getcharspec = nuts.getcharspec
108local getattr = nuts.getattr
109local getattrs = nuts.getattrs
110local getlist = nuts.getlist
111local getwidth = nuts.getwidth
112local getheight = nuts.getheight
113local getdepth = nuts.getdepth
114local getwhd = nuts.getwhd
115local getdelimiter = nuts.getdelimiter
116local getleftdelimiter = nuts.getleftdelimiter
117local getrightdelimiter = nuts.getrightdelimiter
118local gettopdelimiter = nuts.gettopdelimiter
119local getbottomdelimiter = nuts.getbottomdelimiter
120local getnumerator = nuts.getnumerator
121local getdenominator = nuts.getdenominator
122local getdegree = nuts.getdegree
123local gettop = nuts.gettop
124local getmiddle = nuts.getmiddle
125local getbottom = nuts.getbottom
126local getchoice = nuts.getchoice
127
128local getnucleus = nuts.getnucleus
129local getsub = nuts.getsub
130local getsup = nuts.getsup
131local getsubpre = nuts.getsubpre
132local getsuppre = nuts.getsuppre
133local getprime = nuts.getprime
134
135local setnucleus = nuts.setnucleus
136local setsub = nuts.setsub
137local setsup = nuts.setsup
138local setsubpre = nuts.setsubpre
139local setsuppre = nuts.setsuppre
140local setprime = nuts.setprime
141
142local getoffsets = nuts.getoffsets
143local setoffsets = nuts.setoffsets
144
145local getscripts = nuts.getscripts
146local setscripts = nuts.setscripts
147
148local getoptions = nuts.getoptions
149local setoptions = nuts.setoptions
150
151local setprop = nuts.setprop
152
153local flushnode = nuts.flush
154local copy_node = nuts.copy
155local slide_nodes = nuts.slide
156local set_visual = nuts.setvisual
157
158local mlisttohlist = nuts.mlisttohlist
159
160local new_kern = nodepool.kern
161local new_submlist = nodepool.submlist
162local new_noad = nodepool.noad
163local new_delimiter = nodepool.delimiter
164local new_fence = nodepool.fence
165
166local fonthashes = fonts.hashes
167local fontdata = fonthashes.identifiers
168local fontcharacters = fonthashes.characters
169local fontitalics = fonthashes.italics
170local fontparameters = fonthashes.parameters
171
172local variables = interfaces.variables
173local texsetattribute = tex.setattribute
174local texgetattribute = tex.getattribute
175local getfontoffamily = tex.getfontoffamily
176local unsetvalue <const> = attributes.unsetvalue
177local implement = interfaces.implement
178
179local v_reset <const> = variables.reset
180local v_small <const> = variables.small
181local v_medium <const> = variables.medium
182local v_big <const> = variables.big
183local v_line <const> = variables.line
184
185local chardata = characters.data
186
187noads = noads or { }
188local noads = noads
189
190noads.processors = noads.processors or { }
191local processors = noads.processors
192
193noads.handlers = noads.handlers or { }
194local handlers = noads.handlers
195
196local tasks = nodes.tasks
197local enableaction = tasks.enableaction
198local setaction = tasks.setaction
199
200local nodecodes = nodes.nodecodes
201
202
203local classes = mathematics.classes
204
205local ordinary_class <const> = classes.ordinary
206local operator_class <const> = classes.operator
207local binary_class <const> = classes.binary
208local relation_class <const> = classes.relation
209local open_class <const> = classes.open
210local close_class <const> = classes.close
211local middle_class <const> = classes.middle
212local punctuation_class <const> = classes.punctuation
213local fenced_class <const> = classes.fenced
214local fraction_class <const> = classes.fraction
215local radical_class <const> = classes.radical
216local accent_class <const> = classes.accent
217local numbergroup_class <const> = classes.numbergroup
218local digit_class <const> = classes.digit
219
220local noad_code <const> = nodecodes.noad
221local accent_code <const> = nodecodes.accent
222local radical_code <const> = nodecodes.radical
223local fraction_code <const> = nodecodes.fraction
224local subbox_code <const> = nodecodes.subbox
225local submlist_code <const> = nodecodes.submlist
226local mathchar_code <const> = nodecodes.mathchar
227local mathtextchar_code <const> = nodecodes.mathtextchar
228local delimiter_code <const> = nodecodes.delimiter
229
230
231local math_choice <const> = nodecodes.choice
232local fence_code <const> = nodecodes.fence
233
234
235
236
237
238
239
240
241
242local function process(start,what,n,parent)
243
244 if n then
245 n = n + 1
246 else
247 n = 0
248 end
249
250 local initial = start
251
252
253
254 while start do
255 local noad = nil
256 local id = getid(start)
257 if trace_processing then
258 if id == noad_code then
259 report_processing("%w%S, class %a",n*2,nutstring(start),classes[getsubtype(start)])
260 elseif id == mathchar_code then
261 local char, font, fam = getcharspec(start)
262 report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,nutstring(start),fam,font,char,char)
263 else
264 report_processing("%w%S",n*2,nutstring(start))
265 end
266 end
267 local proc = what[id]
268 if proc then
269
270 local done, newstart, newinitial = proc(start,what,n,parent)
271 if newinitial then
272 initial = newinitial
273 if newstart then
274 start = newstart
275
276 else
277
278 break
279 end
280 else
281 if newstart then
282 start = newstart
283
284 else
285
286 end
287 end
288 elseif id == noad_code then
289
290 noad = getnucleus(start) if noad then process(noad,what,n,start) end
291 noad = getsup(start) if noad then process(noad,what,n,start) end
292 noad = getsub(start) if noad then process(noad,what,n,start) end
293 noad = getsuppre(start) if noad then process(noad,what,n,start) end
294 noad = getsubpre(start) if noad then process(noad,what,n,start) end
295 noad = getprime(start) if noad then process(noad,what,n,start) end
296 elseif id == mathchar_code or id == mathtextchar_code or id == delimiter_code then
297 break
298 elseif id == subbox_code or id == submlist_code then
299 noad = getlist(start) if noad then process(noad,what,n,start) end
300 elseif id == fraction_code then
301 noad = getnumerator(start) if noad then process(noad,what,n,start) end
302 noad = getdenominator(start) if noad then process(noad,what,n,start) end
303 noad = getleftdelimiter(start) if noad then process(noad,what,n,start) end
304 noad = getdelimiter(start) if noad then process(noad,what,n,start) end
305 noad = getrightdelimiter(start) if noad then process(noad,what,n,start) end
306 elseif id == fence_code then
307 noad = getdelimiter(start) if noad then process(noad,what,n,start) end
308 noad = gettop(start) if noad then process(noad,what,n,start) end
309 noad = getbottom(start) if noad then process(noad,what,n,start) end
310 elseif id == radical_code then
311 noad = getnucleus(start) if noad then process(noad,what,n,start) end
312 noad = getsup(start) if noad then process(noad,what,n,start) end
313 noad = getsub(start) if noad then process(noad,what,n,start) end
314 noad = getsuppre(start) if noad then process(noad,what,n,start) end
315 noad = getsubpre(start) if noad then process(noad,what,n,start) end
316 noad = getprime(start) if noad then process(noad,what,n,start) end
317 noad = getleftdelimiter(start) if noad then process(noad,what,n,start) end
318 noad = getrightdelimiter(start) if noad then process(noad,what,n,start) end
319 noad = gettopdelimiter(start) if noad then process(noad,what,n,start) end
320 noad = getbottomdelimiter(start) if noad then process(noad,what,n,start) end
321 noad = getdegree(start) if noad then process(noad,what,n,start) end
322 elseif id == accent_code then
323 noad = getnucleus(start) if noad then process(noad,what,n,start) end
324 noad = getsup(start) if noad then process(noad,what,n,start) end
325 noad = getsub(start) if noad then process(noad,what,n,start) end
326 noad = getsuppre(start) if noad then process(noad,what,n,start) end
327 noad = getsubpre(start) if noad then process(noad,what,n,start) end
328 noad = getprime(start) if noad then process(noad,what,n,start) end
329 noad = gettop(start) if noad then process(noad,what,n,start) end
330 noad = getmiddle(start) if noad then process(noad,what,n,start) end
331 noad = getbottom(start) if noad then process(noad,what,n,start) end
332 elseif id == math_choice then
333 noad = getchoice(start,1) if noad then process(noad,what,n,start) end
334 noad = getchoice(start,2) if noad then process(noad,what,n,start) end
335 noad = getchoice(start,3) if noad then process(noad,what,n,start) end
336 noad = getchoice(start,4) if noad then process(noad,what,n,start) end
337
338
339
340
341
342
343 end
344 start = getnext(start)
345 end
346 if not parent then
347 return initial
348 end
349end
350
351local function processnested(current,what,n)
352 local noad = nil
353 local id = getid(current)
354 if id == noad_code then
355 noad = getnucleus(current) if noad then process(noad,what,n,current) end
356 noad = getsup(current) if noad then process(noad,what,n,current) end
357 noad = getsub(current) if noad then process(noad,what,n,current) end
358 noad = getsuppre(current) if noad then process(noad,what,n,current) end
359 noad = getsubpre(current) if noad then process(noad,what,n,current) end
360 noad = getprime(current) if noad then process(noad,what,n,current) end
361 elseif id == subbox_code or id == submlist_code then
362 noad = getlist(current) if noad then process(noad,what,n,current) end
363 elseif id == fraction_code then
364 noad = getnumerator(current) if noad then process(noad,what,n,current) end
365 noad = getdenominator(current) if noad then process(noad,what,n,current) end
366 noad = getleftdelimiter(current) if noad then process(noad,what,n,current) end
367 noad = getdelimiter(current) if noad then process(noad,what,n,current) end
368 noad = getrightdelimiter(current) if noad then process(noad,what,n,current) end
369 elseif id == fence_code then
370 noad = getdelimiter(current) if noad then process(noad,what,n,current) end
371 noad = gettop(current) if noad then process(noad,what,n,current) end
372 noad = getbottom(current) if noad then process(noad,what,n,current) end
373 elseif id == radical_code then
374 noad = getnucleus(current) if noad then process(noad,what,n,current) end
375 noad = getsup(current) if noad then process(noad,what,n,current) end
376 noad = getsub(current) if noad then process(noad,what,n,current) end
377 noad = getsuppre(current) if noad then process(noad,what,n,current) end
378 noad = getsubpre(current) if noad then process(noad,what,n,current) end
379 noad = getprime(current) if noad then process(noad,what,n,current) end
380 noad = getleftdelimiter(current) if noad then process(noad,what,n,current) end
381 noad = getrightdelimiter(current) if noad then process(noad,what,n,current) end
382 noad = gettopdelimiter(current) if noad then process(noad,what,n,current) end
383 noad = getbottomdelimiter(current) if noad then process(noad,what,n,current) end
384 noad = getdegree(current) if noad then process(noad,what,n,current) end
385 elseif id == accent_code then
386 noad = getnucleus(current) if noad then process(noad,what,n,current) end
387 noad = getsup(current) if noad then process(noad,what,n,current) end
388 noad = getsub(current) if noad then process(noad,what,n,current) end
389 noad = getsuppre(current) if noad then process(noad,what,n,current) end
390 noad = getsubpre(current) if noad then process(noad,what,n,current) end
391 noad = getprime(current) if noad then process(noad,what,n,current) end
392 noad = gettop(current) if noad then process(noad,what,n,current) end
393 noad = getmiddle(current) if noad then process(noad,what,n,current) end
394 noad = getbottom(current) if noad then process(noad,what,n,current) end
395 elseif id == math_choice then
396 noad = getchoice(start,1) if noad then process(noad,what,n,current) end
397 noad = getchoice(start,2) if noad then process(noad,what,n,current) end
398 noad = getchoice(start,3) if noad then process(noad,what,n,current) end
399 noad = getchoice(start,4) if noad then process(noad,what,n,current) end
400 end
401end
402
403local function processstep(current,process,n,id)
404 local noad = nil
405 local id = id or getid(current)
406 if id == noad_code then
407 noad = getnucleus(current) if noad then process(noad,n,current) end
408 noad = getsup(current) if noad then process(noad,n,current) end
409 noad = getsub(current) if noad then process(noad,n,current) end
410 noad = getsuppre(current) if noad then process(noad,n,current) end
411 noad = getsubpre(current) if noad then process(noad,n,current) end
412 noad = getprime(current) if noad then process(noad,n,current) end
413 elseif id == subbox_code or id == submlist_code then
414 noad = getlist(current) if noad then process(noad,n,current) end
415 elseif id == fraction_code then
416 noad = getnumerator(current) if noad then process(noad,n,current) end
417 noad = getdenominator(current) if noad then process(noad,n,current) end
418 noad = getleftdelimiter(current) if noad then process(noad,n,current) end
419 noad = getdelimiter(current) if noad then process(noad,n,current) end
420 noad = getrightdelimiter(current) if noad then process(noad,n,current) end
421 elseif id == fence_code then
422 noad = getdelimiter(current) if noad then process(noad,n,current) end
423 noad = gettop(current) if noad then process(noad,n,current) end
424 noad = getbottom(current) if noad then process(noad,n,current) end
425 elseif id == radical_code then
426 noad = getnucleus(current) if noad then process(noad,n,current) end
427 noad = getsup(current) if noad then process(noad,n,current) end
428 noad = getsub(current) if noad then process(noad,n,current) end
429 noad = getsuppre(current) if noad then process(noad,n,current) end
430 noad = getsubpre(current) if noad then process(noad,n,current) end
431 noad = getprime(current) if noad then process(noad,n,current) end
432 noad = getleftdelimiter(current) if noad then process(noad,n,current) end
433 noad = getrightdelimiter(current) if noad then process(noad,n,current) end
434 noad = gettopdelimiter(current) if noad then process(noad,n,current) end
435 noad = getbottomdelimiter(current) if noad then process(noad,n,current) end
436 noad = getdegree(current) if noad then process(noad,n,current) end
437 elseif id == accent_code then
438 noad = getnucleus(current) if noad then process(noad,n,current) end
439 noad = getsup(current) if noad then process(noad,n,current) end
440 noad = getsub(current) if noad then process(noad,n,current) end
441 noad = getsuppre(current) if noad then process(noad,n,current) end
442 noad = getsubpre(current) if noad then process(noad,n,current) end
443 noad = getprime(current) if noad then process(noad,n,current) end
444 noad = gettop(current) if noad then process(noad,n,current) end
445 noad = getmiddle(current) if noad then process(noad,n,current) end
446 noad = getbottom(current) if noad then process(noad,n,current) end
447 elseif id == math_choice then
448 noad = getchoice(start,1) if noad then process(noad,n,current) end
449 noad = getchoice(start,2) if noad then process(noad,n,current) end
450 noad = getchoice(start,3) if noad then process(noad,n,current) end
451 noad = getchoice(start,4) if noad then process(noad,n,current) end
452 end
453end
454
455local function processnoads(head,actions,banner)
456 if trace_processing then
457 report_processing("start %a",banner)
458 head = process(head,actions)
459 report_processing("stop %a",banner)
460 else
461 head = process(head,actions)
462 end
463 return head
464end
465
466noads.process = processnoads
467noads.processnested = processnested
468noads.processouter = process
469
470
471
472local unknowns = setmetatableindex("table")
473local checked = setmetatableindex("table")
474local cached = setmetatableindex("table")
475
476local tracked = false trackers.register("fonts.missing", function(v) tracked = v end)
477
478local variantselectors = {
479 [0xFE00] = true,
480 [0xFE01] = true
481}
482
483local function errorchar(font,char)
484 if variantselectors[char] then
485 return char
486 end
487 local done = unknowns[font]
488 done[char] = (done[char] or 0) + 1
489 if tracked then
490
491
492 local fake = cached[font][char]
493 if fake then
494 return fake
495 else
496 local kind, fake = fonts.checkers.placeholder(font,char)
497 if not fake or kind ~= "char" then
498 fake = 0x3F
499 end
500 cached[font][char] = fake
501 return fake
502 end
503 else
504
505
506
507 if not checked[font][char] then
508 if trace_normalizing then
509 report_normalizing("character %C is not available",char)
510 end
511 checked[font][char] = true
512 end
513 return 0x3F
514 end
515end
516
517
518
519
520
521
522
523
524do
525
526 local families = { }
527 local a_mathfamily <const> = privateattribute("mathfamily")
528 local boldmap = mathematics.boldmap
529
530 local trace_families = false registertracker("math.families", function(v) trace_families = v end)
531 local report_families = logreporter("mathematics","families")
532
533 local familymap = { [0] =
534 "regular",
535 "regular",
536 "regular",
537 "bold",
538 "bold",
539 "bold",
540 "pseudobold",
541 "pseudobold",
542 "pseudobold",
543 }
544
545 families[noad_code] = function(pointer,what,n,parent)
546 local a = getattr(pointer,a_mathfamily)
547 if a and a >= 0 then
548 if a > 0 then
549 setattr(pointer,a_mathfamily,0)
550 if a > 5 then
551 a = a - 3
552 end
553 end
554 setfam(pointer,a)
555 end
556 processnested(pointer,families,n+1)
557 end
558
559 function mathematics.familyname(n)
560 return familymap[n] or "regular"
561 end
562
563 families[fraction_code] = families[noad_code]
564 families[accent_code] = families[noad_code]
565 families[radical_code] = families[noad_code]
566 families[fence_code] = families[noad_code]
567
568 families[mathchar_code] = function(pointer)
569 if getfam(pointer) == 0 then
570 local a = getattr(pointer,a_mathfamily)
571 if a and a > 0 then
572 setattr(pointer,a_mathfamily,0)
573 if a > 5 then
574 local char = getchar(pointer)
575 local bold = boldmap[char]
576 local newa = a - 3
577 if not bold then
578 if trace_families then
579 report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
580 end
581 setfam(pointer,newa)
582 elseif not fontcharacters[getfontoffamily(newa)][bold] then
583 if trace_families then
584 report_families("no bold character for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
585 end
586 if newa > 3 then
587 setfam(pointer,newa-3)
588 end
589 else
590 setattr(pointer,a_exportstatus,char)
591 setchar(pointer,bold)
592 if trace_families then
593 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])
594 end
595 setfam(pointer,newa)
596 end
597 else
598 local char = getchar(pointer)
599 if not fontcharacters[getfontoffamily(a)][char] then
600 if trace_families then
601 report_families("no bold replacement for %C",char)
602 end
603 else
604 if trace_families then
605 report_families("family of %C becomes %s with remap %s",char,a,familymap[a])
606 end
607 setfam(pointer,a)
608 end
609 end
610 end
611 end
612 end
613
614
615
616 families[delimiter_code] = function(pointer)
617 if getfam(pointer) == 0 then
618 local a = getattr(pointer,a_mathfamily)
619 if a and a > 0 then
620 setattr(pointer,a_mathfamily,0)
621 if a > 5 then
622
623 a = a - 3
624 end
625 local char = getchar(pointer)
626 local okay = fontcharacters[getfontoffamily(a)][char]
627 if okay then
628 setfam(pointer,a)
629 elseif a > 2 then
630 setfam(pointer,a-3)
631 end
632 else
633 setfam(pointer,0)
634 end
635 end
636 end
637
638 families[mathtextchar_code] = families[mathchar_code]
639
640 function handlers.families(head,style,penalties)
641 processnoads(head,families,"families")
642 end
643
644end
645
646
647
648do
649
650 local a_mathalphabet <const> = privateattribute("mathalphabet")
651 local a_mathgreek <const> = privateattribute("mathgreek")
652
653 local relocate = { }
654
655 local remapalphabets = mathematics.remapalphabets
656 local fallbackstyleattr = mathematics.fallbackstyleattr
657 local fallbackalphabetattr = mathematics.fallbackalphabetattr
658
659 local setnodecolor = colortracers.set
660
661 local function report_remap(tag,id,old,new,extra)
662 if new then
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 else
666
667 report_remapping("remapping %s in font (%s,%s) from %C to ?",
668 tag,id,fontdata[id].properties.fontname or "",old)
669 end
670 end
671
672 local function checked(pointer)
673 local char, font = getcharspec(pointer)
674 local data = fontcharacters[font]
675 if not data[char] then
676 local specials = chardata[char].specials
677 if specials and (specials[1] == "char" or specials[1] == "font") then
678 local newchar = specials[#specials]
679 if trace_remapping then
680 report_remap("fallback",font,char,newchar)
681 end
682 if trace_analyzing then
683 setnodecolor(pointer,"font:isol")
684 end
685 setattr(pointer,a_exportstatus,char)
686 setchar(pointer,newchar)
687 return true
688 end
689 end
690 end
691
692
693
694
695
696 relocate[mathchar_code] = function(pointer)
697 local greek, alpha = getattrs(pointer,a_mathgreek,a_mathalphabet)
698 local char, font, fam = getcharspec(pointer)
699 local characters = fontcharacters[font]
700 if not alpha then alpha = 0 end
701 if not greek then greek = 0 end
702 if alpha > 0 or greek > 0 then
703 if alpha > 0 then
704
705 setattr(pointer,a_mathgreek,0)
706 end
707 if greek > 0 then
708
709 setattr(pointer,a_mathalphabet,0)
710 end
711 local newchar = remapalphabets(char,alpha,greek)
712 if newchar then
713 local newchardata = characters[newchar]
714 if newchardata then
715 if trace_remapping then
716 report_remap("char",font,char,newchar,newchardata.commands and " (virtual)" or "")
717 end
718 if trace_analyzing then
719 setnodecolor(pointer,"font:isol")
720 end
721 setchar(pointer,newchar)
722 return true
723 else
724 local fallback = fallbackstyleattr(alpha)
725 if fallback then
726 local newchar = remapalphabets(char,fallback,greek)
727 if newchar then
728 if characters[newchar] then
729 if trace_remapping then
730 report_remap("char",font,char,newchar," (fallback remapping used)")
731 end
732 if trace_analyzing then
733 setnodecolor(pointer,"font:isol")
734 end
735 setchar(pointer,newchar)
736 return true
737 elseif trace_remapping then
738 report_remap("char",font,char,newchar," fails (no fallback character)")
739 end
740 elseif trace_remapping then
741 report_remap("char",font,char,newchar," fails (no fallback remap character)")
742 end
743 elseif trace_remapping then
744 report_remap("char",font,char,newchar," fails (no fallback style)")
745 end
746 end
747 elseif trace_remapping then
748 local chardata = characters[char]
749 if chardata and chardata.commands then
750 report_remap("char",font,char,char," (virtual)")
751 end
752 end
753 end
754 if not characters[char] then
755 local fnt = getfontoffamily(fam,1)
756 setchar(pointer,errorchar(font,char))
757 if font ~= fnt then
758 errorchar(fnt,char)
759 errorchar(getfontoffamily(fam,2),char)
760 end
761 end
762 if trace_analyzing then
763 setnodecolor(pointer,"font:medi")
764 end
765 if check_coverage then
766 return checked(pointer)
767 end
768 end
769
770 relocate[mathtextchar_code] = function(pointer)
771 if trace_analyzing then
772 setnodecolor(pointer,"font:init")
773 end
774 end
775
776 relocate[delimiter_code] = function(pointer)
777 if trace_analyzing then
778 setnodecolor(pointer,"font:fina")
779 end
780 end
781
782 function handlers.relocate(head,style,penalties)
783 processnoads(head,relocate,"relocate")
784 end
785
786end
787
788
789
790do
791
792 local render = { }
793
794 local rendersets = mathematics.renderings.numbers or { }
795
796 render[mathchar_code] = function(pointer)
797 local attr = getattr(pointer,a_mathrendering)
798 if attr and attr > 0 then
799 local char, font = getcharspec(pointer)
800 local renderset = rendersets[attr]
801 if renderset then
802 local newchar = renderset[char]
803 if newchar then
804 local characters = fontcharacters[font]
805 if characters and characters[newchar] then
806 setchar(pointer,newchar)
807 setattr(pointer,a_exportstatus,char)
808 end
809 end
810 end
811 end
812 end
813
814 function handlers.render(head,style,penalties)
815 processnoads(head,render,"render")
816 end
817
818end
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894do
895
896 local trace_fences = false registertracker("math.fences", function(v) trace_fences = v end)
897 local report_fences = logreporter("mathematics","fences")
898
899 local a_autofence <const> = privateattribute("mathautofence")
900 local autofences = { }
901 local dummyfencechar = 0x2E
902
903 local fencecodes = nodes.fencecodes
904 local leftfence_code <const> = fencecodes.left
905 local middlefence_code <const> = fencecodes.middle
906 local rightfence_code <const> = fencecodes.right
907
908 local function makefence(what,char,template)
909 local d = new_delimiter()
910 local f = new_fence()
911 if char then
912 local sym = getnucleus(char)
913 local chr, fnt, fam = getcharspec(sym)
914 if chr == dummyfencechar then
915 chr = 0
916 end
917 setchar(d,chr)
918 setfam(d,fam)
919 flushnode(sym)
920 end
921 setattrlist(d,template)
922 setattrlist(f,template)
923 setsubtype(f,what)
924 setdelimiter(f,d)
925 setclass(f,-1)
926 return f
927 end
928
929 local function show(where,pointer)
930 print("")
931 local i = 0
932 for n in nuts.traverse(pointer) do
933 i = i + 1
934 print(i,where,nuts.tonode(n))
935 end
936 print("")
937 end
938
939 local function makelist(middle,noad,f_o,o_next,c_prev,f_c)
940
941
942
943
944
945
946
947
948
949 local list = new_submlist()
950 setsubtype(noad,fenced_class)
951 setnucleus(noad,list)
952 setattrlist(list,noad)
953 setlist(list,f_o)
954 setlink(f_o,o_next)
955 setlink(c_prev,f_c)
956
957 if middle and next(middle) then
958 local prev = f_o
959 local current = o_next
960 while current ~= f_c do
961 local midl = middle[current]
962 local next = getnext(current)
963 if midl then
964 local fence = makefence(middlefence_code,current,current)
965 setnucleus(current)
966 flushnode(current)
967 middle[current] = nil
968
969 setlink(prev,fence,next)
970 prev = fence
971 else
972 prev = current
973 end
974 current = next
975 end
976 end
977 return noad
978 end
979
980
981
982 local function convert_both(open,close,middle)
983 local o_next = getnext(open)
984 if o_next == close then
985 return close
986 else
987 local c_prev, c_next = getboth(close)
988 local f_o = makefence(leftfence_code,open,open)
989 local f_c = makefence(rightfence_code,close,close)
990 makelist(middle,open,f_o,o_next,c_prev,f_c)
991 setnucleus(close)
992 flushnode(close)
993
994 setlink(open,c_next)
995 return open
996 end
997 end
998
999 local function convert_open(open,last,middle)
1000 local f_o = makefence(leftfence_code,open,open)
1001 local f_c = makefence(rightfence_code,nil,open)
1002 local o_next = getnext(open)
1003 makelist(middle,open,f_o,o_next,last,nil)
1004
1005 setlink(open,l_next)
1006 return open
1007 end
1008
1009 local function convert_close(first,close,middle)
1010 local f_o = makefence(leftfence_code,nil,close)
1011 local f_c = makefence(rightfence_code,close)
1012 local c_prev = getprev(close)
1013 local f_next = getnext(first)
1014 makelist(middle,close,f_o,f_next,c_prev,f_c)
1015
1016 if c_prev ~= first then
1017 setlink(first,close)
1018 end
1019 return close
1020 end
1021
1022 local stacks = setmetatableindex("table")
1023
1024
1025
1026 local function processfences(pointer,n,parent)
1027 local current = pointer
1028 local last = pointer
1029 local start = pointer
1030 local done = false
1031 local initial = pointer
1032 local stack = nil
1033 local middle = nil
1034 while current do
1035
1036 local id = getid(current)
1037 if id == noad_code then
1038 local a = getattr(current,a_autofence)
1039 if a and a > 0 then
1040 local stack = stacks[n]
1041 setattr(current,a_autofence,0)
1042 local level = #stack
1043 if a == 1 then
1044 if trace_fences then
1045 report_fences("%2i: level %i, handling %s, action %s",n,level,"open","open")
1046 end
1047 insert(stack,current)
1048 elseif a == 2 then
1049 local open = remove(stack)
1050 if open then
1051 if trace_fences then
1052 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","both")
1053 end
1054 local prime, sup, sub, presup, presub = getscripts(current)
1055 setscripts(current)
1056 current = convert_both(open,current,middle)
1057 setscripts(current,prime, sup, sub, presup, presub)
1058 elseif current == start then
1059 if trace_fences then
1060 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","skip")
1061 end
1062 else
1063 if trace_fences then
1064 report_fences("%2i: level %i, handling %s, action %s",n,level,"close","close")
1065 end
1066 local prime, sup, sub, presup, presub = getscripts(current)
1067 setscripts(current)
1068 current = convert_close(initial,current,middle)
1069 setscripts(current,prime, sup, sub, presup, presub)
1070 if not parent then
1071 initial = current
1072 end
1073 end
1074 elseif a == 3 then
1075 if trace_fences then
1076 report_fences("%2i: level %i, handling %s, action %s",n,level,"middle","middle")
1077 end
1078 if middle then
1079 middle[current] = last
1080 else
1081 middle = { [current] = last }
1082 end
1083 elseif a == 4 then
1084 if not stack or #stack == 0 then
1085 if trace_fences then
1086 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","open")
1087 end
1088 insert(stack,current)
1089 else
1090 local open = remove(stack)
1091 if open then
1092 if trace_fences then
1093 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","both")
1094 end
1095 current = convert_both(open,current,middle)
1096 elseif current == start then
1097 if trace_fences then
1098 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","skip")
1099 end
1100 else
1101 if trace_fences then
1102 report_fences("%2i: level %i, handling %s, action %s",n,level,"both","close")
1103 end
1104 current = convert_close(initial,current,middle)
1105 if not parent then
1106 initial = current
1107 end
1108 end
1109 end
1110 end
1111 done = true
1112 else
1113 processstep(current,processfences,n+1,id)
1114 end
1115 else
1116
1117 processstep(current,processfences,n,id)
1118 end
1119
1120 last = current
1121 current = getnext(current)
1122 end
1123 if done then
1124 local stack = stacks[n]
1125 local s = #stack
1126 if s > 0 then
1127 for i=1,s do
1128 local open = remove(stack)
1129 if trace_fences then
1130 report_fences("%2i: level %i, handling %s, action %s",n,#stack,"flush","open")
1131 end
1132 last = convert_open(open,last,middle)
1133 end
1134
1135 end
1136 end
1137 end
1138
1139
1140
1141
1142 local enabled = false
1143
1144 implement {
1145 name = "enableautofences",
1146 onlyonce = true,
1147 actions = function()
1148 enableaction("math","noads.handlers.autofences")
1149 enabled = true
1150 end
1151 }
1152
1153 function handlers.autofences(head,style,penalties)
1154 if enabled then
1155
1156 processfences(head,1)
1157
1158 end
1159 end
1160
1161end
1162
1163
1164
1165do
1166
1167 local unscript = { } noads.processors.unscript = unscript
1168 local superscripts = characters.superscripts
1169 local subscripts = characters.subscripts
1170 local fractions = characters.fractions
1171 local replaced = { }
1172
1173 local function replace(pointer,what,n,parent)
1174 pointer = parent
1175 local next = getnext(pointer)
1176 local start_super, stop_super, start_sub, stop_sub
1177 local mode = "unset"
1178 while next and getid(next) == noad_code do
1179 local nextnucleus = getnucleus(next)
1180 if nextnucleus and getid(nextnucleus) == mathchar_code and not getsub(next) and not getsup(next) then
1181 local char = getchar(nextnucleus)
1182 local s = superscripts[char]
1183 if s then
1184 if not start_super then
1185 start_super = next
1186 mode = "super"
1187 elseif mode == "sub" then
1188 break
1189 end
1190 stop_super = next
1191 next = getnext(next)
1192 setchar(nextnucleus,s)
1193 replaced[char] = (replaced[char] or 0) + 1
1194 if trace_normalizing then
1195 report_normalizing("superscript %C becomes %C",char,s)
1196 end
1197 else
1198 local s = subscripts[char]
1199 if s then
1200 if not start_sub then
1201 start_sub = next
1202 mode = "sub"
1203 elseif mode == "super" then
1204 break
1205 end
1206 stop_sub = next
1207 next = getnext(next)
1208 setchar(nextnucleus,s)
1209 replaced[char] = (replaced[char] or 0) + 1
1210 if trace_normalizing then
1211 report_normalizing("subscript %C becomes %C",char,s)
1212 end
1213 else
1214 break
1215 end
1216 end
1217 else
1218 break
1219 end
1220 end
1221 if start_super then
1222 if start_super == stop_super then
1223 setsup(pointer,getnucleus(start_super))
1224 else
1225 local list = new_submlist()
1226 setattrlist(list,pointer)
1227 setlist(list,start_super)
1228 setsup(pointer,list)
1229 end
1230 if mode == "super" then
1231 setnext(pointer,getnext(stop_super))
1232 end
1233 setnext(stop_super)
1234 end
1235 if start_sub then
1236 if start_sub == stop_sub then
1237 setsub(pointer,getnucleus(start_sub))
1238 else
1239 local list = new_submlist()
1240 setattrlist(list,pointer)
1241 setlist(list,start_sub)
1242 setsub(pointer,list)
1243 end
1244 if mode == "sub" then
1245 setnext(pointer,getnext(stop_sub))
1246 end
1247 setnext(stop_sub)
1248 end
1249
1250 end
1251
1252 unscript[mathchar_code] = replace
1253
1254 function handlers.unscript(head,style,penalties)
1255 processnoads(head,unscript,"unscript")
1256 end
1257
1258end
1259
1260do
1261
1262 local unstack = { } noads.processors.unstack = unstack
1263 local enabled = false
1264 local a_unstack <const> = privateattribute("mathunstack")
1265
1266 local trace_unstacking = false registertracker("math.unstack", function(v) trace_unstacking = v end)
1267 local report_unstacking = logreporter("mathematics","unstack")
1268
1269 local indexedsubscript_code <const> = tex.noadoptioncodes.indexedsubscript
1270 local indexedsuperscript_code <const> = tex.noadoptioncodes.indexedsuperscript
1271
1272 unstack[noad_code] = function(pointer)
1273 local a = getattr(pointer,a_unstack)
1274 if a then
1275 local sup = getsup(pointer)
1276 local sub = getsub(pointer)
1277 if sup and sub then
1278
1279
1280
1281 if a == 1 then
1282 a = indexedsubscript_code
1283 elseif a == 2 then
1284 a = indexedsuperscript_code
1285 else
1286 a = 0
1287 end
1288 setoptions(pointer,getoptions(pointer) | a)
1289 end
1290 end
1291 end
1292
1293 function handlers.unstack(head,style,penalties)
1294 if enabled then
1295 processnoads(head,unstack,"unstack")
1296
1297 end
1298 end
1299
1300 implement {
1301 name = "enablescriptunstacking",
1302 onlyonce = true,
1303 actions = function()
1304 enableaction("math","noads.handlers.unstack")
1305 enabled = true
1306 end
1307 }
1308
1309end
1310
1311do
1312
1313 local function collected(fontlist)
1314 if fontlist and next(fontlist) then
1315 local properties = fonts.hashes.properties
1316 local result = { }
1317 for font, list in sortedhash(fontlist) do
1318 if list and next(list) then
1319 local n, t = 0, { }
1320 for k, v in sortedhash(list) do
1321 n = n + 1
1322 t[n] = formatters["%C"](k)
1323 end
1324 local p = properties[font]
1325 result[#result+1] = formatters["%s : %i : [ % t ]"](p and p.fontname or "unknown",n,t)
1326 end
1327 end
1328 if #result > 0 then
1329 return concat(result, ", ")
1330 end
1331 end
1332 end
1333
1334 statistics.register("math script replacements", function()
1335 return collected(replaced)
1336 end)
1337
1338 statistics.register("unknown math characters", function()
1339 return collected(unknowns)
1340 end)
1341
1342end
1343
1344
1345
1346
1347
1348
1349
1350
1351do
1352
1353 local trace_alternates = false registertracker("math.alternates", function(v) trace_alternates = v end)
1354 local report_alternates = logreporter("mathematics","alternates")
1355
1356 local last = 0
1357
1358 local known = setmetatableindex(function(t,k)
1359 local v = 0 | 2^last
1360 t[k] = v
1361 last = last + 1
1362 return v
1363 end)
1364
1365 local defaults = {
1366 dotless = { feature = 'dtls', value = 1, comment = "Mathematical Dotless Forms" },
1367
1368 }
1369
1370 local function initializemathalternates(tfmdata)
1371 if use_math_goodies then
1372
1373 local goodies = tfmdata.goodies
1374 local autolist = defaults
1375
1376 local function setthem(newalternates)
1377 local resources = tfmdata.resources
1378 local mathalternates = resources.mathalternates
1379 local alternates, attributes, registered, presets
1380 if mathalternates then
1381 alternates = mathalternates.alternates
1382 attributes = mathalternates.attributes
1383 registered = mathalternates.registered
1384 else
1385 alternates, attributes, registered = { }, { }, { }
1386 mathalternates = {
1387 attributes = attributes,
1388 alternates = alternates,
1389 registered = registered,
1390 presets = { },
1391 resets = { },
1392 hashes = setmetatableindex("table")
1393 }
1394 resources.mathalternates = mathalternates
1395 end
1396
1397 for name, data in sortedhash(newalternates) do
1398 if alternates[name] then
1399
1400 else
1401 local attr = known[name]
1402 attributes[attr] = data
1403 alternates[name] = attr
1404 registered[#registered+1] = attr
1405 end
1406 end
1407 end
1408
1409 if goodies then
1410 local done = { }
1411 for i=1,#goodies do
1412
1413
1414 local mathgoodies = goodies[i].mathematics
1415 local alternates = mathgoodies and mathgoodies.alternates
1416 if alternates then
1417 if trace_goodies then
1418 report_goodies("loading alternates for font %a",tfmdata.properties.name)
1419 end
1420 for k, v in next, autolist do
1421 if not alternates[k] then
1422 alternates[k] = v
1423 end
1424 end
1425 setthem(alternates)
1426 return
1427 end
1428 end
1429 end
1430
1431 if trace_goodies then
1432 report_goodies("loading default alternates for font %a",tfmdata.properties.name)
1433 end
1434 setthem(autolist)
1435 end
1436
1437 end
1438
1439 local otf = fonts.handlers.otf
1440 local registerotffeature = fonts.constructors.features.otf.register
1441
1442
1443 registerotffeature {
1444 name = "mathalternates",
1445 description = "additional math alternative shapes",
1446 initializers = {
1447 base = initializemathalternates,
1448 node = initializemathalternates,
1449 }
1450 }
1451
1452
1453
1454 local a_mathalternate <const> = privateattribute("mathalternate")
1455 local alternate = { }
1456 local fontdata = fonts.hashes.identifiers
1457 local fontresources = fonts.hashes.resources
1458
1459 local function getalternate(fam,tag,current)
1460
1461 local resources = fontresources[getfontoffamily(fam)]
1462 local attribute = unsetvalue
1463 if resources then
1464 local mathalternates = resources.mathalternates
1465 if mathalternates then
1466 local presets = mathalternates.presets
1467 if presets then
1468 local resets = mathalternates.resets
1469 attribute = presets[tag]
1470 if not attribute then
1471 attribute = 0
1472 local alternates = mathalternates.alternates
1473 for s in gmatch(tag,"[^, ]+") do
1474 local n, s = match(s,"^([+-]*)(.*)$")
1475 if s == v_reset then
1476 resets[tag] = true
1477 current = unsetvalue
1478 else
1479 local a = alternates[s]
1480 if a then
1481 if n == "-" then
1482 attribute = attribute & ~a
1483 else
1484 attribute = attribute | a
1485 end
1486 end
1487 end
1488 end
1489 if attribute == 0 then
1490 attribute = unsetvalue
1491 end
1492 presets[tag] = attribute
1493 elseif resets[tag] then
1494 current = unsetvalue
1495 end
1496 end
1497 end
1498 end
1499 if attribute > 0 and current and current > 0 then
1500 return current | attribute
1501 else
1502 return attribute
1503 end
1504 end
1505
1506 implement {
1507 name = "presetmathfontalternate",
1508 arguments = "argument",
1509 public = true,
1510 protected = true,
1511 actions = function(tag)
1512 if texgetmode() == mathmode_code then
1513 texsetattribute(a_mathalternate,getalternate(0,tag))
1514 end
1515 end,
1516 }
1517
1518 implement {
1519 name = "setmathfontalternate",
1520 arguments = "argument",
1521 public = true,
1522 protected = true,
1523 actions = function(tag)
1524 if texgetmode() == mathmode_code then
1525 texsetattribute(a_mathalternate,getalternate(0,tag,texgetattribute(a_mathalternate)))
1526 end
1527 end,
1528 }
1529
1530 alternate[mathchar_code] = function(pointer)
1531 local a = getattr(pointer,a_mathalternate)
1532 if a and a > 0 then
1533 setattr(pointer,a_mathalternate,0)
1534 local fontid = getfont(pointer)
1535 local resources = fontresources[fontid]
1536 if resources then
1537 local mathalternates = resources.mathalternates
1538 if mathalternates then
1539 local attributes = mathalternates.attributes
1540 local registered = mathalternates.registered
1541 local hashes = mathalternates.hashes
1542 local newchar = nil
1543 for i=1,#registered do
1544 local r = registered[i]
1545 if (a & r) ~= 0 then
1546 local char = newchar or getchar(pointer)
1547 local alt = hashes[i][char]
1548 if alt == nil then
1549 local what = attributes[r]
1550 local list = what.list
1551 if not list or list[char] then
1552 alt = otf.getalternate(fontdata[fontid],char,what.feature,what.value) or false
1553 if alt == char then
1554 alt = false
1555 end
1556 hashes[i][char] = alt
1557 end
1558 end
1559 if alt then
1560 if trace_alternates then
1561 local what = attributes[r]
1562 report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U",
1563 tostring(what.feature),tostring(what.value),char,alt)
1564 end
1565
1566
1567 newchar = alt
1568 end
1569 end
1570 end
1571 if newchar then
1572 setchar(pointer,newchar)
1573 end
1574 end
1575 end
1576 end
1577 end
1578
1579 alternate[delimiter_code] = alternate[mathchar_code]
1580
1581 function handlers.alternates(head,style,penalties)
1582 processnoads(head,alternate,"alternate")
1583 end
1584
1585end
1586
1587
1588
1589
1590do
1591
1592 local enable = function()
1593 if trace_italics then
1594 report_italics("enabling math italics")
1595 end
1596
1597 typesetters.italics.enablemath()
1598 enable = false
1599 end
1600
1601 implement {
1602 name = "initializemathitalics",
1603 actions = enable,
1604 onlyonce = true,
1605 }
1606
1607end
1608
1609do
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621 local a_kernpairs <const> = privateattribute("mathkernpairs")
1622 local kernpairs = { }
1623
1624 local trace_kernpairs = false registertracker("math.kernpairs", function(v) trace_kernpairs = v end)
1625 local report_kernpairs = logreporter("mathematics","kernpairs")
1626
1627 local function enable()
1628 enableaction("math", "noads.handlers.kernpairs")
1629 if trace_kernpairs then
1630 report_kernpairs("enabling math kern pairs")
1631 end
1632 enable = false
1633 end
1634
1635 implement {
1636 name = "initializemathkernpairs",
1637 actions = enable,
1638 onlyonce = true,
1639 }
1640
1641 local hash = setmetatableindex(function(t,font)
1642 local g = fontdata[font].goodies
1643 local m = g and g[1] and g[1].mathematics
1644 local k = m and m.kernpairs
1645 t[font] = k
1646 return k
1647 end)
1648
1649
1650
1651 kernpairs[mathchar_code] = function(pointer,what,n,parent)
1652 if getattr(pointer,a_kernpairs) == 1 then
1653 local first, firstfont = getcharspec(pointer)
1654 local list = hash[firstfont]
1655 if list then
1656 local found = list[first]
1657 if found then
1658 local next = isnext(parent,noad_code)
1659 if next then
1660
1661
1662 local second, secondfont = getcharspec(pointer)
1663 if secondfont == firstfont then
1664 local kern = found[second]
1665 if kern then
1666 kern = kern * fontparameters[firstfont].hfactor
1667 if trace_kernpairs then
1668 report_kernpairs("adding %p kerning between %C and %C",kern,first,second)
1669 end
1670 kern = new_kern(kern)
1671 setlink(parent,kern,getnext(parent))
1672 setattrlist(kern,pointer)
1673 end
1674 end
1675
1676 end
1677 end
1678 end
1679 end
1680 end
1681
1682 function handlers.kernpairs(head,style,penalties)
1683 if use_math_goodies then
1684 processnoads(head,kernpairs,"kernpairs")
1685 end
1686 end
1687
1688end
1689
1690do
1691
1692 local a_numbers <const> = privateattribute("mathnumbers")
1693 local a_spacing <const> = privateattribute("mathspacing")
1694 local a_fencing <const> = privateattribute("mathfencing")
1695
1696 local numbers = { }
1697 local spacing = { }
1698 local fencing = { }
1699
1700 local separators = {
1701 [0x2E] = { 0x2E, 0x2C, 0x002E, 0x002C, 0x2008, 0x2008 },
1702 [0x2C] = { 0x2C, 0x2E, 0x2008, 0x2008, 0x002E, 0x002C },
1703 }
1704
1705 local digits = {
1706 [0x30] = true, [0x31] = true,
1707 [0x32] = true, [0x33] = true,
1708 [0x34] = true, [0x35] = true,
1709 [0x36] = true, [0x37] = true,
1710 [0x38] = true, [0x39] = true,
1711 }
1712
1713 local snoloc = {
1714 [punctuation_class] = 0x003A,
1715 [relation_class] = 0x2236,
1716 }
1717
1718 local colons = {
1719 [0x003A] = snoloc,
1720 [0x2236] = snoloc,
1721 }
1722
1723 local middles = {
1724 [0x007C] = true,
1725 [0x2016] = true,
1726 [0x2980] = true,
1727 }
1728
1729 local singles = {
1730 0x007C,
1731 0x2016,
1732 0x2980,
1733 }
1734
1735 local followedbyspace_code <const> = tex.noadoptioncodes.followedbyspace
1736
1737 local function followedbyspace(n)
1738 return n and (getoptions(n) & followedbyspace_code == followedbyspace_code)
1739 end
1740
1741 local function followbyspace(n)
1742 setoptions(n,getoptions(n) | followedbyspace_code)
1743 end
1744
1745 numbers[mathchar_code] = function(pointer,what,n,parent)
1746 local alternative = getattr(pointer,a_numbers)
1747 if alternative then
1748 local oldchar = getcharspec(pointer)
1749 local found = separators[oldchar]
1750 if found then
1751 local prev, next = isboth(parent,noad_code)
1752 if prev and next then
1753
1754 local lc = getcharspec(prev)
1755 if digits[lc] then
1756
1757 local rc = getcharspec(next)
1758 if digits[rc] then
1759 local newchar = found[alternative]
1760 local class = followedbyspace(parent) and punctuation_class or ordinary_class
1761
1762
1763setclass(parent,class)
1764 if newchar ~= oldchar then
1765 setchar(pointer,newchar)
1766 end
1767
1768
1769
1770 end
1771 end
1772 end
1773 return
1774 end
1775 found = digits[oldchar]
1776 if found then
1777 if followedbyspace(parent) then
1778 local next = isnext(parent,noad_code)
1779 if next then
1780
1781 local rc = getcharspec(next)
1782 if rc and digits[rc] then
1783 local n = new_noad(numbergroup_class)
1784 local s = new_submlist()
1785 setnucleus(n,s)
1786 setattrlist(n,pointer)
1787 setattrlist(s,pointer)
1788 setlink(parent,n,next)
1789
1790
1791
1792 end
1793 end
1794 end
1795 return
1796 end
1797 end
1798 end
1799
1800 spacing[mathchar_code] = function(pointer,what,n,parent)
1801 if getattr(pointer,a_spacing) then
1802 local oldchar = getcharspec(pointer)
1803 local found = colons[oldchar]
1804 if found then
1805 local prev = isprev(parent,noad_code)
1806 if prev then
1807 local class = followedbyspace(prev) and relation_class or punctuation_class
1808 local newchar = found[class]
1809
1810setclass(parent,class)
1811 if newchar ~= oldchar then
1812 setchar(pointer,newchar)
1813 end
1814
1815
1816
1817 end
1818 return
1819 end
1820 end
1821 end
1822
1823
1824
1825 local function makefence(chr,fam,subtype,class,template)
1826 local f = new_fence()
1827 local d = new_delimiter()
1828 setchar(d,chr)
1829 setfam(d,fam)
1830 setattrlist(d,template)
1831 setattrlist(f,template)
1832 setsubtype(f,subtype)
1833 setdelimiter(f,d)
1834 setclass(f,class)
1835 return f
1836 end
1837
1838
1839
1840 fencing[mathchar_code] = function(pointer,what,n,parent)
1841 if getattr(pointer,a_fencing) and pointer == getnucleus(parent) then
1842 local oldchar = getcharspec(pointer)
1843 local found = middles[oldchar]
1844 if found then
1845 local prev, next = getboth(parent)
1846 if getcharspec(next) == oldchar and not followedbyspace(parent) then
1847 local nextnext = getnext(next)
1848
1849 if getcharspec(nextnext) == oldchar and not followedbyspace(next) then
1850 oldchar = singles[3]
1851 prev, parent = nuts.remove(prev,parent,true)
1852 prev, parent = nuts.remove(prev,parent,true)
1853 else
1854 oldchar = singles[2]
1855 prev, parent = nuts.remove(prev,parent,true)
1856 end
1857 next = getnext(parent)
1858 pointer = getnucleus(parent)
1859 setchar(pointer,oldchar)
1860 end
1861 if followedbyspace(prev) and followedbyspace(parent) then
1862 local chr, fnt, fam = getcharspec(pointer)
1863 local f1 = makefence(0,0,0,0,pointer)
1864 local f2 = makefence(chr,fam,middlefence_code,middle_class,pointer)
1865 setlink(prev,f1,f2,next)
1866 flushnode(parent)
1867 followbyspace(f1)
1868 followbyspace(f2)
1869 return true, f2
1870 else
1871 return true, parent
1872 end
1873 end
1874 end
1875 end
1876
1877
1878
1879 function handlers.numbers(head,style,penalties)
1880 processnoads(head,numbers,"numbers")
1881 end
1882
1883 local enable = function()
1884 enableaction("math", "noads.handlers.numbers")
1885
1886
1887
1888 enable = false
1889 end
1890
1891 implement {
1892 name = "initializemathnumbers",
1893 actions = enable,
1894 onlyonce = true,
1895 }
1896
1897
1898
1899 function handlers.spacing(head,style,penalties)
1900 processnoads(head,spacing,"spacing")
1901 end
1902
1903 local enable = function()
1904 enableaction("math", "noads.handlers.spacing")
1905
1906
1907
1908 enable = false
1909 end
1910
1911 implement {
1912 name = "initializemathspacing",
1913 actions = enable,
1914 onlyonce = true,
1915 }
1916
1917
1918
1919 function handlers.fencing(head,style,penalties)
1920 processnoads(head,fencing,"fencing")
1921 end
1922
1923 local enable = function()
1924 enableaction("math", "noads.handlers.fencing")
1925
1926
1927
1928 enable = false
1929 end
1930
1931 implement {
1932 name = "initializemathfencing",
1933 actions = enable,
1934 onlyonce = true,
1935 }
1936
1937end
1938
1939
1940
1941do
1942
1943
1944
1945 local a_mathcollapsing <const> = privateattribute("mathcollapsing")
1946 local collapse = { }
1947 local mathlists = characters.mathlists
1948 local validpair = {
1949 [ordinary_class] = true,
1950 [operator_class] = true,
1951 [binary_class] = true,
1952 [relation_class] = true,
1953 [open_class] = true,
1954 [middle_class] = true,
1955 [close_class] = true,
1956 [punctuation_class] = true,
1957 [fraction_class] = true,
1958 [accent_class] = true,
1959 }
1960
1961 local reported = setmetatableindex("table")
1962
1963
1964
1965
1966 mathlists[39] = { [39] = { [39] = { enforced = 0x2034, [39] = { enforced = 0x2057 } }, enforced = 0x2033 }, enforced = 0x2032 }
1967 mathlists[96] = { [96] = { [96] = { enforced = 0x2037 }, enforced = 0x2036 }, enforced = 0x2035 }
1968
1969 local getchardict = nuts.getchardict
1970 local setchardict = nuts.setchardict
1971
1972 local groups = mathematics.dictionaries.groups
1973
1974 collapse[mathchar_code] = function(pointer,what,n,parent)
1975 if parent and mathlists[getchar(pointer)] then
1976 local found, last, lucleus, lsup, lsub, lprime, category
1977 local tree = mathlists
1978 local current = parent
1979 while current and validpair[getsubtype(current)] do
1980 local nucleus, prime, sup, sub = getnucleus(current,true)
1981 local char = getchar(nucleus)
1982 if char then
1983 local match = tree[char]
1984 if match then
1985 local method = getattr(current,a_mathcollapsing)
1986 if method and method > 0 and method <= 3 then
1987 local enforced = match.enforced
1988 local specials = match.specials
1989 local mathlist = match.mathlist
1990 local ligature
1991 if method == 0 then
1992 ligature = enforced
1993 elseif method == 1 then
1994 ligature = enforced or specials
1995 elseif method == 2 then
1996 ligature = enforced or specials or mathlist
1997 else
1998 ligature = enforced or mathlist or specials
1999 end
2000 if ligature then
2001 category = mathlist and "mathlist" or "specials"
2002 found = ligature
2003 last = current
2004 lucleus = nucleus
2005 lsup = sup
2006 lsub = sub
2007 lprime = prime
2008 end
2009 tree = match
2010 if sub or sup or prime then
2011 break
2012 else
2013 current = getnext(current)
2014 end
2015 else
2016 break
2017 end
2018 else
2019 break
2020 end
2021 else
2022 break
2023 end
2024 end
2025 if found and last and lucleus then
2026 local id = getfont(lucleus)
2027 local characters = fontcharacters[id]
2028 local replace = characters and characters[found]
2029 if not replace then
2030 if not reported[id][found] then
2031 reported[id][found] = true
2032 report_collapsing("%s ligature %C from %s","ignoring",found,category)
2033 end
2034 elseif trace_collapsing then
2035 report_collapsing("%s ligature %C from %s","creating",found,category)
2036 end
2037
2038
2039local properties, group = getchardict(pointer)
2040local c = chardata[found]
2041if c then
2042 local g = c.mathgroup
2043 if g then
2044 group = groups[g] or 0xFFFF
2045 else
2046 group = 0xFFFF
2047 end
2048else
2049 group = 0xFFFF
2050end
2051setchardict(pointer,properties,group,found)
2052
2053 setchar(pointer,found)
2054 local l = getnext(last)
2055 local c = getnext(parent)
2056 if lsub then
2057 setsub(parent,lsub)
2058 setsub(last)
2059 end
2060 if lsup then
2061 setsup(parent,lsup)
2062 setsup(last)
2063 end
2064 if lprime then
2065 setprime(parent,lprime)
2066 setprime(last)
2067 end
2068 while c ~= l do
2069 local n = getnext(c)
2070 flushnode(c)
2071 c = n
2072 end
2073 setlink(parent,l)
2074 end
2075 end
2076 end
2077
2078 function noads.handlers.collapse(head,style,penalties)
2079 processnoads(head,collapse,"collapse")
2080 end
2081
2082 local enable = function()
2083 enableaction("math", "noads.handlers.collapse")
2084 if trace_collapsing then
2085 report_collapsing("enabling math collapsing")
2086 end
2087 enable = false
2088 end
2089
2090 implement {
2091 name = "initializemathcollapsing",
2092 actions = enable,
2093 onlyonce = true,
2094 }
2095
2096end
2097
2098do
2099
2100 local variants = { }
2101
2102 local a_variant <const> = privateattribute("mathvariant")
2103
2104 local trace_variants = false registertracker("math.variants", function(v) trace_variants = v end)
2105 local report_variants = logreporter("mathematics","variants")
2106
2107 local function setvariant(pointer,selector,char)
2108 local tfmdata = fontdata[getfont(pointer)]
2109 local mathvariants = tfmdata.resources.variants
2110 if mathvariants then
2111 mathvariants = mathvariants[selector]
2112 if mathvariants then
2113 local variant = mathvariants[char]
2114 if variant then
2115 setchar(pointer,variant)
2116 setattr(pointer,a_exportstatus,char)
2117 if trace_variants then
2118 report_variants("variant (%U,%U) replaced by %U",char,selector,variant)
2119 end
2120 else
2121 if trace_variants then
2122 report_variants("no variant (%U,%U)",char,selector)
2123 end
2124 end
2125 end
2126 end
2127 end
2128
2129 variants[mathchar_code] = function(pointer,what,n,parent)
2130 local char = getchar(pointer)
2131 local data = chardata[char]
2132 if data then
2133 local variants = data.variants
2134 if variants then
2135 local next = isnext(parent,noad_code)
2136 if next then
2137 local nucleus = getnucleus(next)
2138 if nucleus and getid(nucleus) == mathchar_code then
2139 local selector = getchar(nucleus)
2140 if variants[selector] then
2141 setvariant(pointer,selector,char)
2142 setprev(next,pointer)
2143 setnext(parent,getnext(next))
2144 flushnode(next)
2145 end
2146 end
2147 end
2148 local selector = getattr(pointer,a_variant)
2149 if selector and variants[selector] then
2150 setvariant(pointer,selector,char)
2151 end
2152 end
2153 end
2154 end
2155
2156 function mathematics.addvariant(tfmdata,char,variant,selector)
2157 if char and variant and selector then
2158 local data = chardata[char]
2159 if data then
2160 local variants = data.variants
2161 if variants and variants[selector] then
2162 local resources = tfmdata.resources
2163 local variants = resources.variants
2164 if not variants then
2165 variants = { }
2166 resources.variants = variants
2167 end
2168 local selectors = variants[selector]
2169 if not selectors then
2170 selectors = { }
2171 variants[selector] = selectors
2172 end
2173 selectors[char] = variant
2174 return true
2175 end
2176 end
2177 end
2178 return false
2179 end
2180
2181 function handlers.variants(head,style,penalties)
2182 processnoads(head,variants,"unicode variant")
2183 end
2184
2185 local valid = {
2186 calligraphic = 0xFE00,
2187 calligraphy = 0xFE00,
2188 script = 0xFE01,
2189 handwriting = 0xFE01,
2190 }
2191
2192 function mathematics.setvariant(s)
2193 texsetattribute(a_variant,valid[s] or unsetvalue)
2194 end
2195
2196 implement {
2197 name = "setmathvariant",
2198 public = true,
2199 protected = true,
2200 arguments = "argument",
2201 actions = mathematics.setvariant,
2202 }
2203
2204end
2205
2206
2207
2208do
2209
2210
2211
2212
2213 local classes = { }
2214 local colors = {
2215 [relation_class] = "trace:dr",
2216 [ordinary_class] = "trace:db",
2217 [binary_class] = "trace:dg",
2218 [open_class] = "trace:dm",
2219 [middle_class] = "trace:dm",
2220 [close_class] = "trace:dm",
2221 [punctuation_class] = "trace:dc",
2222 }
2223
2224 local setcolor = colortracers.set
2225 local resetcolor = colortracers.reset
2226
2227 classes[mathchar_code] = function(pointer,what,n,parent)
2228 local color = colors[getsubtype(parent)]
2229 if color then
2230 setcolor(pointer,color)
2231 else
2232 resetcolor(pointer)
2233 end
2234 end
2235
2236 function handlers.classes(head,style,penalties)
2237 processnoads(head,classes,"classes")
2238 end
2239
2240 registertracker("math.classes",function(v)
2241 setaction("math","noads.handlers.classes",v)
2242 end)
2243
2244end
2245
2246do
2247
2248 local traversehlist = nuts.traversers.hlist
2249
2250 local getshift = nuts.getshift
2251 local setwhd = nuts.setwhd
2252 local setshift = nuts.setshift
2253
2254
2255
2256 local function normalize(h)
2257 for n, s in traversehlist, h do
2258 if s > 0 then
2259 local sh = getshift(n)
2260 local ox, oy = getoffsets(n)
2261 if sh ~= 0 then
2262 local w, h, d = getwhd(n)
2263 h = h - sh
2264 d = d + sh
2265 setshift(n)
2266 setwhd(n,w,h > 0 and h or 0,d > 0 and d or 0)
2267 setoffsets(n,ox,oy - sh)
2268 end
2269 end
2270 local l = getlist(l)
2271 if l then
2272 normalize(l)
2273 end
2274 end
2275 end
2276
2277 function handlers.normalize(h)
2278 return normalize(h)
2279 end
2280
2281end
2282
2283do
2284
2285 local traversehlist = nuts.traversers.hlist
2286
2287 local texgetdimen = tex.getdimen
2288 local texgetcount = tex.getcount
2289
2290 local newrule = nuts.pool.outlinerule
2291 local newkern = nuts.pool.kern
2292 local setcolor = nodes.tracers.colors.set
2293
2294 local a_mathsnap <const> = privateattribute("mathsnap")
2295
2296 local d_mathstrutht <const> = tex.isdimen("mathstrutht")
2297 local d_mathstrutdp <const> = tex.isdimen("mathstrutdp")
2298 local c_mathnesting <const> = tex.iscount("mathnestinglevel")
2299
2300 local trace_snapping = false registertracker("math.snapping", function(v) trace_snapping = v end)
2301 local report_snapping = logreporter("mathematics","snapping")
2302
2303 function handlers.snap(h,_,_,_,_,level)
2304
2305 if texgetcount(c_mathnesting) == 1 then
2306 local trace_color
2307 if trace_snapping == "frame" then
2308 trace_color = "darkgray"
2309 elseif type(trace_snapping) == "string" then
2310 trace_color = trace_snapping
2311 else
2312 trace_color = false
2313 end
2314 local ht, dp, dd, hs, ds, hd
2315 for n, s in traversehlist, h do
2316 local step = getattr(n,a_mathsnap)
2317 if step then
2318 local done = false
2319 if not dd then
2320 ht = texgetdimen(d_mathstrutht)
2321 dp = texgetdimen(d_mathstrutdp)
2322 hd = ht + dp
2323
2324
2325 dd = hd / 6
2326 if step == 0xFFFF then
2327 hs = dd
2328 ds = dd
2329 else
2330 hs = ht/step
2331 ds = dp/step
2332 end
2333 end
2334 local w, h, d = getwhd(n)
2335
2336 ::height::
2337 if h-dd < ht then
2338 if trace_snapping == true then
2339 report_snapping("adapting ht: old %p, new %p, lineskip %p",h,ht,dd)
2340 end
2341 done = true
2342 setheight(n,ht)
2343 goto depth
2344 end
2345 if h > ht then
2346
2347 while ht < h do
2348 ht = round(ht + hs)
2349 end
2350 if h ~= ht then
2351 setheight(n,ht)
2352 if trace_snapping == true then
2353 report_snapping("enlarging ht: old %p, new %p, step %p",h,ht,hs)
2354 end
2355 done = true
2356 end
2357 end
2358 ::depth::
2359 if d-dd < dp then
2360 if trace_snapping == true then
2361 report_snapping("adapting dp: old %p, new %p, lineskip %p",d,dp,dd)
2362 end
2363 setdepth(n,dp)
2364 done = true
2365 goto done
2366 end
2367 if d > dp then
2368
2369 while dp < d do
2370 dp = round(dp + ds)
2371 end
2372 if d ~= dp then
2373 setdepth(n,dp)
2374 if trace_snapping == true then
2375 report_snapping("enlarging dp: old %p, new %p, step %p",d,dp,ds)
2376 end
2377 done = true
2378 end
2379 end
2380 ::done::
2381 if done and trace_color then
2382
2383
2384
2385
2386
2387
2388 local old = newrule(w,h,d,65536)
2389 setcolor(old,"middlegray")
2390 w, h, d = getwhd(n)
2391 local new = newrule(w,h,d,65536/4)
2392 setcolor(new,trace_color)
2393 setlink(old,newkern(-w),new,newkern(-w),getlist(n))
2394 local ox, oy = getoffsets(n)
2395 setoffsets(old,-ox,-oy)
2396 setoffsets(new,-ox,-oy)
2397 setlist(n,old)
2398 end
2399 end
2400 end
2401 end
2402 end
2403
2404 local valid = {
2405 [v_reset] = unsetvalue,
2406 [v_line] = 0xFFFF,
2407 [v_small] = 8,
2408 [v_medium] = 4,
2409 [v_big] = 2,
2410 }
2411
2412 function mathematics.setsnapping(s)
2413 if not enabled then
2414 enableaction("math", "noads.handlers.snap")
2415 enabled = true
2416 end
2417 texsetattribute(a_mathsnap,valid[s] or unsetvalue)
2418 end
2419
2420 implement {
2421 name = "setmathsnapping",
2422 public = true,
2423 protected = true,
2424 arguments = "argument",
2425 actions = mathematics.setsnapping,
2426 }
2427
2428end
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
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
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594function handlers.showtree(head,style,penalties)
2595 inspect(nodes.totree(tonut(head)))
2596end
2597
2598registertracker("math.showtree",function(v)
2599 setaction("math","noads.handlers.showtree",v)
2600end)
2601
2602
2603
2604do
2605
2606 local applyvisuals = nuts.applyvisuals
2607 local visual = false
2608
2609 function handlers.makeup(head)
2610 applyvisuals(head,visual)
2611 end
2612
2613 registertracker("math.makeup",function(v)
2614 visual = v
2615 setaction("math","noads.handlers.makeup",v)
2616 end)
2617
2618end
2619
2620
2621
2622
2623
2624
2625do
2626
2627 local trace_dictionaries = false registertracker("math.dictionaries", function(v) trace_dictionaries = v end)
2628 local trace_details = false registertracker("math.dictionaries.details", function(v) trace_details = v end)
2629
2630 local report_dictionaries = logreporter("mathematics","dictionaries")
2631
2632 local setnodecolor = colortracers.set
2633 local getchardict = nuts.getchardict
2634 local setchardict = nuts.setchardict
2635
2636 local dictionaries = { } noads.processors.dictionaries = dictionaries
2637 local groups = mathematics.dictionaries.groups
2638 local sets = mathematics.dictionaries.sets
2639 local variants = mathematics.dictionaries.variants
2640 local defaults = mathematics.dictionaries.defaults
2641
2642 local function check(pointer,group,index)
2643 local v = variants[index]
2644 if v then
2645 local c = v[group]
2646 if c then
2647 return group, c
2648 end
2649 end
2650 return 1
2651 end
2652
2653 dictionaries[mathchar_code] = function(pointer,what,n,parent)
2654 local properties, oldgroup, index, font, char = getchardict(pointer)
2655 local newgroup = 1
2656 local newclass = false
2657
2658 local oldclass = getsubtype(parent)
2659 if (properties & 0x1) == 0x1 then
2660 newclass = oldclass
2661 newgroup = oldgroup
2662 else
2663 local set = sets[oldgroup]
2664 if set then
2665 local groups = set.groups
2666 local nofgroups = groups and #groups
2667 if nofgroups > 0 then
2668 for i=1,nofgroups do
2669 local group = groups[i]
2670 local real, class = check(pointer,group,index)
2671 if real ~= 1 then
2672 newclass = class
2673 newgroup = group
2674 goto done
2675 end
2676 end
2677 end
2678 else
2679 newgroup, newclass = check(pointer,oldgroup,index)
2680 end
2681 ::done::
2682 if newgroup == 1 then
2683 newgroup = defaults[index] or 1
2684 end
2685 setchardict(pointer,properties,newgroup,index)
2686 if type(newclass) == "number" then
2687
2688 setsubtype(parent,newclass)
2689
2690 else
2691 newclass = oldclass
2692 end
2693 end
2694 if trace_dictionaries or trace_details then
2695 if newgroup > 1 then
2696 local groupname = groups[newgroup]
2697 if groupname then
2698 setnodecolor(pointer,"dictionary:"..groupname)
2699 end
2700 end
2701 if trace_details then
2702 report_dictionaries("properties 0x%02X, group 0x%02X -> 0x%02X, class 0x%02X -> 0x%02X, index %05X, %U %c",properties,oldgroup,newgroup,oldclass,newclass,index,char,char)
2703 end
2704 end
2705 end
2706
2707 function handlers.dictionaries(head,style,penalties)
2708 processnoads(head,dictionaries,"dictionaries")
2709 end
2710
2711end
2712
2713do
2714
2715 local trace_suspicious = false registertracker("math.suspicious", function(v) trace_suspicious = v end)
2716 local report_suspicious = logreporter("mathematics","suspicious")
2717
2718 local suspicious = { } noads.processors.suspicious = suspicious
2719
2720 local candidates = {
2721 [classes.maybeordinary] = "maybeordinary",
2722 [classes.mayberelation] = "mayberelation",
2723 [classes.maybebinary ] = "maybebinary",
2724 }
2725 local registered = setmetatableindex("table")
2726
2727 suspicious[mathchar_code] = function(pointer,what,n,parent)
2728 local class = getsubtype(pointer)
2729 local found = candidates[class]
2730 if found then
2731 local char = getchar(pointer)
2732 if not registered[class][char] then
2733 report_suspicious("class %a, %U %c",found,char,char)
2734 registered[class][char] = true
2735 end
2736 end
2737 end
2738
2739 function handlers.suspicious(head,style,penalties)
2740 if trace_suspicious then
2741 processnoads(head,suspicious,"suspicious")
2742 end
2743 end
2744
2745end
2746
2747do
2748
2749 local trace_intervals = false registertracker("math.intervals", function(v) trace_intervals = v end)
2750 local report_intervals = logreporter("mathematics","intervals")
2751
2752 local a_mathintervals <const> = privateattribute("mathintervals")
2753
2754 local intervals = { } noads.processors.intervals = intervals
2755
2756
2757
2758 local firstclass = false
2759 local firstparent = false
2760 local firstpointer = false
2761 local punctuation = false
2762
2763 local ignored = {
2764 [relation_class] = true,
2765 [middle_class] = true,
2766 [fenced_class] = true,
2767 [fraction_class] = true,
2768 [radical_class] = true,
2769 [accent_class] = true,
2770 }
2771
2772 intervals[mathchar_code] = function(pointer,what,n,parent)
2773 local class = getsubtype(pointer)
2774 if class == open_class or class == close_class then
2775 local c = getchar(pointer)
2776 if c == 0x5B or c == 0x5D then
2777 local a = getattr(pointer,a_mathintervals)
2778 if not a then
2779 firstclass = false
2780 punctuation = false
2781 elseif firstclass then
2782 if not punctuation then
2783
2784 else
2785 if firstclass ~= open_class then
2786 setclass(firstparent, open_class)
2787 setprop(firstpointer,"swappedclass",open_class)
2788 if trace_intervals then
2789 report_intervals("setting class of %C to %a",getchar(firstpointer),"open")
2790 end
2791 end
2792 if class ~= close_class then
2793 setclass(parent, close_class)
2794 setprop(pointer,"swappedclass",close_class)
2795 if trace_intervals then
2796 report_intervals("setting class of %C to %a",getchar(pointer),"close")
2797 end
2798 end
2799 end
2800 firstclass = false
2801 punctuation = false
2802 else
2803 firstclass = class
2804 firstpointer = pointer
2805 firstparent = parent
2806 end
2807 else
2808 firstclass = false
2809 punctuation = false
2810 end
2811 elseif class == punctuation_class then
2812 local c = getchar(pointer)
2813 if c == 0x2C or c == 0x3A or c == 0x3B then
2814 punctuation = true
2815 else
2816 punctuation = false
2817 end
2818 elseif ignored[class] then
2819 firstclass = false
2820 punctuation = false
2821 end
2822 end
2823
2824 function handlers.intervals(head,style,penalties)
2825 processnoads(head,intervals,"intervals")
2826 end
2827
2828end
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850builders.kernel.mlisttohlist = mlisttohlist
2851
2852local actions = tasks.actions("math")
2853
2854local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
2855
2856function processors.mlisttohlist(head,style,penalties,beginclass,endclass,level,style)
2857 starttiming(noads)
2858 head = actions(head,style,penalties,beginclass,endclass,level,style)
2859 stoptiming(noads)
2860 return head
2861end
2862
2863callbacks.register("mlist_to_hlist",processors.mlisttohlist,"convert a noad list into a node list")
2864
2865
2866
2867statistics.register("math processing time", function()
2868 return statistics.elapsedseconds(noads)
2869end)
2870 |