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
43
44local getid = nuts.getid
45local getfont = nuts.getfont
46local getattr = nuts.getattr
47local getsubtype = nuts.getsubtype
48local getbox = nuts.getbox
49local getlist = nuts.getlist
50local getnext = nuts.getnext
51local getboth = nuts.getboth
52local getwhd = nuts.getwhd
53local getkern = nuts.getkern
54local getwidth = nuts.getwidth
55local getdepth = nuts.getdepth
56local getexpansion = nuts.getexpansion
57local getstate = nuts.getstate
58local getoffsets = nuts.getoffsets
59local getindex = nuts.getindex
60
61local isglyph = nuts.isglyph
62
63local hpack_nodes = nuts.hpack
64local vpack_nodes = nuts.vpack
65local copylist = nuts.copylist
66local copy_node = nuts.copy
67local insertnodebefore = nuts.insertbefore
68local insertnodeafter = nuts.insertafter
69local apply_to_nodes = nuts.apply
70local flushnodelist = nuts.flushlist
71
72local hpack_string = nuts.typesetters.tohpack
73
74local texgetattribute = tex.getattribute
75local texsetattribute = tex.setattribute
76
77local setmetatableindex = table.setmetatableindex
78
79local unsetvalue = attributes.unsetvalue
80
81local current_font = font.current
82
83local fonthashes = fonts.hashes
84local chardata = fonthashes.characters
85local exheights = fonthashes.exheights
86local emwidths = fonthashes.emwidths
87local pt_factor = number.dimenfactors.pt
88
89local nodepool = nuts.pool
90local new_rule = nodepool.rule
91local new_kern = nodepool.kern
92local new_glue = nodepool.glue
93local new_hlist = nodepool.hlist
94local new_vlist = nodepool.vlist
95
96local tracers = nodes.tracers
97local visualizers = nodes.visualizers
98
99local setcolor = tracers.colors.set
100local setlistcolor = tracers.colors.setlist
101local settransparency = tracers.transparencies.set
102local setlisttransparency = tracers.transparencies.setlist
103
104local starttiming = statistics.starttiming
105local stoptiming = statistics.stoptiming
106
107local a_visual = attributes.private("visual")
108local a_layer = attributes.private("viewerlayer")
109
110local enableaction = nodes.tasks.enableaction
111
112local report_visualize = logs.reporter("visualize")
113
114local modes = {
115 hbox = 0x0000001,
116 vbox = 0x0000002,
117 vtop = 0x0000004,
118 kern = 0x0000008,
119 glue = 0x0000010,
120 penalty = 0x0000020,
121 fontkern = 0x0000040,
122 strut = 0x0000080,
123 whatsit = 0x0000100,
124 glyph = 0x0000200,
125 simple = 0x0000400,
126 simplehbox = 0x0000401,
127 simplevbox = 0x0000402,
128 simplevtop = 0x0000404,
129 user = 0x0000800,
130 math = 0x0001000,
131 italic = 0x0002000,
132 origin = 0x0004000,
133 discretionary = 0x0008000,
134 expansion = 0x0010000,
135 line = 0x0020000,
136 space = 0x0040000,
137 depth = 0x0080000,
138 marginkern = 0x0100000,
139 mathlistkern = 0x0200000,
140 dir = 0x0400000,
141 par = 0x0800000,
142 mathglue = 0x1000000,
143 mark = 0x2000000,
144 insert = 0x4000000,
145}
146
147visualizers.modes = modes
148
149local usedfont, exheight, emwidth
150local 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_mathlistkern, l_italic, l_origin, l_discretionary, l_expansion, l_line, l_space, l_depth,
151 l_dir, l_whatsit, l_mark, l_insert
152
153local enabled = false
154local layers = { }
155
156local preset_boxes = modes.hbox + modes.vbox + modes.vtop + modes.origin
157local preset_makeup = preset_boxes
158 + modes.kern + modes.glue + modes.penalty
159local preset_all = preset_makeup
160 + modes.fontkern + modes.marginkern + modes.mathlistkern
161 + modes.whatsit + modes.glyph + modes.user + modes.math
162 + modes.dir + modes.whatsit + modes.mathglue
163 + modes.mark + modes.insert
164
165function visualizers.setfont(id)
166 usedfont = id or current_font()
167 exheight = exheights[usedfont]
168 emwidth = emwidths[usedfont]
169end
170
171
172
173local userrule
174local outlinerule
175local emptyrule
176
177local function initialize()
178
179 if not usedfont then
180
181 visualizers.setfont(fonts.definers.define { name = "lmmonoltcond10regular", size = tex.sp("4pt") })
182 end
183
184 for mode, value in next, modes do
185 local tag = formatters["v_%s"](mode)
186 attributes.viewerlayers.define {
187 tag = tag,
188 title = formatters["visualizer %s"](mode),
189 visible = "start",
190 editable = "yes",
191 printable = "yes"
192 }
193 layers[mode] = attributes.viewerlayers.register(tag,true)
194 end
195 l_hbox = layers.hbox
196 l_vbox = layers.vbox
197 l_vtop = layers.vtop
198 l_glue = layers.glue
199 l_kern = layers.kern
200 l_penalty = layers.penalty
201 l_fontkern = layers.fontkern
202 l_strut = layers.strut
203 l_whatsit = layers.whatsit
204 l_glyph = layers.glyph
205 l_user = layers.user
206 l_math = layers.math
207 l_italic = layers.italic
208 l_marginkern = layers.marginkern
209 l_mathlistkern = layers.mathlistkern
210 l_origin = layers.origin
211 l_discretionary = layers.discretionary
212 l_expansion = layers.expansion
213 l_line = layers.line
214 l_space = layers.space
215 l_depth = layers.depth
216 l_dir = layers.dir
217 l_par = layers.par
218 l_mark = layers.mark
219 l_insert = layers.insert
220
221 if not userrule then
222 userrule = nuts.rules.userrule
223 end
224
225 if not outlinerule then
226 outlinerule = nuts.pool.outlinerule
227 end
228
229 if not emptyrule then
230 emptyrule = nuts.pool.emptyrule
231 end
232 initialize = false
233end
234
235local function enable()
236 if initialize then
237 initialize()
238 end
239 enableaction("shipouts","nodes.visualizers.handler")
240 report_visualize("enabled")
241 enabled = true
242 tex.setcount("global","c_syst_visualizers_state",1)
243end
244
245local function setvisual(n,a,what,list)
246 if not n or n == "reset" then
247 return unsetvalue
248 elseif n == true or n == "makeup" then
249 if not a or a == 0 or a == unsetvalue then
250 a = preset_makeup
251 else
252 a = a | preset_makeup
253 end
254 elseif n == "boxes" then
255 if not a or a == 0 or a == unsetvalue then
256 a = preset_boxes
257 else
258 a = a | preset_boxes
259 end
260 elseif n == "all" then
261 if what == false then
262 return unsetvalue
263 elseif not a or a == 0 or a == unsetvalue then
264 a = preset_all
265 else
266 a = a | preset_all
267 end
268 elseif type(n) == "string" then
269 for s in gmatch(n,"[a-z]+") do
270 local m = modes[s]
271 if not m then
272
273 elseif not a or a == 0 or a == unsetvalue then
274 a = m
275 else
276 a = a | m
277 end
278 end
279 elseif type(n) == "number" then
280 if not a or a == 0 or a == unsetvalue then
281 a = n
282 else
283 a = a | n
284 end
285 end
286 if not a or a == 0 or a == unsetvalue then
287 return unsetvalue
288 elseif not enabled then
289 enable()
290 end
291 return a
292end
293
294function nuts.setvisual(n,mode)
295 setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true))
296end
297
298function nuts.setvisuals(n,mode)
299 setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true,true))
300end
301
302
303
304do
305
306 local cached = setmetatableindex(function(t,k)
307 if k == true then
308 return texgetattribute(a_visual)
309 elseif not k then
310 t[k] = unsetvalue
311 return unsetvalue
312 else
313 local v = setvisual(k)
314 t[k] = v
315 return v
316 end
317 end)
318
319
320
321
322
323
324 local a = unsetvalue
325
326 local f = function(n) setattr(n,a_visual,a) end
327
328 local function applyvisuals(n,mode)
329 a = cached[mode]
330 apply_to_nodes(n,f)
331 end
332
333 nuts.applyvisuals = applyvisuals
334
335 function nodes.applyvisuals(n,mode)
336 applyvisuals(tonut(n),mode)
337 end
338
339 function visualizers.attribute(mode)
340 return cached[mode]
341 end
342
343 visualizers.attributes = cached
344
345end
346
347function nuts.copyvisual(n,m)
348 setattr(n,a_visual,getattr(m,a_visual))
349end
350
351function visualizers.setvisual(n)
352 texsetattribute(a_visual,setvisual(n,texgetattribute(a_visual)))
353end
354
355function visualizers.setlayer(n)
356 texsetattribute(a_layer,layers[n] or unsetvalue)
357end
358
359local function set(mode,v)
360 texsetattribute(a_visual,setvisual(mode,texgetattribute(a_visual),v))
361end
362
363for mode, value in next, modes do
364 trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end)
365end
366
367local fraction = 10
368local normalize = true
369
370trackers .register("visualizers.reset", function(v) set("reset", v) end)
371trackers .register("visualizers.all", function(v) set("all", v) end)
372trackers .register("visualizers.makeup", function(v) set("makeup",v) end)
373trackers .register("visualizers.boxes", function(v) set("boxes", v) end)
374directives.register("visualizers.fraction", function(v) fraction = (v and tonumber(v)) or (v == "more" and 5) or 10 end)
375
376
377
378local function sometext(str,layer,color,textcolor,lap,variant)
379 local text = hpack_string(str,usedfont)
380 local size = getwidth(text)
381 local rule = new_rule(size,2*exheight,exheight/2)
382 local kern = new_kern(-size)
383 if color then
384 setcolor(rule,color)
385 end
386 if textcolor then
387 setlistcolor(getlist(text),textcolor)
388 end
389 local info = setlink(rule,kern,text)
390 setlisttransparency(info,"trace:g")
391 info = hpack_nodes(info)
392 local width = getwidth(info)
393 if variant then
394 nuts.setoffsets(info,0,variant*exheight)
395 end
396 if lap then
397 info = new_hlist(setlink(new_kern(-width),info))
398 else
399 info = new_hlist(info)
400 end
401 if layer then
402 setattr(info,a_layer,layer)
403 end
404 return info, width
405end
406
407local function someblob(str,layer,color,textcolor,width)
408 local text = hpack_string(str,usedfont)
409 local size = getwidth(text)
410 local rule = new_rule(width,2*exheight,exheight/2)
411 local kern = new_kern(-width + (width-size)/2)
412 if color then
413 setcolor(rule,color)
414 end
415 if textcolor then
416 setlistcolor(getlist(text),textcolor)
417 end
418 local info = setlink(rule,kern,text)
419 setlisttransparency(info,"trace:g")
420 info = hpack_nodes(info)
421 local width = getwidth(info)
422 info = new_hlist(info)
423 if layer then
424 setattr(info,a_layer,layer)
425 end
426 return info, width
427end
428
429local caches = setmetatableindex("table")
430
431local fontkern, italickern, marginkern, mathlistkern do
432
433 local f_cache = caches["fontkern"]
434 local i_cache = caches["italickern"]
435 local m_cache = caches["marginkern"]
436 local l_cache = caches["mathlistkern"]
437
438 local function somekern(head,current,cache,color,layer)
439 local width = getkern(current)
440 local extra = getexpansion(current)
441 local kern = width + extra
442 local info = cache[kern]
443 if not info then
444 local text = hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)
445 local rule = new_rule(emwidth/fraction,6*exheight,2*exheight)
446 local list = getlist(text)
447 if kern > 0 then
448 setlistcolor(list,"trace:db")
449 elseif kern < 0 then
450 setlistcolor(list,"trace:dr")
451 else
452 setlistcolor(list,"trace:dg")
453 end
454 setlisttransparency(list,color)
455 setcolor(rule,color)
456 settransparency(rule,color)
457 setshift(text,-5 * exheight)
458 info = new_hlist(setlink(rule,text))
459 setattr(info,a_layer,layer)
460 f_cache[kern] = info
461 end
462 head = insertnodebefore(head,current,copylist(info))
463 return head, current
464 end
465
466 fontkern = function(head,current)
467 return somekern(head,current,f_cache,"trace:ds",l_fontkern)
468 end
469
470 italickern = function(head,current)
471 return somekern(head,current,i_cache,"trace:do",l_italic)
472 end
473
474 marginkern = function(head,current)
475 return somekern(head,current,m_cache,"trace:do",l_marginkern)
476 end
477
478 mathlistkern = function(head,current)
479 return somekern(head,current,l_cache,"trace:do",l_mathlistkern)
480 end
481
482end
483
484local ruledglyphexpansion do
485
486 local f_cache = caches["glyphexpansion"]
487
488 ruledglyphexpansion = function(head,current)
489 local extra = getexpansion(current)
490 if extra and extra ~= 0 then
491 extra = extra / 1000
492 local info = f_cache[extra]
493 if not info then
494 local text = hpack_string(round(extra),usedfont)
495 local rule = new_rule(emwidth/fraction,exheight,2*exheight)
496 local list = getlist(text)
497 if extra > 0 then
498 setlistcolor(list,"trace:db")
499 elseif extra < 0 then
500 setlistcolor(list,"trace:dr")
501 end
502 setlisttransparency(list,"trace:ds")
503 setcolor(rule,"trace:ds")
504 settransparency(rule,"trace:ds")
505 setshift(text,1.5 * exheight)
506 info = new_hlist(setlink(rule,text))
507 setattr(info,a_layer,l_expansion)
508 f_cache[extra] = info
509 end
510 head = insertnodebefore(head,current,copylist(info))
511 return head, current
512 end
513 return head, current
514 end
515
516end
517
518local kernexpansion do
519
520 local f_cache = caches["kernexpansion"]
521
522
523
524 kernexpansion = function(head,current)
525 local extra = getexpansion(current)
526 if extra ~= 0 then
527 extra = extra / 1000
528 local info = f_cache[extra]
529 if not info then
530 local text = hpack_string(round(extra),usedfont)
531 local rule = new_rule(emwidth/fraction,exheight,4*exheight)
532 local list = getlist(text)
533 if extra > 0 then
534 setlistcolor(list,"trace:db")
535 elseif extra < 0 then
536 setlistcolor(list,"trace:dr")
537 end
538 setlisttransparency(list,"trace:ds")
539 setcolor(rule,"trace:ds")
540 settransparency(rule,"trace:ds")
541 setshift(text,3.5 * exheight)
542 info = new_hlist(setlink(rule,text))
543 setattr(info,a_layer,l_expansion)
544 f_cache[extra] = info
545 end
546 head = insertnodebefore(head,current,copylist(info))
547 return head, current
548 end
549 return head, current
550 end
551
552end
553
554local ruledmark do
555
556 local set_code = nodes.markcodes.set
557
558 local sm_cache = setmetatableindex(caches["setmark"], function(t,index)
559 local info = sometext(formatters["SM:%i"](index),usedfont,nil,"trace:w")
560 setattr(info,a_layer,l_mark)
561 t[index] = info
562 return info
563 end)
564
565 local rm_cache = setmetatableindex(caches["resetmark"], function(t,index)
566 local info = sometext(formatters["RM:%i"](index),usedfont,nil,"trace:w")
567 setattr(info,a_layer,l_mark)
568 t[index] = info
569 return info
570 end)
571
572 ruledmark = function(head,current)
573 local index = getindex(current)
574 local info = getsubtype(current) == set_code and sm_cache[index] or rm_cache[index]
575 head, current = insertnodeafter(head,current,copylist(info))
576 return head, current
577 end
578
579end
580
581local ruledinsert do
582
583 local si_cache = setmetatableindex(caches["insert"], function(f,index)
584 local info = sometext(formatters["SI:%i"](index),usedfont,nil,"trace:w")
585 setattr(info,a_layer,l_insert)
586 i_cache[index] = info
587 end)
588
589 ruledinsert = function(head,current)
590 local info = si_cache[getindex(current)]
591 head, current = insertnodeafter(head,current,copylist(info))
592 return head, current
593 end
594
595end
596
597local ruledwhatsit do
598
599 local whatsitcodes = nodes.whatsitcodes
600 local w_cache = caches["whatsit"]
601
602 local tags = {
603 [whatsitcodes.open] = "OPN",
604 [whatsitcodes.write] = "WRI",
605 [whatsitcodes.close] = "CLS",
606
607 [whatsitcodes.latelua] = "LUA",
608 [whatsitcodes.savepos] = "POS",
609 [whatsitcodes.userdefined] = "USR",
610 [whatsitcodes.literal] = "LIT",
611 [whatsitcodes.setmatrix] = "MAT",
612 [whatsitcodes.save] = "SAV",
613 [whatsitcodes.restore] = "RES",
614 [whatsitcodes.startscaling] = "+SCA",
615 [whatsitcodes.stopscaling] = "-SCA",
616 [whatsitcodes.startrotation] = "+ROT",
617 [whatsitcodes.stoprotation] = "-ROT",
618 [whatsitcodes.startmirroring] = "+MIR",
619 [whatsitcodes.stopmirroring] = "-MIR",
620 [whatsitcodes.startclipping] = "+CLP",
621 [whatsitcodes.stopclipping] = "-CLP",
622 [whatsitcodes.startmatrix] = "+MAT",
623 [whatsitcodes.stopmatrix] = "-MAT",
624 [whatsitcodes.setstate] = "SET",
625 }
626
627 ruledwhatsit = function(head,current)
628 local what = getsubtype(current)
629 local info = w_cache[what]
630 if info then
631
632 else
633 info = sometext(formatters["W:%s"](tags[what] or what),usedfont,nil,"trace:w")
634 setattr(info,a_layer,l_whatsit)
635 w_cache[what] = info
636 end
637 head, current = insertnodeafter(head,current,copylist(info))
638 return head, current
639 end
640
641end
642
643local ruleddir, ruledpar do
644
645 local dircodes = nodes.dircodes
646 local dirvalues = nodes.dirvalues
647
648 local cancel_code = dircodes.cancel
649 local l2r_code = dirvalues.l2r
650 local r2l_code = dirvalues.r2l
651
652 local d_cache = caches["dir"]
653
654 local getdirection = nuts.getdirection
655
656 local tags = {
657 l2r = "L2R",
658 r2l = "R2L",
659 cancel = "CAN",
660 par = "PAR",
661 }
662
663 ruledpar = function(head,current)
664 local what = "par"
665 local info = d_cache[what]
666 if info then
667
668 else
669 info = sometext(formatters["L:%s"](what),usedfont,nil,"trace:w")
670 setattr(info,a_layer,l_dir)
671 d_cache[what] = info
672 end
673 head, current = insertnodeafter(head,current,copylist(info))
674 return head, current
675 end
676
677 ruleddir = function(head,current)
678 local what = getsubtype(current)
679 if what == cancel_code then
680 what = "cancel"
681 elseif getdirection(current) == r2l_code then
682 what = "r2l"
683 else
684 what = "l2r"
685 end
686 local info = d_cache[what]
687 if info then
688
689 else
690 info = sometext(formatters["D:%s"](what),usedfont,nil,"trace:w")
691 setattr(info,a_layer,l_dir)
692 d_cache[what] = info
693 end
694 head, current = insertnodeafter(head,current,copylist(info))
695 return head, current
696 end
697
698end
699
700local ruleduser do
701
702 local u_cache = caches["user"]
703
704 ruleduser = function(head,current)
705 local what = getsubtype(current)
706 local info = u_cache[what]
707 if info then
708
709 else
710 info = sometext(formatters["U:%s"](what),usedfont)
711 setattr(info,a_layer,l_user)
712 u_cache[what] = info
713 end
714 head, current = insertnodeafter(head,current,copylist(info))
715 return head, current
716 end
717
718end
719
720local ruledmath do
721
722 local mathcodes = nodes.mathcodes
723 local m_cache = {
724 beginmath = caches["bmath"],
725 endmath = caches["emath"],
726 }
727 local tags = {
728 beginmath = "B",
729 endmath = "E",
730 }
731
732 ruledmath = function(head,current)
733 local what = getsubtype(current)
734 local tag = mathcodes[what]
735 local skip = getkern(current) + getwidth(current)
736 local info = m_cache[tag][skip]
737 if info then
738
739 else
740 local text, width = sometext(formatters["M:%s"](tag and tags[tag] or what),usedfont,nil,"trace:dr")
741 local rule = new_rule(skip,-655360/fraction,2*655360/fraction)
742 local dist = tag == "beginmath" and width or skip
743 setcolor(rule,"trace:dr")
744 settransparency(rule,"trace:dr")
745 setattr(rule,a_layer,l_math)
746 info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-dist),text))
747 setattr(info,a_layer,l_math)
748 m_cache[tag][skip] = info
749 end
750 head, current = insertnodeafter(head,current,copylist(info))
751 return head, current
752 end
753
754end
755
756local ruleddepth do
757
758 ruleddepth = function(current,wd,ht,dp)
759 local wd, ht, dp = getwhd(current)
760 if dp ~= 0 then
761 local rule = new_rule(wd,0,dp)
762 setcolor(rule,"trace:o")
763 settransparency(rule,"trace:g")
764 setattr(rule,a_layer,l_depth)
765 setlist(current,setlink(rule,new_kern(-wd),getlist(current)))
766 end
767 end
768
769end
770
771local ruledbox do
772
773 local b_cache = caches["box"]
774 local o_cache = caches["origin"]
775
776 local getshift = nuts.getshift
777
778 setmetatableindex(o_cache,function(t,size)
779 local rule = new_rule(2*size,size,size)
780 local origin = hpack_nodes(rule)
781 setcolor(rule,"trace:do")
782 settransparency(rule,"trace:do")
783 setattr(rule,a_layer,l_origin)
784 t[size] = origin
785 return origin
786 end)
787
788 ruledbox = function(head,current,vertical,layer,what,simple,previous,trace_origin,parent)
789 local wd, ht, dp = getwhd(current)
790 local force_origin = wd == 0 or (dp + ht) == 0
791 local shift = getshift(current)
792 local next = getnext(current)
793 local prev = previous
794 setboth(current)
795 local linewidth = emwidth/fraction
796 local size = 2*linewidth
797 local this
798 if not simple then
799 this = b_cache[what]
800 if not this then
801 local text = hpack_string(what,usedfont)
802 this = setlink(new_kern(-getwidth(text)),text)
803 setlisttransparency(this,"trace:s")
804 this = new_hlist(this)
805 b_cache[what] = this
806 end
807 end
808
809 local info = setlink(
810 this and copylist(this) or nil,
811 (force_origin and emptyrule(wd,ht,dp))
812 or (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth))
813 or userrule {
814 width = wd,
815 height = ht,
816 depth = dp,
817 line = linewidth,
818 type = "box",
819 dashed = 3*size,
820 }
821 )
822
823 setlisttransparency(info,"trace:s")
824 info = new_hlist(info)
825
826 setattr(info,a_layer,layer)
827 if vertical then
828 if not force_origin and shift == 0 then
829 info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
830 elseif trace_origin or force_origin then
831 local size = 2*size
832 local origin = o_cache[size]
833 origin = copylist(origin)
834 if getid(parent) == vlist_code then
835 setshift(origin,-shift)
836 info = setlink(current,new_kern(-size),origin,new_kern(-size-dp),info)
837 else
838
839 info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
840 end
841 setshift(current,0)
842 else
843 info = setlink(current,new_dp ~= 0 and new_kern(-dp) or nil,info)
844 setshift(current,0)
845 end
846 info = new_vlist(info,wd,ht,dp,shift)
847 else
848 if not force_origin and shift == 0 then
849 info = setlink(current,new_kern(-wd),info)
850 elseif trace_origin or force_origin then
851 local size = 2*size
852 local origin = o_cache[size]
853 origin = copylist(origin)
854 if getid(parent) == vlist_code then
855 info = setlink(current,new_kern(-wd-size-shift),origin,new_kern(-size+shift),info)
856 else
857 setshift(origin,-shift)
858 info = setlink(current,new_kern(-wd-size),origin,new_kern(-size),info)
859 end
860 setshift(current,0)
861 else
862 info = setlink(current,new_kern(-wd),info)
863 setshift(current,0)
864 end
865 info = new_hlist(info,wd,ht,dp,shift)
866 end
867 if next then
868 setlink(info,next)
869 end
870 if prev and prev > 0 then
871 setlink(prev,info)
872 end
873 if head == current then
874 return info, info
875 else
876 return head, info
877 end
878
879
880
881 end
882
883end
884
885local ruledglyph do
886
887
888
889
890
891
892
893 ruledglyph = function(head,current,previous)
894 local wd = getwidth(current)
895 if wd ~= 0 then
896 local wd, ht, dp = getwhd(current)
897 local next = getnext(current)
898 local prev = previous
899 setboth(current)
900 local linewidth = emwidth/(2*fraction)
901 local x_offset, y_offset, l_margin, r_margin, raise = getoffsets(current)
902 local info = setlink((dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule {
903 width = wd,
904 height = ht,
905 depth = dp,
906 line = linewidth,
907 type = "box",
908 },new_kern(-wd))
909
910 local c, f = isglyph(current)
911 local char = chardata[f][c]
912 if char and type(char.unicode) == "table" then
913 setlistcolor(info,"trace:s")
914 setlisttransparency(info,"trace:ds")
915 else
916 setlistcolor(info,"trace:o")
917 setlisttransparency(info,"trace:do")
918 end
919 info = new_hlist(info)
920 setattr(info,a_layer,l_glyph)
921 local info = setlink(current,new_kern(-wd),info)
922 info = hpack_nodes(info)
923 setwidth(info,wd)
924 if next then
925 setlink(info,next)
926 end
927 if prev then
928 setlink(prev,info)
929 end
930 if head == current then
931 return info, info
932 else
933 return head, info
934 end
935 else
936 return head, current
937 end
938 end
939
940 function visualizers.setruledglyph(f)
941 ruledglyph = f or ruledglyph
942 end
943
944end
945
946local ruledglue, ruledmathglue do
947
948 local effectiveglue = nuts.effectiveglue
949
950 local gluecodes = nodes.gluecodes
951
952 local userskip_code = gluecodes.userskip
953 local spaceskip_code = gluecodes.spaceskip
954 local xspaceskip_code = gluecodes.xspaceskip
955 local zerospaceskip_code = gluecodes.zerospaceskip or gluecodes.userskip
956
957 local leftskip_code = gluecodes.leftskip
958 local rightskip_code = gluecodes.rightskip
959 local lefthangskip_code = gluecodes.lefthangskip
960 local righthangskip_code = gluecodes.righthangskip
961 local parfillleftskip_code = gluecodes.parfillleftskip or parfillskip_code
962 local parfillrightskip_code = gluecodes.parfillrightskip or parfillskip_code
963 local indentskip_code = gluecodes.indentskip
964 local intermathskip_code = gluecodes.intermathskip
965 local correctionskip_code = gluecodes.correctionskip
966 local tabskip_code = gluecodes.tabskip
967
968 local g_cache_v = caches["vglue"]
969 local g_cache_h = caches["hglue"]
970 local g_cache_ls = caches["leftskip"]
971 local g_cache_rs = caches["rightskip"]
972 local g_cache_lh = caches["lefthang"]
973 local g_cache_rh = caches["righthang"]
974 local g_cache_lp = caches["parfillleftskip"]
975 local g_cache_rp = caches["parfillrightskip"]
976 local g_cache_is = caches["indentskip"]
977 local g_cache_cs = caches["correctionskip"]
978 local g_cache_gn = caches["gluename"]
979
980 local tags = {
981
982 [gluecodes.lineskip] = "LI",
983 [gluecodes.baselineskip] = "BS",
984 [gluecodes.parskip] = "PS",
985 [gluecodes.abovedisplayskip] = "DA",
986 [gluecodes.belowdisplayskip] = "DB",
987 [gluecodes.abovedisplayshortskip] = "SA",
988 [gluecodes.belowdisplayshortskip] = "SB",
989 [gluecodes.topskip] = "TS",
990 [gluecodes.splittopskip] = "ST",
991 [tabskip_code] = "TB",
992 [gluecodes.thinmuskip] = "MS",
993 [gluecodes.medmuskip] = "MM",
994 [gluecodes.thickmuskip] = "ML",
995 [intermathskip_code] = "IM",
996 [gluecodes.keepskip or 99] = "KS",
997 [gluecodes.mathskip] = "MT",
998 [gluecodes.leaders] = "NL",
999 [gluecodes.cleaders] = "CL",
1000 [gluecodes.xleaders] = "XL",
1001 [gluecodes.gleaders] = "GL",
1002
1003
1004 [leftskip_code] = "LS",
1005 [rightskip_code] = "RS",
1006 [lefthangskip_code] = "LH",
1007 [righthangskip_code] = "RH",
1008 [spaceskip_code] = "SP",
1009 [xspaceskip_code] = "XS",
1010 [zerospaceskip_code] = "ZS",
1011 [parfillleftskip_code] = "PL",
1012 [parfillrightskip_code] = "PR",
1013 [indentskip_code] = "IN",
1014 [correctionskip_code] = "CS",
1015 }
1016
1017 local stags = {
1018 [lefthangskip_code] = 0.5,
1019 [righthangskip_code] = 0.5,
1020 [leftskip_code] = -2,
1021 [rightskip_code] = -2,
1022 [parfillleftskip_code] = -0.75,
1023 [parfillrightskip_code] = -0.75,
1024 }
1025
1026
1027
1028 local f_amount = formatters["%s:%0.3f"]
1029 local mathvalues = nodes.mathvalues
1030
1031 local function gluename(name,layer,color)
1032 local info = g_cache_gn[name]
1033 if not info then
1034 local text = hpack_string((gsub(mathvalues[name],"spacing","")),usedfont)
1035 local rule = new_rule(emwidth/fraction,2*exheight,2.75*exheight)
1036 local list = getlist(text)
1037 setlisttransparency(list,color)
1038 settransparency(rule,color)
1039 setshift(text,2*exheight)
1040 info = new_hlist(setlink(rule,text))
1041 setattr(info,a_layer,layer)
1042 g_cache_gn[name] = info
1043 end
1044 return copylist(info)
1045 end
1046
1047 ruledmathglue = function(head,current)
1048 return insertnodebefore(head,current,gluename(getfont(current),l_glue,"trace:m"))
1049 end
1050
1051 ruledglue = function(head,current,vertical,parent)
1052 local subtype = getsubtype(current)
1053 local width = effectiveglue(current,parent)
1054 local stag = normalize and stags[subtype]
1055 local amount = f_amount(tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
1056 local info = (vertical and g_cache_v or g_cache_h)[amount]
1057 if subtype == intermathskip_code then
1058 head = ruledmathglue(head, current)
1059 end
1060 if info then
1061
1062 else
1063 if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
1064 info = sometext(amount,l_glue,"trace:y")
1065 elseif subtype == userskip_code then
1066 if width > 0 then
1067 info = sometext(amount,l_glue,"trace:b")
1068 elseif width < 0 then
1069 info = sometext(amount,l_glue,"trace:r")
1070 else
1071 info = sometext(amount,l_glue,"trace:g")
1072 end
1073 elseif subtype == tabskip_code then
1074 info = sometext(amount,l_glue,"trace:s")
1075 elseif subtype == indentskip_code or subtype == correctionskip_code then
1076 info = sometext(amount,l_glue,"trace:s")
1077 elseif subtype == leftskip_code then
1078 info = sometext(amount,l_glue,normalize and "trace:y" or "trace:c",false,true,stag)
1079 elseif subtype == rightskip_code then
1080 info = sometext(amount,l_glue,normalize and "trace:y" or "trace:c",false,false,stag)
1081 elseif subtype == lefthangskip_code then
1082 info = sometext(amount,l_glue,normalize and "trace:y" or "trace:m",false,true,stag)
1083 elseif subtype == righthangskip_code then
1084 info = sometext(amount,l_glue,normalize and "trace:y" or "trace:m",false,false,stag)
1085 elseif subtype == parfillleftskip_code then
1086 info = sometext(amount,l_glue,"trace:s",false,true,stag)
1087 elseif subtype == parfillrightskip_code then
1088 info = sometext(amount,l_glue,"trace:s",false,false,stag)
1089 else
1090 info = sometext(amount,l_glue,"trace:m")
1091 end
1092 (vertical and g_cache_v or g_cache_h)[amount] = info
1093 end
1094 info = copylist(info)
1095 if vertical then
1096 info = vpack_nodes(info)
1097 end
1098 head, current = insertnodebefore(head,current,info)
1099 return head, getnext(current)
1100 end
1101
1102 local g_cache_s = caches["space"]
1103 local g_cache_x = caches["xspace"]
1104
1105 ruledspace = function(head,current,parent)
1106 local subtype = getsubtype(current)
1107 local width = effectiveglue(current,parent)
1108 local info
1109 if subtype == spaceskip_code then
1110 info = g_cache_s[width]
1111 if not info then
1112 info = someblob("SP",l_glue,"trace:y",nil,width)
1113 g_cache_s[width] = info
1114 end
1115 else
1116 info = g_cache_x[width]
1117 if not info then
1118 info = someblob("XS",l_glue,"trace:m",nil,width)
1119 g_cache_x[width] = info
1120 end
1121 end
1122 info = copylist(info)
1123 head, current = insertnodebefore(head,current,info)
1124 return head, getnext(current)
1125 end
1126
1127end
1128
1129local ruledkern do
1130
1131 local k_cache_v = caches["vkern"]
1132 local k_cache_h = caches["hkern"]
1133
1134 ruledkern = function(head,current,vertical,mk)
1135 local kern = getkern(current)
1136 local cache = vertical and k_cache_v or k_cache_h
1137 local info = cache[kern]
1138 if not info then
1139 local amount = formatters["%s:%0.3f"](vertical and "VK" or (mk and "MK") or "HK",kern*pt_factor)
1140 if kern > 0 then
1141 info = sometext(amount,l_kern,"trace:b")
1142 elseif kern < 0 then
1143 info = sometext(amount,l_kern,"trace:r")
1144 else
1145 info = sometext(amount,l_kern,"trace:g")
1146 end
1147 cache[kern] = info
1148 end
1149 info = copylist(info)
1150 if vertical then
1151 info = vpack_nodes(info)
1152 end
1153 head, current = insertnodebefore(head,current,info)
1154 return head, getnext(current)
1155 end
1156
1157end
1158
1159local ruleditalic do
1160
1161 local i_cache = caches["italic"]
1162
1163 ruleditalic = function(head,current)
1164 local kern = getkern(current)
1165 local info = i_cache[kern]
1166 if not info then
1167 local amount = formatters["%s:%0.3f"]("IC",kern*pt_factor)
1168 if kern > 0 then
1169 info = sometext(amount,l_kern,"trace:b")
1170 elseif kern < 0 then
1171 info = sometext(amount,l_kern,"trace:r")
1172 else
1173 info = sometext(amount,l_kern,"trace:g")
1174 end
1175 i_cache[kern] = info
1176 end
1177 info = copylist(info)
1178 head, current = insertnodebefore(head,current,info)
1179 return head, getnext(current)
1180 end
1181
1182end
1183
1184local ruledmarginkern do
1185
1186 local m_cache = caches["marginkern"]
1187
1188 ruledmarginkern = function(head,current)
1189 local kern = getkern(current)
1190 local info = m_cache[kern]
1191 if not info then
1192 local amount = formatters["%s:%0.3f"]("MK",kern*pt_factor)
1193 if kern > 0 then
1194 info = sometext(amount,l_marginkern,"trace:b")
1195 elseif kern < 0 then
1196 info = sometext(amount,l_marginkern,"trace:r")
1197 else
1198 info = sometext(amount,l_marginkern,"trace:g")
1199 end
1200 m_cache[kern] = info
1201 end
1202 info = copylist(info)
1203 head, current = insertnodebefore(head,current,info)
1204 return head, getnext(current)
1205 end
1206
1207end
1208
1209local ruledmathlistkern do
1210
1211 local l_cache = caches["mathlistkern"]
1212
1213 ruledmathlistkern = function(head,current)
1214 local kern = getkern(current)
1215 local info = l_cache[kern]
1216 if not info then
1217 local amount = formatters["%s:%0.3f"]("LK",kern*pt_factor)
1218 if kern > 0 then
1219 info = sometext(amount,l_mathlistkern,"trace:b")
1220 elseif kern < 0 then
1221 info = sometext(amount,l_mathlistkern,"trace:r")
1222 else
1223 info = sometext(amount,l_mathlistkern,"trace:g")
1224 end
1225 l_cache[kern] = info
1226 end
1227 info = copylist(info)
1228 head, current = insertnodebefore(head,current,info)
1229 return head, getnext(current)
1230 end
1231
1232end
1233
1234local ruleddiscretionary do
1235
1236 local d_cache = caches["discretionary"]
1237
1238 ruleddiscretionary = function(head,current)
1239 local d = d_cache[true]
1240 if not the_discretionary then
1241 local rule = new_rule(4*emwidth/fraction,4*exheight,exheight)
1242 local kern = new_kern(-2*emwidth/fraction)
1243 setlink(kern,rule)
1244 setcolor(rule,"trace:dd")
1245 settransparency(rule,"trace:dd")
1246 setattr(rule,a_layer,l_discretionary)
1247 d = new_hlist(kern)
1248 d_cache[true] = d
1249 end
1250 insertnodeafter(head,current,copylist(d))
1251 return head, current
1252 end
1253
1254end
1255
1256local ruledpenalty do
1257
1258 local p_cache_v = caches["vpenalty"]
1259 local p_cache_h = caches["hpenalty"]
1260
1261 local raisepenalties = false
1262
1263 local getpenalty = nuts.getpenalty
1264
1265 directives.register("visualizers.raisepenalties",function(v) raisepenalties = v end)
1266
1267 ruledpenalty = function(head,current,vertical)
1268 local penalty = getpenalty(current)
1269 local info = (vertical and p_cache_v or p_cache_h)[penalty]
1270 if info then
1271
1272 else
1273 local amount = formatters["%s:%s"](vertical and "VP" or "HP",penalty)
1274 if penalty > 0 then
1275 info = sometext(amount,l_penalty,"trace:b")
1276 elseif penalty < 0 then
1277 info = sometext(amount,l_penalty,"trace:r")
1278 else
1279 info = sometext(amount,l_penalty,"trace:g")
1280 end
1281 (vertical and p_cache_v or p_cache_h)[penalty] = info
1282 end
1283 info = copylist(info)
1284 if vertical then
1285 info = vpack_nodes(info)
1286 elseif raisepenalties then
1287 setshift(info,-65536*4)
1288 end
1289 head, current = insertnodebefore(head,current,info)
1290 return head, getnext(current)
1291 end
1292
1293end
1294
1295do
1296
1297 local nodecodes = nodes.nodecodes
1298 local disc_code = nodecodes.disc
1299 local kern_code = nodecodes.kern
1300 local glyph_code = nodecodes.glyph
1301 local glue_code = nodecodes.glue
1302 local penalty_code = nodecodes.penalty
1303 local whatsit_code = nodecodes.whatsit
1304 local user_code = nodecodes.user
1305 local math_code = nodecodes.math
1306 local hlist_code = nodecodes.hlist
1307 local vlist_code = nodecodes.vlist
1308 local marginkern_code = nodecodes.marginkern
1309 local mathlistkern_code = nodecodes.mathlistkern
1310 local dir_code = nodecodes.dir
1311 local par_code = nodecodes.par
1312 local mark_code = nodecodes.mark
1313 local insert_code = nodecodes.insert
1314
1315 local kerncodes = nodes.kerncodes
1316 local fontkern_code = kerncodes.fontkern
1317 local italickern_code = kerncodes.italiccorrection
1318 local leftmarginkern_code = kerncodes.leftmarginkern
1319 local rightmarginkern_code = kerncodes.rightmarginkern
1320 local mathlistkern_code = kerncodes.mathlistkern
1321
1322
1323 local skipcodes = nodes.skipcodes
1324 local spaceskip_code = skipcodes.spaceskip
1325 local xspaceskip_code = skipcodes.xspaceskip
1326 local zerospaceskip_code = skipcodes.zerospaceskip
1327 local intermathskip_code = skipcodes.intermathskip
1328
1329 local listcodes = nodes.listcodes
1330 local linelist_code = listcodes.line
1331
1332 local vtop_package_state = 3
1333
1334 local getleader = nuts.getleader
1335 local getdisc = nuts.getdisc
1336
1337 local setleader = nuts.setleader
1338 local setdisc = nuts.setdisc
1339
1340
1341
1342 local function visualize(head,vertical,forced,parent)
1343 local trace_hbox = false
1344 local trace_vbox = false
1345 local trace_vtop = false
1346 local trace_kern = false
1347 local trace_glue = false
1348 local trace_penalty = false
1349 local trace_fontkern = false
1350 local trace_strut = false
1351 local trace_whatsit = false
1352 local trace_glyph = false
1353 local trace_simple = false
1354 local trace_user = false
1355 local trace_math = false
1356 local trace_italic = false
1357 local trace_origin = false
1358 local trace_discretionary = false
1359 local trace_expansion = false
1360 local trace_line = false
1361 local trace_space = false
1362 local trace_depth = false
1363 local trace_dir = false
1364 local trace_par = false
1365 local trace_math_glue = false
1366 local trace_mark = false
1367 local trace_insert = false
1368 local current = head
1369 local previous = nil
1370 local attr = unsetvalue
1371 local prev_trace_fontkern = nil
1372 local prev_trace_italic = nil
1373 local prev_trace_marginkern = nil
1374
1375 local prev_trace_expansion = nil
1376
1377 while current do
1378 local id = getid(current)
1379 local a = forced or getattr(current,a_visual) or unsetvalue
1380 local subtype, content
1381 if a ~= attr then
1382 prev_trace_fontkern = trace_fontkern
1383 prev_trace_italic = trace_italic
1384 prev_trace_marginkern = trace_marginkern
1385
1386 prev_trace_expansion = trace_expansion
1387 attr = a
1388 if a == unsetvalue then
1389 trace_hbox = false
1390 trace_vbox = false
1391 trace_vtop = false
1392 trace_kern = false
1393 trace_glue = false
1394 trace_penalty = false
1395 trace_fontkern = false
1396 trace_strut = false
1397 trace_whatsit = false
1398 trace_glyph = false
1399 trace_simple = false
1400 trace_user = false
1401 trace_math = false
1402 trace_italic = false
1403 trace_origin = false
1404 trace_discretionary = false
1405 trace_expansion = false
1406 trace_line = false
1407 trace_space = false
1408 trace_depth = false
1409 trace_marginkern = false
1410 trace_mathlistkern = false
1411 trace_dir = false
1412 trace_par = false
1413 trace_math_glue = false
1414 trace_mark = false
1415 trace_insert = false
1416 if id == kern_code then
1417 goto kern
1418 else
1419 goto list
1420 end
1421 else
1422
1423 trace_hbox = a & 0x0000001 ~= 0
1424 trace_vbox = a & 0x0000002 ~= 0
1425 trace_vtop = a & 0x0000004 ~= 0
1426 trace_kern = a & 0x0000008 ~= 0
1427 trace_glue = a & 0x0000010 ~= 0
1428 trace_penalty = a & 0x0000020 ~= 0
1429 trace_fontkern = a & 0x0000040 ~= 0
1430 trace_strut = a & 0x0000080 ~= 0
1431 trace_whatsit = a & 0x0000100 ~= 0
1432 trace_glyph = a & 0x0000200 ~= 0
1433 trace_simple = a & 0x0000400 ~= 0
1434 trace_user = a & 0x0000800 ~= 0
1435 trace_math = a & 0x0001000 ~= 0
1436 trace_italic = a & 0x0002000 ~= 0
1437 trace_origin = a & 0x0004000 ~= 0
1438 trace_discretionary = a & 0x0008000 ~= 0
1439 trace_expansion = a & 0x0010000 ~= 0
1440 trace_line = a & 0x0020000 ~= 0
1441 trace_space = a & 0x0040000 ~= 0
1442 trace_depth = a & 0x0080000 ~= 0
1443 trace_marginkern = a & 0x0100000 ~= 0
1444 trace_mathlistkern = a & 0x0200000 ~= 0
1445 trace_dir = a & 0x0400000 ~= 0
1446 trace_par = a & 0x0800000 ~= 0
1447 trace_math_glue = a & 0x1000000 ~= 0
1448 trace_mark = a & 0x2000000 ~= 0
1449 trace_insert = a & 0x4000000 ~= 0
1450 end
1451 elseif a == unsetvalue then
1452 goto list
1453 end
1454 if trace_strut then
1455 setattr(current,a_layer,l_strut)
1456 elseif id == glyph_code then
1457 if trace_glyph then
1458 head, current = ruledglyph(head,current,previous)
1459 end
1460 if trace_expansion then
1461 head, current = ruledglyphexpansion(head,current)
1462 end
1463 elseif id == disc_code then
1464 if trace_discretionary then
1465 head, current = ruleddiscretionary(head,current)
1466 end
1467 local pre, post, replace = getdisc(current)
1468 if pre then
1469 pre = visualize(pre,false,a,parent)
1470 end
1471 if post then
1472 post = visualize(post,false,a,parent)
1473 end
1474 if replace then
1475 replace = visualize(replace,false,a,parent)
1476 end
1477 setdisc(current,pre,post,replace)
1478 elseif id == kern_code then
1479 goto kern
1480 elseif id == glue_code then
1481 goto glue
1482 elseif id == penalty_code then
1483 if trace_penalty then
1484 head, current = ruledpenalty(head,current,vertical)
1485 end
1486 elseif id == hlist_code or id == vlist_code then
1487 goto list
1488 elseif id == whatsit_code then
1489 if trace_whatsit then
1490 head, current = ruledwhatsit(head,current)
1491 end
1492 elseif id == user_code then
1493 if trace_user then
1494 head, current = ruleduser(head,current)
1495 end
1496 elseif id == math_code then
1497 if trace_math then
1498 head, current = ruledmath(head,current)
1499 end
1500 elseif id == marginkern_code then
1501 if trace_kern then
1502 head, current = ruledkern(head,current,vertical,true)
1503 end
1504 elseif id == dir_code then
1505 if trace_dir then
1506 head, current = ruleddir(head,current)
1507 end
1508 elseif id == par_code then
1509 if trace_par then
1510 head, current = ruledpar(head,current)
1511 end
1512 elseif id == mark_code then
1513 if trace_mark then
1514 head, current = ruledmark(head,current)
1515 end
1516 elseif id == insert_code then
1517 if trace_insert then
1518 head, current = ruledinsert(head,current)
1519 end
1520 end
1521 goto next
1522 ::glue::
1523 content = getleader(current)
1524 if content then
1525 setleader(current,visualize(content,false,nil,parent))
1526 elseif trace_glue then
1527 head, current = ruledglue(head,current,vertical,parent)
1528 else
1529 subtype = getsubtype(current)
1530 if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
1531 if trace_space then
1532 head, current = ruledspace(head,current,parent)
1533 end
1534 elseif subtype == intermathskip_code then
1535 if trace_math or trace_math_glue then
1536 head = ruledmathglue(head,current,parent)
1537 end
1538 end
1539 end
1540 goto next
1541 ::kern::
1542 subtype = getsubtype(current)
1543 if subtype == fontkern_code then
1544 if trace_fontkern or prev_trace_fontkern then
1545 head, current = fontkern(head,current)
1546 end
1547 if trace_expansion or prev_trace_expansion then
1548 head, current = kernexpansion(head,current)
1549 end
1550 elseif subtype == italickern_code then
1551 if trace_italic or prev_trace_italic then
1552 head, current = italickern(head,current)
1553 elseif trace_kern then
1554 head, current = ruleditalic(head,current)
1555 end
1556 elseif subtype == leftmarginkern_code or subtype == rightmarginkern_code then
1557 if trace_marginkern or prev_trace_marginkern then
1558 head, current = marginkern(head,current)
1559 elseif trace_kern then
1560 head, current = ruledmarginkern(head,current)
1561 end
1562 elseif subtype == mathlistkern_code then
1563 if trace_mathlist then
1564 head, current = mathlistkern(head,current)
1565 elseif trace_kern then
1566 head, current = ruledmathlistkern(head,current)
1567 end
1568 else
1569 if trace_kern then
1570 head, current = ruledkern(head,current,vertical)
1571 end
1572 end
1573 goto next;
1574 ::list::
1575 if id == hlist_code then
1576 local content = getlist(current)
1577 if content then
1578 setlist(current,visualize(content,false,nil,current))
1579 end
1580 if trace_depth then
1581 ruleddepth(current)
1582 end
1583 if trace_line and getsubtype(current) == linelist_code then
1584 head, current = ruledbox(head,current,false,l_line,"L__",trace_simple,previous,trace_origin,parent)
1585 elseif trace_hbox then
1586 head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent)
1587 end
1588 elseif id == vlist_code then
1589 local content = getlist(current)
1590 local isvtop = getstate(current) == vtop_package_state
1591 local tag = nil
1592 local layer = nil
1593 if content then
1594 setlist(current,visualize(content,true,nil,current))
1595 end
1596 if trace_vtop then
1597 if isvtop then
1598 tag = "_T_"
1599 layer = l_vtop
1600 elseif trace_vbox then
1601 tag = "__V"
1602 layer = l_vbox
1603 end
1604 elseif trace_vbox then
1605 if not isvtop then
1606 tag = "__V"
1607 layer = l_vbox
1608 end
1609 end
1610 if tag then
1611 head, current = ruledbox(head,current,true,layer,tag,trace_simple,previous,trace_origin,parent)
1612 end
1613 end
1614 ::next::
1615 previous = current
1616 current = getnext(current)
1617 end
1618 return head
1619 end
1620
1621 local function cleanup()
1622 for tag, cache in next, caches do
1623 for k, v in next, cache do
1624 flushnodelist(v)
1625 end
1626 end
1627 cleanup = function()
1628 report_visualize("error, duplicate cleanup")
1629 end
1630 end
1631
1632 luatex.registerstopactions(cleanup)
1633
1634 function visualizers.handler(head)
1635 if usedfont then
1636 starttiming(visualizers)
1637 head = visualize(head,true)
1638 stoptiming(visualizers)
1639 return head, true
1640 else
1641 return head, false
1642 end
1643 end
1644
1645 function visualizers.box(n)
1646 if usedfont then
1647 starttiming(visualizers)
1648 local box = getbox(n)
1649 if box then
1650 setlist(box,visualize(getlist(box),getid(box) == vlist_code))
1651 end
1652 stoptiming(visualizers)
1653 return head, true
1654 else
1655 return head, false
1656 end
1657 end
1658
1659end
1660
1661do
1662
1663 local nodecodes = nodes.nodecodes
1664 local hlist_code = nodecodes.hlist
1665 local vlist_code = nodecodes.vlist
1666 local nextnode = nuts.traversers.node
1667
1668 local last = nil
1669 local used = nil
1670
1671 local mark = {
1672 "trace:1", "trace:2", "trace:3",
1673 "trace:4", "trace:5", "trace:6",
1674 "trace:7",
1675 }
1676
1677 local function markfonts(list)
1678 for n, id in nextnode, list do
1679 if id == glyph_code then
1680 local font = getfont(n)
1681 local okay = used[font]
1682 if not okay then
1683 last = last + 1
1684 okay = mark[last]
1685 used[font] = okay
1686 end
1687 setcolor(n,okay)
1688 elseif id == hlist_code or id == vlist_code then
1689 markfonts(getlist(n))
1690 end
1691 end
1692 end
1693
1694 function visualizers.markfonts(list)
1695 last, used = 0, { }
1696 markfonts(type(n) == "number" and getlist(getbox(n)) or n)
1697 end
1698
1699end
1700
1701statistics.register("visualization time",function()
1702 if enabled then
1703
1704 return formatters["%s seconds"](statistics.elapsedtime(visualizers))
1705 end
1706end)
1707
1708
1709
1710do
1711
1712 local implement = interfaces.implement
1713
1714 implement {
1715 name = "setvisual",
1716 arguments = "string",
1717 actions = visualizers.setvisual
1718 }
1719
1720 implement {
1721 name = "setvisuals",
1722 arguments = "string",
1723 actions = visualizers.setvisual
1724 }
1725
1726 implement {
1727 name = "getvisual",
1728 arguments = "string",
1729 actions = { setvisual, context }
1730 }
1731
1732 implement {
1733 name = "setvisuallayer",
1734 arguments = "string",
1735 actions = visualizers.setlayer
1736 }
1737
1738 implement {
1739 name = "markvisualfonts",
1740 arguments = "integer",
1741 actions = visualizers.markfonts
1742 }
1743
1744 implement {
1745 name = "setvisualfont",
1746 arguments = "integer",
1747 actions = visualizers.setfont
1748 }
1749
1750end
1751
1752
1753
1754do
1755
1756 local function make(str,forecolor,rulecolor,layer)
1757 if initialize then
1758 initialize()
1759 end
1760 local rule = new_rule(emwidth/fraction,exheight,4*exheight)
1761 setcolor(rule,rulecolor)
1762 settransparency(rule,rulecolor)
1763 local info
1764 if str == "" then
1765 info = new_hlist(rule)
1766 else
1767 local text = hpack_string(str,usedfont)
1768 local list = getlist(text)
1769 setlistcolor(list,textcolor)
1770 setlisttransparency(list,textcolor)
1771 setshift(text,3.5 * exheight)
1772 info = new_hlist(setlink(rule,text))
1773 end
1774 setattr(info,a_layer,layer)
1775 return info
1776 end
1777
1778 function visualizers.register(name,textcolor,rulecolor)
1779 if rawget(layers,name) then
1780
1781 return
1782 end
1783 local cache = caches[name]
1784 local layer = layers[name]
1785 if not textcolor then
1786 textcolor = "trace:ds"
1787 end
1788 if not rulecolor then
1789 rulecolor = "trace:do"
1790 end
1791 return function(str)
1792 if not str then
1793 str = ""
1794 end
1795 local info = cache[str]
1796 if not info then
1797 info = make(str,textcolor,rulecolor,layer)
1798 cache[str] = info
1799 end
1800 return copy_node(info)
1801 end
1802 end
1803
1804end
1805 |