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