1if not modules then modules = { } end modules ['trac-vis'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to trac-vis.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
10local node, nodes, attributes, tex = node, nodes, attributes, tex
11local type, tonumber, next, rawget = type, tonumber, next, rawget
12local gmatch, gsub = string.gmatch, string.gsub
13local formatters = string.formatters
14local round = math.round
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33local nuts = nodes.nuts
34local tonut = nuts.tonut
35
36local setboth = nuts.setboth
37local setlink = nuts.setlink
38local setlist = nuts.setlist
39local setsubtype = nuts.setsubtype
40local setattr = nuts.setattr
41local setwidth = nuts.setwidth
42local setshift = nuts.setshift
43local setoffsets = nuts.setoffsets
44
45local getid = nuts.getid
46local getfont = nuts.getfont
47local getattr = nuts.getattr
48local getsubtype = nuts.getsubtype
49local getbox = nuts.getbox
50local getlist = nuts.getlist
51local getprev = nuts.getprev
52local getnext = nuts.getnext
53local getboth = nuts.getboth
54local getwhd = nuts.getwhd
55local getkern = nuts.getkern
56local getpenalty = nuts.getpenalty
57local getwidth = nuts.getwidth
58local getdepth = nuts.getdepth
59local getexpansion = nuts.getexpansion
60local getstate = nuts.getstate
61local getoffsets = nuts.getoffsets
62local getindex = nuts.getindex
63local getprop = nuts.getprop
64
65local isglyph = nuts.isglyph
66
67local hpack_nodes = nuts.hpack
68local vpack_nodes = nuts.vpack
69local copylist = nuts.copylist
70local copy_node = nuts.copy
71local insertnodebefore = nuts.insertbefore
72local insertnodeafter = nuts.insertafter
73local flushnodelist = nuts.flushlist
74
75local hpack_string = nuts.typesetters.tohpack
76
77local texgetattribute = tex.getattribute
78local texsetattribute = tex.setattribute
79
80local setmetatableindex = table.setmetatableindex
81
82local unsetvalue = attributes.unsetvalue
83
84local current_font = font.current
85
86local fonthashes = fonts.hashes
87local chardata = fonthashes.characters
88local exheights = fonthashes.exheights
89local emwidths = fonthashes.emwidths
90local pt_factor = number.dimenfactors.pt
91
92local nodepool = nuts.pool
93local new_rule = nodepool.rule
94local new_virtual_rule = nodepool.virtualrule
95local new_kern = nodepool.kern
96local new_glue = nodepool.glue
97local new_hlist = nodepool.hlist
98local new_vlist = nodepool.vlist
99
100local tracers = nodes.tracers
101local visualizers = nodes.visualizers
102
103local setcolor = tracers.colors.set
104local setlistcolor = tracers.colors.setlist
105local settransparency = tracers.transparencies.set
106local setlisttransparency = tracers.transparencies.setlist
107
108local starttiming = statistics.starttiming
109local stoptiming = statistics.stoptiming
110
111local a_visual = attributes.private("visual")
112local a_filter = attributes.private("filter")
113local a_layer = attributes.private("viewerlayer")
114
115local enableaction = nodes.tasks.enableaction
116
117local report_visualize = logs.reporter("visualize")
118
119local modes = {
120 hbox = 0x0000001,
121 vbox = 0x0000002,
122 vtop = 0x0000004,
123 kern = 0x0000008,
124 glue = 0x0000010,
125 penalty = 0x0000020,
126 fontkern = 0x0000040,
127 strut = 0x0000080,
128 whatsit = 0x0000100,
129 glyph = 0x0000200,
130 simple = 0x0000400,
131 simplehbox = 0x0000401,
132 simplevbox = 0x0000402,
133 simplevtop = 0x0000404,
134 user = 0x0000800,
135 math = 0x0001000,
136 italic = 0x0002000,
137 origin = 0x0004000,
138 discretionary = 0x0008000,
139 expansion = 0x0010000,
140 line = 0x0020000,
141 space = 0x0040000,
142 depth = 0x0080000,
143 marginkern = 0x0100000,
144 mathkern = 0x0200000,
145 dir = 0x0400000,
146 par = 0x0800000,
147 mathglue = 0x1000000,
148 mark = 0x2000000,
149 insert = 0x4000000,
150 boundary = 0x8000000,
151
152 vkern = 0x0000008,
153 hkern = 0x0000008,
154 vglue = 0x0000010,
155 hglue = 0x0000010,
156 vpenalty = 0x0000020,
157 hpenalty = 0x0000020,
158}
159
160local filters = {
161 vglue = 0x001,
162 hglue = 0x002,
163
164 vkern = 0x010,
165 hkern = 0x020,
166
167 vpenalty = 0x100,
168 hpenalty = 0x200,
169
170}
171
172visualizers.modes = modes
173visualizers.filters = filters
174
175local usedfont, exheight, emwidth
176local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_marginkern, l_mathkern, l_mathshape, l_italic, l_origin, l_discretionary, l_expansion, l_line, l_space, l_depth,
177 l_dir, l_whatsit, l_mark, l_insert, l_boundary
178
179local enabled = false
180local layers = { }
181
182local preset_boxes = modes.hbox + modes.vbox + modes.vtop + modes.origin
183local preset_makeup = preset_boxes
184 + modes.kern + modes.glue + modes.penalty + modes.boundary
185local preset_all = preset_makeup
186 + modes.fontkern + modes.marginkern + modes.mathkern
187 + modes.whatsit + modes.glyph + modes.user + modes.math
188 + modes.dir + modes.mathglue + modes.mark + modes.insert
189
190function visualizers.setfont(id)
191 usedfont = id or current_font()
192 exheight = exheights[usedfont]
193 emwidth = emwidths[usedfont]
194end
195
196
197
198local userrule
199local outlinerule
200local emptyrule
201
202local function getusedfont()
203 if not usedfont then
204
205 visualizers.setfont(fonts.definers.define { name = "lmmonoltcond10regular", size = tex.sp("4pt") })
206 end
207 return usedfont
208end
209
210visualizers.getusedfont = getusedfont
211
212local function initialize()
213
214 if not usedfont then
215 getusedfont()
216 end
217
218 for mode, value in next, modes do
219 local tag = formatters["v_%s"](mode)
220 attributes.viewerlayers.define {
221 tag = tag,
222 title = formatters["visualizer %s"](mode),
223 visible = "start",
224 editable = "yes",
225 printable = "yes"
226 }
227 layers[mode] = attributes.viewerlayers.register(tag,true)
228 end
229 l_hbox = layers.hbox
230 l_vbox = layers.vbox
231 l_vtop = layers.vtop
232 l_glue = layers.glue
233 l_kern = layers.kern
234 l_penalty = layers.penalty
235 l_fontkern = layers.fontkern
236 l_strut = layers.strut
237 l_whatsit = layers.whatsit
238 l_glyph = layers.glyph
239 l_user = layers.user
240 l_math = layers.math
241 l_italic = layers.italic
242 l_marginkern = layers.marginkern
243 l_mathkern = layers.mathkern
244 l_mathshapekern = layers.mathshapekern
245 l_origin = layers.origin
246 l_discretionary = layers.discretionary
247 l_expansion = layers.expansion
248 l_line = layers.line
249 l_space = layers.space
250 l_depth = layers.depth
251 l_dir = layers.dir
252 l_par = layers.par
253 l_mark = layers.mark
254 l_insert = layers.insert
255 l_boundary = layers.boundary
256
257 if not userrule then
258 userrule = nuts.rules.userrule
259 end
260
261 if not outlinerule then
262 outlinerule = nuts.pool.outlinerule
263 end
264
265 if not emptyrule then
266 emptyrule = nuts.pool.emptyrule
267 end
268 initialize = false
269end
270
271local function enable()
272 if initialize then
273 initialize()
274 end
275 enableaction("shipouts","nodes.visualizers.handler")
276 report_visualize("enabled")
277 enabled = true
278 tex.setcount("global","c_syst_visualizers_state",1)
279end
280
281function visualizers.enable()
282 if not enabled then
283 enable()
284 end
285end
286
287local function setvisual(n,a,what,list)
288 if not n or n == "reset" then
289 return unsetvalue
290 elseif n == true or n == "makeup" then
291 if not a or a == 0 or a == unsetvalue then
292 a = preset_makeup
293 else
294 a = a | preset_makeup
295 end
296 elseif n == "boxes" then
297 if not a or a == 0 or a == unsetvalue then
298 a = preset_boxes
299 else
300 a = a | preset_boxes
301 end
302 elseif n == "all" then
303 if what == false then
304 return unsetvalue
305 elseif not a or a == 0 or a == unsetvalue then
306 a = preset_all
307 else
308 a = a | preset_all
309 end
310 elseif type(n) == "string" then
311 for s in gmatch(n,"[a-z]+") do
312 local m = modes[s]
313 if not m then
314
315 elseif not a or a == 0 or a == unsetvalue then
316 a = m
317 else
318 a = a | m
319 end
320 end
321 elseif type(n) == "number" then
322 if not a or a == 0 or a == unsetvalue then
323 a = n
324 else
325 a = a | n
326 end
327 end
328 if not a or a == 0 or a == unsetvalue then
329 return unsetvalue
330 elseif not enabled then
331 enable()
332 end
333 return a
334end
335
336local function setfilter(n,a,what,list)
337 if not n or n == "reset" then
338 return unsetvalue
339 elseif type(n) == "string" then
340 for s in gmatch(n,"[a-z]+") do
341 local m = filters[s]
342 if not m then
343
344 elseif not a or a == 0 or a == unsetvalue then
345 a = m
346 else
347 a = a | m
348 end
349 end
350 end
351 if not a or a == 0 or a == unsetvalue then
352 return unsetvalue
353 else
354 return a
355 end
356end
357
358function nuts.setvisual(n,mode)
359 if mode then
360 setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true))
361 setattr(n,a_filter,setfilter(mode,getattr(n,a_filter),true))
362 else
363 local a = texgetattribute(a_visual)
364 if a ~= unsetvalue then
365 setattr(n,a_visual,a)
366 setattr(n,a_filter,texgetattribute(a_visual))
367 end
368 end
369end
370
371function nuts.setvisuals(n,mode)
372 setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true,true))
373 setattr(n,a_filter,setfilter(mode,getattr(n,a_filter),true,true))
374end
375
376
377
378do
379
380 local apply_to_nodes = nuts.apply
381
382 local cached = setmetatableindex(function(t,k)
383 if k == true then
384 return texgetattribute(a_visual)
385 elseif not k then
386 t[k] = unsetvalue
387 return unsetvalue
388 else
389 local v = setvisual(k)
390 t[k] = v
391 return v
392 end
393 end)
394
395
396
397
398
399
400 local a = unsetvalue
401
402 local f = function(n) setattr(n,a_visual,a) end
403
404 local function applyvisuals(n,mode)
405 a = cached[mode]
406 apply_to_nodes(n,f)
407 end
408
409 nuts.applyvisuals = applyvisuals
410
411 function nodes.applyvisuals(n,mode)
412 applyvisuals(tonut(n),mode)
413 end
414
415 function visualizers.attribute(mode)
416 return cached[mode]
417 end
418
419 visualizers.attributes = cached
420
421end
422
423function nuts.copyvisual(n,m)
424 setattr(n,a_visual,getattr(m,a_visual))
425end
426
427function visualizers.setvisual(n)
428 texsetattribute(a_visual,setvisual(n,texgetattribute(a_visual)))
429 texsetattribute(a_filter,setfilter(n,texgetattribute(a_filter)))
430end
431
432function visualizers.setlayer(n)
433 texsetattribute(a_layer,layers[n] or unsetvalue)
434end
435
436local fraction = 10 do
437
438 local function set(mode,v)
439 texsetattribute(a_visual,setvisual(mode,texgetattribute(a_visual),v))
440 texsetattribute(a_filter,setfilter(mode,texgetattribute(a_filter),v))
441 end
442
443 for mode, value in next, modes do
444 trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end)
445 end
446
447 trackers .register("visualizers.reset", function(v) set("reset", v) end)
448 trackers .register("visualizers.all", function(v) set("all", v) end)
449 trackers .register("visualizers.makeup", function(v) set("makeup",v) end)
450 trackers .register("visualizers.boxes", function(v) set("boxes", v) end)
451 directives.register("visualizers.fraction", function(v) fraction = (v and tonumber(v)) or (v == "more" and 5) or 10 end)
452
453end
454
455
456
457
458
459 local expandmacro = token.expand_macro
460 local takebox = tex.takebox
461
462 local function hpack_string(str)
463 expandmacro("syst_v_p",true,str)
464 return tonut(takebox("scratchbox"))
465 end
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524local function sometext(str,layer,color,textcolor,lap,variant)
525 local text = hpack_string(str,usedfont)
526 local size = getwidth(text)
527 local rule = new_virtual_rule(size,2*exheight,exheight/2)
528 if color then
529 setcolor(rule,color)
530 end
531 if textcolor then
532 setlistcolor(getlist(text),textcolor)
533 end
534 local info = setlink(rule,text)
535 setlisttransparency(info,"trace:g")
536 info = new_hlist(info)
537 local x, y
538 if lap then
539 x = -size
540 end
541 if variant then
542 y = variant * exheight
543 end
544 if x or y then
545 setoffsets(info,x,y)
546 end
547 if layer then
548 setattr(info,a_layer,layer)
549 end
550 return info, size
551end
552
553local function someblob(str,layer,color,textcolor,width)
554 local text = hpack_string(str,usedfont)
555 local size = getwidth(text)
556 local rule = new_rule(width,2*exheight,exheight/2)
557 local kern = new_kern(-width + (width-size)/2)
558 if color then
559 setcolor(rule,color)
560 end
561 if textcolor then
562 setlistcolor(getlist(text),textcolor)
563 end
564 local info = setlink(rule,kern,text)
565 setlisttransparency(info,"trace:g")
566 info = hpack_nodes(info)
567 local width = getwidth(info)
568 info = new_hlist(info)
569 if layer then
570 setattr(info,a_layer,layer)
571 end
572 return info, width
573end
574
575local caches = setmetatableindex("table")
576
577local fontkern, italickern, marginkern, mathkern do
578
579 local f_cache = caches["fontkern"]
580 local i_cache = caches["italickern"]
581 local s_cache = caches["shapekern"]
582 local m_cache = caches["marginkern"]
583 local l_cache = caches["mathkern"]
584
585 local function somekern(head,current,cache,color,layer)
586 local width = getkern(current)
587 local extra = getexpansion(current)
588 local kern = width + extra
589 local info = cache[kern]
590 if not info then
591 local text = hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)
592 local rule = new_rule(emwidth/fraction,6*exheight,2*exheight)
593 local list = getlist(text)
594 if kern > 0 then
595 setlistcolor(list,"trace:db")
596 elseif kern < 0 then
597 setlistcolor(list,"trace:dr")
598 else
599 setlistcolor(list,"trace:dg")
600 end
601 setlisttransparency(list,color)
602 setcolor(rule,color)
603 settransparency(rule,color)
604 setshift(text,-5 * exheight)
605 info = new_hlist(setlink(rule,text))
606 setattr(info,a_layer,layer)
607 cache[kern] = info
608 end
609 head = insertnodebefore(head,current,copylist(info))
610 return head, current
611 end
612
613 fontkern = function(head,current)
614 return somekern(head,current,f_cache,"trace:ds",l_fontkern)
615 end
616
617 italickern = function(head,current)
618 return somekern(head,current,i_cache,"trace:do",l_italic)
619 end
620
621 mathshapekern = function(head,current)
622 return somekern(head,current,s_cache,"trace:dg",l_mathshapekern)
623 end
624
625 marginkern = function(head,current)
626 return somekern(head,current,m_cache,"trace:dr",l_marginkern)
627 end
628
629 mathkern = function(head,current)
630 return somekern(head,current,l_cache,"trace:db",l_mathkern)
631 end
632
633end
634
635local ruledglyphexpansion do
636
637 local f_cache = caches["glyphexpansion"]
638
639 ruledglyphexpansion = function(head,current)
640 local extra = getexpansion(current)
641 if extra and extra ~= 0 then
642 extra = extra / 1000
643 local info = f_cache[extra]
644 if not info then
645 local text = hpack_string(tostring(round(extra)),usedfont)
646 local rule = new_rule(emwidth/fraction,exheight,2*exheight)
647 local list = getlist(text)
648 if extra > 0 then
649 setlistcolor(list,"trace:db")
650 elseif extra < 0 then
651 setlistcolor(list,"trace:dr")
652 end
653 setlisttransparency(list,"trace:ds")
654 setcolor(rule,"trace:ds")
655 settransparency(rule,"trace:ds")
656 setshift(text,1.75 * exheight)
657 info = new_hlist(setlink(rule,text))
658 setattr(info,a_layer,l_expansion)
659 f_cache[extra] = info
660 end
661 head = insertnodebefore(head,current,copylist(info))
662 end
663 return head, current
664 end
665
666end
667
668local kernexpansion do
669
670 local f_cache = caches["kernexpansion"]
671
672
673
674 kernexpansion = function(head,current)
675 local extra = getexpansion(current)
676 if extra ~= 0 then
677 extra = extra / 1000
678 local info = f_cache[extra]
679 if not info then
680 local text = hpack_string(tostring(round(extra)),usedfont)
681 local rule = new_rule(emwidth/fraction,exheight,4*exheight)
682 local list = getlist(text)
683 if extra > 0 then
684 setlistcolor(list,"trace:db")
685 elseif extra < 0 then
686 setlistcolor(list,"trace:dr")
687 end
688 setlisttransparency(list,"trace:ds")
689 setcolor(rule,"trace:ds")
690 settransparency(rule,"trace:ds")
691 setshift(text,3.5 * exheight)
692 info = new_hlist(setlink(rule,text))
693 setattr(info,a_layer,l_expansion)
694 f_cache[extra] = info
695 end
696 head = insertnodebefore(head,current,copylist(info))
697 end
698 return head, current
699 end
700
701end
702
703local ruledmark do
704
705 local set_code = nodes.markcodes.set
706
707 local sm_cache = setmetatableindex(caches["setmark"], function(t,index)
708 local info = sometext(formatters["SM:%i"](index),usedfont,nil,"trace:w")
709 setattr(info,a_layer,l_mark)
710 t[index] = info
711 return info
712 end)
713
714 local rm_cache = setmetatableindex(caches["resetmark"], function(t,index)
715 local info = sometext(formatters["RM:%i"](index),usedfont,nil,"trace:w")
716 setattr(info,a_layer,l_mark)
717 t[index] = info
718 return info
719 end)
720
721 ruledmark = function(head,current)
722 local index = getindex(current)
723 local info = getsubtype(current) == set_code and sm_cache[index] or rm_cache[index]
724 head, current = insertnodeafter(head,current,copylist(info))
725 return head, current
726 end
727
728end
729
730local ruledinsert do
731
732 local si_cache = setmetatableindex(caches["insert"], function(f,index)
733 local info = sometext(formatters["SI:%i"](index),usedfont,nil,"trace:w")
734 setattr(info,a_layer,l_insert)
735 si_cache[index] = info
736 end)
737
738 ruledinsert = function(head,current)
739 local info = si_cache[getindex(current)]
740 head, current = insertnodeafter(head,current,copylist(info))
741 return head, current
742 end
743
744end
745
746local ruledwhatsit do
747
748 local whatsitcodes = nodes.whatsitcodes
749 local w_cache = caches["whatsit"]
750
751 local tags = {
752 [whatsitcodes.open] = "OPN",
753 [whatsitcodes.write] = "WRI",
754 [whatsitcodes.close] = "CLS",
755
756 [whatsitcodes.latelua] = "LUA",
757 [whatsitcodes.savepos] = "POS",
758 [whatsitcodes.userdefined] = "USR",
759 [whatsitcodes.literal] = "LIT",
760 [whatsitcodes.setmatrix] = "MAT",
761 [whatsitcodes.save] = "SAV",
762 [whatsitcodes.restore] = "RES",
763 [whatsitcodes.startscaling] = "+SCA",
764 [whatsitcodes.stopscaling] = "-SCA",
765 [whatsitcodes.startrotation] = "+ROT",
766 [whatsitcodes.stoprotation] = "-ROT",
767 [whatsitcodes.startmirroring] = "+MIR",
768 [whatsitcodes.stopmirroring] = "-MIR",
769 [whatsitcodes.startclipping] = "+CLP",
770 [whatsitcodes.stopclipping] = "-CLP",
771 [whatsitcodes.startmatrix] = "+MAT",
772 [whatsitcodes.stopmatrix] = "-MAT",
773 [whatsitcodes.setstate] = "SET",
774 }
775
776 ruledwhatsit = function(head,current)
777 local what = getsubtype(current)
778 local info = w_cache[what]
779 if info then
780
781 else
782 info = sometext(formatters["W:%s"](tags[what] or what),usedfont,nil,"trace:w")
783 setattr(info,a_layer,l_whatsit)
784 w_cache[what] = info
785 end
786 head, current = insertnodeafter(head,current,copylist(info))
787 return head, current
788 end
789
790end
791
792local ruledboundary do
793
794 local boundarycodes = nodes.boundarycodes
795 local b_cache = caches["boundary"]
796
797 local tags = {
798 [boundarycodes.cancel] = "CAN",
799 [boundarycodes.user] = "USR",
800 [boundarycodes.protrusion] = "PRO",
801 [boundarycodes.word] = "WRD",
802 }
803
804 ruledboundary = function(head,current)
805 local what = getsubtype(current)
806 local info = b_cache[what]
807 if info then
808
809 else
810 info = sometext(formatters["B:%s"](tags[what] or what),usedfont,nil,"trace:w")
811 setattr(info,a_layer,l_boundary)
812 b_cache[what] = info
813 end
814 head, current = insertnodeafter(head,current,copylist(info))
815 return head, current
816 end
817
818end
819
820local ruleddir, ruledpar do
821
822 local dircodes = nodes.dircodes
823 local directioncodes = tex.directioncodes
824
825 local cancel_code = dircodes.cancel
826 local l2r_code = directioncodes.l2r
827 local r2l_code = directioncodes.r2l
828
829 local d_cache = caches["dir"]
830
831 local getdirection = nuts.getdirection
832
833 local tags = {
834 l2r = "L2R",
835 r2l = "R2L",
836 cancel = "CAN",
837 par = "PAR",
838 }
839
840 ruledpar = function(head,current)
841 local what = "par"
842 local info = d_cache[what]
843 if info then
844
845 else
846 info = sometext(formatters["L:%s"](what),usedfont,nil,"trace:w")
847 setattr(info,a_layer,l_dir)
848 d_cache[what] = info
849 end
850 head, current = insertnodeafter(head,current,copylist(info))
851 return head, current
852 end
853
854 ruleddir = function(head,current)
855 local what = getsubtype(current)
856 if what == cancel_code then
857 what = "cancel"
858 elseif getdirection(current) == r2l_code then
859 what = "r2l"
860 else
861 what = "l2r"
862 end
863 local info = d_cache[what]
864 if info then
865
866 else
867 info = sometext(formatters["D:%s"](what),usedfont,nil,"trace:w")
868 setattr(info,a_layer,l_dir)
869 d_cache[what] = info
870 end
871 head, current = insertnodeafter(head,current,copylist(info))
872 return head, current
873 end
874
875end
876
877local ruleduser do
878
879 local u_cache = caches["user"]
880
881 ruleduser = function(head,current)
882 local what = getsubtype(current)
883 local info = u_cache[what]
884 if info then
885
886 else
887 info = sometext(formatters["U:%s"](what),usedfont)
888 setattr(info,a_layer,l_user)
889 u_cache[what] = info
890 end
891 head, current = insertnodeafter(head,current,copylist(info))
892 return head, current
893 end
894
895end
896
897local ruleddepth do
898
899 ruleddepth = function(current,wd,ht,dp)
900 local wd, ht, dp = getwhd(current)
901 if dp ~= 0 then
902 local rule = new_rule(wd,0,dp)
903 setcolor(rule,"trace:o")
904 settransparency(rule,"trace:g")
905 setattr(rule,a_layer,l_depth)
906 setlist(current,setlink(rule,new_kern(-wd),getlist(current)))
907 end
908 end
909
910end
911
912local ruledbox do
913
914 local b_cache = caches["box"]
915 local o_cache = caches["origin"]
916
917 local getshift = nuts.getshift
918 local getorientation = nuts.getorientation
919 local setorientation = nuts.setorientation
920 local getheight = nuts.getheight
921
922 setmetatableindex(o_cache,function(t,size)
923 local rule = new_rule(2*size,size,size)
924 local origin = hpack_nodes(rule)
925 setcolor(rule,"trace:do")
926 settransparency(rule,"trace:do")
927 setattr(rule,a_layer,l_origin)
928 t[size] = origin
929 return origin
930 end)
931
932 ruledbox = function(head,current,vertical,layer,what,simple,previous,trace_origin,parent)
933 local wd, ht, dp = getwhd(current)
934
935 local force_origin = wd == 0 or (dp + ht) == 0
936 local shift = getshift(current)
937
938 local orientation, xoffset, yoffset, w, h, d = getorientation(current)
939if orientation and orientation ~= 0 and (h ~= 0 or d ~= 0) then
940
941 ht = h
942 dp = d
943end
944 local next = getnext(current)
945 local prev = previous
946 setboth(current)
947 local linewidth = emwidth/fraction
948 local size = 2*linewidth
949 local this
950 if not simple then
951 this = b_cache[what]
952 if not this then
953 local text = hpack_string(what,usedfont)
954 this = setlink(new_kern(-getwidth(text)),text)
955 setlisttransparency(this,"trace:s")
956 this = new_hlist(this)
957 b_cache[what] = this
958 end
959 end
960 local rest, more
961 if force_origin then
962 rest = emptyrule(wd,ht,dp)
963 elseif what == "_D_" then
964
965 local list = getlist(current)
966 local up = list and getheight(list) or 0
967 rest = userrule {
968 width = wd,
969 height = ht,
970 depth = dp,
971 line = linewidth,
972 type = "box",
973 dashed = 3*size,
974 double = ht - up,
975 }
976 elseif dp == 0 then
977 rest = userrule {
978 width = wd,
979 height = ht,
980 line = linewidth,
981 type = "box",
982 baseline = false,
983 }
984 else
985 rest = userrule {
986 width = wd,
987 height = ht,
988 depth = dp,
989 line = linewidth,
990 type = "box",
991 dashed = 3*size,
992 }
993 end
994
995 local info = setlink(this and copylist(this) or nil,rest,more)
996
997 setlisttransparency(info,"trace:s")
998 info = new_hlist(info)
999
1000 setattr(info,a_layer,layer)
1001 if vertical then
1002 if not force_origin and shift == 0 then
1003 info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
1004 elseif trace_origin or force_origin then
1005 local size = 2*size
1006 local origin = o_cache[size]
1007 origin = copylist(origin)
1008 if getid(parent) == vlist_code then
1009 setshift(origin,-shift)
1010 info = setlink(current,new_kern(-size),origin,new_kern(-size-dp),info)
1011 else
1012
1013 info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
1014 end
1015 setshift(current,0)
1016 else
1017 info = setlink(current,new_dp ~= 0 and new_kern(-dp) or nil,info)
1018 setshift(current,0)
1019 end
1020 info = new_vlist(info,wd,ht,dp,shift)
1021 else
1022 if not force_origin and shift == 0 then
1023 info = setlink(current,new_kern(-wd),info)
1024 elseif trace_origin or force_origin then
1025 local size = 2*size
1026 local origin = o_cache[size]
1027 origin = copylist(origin)
1028 if getid(parent) == vlist_code then
1029 info = setlink(current,new_kern(-wd-size-shift),origin,new_kern(-size+shift),info)
1030 else
1031 setshift(origin,-shift)
1032 info = setlink(current,new_kern(-wd-size),origin,new_kern(-size),info)
1033 end
1034 setshift(current,0)
1035 else
1036 info = setlink(current,new_kern(-wd),info)
1037 setshift(current,0)
1038 end
1039 info = new_hlist(info,wd,ht,dp,shift)
1040 end
1041
1042
1043
1044
1045 if next then
1046 setlink(info,next)
1047 end
1048 if prev and prev > 0 then
1049 setlink(prev,info)
1050 end
1051 if head == current then
1052 return info, info
1053 else
1054 return head, info
1055 end
1056 end
1057
1058end
1059
1060local ruledglyph do
1061
1062
1063
1064
1065
1066
1067
1068 local getglyphdimensions = nuts.getglyphdimensions
1069
1070 ruledglyph = function(head,current,previous)
1071 local wd, ht, dp = getglyphdimensions(current)
1072 if wd ~= 0 then
1073 local next = getnext(current)
1074 local prev = previous
1075 setboth(current)
1076 local linewidth = emwidth/(2*fraction)
1077
1078 local c, f = isglyph(current)
1079 local char = chardata[f][c]
1080 local info = (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule {
1081 width = wd,
1082 height = ht,
1083 depth = dp,
1084 line = linewidth,
1085 type = "box",
1086 }
1087 if char and type(char.unicode) == "table" then
1088 setlistcolor(info,"trace:s")
1089 setlisttransparency(info,"trace:ds")
1090 else
1091 setlistcolor(info,"trace:o")
1092 setlisttransparency(info,"trace:do")
1093 end
1094 info = new_hlist(info)
1095 setattr(info,a_layer,l_glyph)
1096 local info = setlink(current,new_kern(-wd),info)
1097 info = hpack_nodes(info)
1098 setwidth(info,wd)
1099 if next then
1100 setlink(info,next)
1101 end
1102 if prev then
1103 setlink(prev,info)
1104 end
1105 if head == current then
1106 return info, info
1107 else
1108 return head, info
1109 end
1110 else
1111 return head, current
1112 end
1113 end
1114
1115 function visualizers.setruledglyph(f)
1116 ruledglyph = f or ruledglyph
1117 end
1118
1119end
1120
1121local ruledglue, ruledmathglue do
1122
1123 local effectiveglue = nuts.effectiveglue
1124 local iszeroglue = nuts.iszeroglue
1125
1126 local gluecodes = nodes.gluecodes
1127
1128 local userskip_code = gluecodes.userskip
1129 local spaceskip_code = gluecodes.spaceskip
1130 local xspaceskip_code = gluecodes.xspaceskip
1131 local zerospaceskip_code = gluecodes.zerospaceskip or gluecodes.userskip
1132
1133 local leftskip_code = gluecodes.leftskip
1134 local rightskip_code = gluecodes.rightskip
1135 local lefthangskip_code = gluecodes.lefthangskip
1136 local righthangskip_code = gluecodes.righthangskip
1137 local parfillleftskip_code = gluecodes.parfillleftskip or parfillskip_code
1138 local parfillrightskip_code = gluecodes.parfillrightskip or parfillskip_code
1139 local parinitleftskip_code = gluecodes.parinitleftskip
1140 local parinitrightskip_code = gluecodes.parinitrightskip
1141 local indentskip_code = gluecodes.indentskip
1142 local intermathskip_code = gluecodes.intermathskip
1143 local correctionskip_code = gluecodes.correctionskip
1144 local tabskip_code = gluecodes.tabskip
1145
1146 local g_cache_v = caches["vglue"]
1147 local g_cache_h = caches["hglue"]
1148
1149 local g_cache_gn = caches["gluename"]
1150 local g_cache_gz = caches["gluezero"]
1151 local g_cache_gf = caches["gluefixed"]
1152
1153 local tags = {
1154
1155 [gluecodes.lineskip] = "LI",
1156 [gluecodes.baselineskip] = "BS",
1157 [gluecodes.parskip] = "PS",
1158 [gluecodes.abovedisplayskip] = "DA",
1159 [gluecodes.belowdisplayskip] = "DB",
1160 [gluecodes.abovedisplayshortskip] = "SA",
1161 [gluecodes.belowdisplayshortskip] = "SB",
1162 [gluecodes.topskip] = "TS",
1163 [gluecodes.splittopskip] = "ST",
1164 [tabskip_code] = "TB",
1165 [gluecodes.thinmuskip] = "MS",
1166 [gluecodes.medmuskip] = "MM",
1167 [gluecodes.thickmuskip] = "ML",
1168 [intermathskip_code] = "IM",
1169 [gluecodes.keepskip or 99] = "KS",
1170 [gluecodes.mathskip] = "MT",
1171 [gluecodes.leaders] = "NL",
1172 [gluecodes.cleaders] = "CL",
1173 [gluecodes.xleaders] = "XL",
1174 [gluecodes.gleaders] = "GL",
1175
1176
1177 [leftskip_code] = "LS",
1178 [rightskip_code] = "RS",
1179 [lefthangskip_code] = "LH",
1180 [righthangskip_code] = "RH",
1181 [spaceskip_code] = "SP",
1182 [xspaceskip_code] = "XS",
1183 [zerospaceskip_code] = "ZS",
1184 [parfillleftskip_code] = "PL",
1185 [parfillrightskip_code] = "PR",
1186 [parinitleftskip_code] = "IL",
1187 [parinitrightskip_code] = "IR",
1188 [indentskip_code] = "IN",
1189 [correctionskip_code] = "CS",
1190 }
1191
1192 local stags = {
1193 [lefthangskip_code] = 0.5,
1194 [righthangskip_code] = 0.5,
1195 [leftskip_code] = -2,
1196 [rightskip_code] = -2,
1197 [parinitleftskip_code] = -1.3775,
1198 [parinitrightskip_code] = -1.3775,
1199 [parfillleftskip_code] = -0.75,
1200 [parfillrightskip_code] = -0.75,
1201 }
1202
1203 local f_amount = formatters["%s:%0.3f"]
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 local g_caches = { } for k, v in next, tags do g_caches[k] = caches[v] end
1261
1262 ruledglue = function(head,current,vertical,parent)
1263 local subtype = getsubtype(current)
1264 local width = effectiveglue(current,parent)
1265 local stag = stags[subtype]
1266 local cache = g_caches[subtype] or (vertical and g_cache_v) or g_cache_h
1267 local info = cache[amount]
1268 if subtype == intermathskip_code then
1269 head = ruledmathglue(head, current)
1270 end
1271 if info then
1272
1273 else
1274 local amount = f_amount(tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
1275 if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
1276 info = sometext(amount,l_glue,"trace:y")
1277 elseif subtype == userskip_code then
1278 if width > 0 then
1279 info = sometext(amount,l_glue,"trace:b")
1280 elseif width < 0 then
1281 info = sometext(amount,l_glue,"trace:r")
1282 else
1283 info = sometext(amount,l_glue,"trace:g")
1284 end
1285 elseif subtype == tabskip_code then
1286 info = sometext(amount,l_glue,"trace:s")
1287 elseif subtype == indentskip_code or subtype == correctionskip_code then
1288 info = sometext(amount,l_glue,"trace:s")
1289 elseif subtype == leftskip_code then
1290 info = sometext(amount,l_glue,"trace:y",false,true,stag)
1291 elseif subtype == rightskip_code then
1292 info = sometext(amount,l_glue,"trace:y",false,false,stag)
1293 elseif subtype == lefthangskip_code then
1294 info = sometext(amount,l_glue,"trace:y",false,true,stag)
1295 elseif subtype == righthangskip_code then
1296 info = sometext(amount,l_glue,"trace:y",false,false,stag)
1297 elseif subtype == parfillleftskip_code then
1298 info = sometext(amount,l_glue,"trace:s",false,true,stag)
1299 elseif subtype == parfillrightskip_code then
1300 info = sometext(amount,l_glue,"trace:s",false,false,stag)
1301 elseif subtype == parinitleftskip_code then
1302 info = sometext(amount,l_glue,"trace:s",false,true,stag)
1303 elseif subtype == parinitrightskip_code then
1304 info = sometext(amount,l_glue,"trace:s",false,false,stag)
1305 else
1306 info = sometext(amount,l_glue,"trace:m")
1307 end
1308 cache[amount] = info
1309 end
1310 info = copylist(info)
1311 if vertical then
1312 info = vpack_nodes(info)
1313 end
1314 head, current = insertnodebefore(head,current,info)
1315 return head, getnext(current)
1316 end
1317
1318 local g_cache_s = caches["space"]
1319 local g_cache_x = caches["xspace"]
1320 local g_cache_f = caches["fspace"]
1321
1322 local getoptions = nuts.getoptions
1323
1324 ruledspace = function(head,current,parent)
1325 local subtype = getsubtype(current)
1326 local width = effectiveglue(current,parent)
1327 local info
1328 if subtype ~= spaceskip_code then
1329 info = g_cache_x[width]
1330 if not info then
1331 info = someblob("XS",l_glue,"trace:m",nil,width)
1332 g_cache_x[width] = info
1333 end
1334 elseif (getoptions(current) & 2) == 2 then
1335 info = g_cache_f[width]
1336 if not info then
1337 info = someblob("SF",l_glue,"trace:c",nil,width)
1338 g_cache_f[width] = info
1339 end
1340 else
1341 info = g_cache_s[width]
1342 if not info then
1343 info = someblob("SP",l_glue,"trace:y",nil,width)
1344 g_cache_s[width] = info
1345 end
1346 end
1347 info = copylist(info)
1348 head, current = insertnodebefore(head,current,info)
1349 return head, getnext(current)
1350 end
1351
1352
1353
1354 local mathvalues = tex.mathparametercodes
1355
1356 mathvalues[-1] = "left"
1357 mathvalues[-2] = "right"
1358
1359 local morehack = setmetatableindex(function(t,k)
1360 local v = mathematics.classnames[k]
1361 v = v and string.sub(v,1,3) or string.formatters["x%02x"](k)
1362 t[k] = v
1363 return v
1364 end)
1365
1366 local temphack = setmetatableindex(function(t,k)
1367 local v = mathvalues[k]
1368 if v then
1369 v = gsub(v,"spacing","")
1370 else
1371 v = k - 256
1372 v = morehack[v//64] .. morehack[v%64]
1373 end
1374 t[k] = v
1375 return v
1376 end)
1377
1378 local g_cache_qd = caches["mathquad"]
1379
1380 ruledmathglue = function(head,current,parent)
1381 local name = getfont(current)
1382 local zero = iszeroglue(current)
1383 local fixed = getprop(current,"fixedmathalign")
1384 local color = false
1385 local info = zero and g_cache_gz[name]
1386 local quad = name == 0
1387 local width = quad and effectiveglue(current,parent)
1388 if not info then
1389 if quad then
1390 info = g_cache_qd[width]
1391 color = "trace:z"
1392 elseif fixed then
1393 info = g_cache_gf[name]
1394 color = "trace:dr"
1395 else
1396 info = g_cache_gn[name]
1397 color = "trace:z"
1398 end
1399 end
1400 if not info then
1401 local amount = quad and f_amount("QUAD",width*pt_factor) or temphack[name]
1402
1403 local text = hpack_string(amount,usedfont)
1404 local rule = new_rule(emwidth/fraction,2*exheight,(zero and 4.25 or 2.75)*exheight)
1405 local list = getlist(text)
1406 setlistcolor(list,color)
1407 setcolor(rule,color)
1408 setlisttransparency(list,color)
1409 settransparency(rule,color)
1410 setshift(text,(zero and 3.5 or 2)*exheight)
1411 info = new_hlist(setlink(rule,text))
1412 setattr(info,a_layer,l_glue)
1413 if quad then
1414 g_cache_qd[width] = info
1415 elseif fixed then
1416 g_cache_gf[name] = info
1417 else
1418 g_cache_gn[name] = info
1419 end
1420 end
1421 return insertnodebefore(head,current,copylist(info))
1422 end
1423
1424end
1425
1426local ruledkern do
1427
1428 local v_cache = caches["vkern"]
1429 local h_cache = caches["hkern"]
1430
1431 ruledkern = function(head,current,vertical)
1432 local kern = getkern(current)
1433 local cache = vertical and v_cache or h_cache
1434 local info = cache[kern]
1435 if not info then
1436 local amount = formatters["%s:%0.3f"](vertical and "VK" or "HK",kern*pt_factor)
1437 if kern > 0 then
1438 info = sometext(amount,l_kern,"trace:b")
1439 elseif kern < 0 then
1440 info = sometext(amount,l_kern,"trace:r")
1441 else
1442 info = sometext(amount,l_kern,"trace:g")
1443 end
1444 cache[kern] = info
1445 end
1446 info = copylist(info)
1447 if vertical then
1448 info = vpack_nodes(info)
1449 end
1450 head, current = insertnodebefore(head,current,info)
1451 return head, getnext(current)
1452 end
1453
1454end
1455
1456local ruledstrut do
1457
1458 local strut_size = 65536 * 8 / 10
1459 local strut_code = nodes.rulecodes.strut
1460 local math_code = nodes.nodecodes.math
1461 local traverseid = nuts.traverseid
1462 local rangedimensions = nuts.rangedimensions
1463 local a_mathaxis = attributes.private("mathaxis")
1464
1465 ruledstrut = function(head,current,parent)
1466 if getwidth(current) == 0 then
1467 if getsubtype(current) == strut_code then
1468 local w = strut_size
1469 local a = getattr(current,a_mathaxis)
1470 setattr(current,a_layer,l_strut)
1471 if a then
1472 local p = getprev(current)
1473 local b, e
1474 for n in traverseid(math_code,current) do
1475 e = n
1476 break
1477 end
1478 for n in traverseid(math_code,current,true) do
1479 b = n
1480 break
1481 end
1482 if not b then
1483 b = head
1484 end
1485 if not e then
1486 e = nuts.tail(b)
1487 end
1488 w = rangedimensions(parent,b,e)
1489 setwidth(current,w)
1490 setcolor(current,"trace:ds")
1491 settransparency(current,"trace:ds")
1492 head, current, rule = nuts.remove(head,current)
1493 local kern = new_kern(-w)
1494 if a == 2 then
1495 head = insertnodebefore(head,e,kern)
1496 head = insertnodebefore(head,e,rule)
1497 else
1498 insertnodeafter(head,b,kern)
1499 insertnodeafter(head,b,rule)
1500 end
1501 current = p
1502 else
1503 setwidth(current,w)
1504 head, current = insertnodeafter(head,current,new_kern(-w))
1505 end
1506 end
1507 end
1508 return head, current
1509 end
1510
1511end
1512
1513local ruleditalic do
1514
1515 local i_cache = caches["italic"]
1516
1517 ruleditalic = function(head,current)
1518 local kern = getkern(current)
1519 local info = i_cache[kern]
1520 if not info then
1521 local amount = formatters["%s:%0.3f"]("IC",kern*pt_factor)
1522 if kern > 0 then
1523 info = sometext(amount,l_kern,"trace:b")
1524 elseif kern < 0 then
1525 info = sometext(amount,l_kern,"trace:r")
1526 else
1527 info = sometext(amount,l_kern,"trace:g")
1528 end
1529 i_cache[kern] = info
1530 end
1531 info = copylist(info)
1532 head, current = insertnodebefore(head,current,info)
1533 return head, getnext(current)
1534 end
1535
1536end
1537
1538local ruledmarginkern do
1539
1540 local l_cache = caches["leftmarginkern"]
1541 local r_cache = caches["rightmarginkern"]
1542
1543 ruledmarginkern = function(head,current,subtype)
1544 local kern = getkern(current)
1545 local left = subtype == leftmarginkern_code
1546 local cache = left and l_cache or r_cache
1547 local info = cache[kern]
1548 if not info then
1549 local amount = formatters["%s:%0.3f"](left and "ML" or "MR",kern*pt_factor)
1550 if kern > 0 then
1551 info = sometext(amount,l_marginkern,"trace:b")
1552 elseif kern < 0 then
1553 info = sometext(amount,l_marginkern,"trace:r")
1554 else
1555 info = sometext(amount,l_marginkern,"trace:g")
1556 end
1557 cache[kern] = info
1558 end
1559 info = copylist(info)
1560 head, current = insertnodebefore(head,current,info)
1561 return head, getnext(current)
1562 end
1563
1564end
1565
1566local ruledmathkern do
1567
1568 local h_cache = caches["horizontalmathkern"]
1569 local v_cache = caches["verticalmathkern"]
1570
1571 ruledmathkern = function(head,current,vertical)
1572 local kern = getkern(current)
1573 local cache = vertical and v_cache or h_cache
1574 local info = cache[kern]
1575 if not info then
1576 local amount = formatters["%s:%0.3f"](vertical and "MV" or "MH",kern*pt_factor)
1577 if kern > 0 then
1578 info = sometext(amount,l_mathkern,"trace:b")
1579 elseif kern < 0 then
1580 info = sometext(amount,l_mathkern,"trace:r")
1581 else
1582 info = sometext(amount,l_mathkern,"trace:g")
1583 end
1584 cache[kern] = info
1585 end
1586 info = copylist(info)
1587 head, current = insertnodebefore(head,current,info)
1588 return head, getnext(current)
1589 end
1590
1591end
1592
1593local ruleddiscretionary do
1594
1595 local d_cache = caches["discretionary"]
1596
1597 ruleddiscretionary = function(head,current)
1598 local d = d_cache[true]
1599 if not the_discretionary then
1600 local rule = new_rule(4*emwidth/fraction,4*exheight,exheight)
1601 local kern = new_kern(-2*emwidth/fraction)
1602 setlink(kern,rule)
1603 setcolor(rule,"trace:dd")
1604 settransparency(rule,"trace:dd")
1605 setattr(rule,a_layer,l_discretionary)
1606 d = new_hlist(kern)
1607 d_cache[true] = d
1608 end
1609 insertnodeafter(head,current,copylist(d))
1610 return head, current
1611 end
1612
1613end
1614
1615local ruledpenalty do
1616
1617 local cachehash = {
1618 ["MP=%s"] = caches["MP=%s"],
1619 ["MP>%s"] = caches["MP>%s"],
1620 ["MP<%s"] = caches["MP<%s"],
1621 ["MP:%s"] = caches["MP:%s"],
1622 ["VP:%s"] = caches["VP:%s"],
1623 ["HP:%s"] = caches["HP:%s"],
1624 }
1625
1626 local raisepenalties = false
1627
1628
1629 local pre_penalty_code = nodes.penaltycodes.mathprepenalty
1630 local post_penalty_code = nodes.penaltycodes.mathpostpenalty
1631
1632 directives.register("visualizers.raisepenalties",function(v) raisepenalties = v end)
1633
1634 local getoptions = nuts.getoptions
1635
1636 local mathforward = tex.penaltyoptioncodes.mathforward
1637 local mathbackward = tex.penaltyoptioncodes.mathbackward
1638
1639 ruledpenalty = function(head,current,vertical,subtype)
1640 local penalty = getpenalty(current)
1641 local ismath = subtype == pre_penalty_code or subtype == post_penalty_code or subtype == true
1642 local amount
1643 if ismath then
1644 local options = getoptions(current)
1645 local forward = (options & mathforward) ~= 0
1646 local backward = (options & mathbackward) ~= 0
1647 if forward then
1648 if backward then
1649 amount = "MP=%s"
1650 else
1651 amount = "MP>%s"
1652 end
1653 elseif backward then
1654 amount = "MP<%s"
1655 else
1656 amount = "MP:%s"
1657 end
1658 elseif vertical then
1659 amount = "VP:%s"
1660 else
1661 amount = "HP:%s"
1662 end
1663 local cache = cachehash[amount]
1664 local info = cache[penalty]
1665 if info then
1666
1667 else
1668 amount = formatters[amount](penalty)
1669 if ismath then
1670 info = sometext(amount,l_penalty,"trace:s")
1671 elseif penalty > 0 then
1672 info = sometext(amount,l_penalty,"trace:b")
1673 elseif penalty < 0 then
1674 info = sometext(amount,l_penalty,"trace:r")
1675 else
1676 info = sometext(amount,l_penalty,"trace:g")
1677 end
1678 cache[penalty] = info
1679 end
1680 info = copylist(info)
1681 if vertical then
1682 info = vpack_nodes(info)
1683 elseif ismath then
1684 setshift(info, 65536*4)
1685 elseif raisepenalties then
1686 setshift(info,-65536*4)
1687 end
1688 head, current = insertnodebefore(head,current,info)
1689 return head, getnext(current)
1690 end
1691
1692end
1693
1694local ruledmath do
1695
1696 local mathcodes = nodes.mathcodes
1697 local tags = {
1698 [true] = {
1699 math = { "SM:?", caches["SM:?"] },
1700 beginmath = { "SM:B", caches["SM:B"] },
1701 endmath = { "SM:E", caches["SM:E"] },
1702 },
1703 [false] = {
1704 math = { "M:?", caches["M:?"] },
1705 beginmath = { "M:B", caches["M:B"] },
1706 endmath = { "M:E", caches["M:E"] },
1707 },
1708 }
1709
1710 local getoptions = nuts.getoptions
1711 local shortmath = tex.mathoptioncodes.short
1712
1713 ruledmath = function(head,current)
1714 local what = getsubtype(current)
1715 local mtag = mathcodes[what]
1716 local skip = getkern(current) + getwidth(current)
1717 local short = (getoptions(current) & shortmath) ~= 0
1718 local htag = tags[short][mtag or "math"] or tags[false].math
1719 local ttag = htag[1]
1720 local cache = htag[2]
1721 local info = cache[skip]
1722 if info then
1723
1724 else
1725 local text, width = sometext(ttag,usedfont,nil,"trace:dr")
1726 local rule = new_rule(skip,-655360/fraction,2*655360/fraction)
1727 local dist = mtag == "beginmath" and width or skip
1728 setcolor(rule,"trace:dr")
1729 settransparency(rule,"trace:dr")
1730 setattr(rule,a_layer,l_math)
1731 info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-dist),text))
1732 setattr(info,a_layer,l_math)
1733 cache[skip] = info
1734 end
1735 local saved = current
1736 head, current = insertnodeafter(head,current,copylist(info))
1737 if getpenalty(saved) ~= 0 then
1738 head, current = ruledpenalty(head,saved,false,true)
1739 end
1740 return head, current
1741 end
1742
1743end
1744
1745do
1746
1747 local nodecodes = nodes.nodecodes
1748 local disc_code = nodecodes.disc
1749 local kern_code = nodecodes.kern
1750 local glyph_code = nodecodes.glyph
1751 local glue_code = nodecodes.glue
1752 local penalty_code = nodecodes.penalty
1753 local whatsit_code = nodecodes.whatsit
1754 local user_code = nodecodes.user
1755 local math_code = nodecodes.math
1756 local hlist_code = nodecodes.hlist
1757 local vlist_code = nodecodes.vlist
1758 local dir_code = nodecodes.dir
1759 local par_code = nodecodes.par
1760 local mark_code = nodecodes.mark
1761 local insert_code = nodecodes.insert
1762 local rule_code = nodecodes.rule
1763 local boundary_code = nodecodes.boundary
1764
1765 local kerncodes = nodes.kerncodes
1766 local fontkern_code = kerncodes.fontkern
1767 local italickern_code = kerncodes.italiccorrection
1768 local spacefontkern_code = kerncodes.spacefontkern
1769 local leftkern_code = kerncodes.leftcorrectionkern
1770 local rightkern_code = kerncodes.rightcorrectionkern
1771 local leftmarginkern_code = kerncodes.leftmarginkern
1772 local rightmarginkern_code = kerncodes.rightmarginkern
1773 local mathshapekern_code = kerncodes.mathshapekern
1774 local horizontalmathkern_code = kerncodes.horizontalmathkern
1775 local verticalmathkern_code = kerncodes.verticalmathkern
1776
1777
1778 local skipcodes = nodes.skipcodes
1779 local spaceskip_code = skipcodes.spaceskip
1780 local xspaceskip_code = skipcodes.xspaceskip
1781 local zerospaceskip_code = skipcodes.zerospaceskip
1782 local intermathskip_code = skipcodes.intermathskip
1783
1784 local listcodes = nodes.listcodes
1785 local linelist_code = listcodes.line
1786 local rowlist_code = listcodes.alignment
1787
1788
1789
1790
1791 local getleader = nuts.getleader
1792 local getdisc = nuts.getdisc
1793
1794 local setleader = nuts.setleader
1795 local setdisc = nuts.setdisc
1796
1797
1798
1799 local function visualize(head,vertical,forced,parent)
1800 local trace_hbox = false
1801 local trace_vbox = false
1802 local trace_vtop = false
1803 local trace_kern = false
1804 local trace_glue = false
1805 local trace_penalty = false
1806 local trace_fontkern = false
1807 local trace_strut = false
1808 local trace_whatsit = false
1809 local trace_glyph = false
1810 local trace_simple = false
1811 local trace_user = false
1812 local trace_math = false
1813 local trace_mathkern = false
1814 local trace_marginkern = false
1815 local trace_italic = false
1816 local trace_origin = false
1817 local trace_discretionary = false
1818 local trace_expansion = false
1819 local trace_line = false
1820 local trace_space = false
1821 local trace_depth = false
1822 local trace_dir = false
1823 local trace_par = false
1824 local trace_mathglue = false
1825 local trace_mark = false
1826 local trace_insert = false
1827 local trace_boundary = false
1828 local current = head
1829 local previous = nil
1830 local attr = unsetvalue
1831 local prev_trace_fontkern = nil
1832 local prev_trace_italic = nil
1833 local prev_trace_expansion = nil
1834
1835
1836
1837 local vglue_code = filters.vglue
1838 local hglue_code = filters.hglue
1839 local vkern_code = filters.vkern
1840 local hkern_code = filters.hkern
1841 local vpenalty_code = filters.vpenalty
1842 local hpenalty_code = filters.hpenalty
1843
1844 while current do
1845 local id = getid(current)
1846 local a = forced or getattr(current,a_visual) or unsetvalue
1847 local subtype, content
1848 if a ~= attr then
1849 prev_trace_fontkern = trace_fontkern
1850 prev_trace_italic = trace_italic
1851 prev_trace_expansion = trace_expansion
1852 attr = a
1853 if a == unsetvalue then
1854 trace_hbox = false
1855 trace_vbox = false
1856 trace_vtop = false
1857 trace_kern = false
1858 trace_glue = false
1859 trace_penalty = false
1860 trace_fontkern = false
1861 trace_strut = false
1862 trace_whatsit = false
1863 trace_glyph = false
1864 trace_simple = false
1865 trace_user = false
1866 trace_math = false
1867 trace_italic = false
1868 trace_origin = false
1869 trace_discretionary = false
1870 trace_expansion = false
1871 trace_line = false
1872 trace_space = false
1873 trace_depth = false
1874 trace_marginkern = false
1875 trace_mathkern = false
1876 trace_dir = false
1877 trace_par = false
1878 trace_mathglue = false
1879 trace_mark = false
1880 trace_insert = false
1881 trace_boundary = false
1882 if id == kern_code then
1883 goto kern
1884 else
1885 goto list
1886 end
1887 else
1888
1889 trace_hbox = a & 0x0000001 ~= 0
1890 trace_vbox = a & 0x0000002 ~= 0
1891 trace_vtop = a & 0x0000004 ~= 0
1892 trace_kern = a & 0x0000008 ~= 0
1893 trace_glue = a & 0x0000010 ~= 0
1894 trace_penalty = a & 0x0000020 ~= 0
1895 trace_fontkern = a & 0x0000040 ~= 0
1896 trace_strut = a & 0x0000080 ~= 0
1897 trace_whatsit = a & 0x0000100 ~= 0
1898 trace_glyph = a & 0x0000200 ~= 0
1899 trace_simple = a & 0x0000400 ~= 0
1900 trace_user = a & 0x0000800 ~= 0
1901 trace_math = a & 0x0001000 ~= 0
1902 trace_italic = a & 0x0002000 ~= 0
1903 trace_origin = a & 0x0004000 ~= 0
1904 trace_discretionary = a & 0x0008000 ~= 0
1905 trace_expansion = a & 0x0010000 ~= 0
1906 trace_line = a & 0x0020000 ~= 0
1907 trace_space = a & 0x0040000 ~= 0
1908 trace_depth = a & 0x0080000 ~= 0
1909 trace_marginkern = a & 0x0100000 ~= 0
1910 trace_mathkern = a & 0x0200000 ~= 0
1911 trace_dir = a & 0x0400000 ~= 0
1912 trace_par = a & 0x0800000 ~= 0
1913 trace_mathglue = a & 0x1000000 ~= 0
1914 trace_mark = a & 0x2000000 ~= 0
1915 trace_insert = a & 0x4000000 ~= 0
1916 trace_boundary = a & 0x8000000 ~= 0
1917 end
1918 elseif a == unsetvalue then
1919 goto list
1920 end
1921
1922
1923
1924 if id == glyph_code then
1925 if trace_glyph then
1926 head, current = ruledglyph(head,current,previous)
1927 end
1928 if trace_expansion then
1929 head, current = ruledglyphexpansion(head,current)
1930 end
1931 elseif id == disc_code then
1932 if trace_discretionary then
1933 head, current = ruleddiscretionary(head,current)
1934 end
1935 local pre, post, replace = getdisc(current)
1936 if pre then
1937 pre = visualize(pre,false,a,parent)
1938 end
1939 if post then
1940 post = visualize(post,false,a,parent)
1941 end
1942 if replace then
1943 replace = visualize(replace,false,a,parent)
1944 end
1945 setdisc(current,pre,post,replace)
1946 elseif id == kern_code then
1947 goto kern
1948 elseif id == glue_code then
1949 goto glue
1950 elseif id == penalty_code then
1951 goto penalty
1952 elseif id == hlist_code or id == vlist_code then
1953 goto list
1954 elseif id == rule_code then
1955 if trace_strut then
1956 head, current = ruledstrut(head,current,parent)
1957 end
1958 elseif id == whatsit_code then
1959 if trace_whatsit then
1960 head, current = ruledwhatsit(head,current)
1961 end
1962 elseif id == user_code then
1963 if trace_user then
1964 head, current = ruleduser(head,current)
1965 end
1966 elseif id == math_code then
1967 local saved = current
1968 if trace_math then
1969 head, current = ruledmath(head,current)
1970 elseif trace_penalty then
1971 head, current = ruledpenalty(head,current,false,true)
1972 end
1973 elseif id == dir_code then
1974 if trace_dir then
1975 head, current = ruleddir(head,current)
1976 end
1977 elseif id == par_code then
1978 if trace_par then
1979 head, current = ruledpar(head,current)
1980 end
1981 elseif id == mark_code then
1982 if trace_mark then
1983 head, current = ruledmark(head,current)
1984 end
1985 elseif id == insert_code then
1986 if trace_insert then
1987 head, current = ruledinsert(head,current)
1988 end
1989 elseif id == boundary_code then
1990 if trace_boundary then
1991 head, current = ruledboundary(head,current)
1992 end
1993 end
1994 goto next
1995 ::penalty::
1996 if trace_penalty then
1997 local f = getattr(current,a_filter)
1998 if f then
1999 if vertical then
2000 if (f & hpenalty_code) == hpenalty_code then
2001 goto next
2002 end
2003 else
2004 if (f & vpenalty_code) == vpenalty_code then
2005 goto next
2006 end
2007 end
2008 end
2009 subtype = getsubtype(current)
2010 head, current = ruledpenalty(head,current,vertical,subtype)
2011 end
2012 ::glue::
2013 content = getleader(current)
2014 if content then
2015 setleader(current,visualize(content,false,nil,parent))
2016 elseif trace_glue then
2017 local f = getattr(current,a_filter)
2018 if f then
2019 if vertical then
2020 if (f & hglue_code) == hglue_code then
2021 goto next
2022 end
2023 else
2024 if (f & vglue_code) == vglue_code then
2025 goto next
2026 end
2027 end
2028 end
2029 head, current = ruledglue(head,current,vertical,parent)
2030 else
2031 subtype = getsubtype(current)
2032 if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
2033 if trace_space then
2034 head, current = ruledspace(head,current,parent)
2035 end
2036 elseif subtype == intermathskip_code then
2037 if trace_math or trace_mathglue then
2038 head = ruledmathglue(head,current,parent)
2039 end
2040 end
2041 end
2042 goto next
2043 ::kern::
2044 subtype = getsubtype(current)
2045 if subtype == fontkern_code or subtype == spacefontkern_code then
2046 if trace_fontkern or prev_trace_fontkern then
2047 head, current = fontkern(head,current)
2048 end
2049 if trace_expansion or prev_trace_expansion then
2050 head, current = kernexpansion(head,current)
2051 end
2052 elseif subtype == italickern_code or subtype == leftkern_code or subtype == rightkern_code then
2053 if trace_italic or prev_trace_italic then
2054 head, current = italickern(head,current)
2055 elseif trace_kern then
2056 head, current = ruleditalic(head,current)
2057 end
2058 elseif subtype == leftmarginkern_code or subtype == rightmarginkern_code then
2059 if trace_marginkern then
2060 head, current = marginkern(head,current)
2061 elseif trace_kern then
2062 head, current = ruledmarginkern(head,current,subtype)
2063 end
2064 elseif subtype == verticalmathkern_code then
2065 if trace_mathkern or trace_kern then
2066 head, current = ruledmathkern(head,current,true)
2067 end
2068 elseif subtype == horizontalmathkern_code then
2069 if trace_mathkern then
2070 head, current = mathkern(head,current)
2071 elseif trace_kern then
2072 head, current = ruledmathkern(head,current,false)
2073 end
2074 elseif subtype == mathshapekern_code then
2075 if trace_mathkern or trace_italic then
2076 head, current = mathshapekern(head,current)
2077 elseif trace_kern then
2078 head, current = ruledmathkern(head,current,false)
2079 end
2080 else
2081 if trace_kern then
2082 local f = getattr(current,a_filter)
2083 if f then
2084 if vertical then
2085 if (f & hkern_code) == hkern_code then
2086 goto next
2087 end
2088 else
2089 if (f & vkern_code) == vkern_code then
2090 goto next
2091 end
2092 end
2093 end
2094 head, current = ruledkern(head,current,vertical)
2095 end
2096 end
2097 goto next
2098 ::list::
2099 if id == hlist_code then
2100 local content = getlist(current)
2101 if content then
2102 setlist(current,visualize(content,false,nil,current))
2103 end
2104 if trace_depth then
2105 ruleddepth(current)
2106 end
2107 if trace_line and (getsubtype(current) == linelist_code or getsubtype(current) == rowlist_code) then
2108 head, current = ruledbox(head,current,false,l_line,"L__",trace_simple,previous,trace_origin,parent)
2109 elseif trace_hbox then
2110 head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent)
2111 end
2112 elseif id == vlist_code then
2113 local content = getlist(current)
2114 local state = getstate(current)
2115 local isvtop = state == 3
2116 local isdbox = state == 4
2117 local tag = nil
2118 local layer = nil
2119 if content then
2120 setlist(current,visualize(content,true,nil,current))
2121 end
2122 if trace_vtop then
2123 if isdbox then
2124 tag = "_D_"
2125 layer = l_vtop
2126 elseif isvtop then
2127 tag = "_T_"
2128 layer = l_vtop
2129 elseif trace_vbox then
2130 tag = "__V"
2131 layer = l_vbox
2132 end
2133 elseif trace_vbox then
2134 if isdbox then
2135 tag = "_D_"
2136 layer = l_vtop
2137 elseif not isvtop then
2138 tag = "__V"
2139 layer = l_vbox
2140 end
2141 end
2142 if tag then
2143 head, current = ruledbox(head,current,true,layer,tag,trace_simple,previous,trace_origin,parent)
2144 end
2145 end
2146 ::next::
2147 previous = current
2148 current = getnext(current)
2149 end
2150 return head
2151 end
2152
2153 local function cleanup()
2154 for tag, cache in next, caches do
2155 for k, v in next, cache do
2156 flushnodelist(v)
2157 end
2158 end
2159 cleanup = function()
2160 report_visualize("error, duplicate cleanup")
2161 end
2162 end
2163
2164 luatex.registerstopactions(cleanup)
2165
2166 function visualizers.handler(head)
2167 if usedfont then
2168 starttiming(visualizers)
2169 head = visualize(head,true)
2170 stoptiming(visualizers)
2171 return head, true
2172 else
2173 return head, false
2174 end
2175 end
2176
2177 function visualizers.box(n)
2178 if usedfont then
2179 starttiming(visualizers)
2180 local box = getbox(n)
2181 if box then
2182 setlist(box,visualize(getlist(box),getid(box) == vlist_code))
2183 end
2184 stoptiming(visualizers)
2185 return head, true
2186 else
2187 return head, false
2188 end
2189 end
2190
2191end
2192
2193do
2194
2195 local nodecodes = nodes.nodecodes
2196 local hlist_code = nodecodes.hlist
2197 local vlist_code = nodecodes.vlist
2198 local nextnode = nuts.traversers.node
2199
2200 local last = nil
2201 local used = nil
2202
2203 local mark = {
2204 "trace:1", "trace:2", "trace:3",
2205 "trace:4", "trace:5", "trace:6",
2206 "trace:7",
2207 }
2208
2209 local function markfonts(list)
2210 for n, id in nextnode, list do
2211 if id == glyph_code then
2212 local font = getfont(n)
2213 local okay = used[font]
2214 if not okay then
2215 last = last + 1
2216 okay = mark[last]
2217 used[font] = okay
2218 end
2219 setcolor(n,okay)
2220 elseif id == hlist_code or id == vlist_code then
2221 markfonts(getlist(n))
2222 end
2223 end
2224 end
2225
2226 function visualizers.markfonts(list)
2227 last, used = 0, { }
2228 markfonts(type(n) == "number" and getlist(getbox(n)) or n)
2229 end
2230
2231end
2232
2233statistics.register("visualization time",function()
2234 if enabled then
2235
2236 return formatters["%s seconds"](statistics.elapsedtime(visualizers))
2237 end
2238end)
2239
2240
2241
2242do
2243
2244 local implement = interfaces.implement
2245
2246 implement {
2247 name = "setvisual",
2248 arguments = "string",
2249 actions = visualizers.setvisual
2250 }
2251
2252 implement {
2253 name = "setvisuals",
2254 arguments = "string",
2255 actions = visualizers.setvisual
2256 }
2257
2258 implement {
2259 name = "getvisual",
2260 arguments = "string",
2261 actions = { setvisual, context }
2262 }
2263
2264 implement {
2265 name = "setvisuallayer",
2266 arguments = "string",
2267 actions = visualizers.setlayer
2268 }
2269
2270 implement {
2271 name = "markvisualfonts",
2272 arguments = "integer",
2273 actions = visualizers.markfonts
2274 }
2275
2276 implement {
2277 name = "setvisualfont",
2278 arguments = "integer",
2279 actions = visualizers.setfont
2280 }
2281
2282end
2283
2284
2285
2286do
2287
2288 local function make(str,forecolor,rulecolor,layer)
2289 if initialize then
2290 initialize()
2291 end
2292 local rule = new_rule(emwidth/fraction,exheight,4*exheight)
2293 setcolor(rule,rulecolor)
2294 settransparency(rule,rulecolor)
2295 local info
2296 if str == "" then
2297 info = new_hlist(rule)
2298 else
2299 local text = hpack_string(str,usedfont)
2300 local list = getlist(text)
2301 setlistcolor(list,textcolor)
2302 setlisttransparency(list,textcolor)
2303 setshift(text,3.5 * exheight)
2304 info = new_hlist(setlink(rule,text))
2305 end
2306 setattr(info,a_layer,layer)
2307 return info
2308 end
2309
2310 function visualizers.register(name,textcolor,rulecolor)
2311 if rawget(layers,name) then
2312
2313 return
2314 end
2315 local cache = caches[name]
2316 local layer = layers[name]
2317 if not textcolor then
2318 textcolor = "trace:ds"
2319 end
2320 if not rulecolor then
2321 rulecolor = "trace:do"
2322 end
2323 return function(str)
2324 if not str then
2325 str = ""
2326 end
2327 local info = cache[str]
2328 if not info then
2329 info = make(str,textcolor,rulecolor,layer)
2330 cache[str] = info
2331 end
2332 return copy_node(info)
2333 end
2334 end
2335
2336end
2337 |