1if not modules then modules = { } end modules ['node-tra'] = {
2 version = 1.001,
3 comment = "companion to node-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12local next = next
13local utfchar = utf.char
14local format, match, gmatch, concat, rep = string.format, string.match, string.gmatch, table.concat, string.rep
15local lpegmatch = lpeg.match
16local clock = os.gettimeofday or os.clock
17
18local report_nodes = logs.reporter("nodes","tracing")
19
20local nodes, node, context = nodes, node, context
21
22local texgetattribute = tex.getattribute
23
24local tracers = nodes.tracers or { }
25nodes.tracers = tracers
26
27local tasks = nodes.tasks or { }
28nodes.tasks = tasks
29
30local handlers = nodes.handlers or {}
31nodes.handlers = handlers
32
33local injections = nodes.injections or { }
34nodes.injections = injections
35
36local nuts = nodes.nuts
37local tonut = nuts.tonut
38local tonode = nuts.tonode
39
40local getnext = nuts.getnext
41local getprev = nuts.getprev
42local getid = nuts.getid
43local getsubtype = nuts.getsubtype
44local getlist = nuts.getlist
45local getdisc = nuts.getdisc
46local setattr = nuts.setattr
47local setattrs = nuts.setattrs
48local getglue = nuts.getglue
49local isglyph = nuts.isglyph
50local getdirection = nuts.getdirection
51local getwidth = nuts.getwidth
52
53local count_nodes = nuts.countall
54local used_nodes = nuts.usedlist
55
56local nextnode = nuts.traversers.node
57local nextglyph = nuts.traversers.glyph
58
59local shownodes = nuts.show
60
61local d_tostring = nuts.tostring
62
63local nutpool = nuts.pool
64local new_rule = nutpool.rule
65
66local nodecodes = nodes.nodecodes
67local whatsitcodes = nodes.whatsitcodes
68local fillcodes = tex.fillcodes
69
70local subtypes = nodes.subtypes
71
72local glyph_code = nodecodes.glyph
73local hlist_code = nodecodes.hlist
74local vlist_code = nodecodes.vlist
75local disc_code = nodecodes.disc
76local glue_code = nodecodes.glue
77local kern_code = nodecodes.kern
78local rule_code = nodecodes.rule
79local dir_code = nodecodes.dir
80local par_code = nodecodes.par
81local whatsit_code = nodecodes.whatsit
82
83local dimenfactors = number.dimenfactors
84local formatters = string.formatters
85
86local startofpar = nuts.startofpar
87
88
89
90function nodes.showlist(head, message)
91 if message then
92 report_nodes(message)
93 end
94 for n in nextnode, tonut(head) do
95 report_nodes(d_tostring(n))
96 end
97end
98
99function nodes.handlers.checkglyphs(head,message)
100 local h = tonut(head)
101 local t = { }
102 local n = 0
103 local f = formatters["%U:%s"]
104 for g, char, font in nextglyph, h do
105 n = n + 1
106 t[n] = f(char,getsubtype(g))
107 end
108 if n == 0 then
109
110 elseif message and message ~= "" then
111 report_nodes("%s, %s glyphs: % t",message,n,t)
112 else
113 report_nodes("%s glyphs: % t",n,t)
114 end
115 return false
116end
117
118local fontcharacters
119
120local function tosequence(start,stop,compact)
121 if start then
122 if not fontcharacters then
123 fontcharacters = fonts.hashes.descriptions
124 if not fontcharacters then
125 return "[no char data]"
126 end
127 end
128 local f_sequence = formatters["U+%04X:%s"]
129 local f_subrange = formatters["[[ %s ][ %s ][ %s ]]"]
130 start = tonut(start)
131 stop = stop and tonut(stop)
132 local t = { }
133 local n = 0
134 while start do
135 local c, id = isglyph(start)
136 if c then
137 local u = fontcharacters[id][c]
138 u = u and u.unicode or c
139 if type(u) == "table" then
140 local tt = { }
141 for i=1,#u do
142 local c = u[i]
143 tt[i] = compact and utfchar(c) or f_sequence(c,utfchar(c))
144 end
145 n = n + 1 ; t[n] = "(" .. concat(tt," ") .. ")"
146 else
147 n = n + 1 ; t[n] = compact and utfchar(c) or f_sequence(c,utfchar(c))
148 end
149 elseif id == disc_code then
150 local pre, post, replace = getdisc(start)
151 t[#t+1] = f_subrange(pre and tosequence(pre),post and tosequence(post),replace and tosequence(replace))
152 elseif id == rule_code then
153 n = n + 1 ; t[n] = compact and "|" or nodecodes[id] or "?"
154 elseif id == dir_code then
155 local d, p = getdirection(start)
156 n = n + 1 ; t[n] = "[<" .. (p and "-" or "+") .. d .. ">]"
157 elseif id == par_code and startofpar(current) then
158 n = n + 1 ; t[n] = "[<" .. getdirection(start) .. ">]"
159 elseif compact then
160 n = n + 1 ; t[n] = "[]"
161 else
162 n = n + 1 ; t[n] = nodecodes[id]
163 end
164 if start == stop then
165 break
166 else
167 start = getnext(start)
168 end
169 end
170 if compact then
171 return concat(t)
172 else
173 return concat(t," ")
174 end
175 else
176 return "[empty]"
177 end
178end
179
180nodes.tosequence = tosequence
181nuts .tosequence = tosequence
182
183function nodes.report(t)
184 report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t))
185end
186
187function nodes.packlist(head)
188 local t = { }
189 for n in nextnode, tonut(head) do
190 t[#t+1] = d_tostring(n)
191 end
192 return t
193end
194
195function nodes.idstostring(head,tail)
196 head = tonut(head)
197 tail = tail and tonut(tail)
198 local t = { }
199 local last_id = nil
200 local last_n = 0
201 local f_two = formatters["[%s*%s]"]
202 local f_one = formatters["[%s]"]
203 for n, id, subtype in nextnode, head do
204 if id == whatsit_code then
205 id = whatsitcodes[subtype]
206 else
207 id = nodecodes[id]
208 end
209 if not last_id then
210 last_id = id
211 last_n = 1
212 elseif last_id == id then
213 last_n = last_n + 1
214 else
215 if last_n > 1 then
216 t[#t+1] = f_two(last_n,last_id)
217 else
218 t[#t+1] = f_one(last_id)
219 end
220 last_id = id
221 last_n = 1
222 end
223 if n == tail then
224 break
225 end
226 end
227 if not last_id then
228 t[#t+1] = "no nodes"
229 else
230 if last_n > 1 then
231 t[#t+1] = f_two(last_n,last_id)
232 else
233 t[#t+1] = f_one(last_id)
234 end
235 end
236 return concat(t," ")
237end
238
239function nodes.idsandsubtypes(head)
240 local h = tonut(head)
241 local t = { }
242 local f = formatters["%s:%s"]
243 for n, id, subtype in nextnode, h do
244 local c = nodecodes[id]
245 if subtype then
246 t[#t+1] = f(c,subtypes[id][subtype])
247 else
248 t[#t+1] = c
249 end
250 end
251 return concat(t, " ")
252end
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291local function showsimplelist(h,depth,n)
292 h = h and tonut(h)
293 while h do
294 report_nodes("% w%s",n,d_tostring(h))
295 if not depth or n < depth then
296 local id = getid(h)
297 if id == hlist_code or id == vlist_code then
298 showsimplelist(getlist(h),depth,n+1)
299 end
300 end
301 h = getnext(h)
302 end
303end
304
305nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end
306
307local function listtoutf(h,joiner,textonly,last,nodisc)
308 if h then
309 local w = { }
310 local n = 0
311 local g = formatters["<%i>"]
312 local d = formatters["[%s|%s|%s]"]
313 while h do
314 local c, id = isglyph(h)
315 if c then
316 n = n + 1 ; w[n] = c >= 0 and utfchar(c) or g(c)
317 if joiner then
318 n = n + 1 ; w[n] = joiner
319 end
320 elseif id == disc_code then
321 local pre, pos, rep = getdisc(h)
322 if not nodisc then
323 n = n + 1 ; w[n] = d(
324 pre and listtoutf(pre,joiner,textonly) or "",
325 pos and listtoutf(pos,joiner,textonly) or "",
326 rep and listtoutf(rep,joiner,textonly) or ""
327 )
328 elseif rep then
329 n = n + 1 ; w[n] = listtoutf(rep,joiner,textonly) or ""
330 end
331 if joiner then
332 n = n + 1 ; w[n] = joiner
333 end
334 elseif textonly then
335 if id == glue_code then
336 if getwidth(h) > 0 then
337 n = n + 1 ; w[n] = " "
338 end
339 elseif id == hlist_code or id == vlist_code then
340 local l = getlist(h)
341 n = n + 1 ; w[n] = "["
342 if l then
343 n = n + 1 ; w[n] = listtoutf(l,joiner,textonly,last,nodisc)
344 end
345 n = n + 1 ; w[n] = "]"
346 end
347 else
348 n = n + 1 ; w[n] = "[-]"
349 end
350 if h == last then
351 break
352 else
353 h = getnext(h)
354 end
355 end
356 return concat(w,"",1,(w[n] == joiner) and (n-1) or n)
357 else
358 return ""
359 end
360end
361
362function nodes.listtoutf(h,joiner,textonly,last,nodisc)
363 if h then
364 local joiner = joiner == true and utfchar(0x200C) or joiner
365 return listtoutf(tonut(h),joiner,textonly,last and tonut(last) or nil,nodisc)
366 else
367 return ""
368 end
369end
370
371local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }
372
373local function showboxes(n,symbol,depth)
374 depth = depth or 0
375 symbol = symbol or "."
376 for n, id, subtype in nextnode, tonut(n) do
377 if id == hlist_code or id == vlist_code then
378 report_nodes(rep(symbol,depth) .. what[subtype] or subtype)
379 showboxes(getlist(n),symbol,depth+1)
380 end
381 end
382end
383
384nodes.showboxes = showboxes
385
386local ptfactor = dimenfactors.pt
387local bpfactor = dimenfactors.bp
388local stripper = lpeg.patterns.stripzeros
389
390local f_f_f = formatters["%0.5Fpt plus %0.5F%s minus %0.5F%s"]
391local f_f_m = formatters["%0.5Fpt plus %0.5F%s minus %0.5Fpt"]
392local f_p_f = formatters["%0.5Fpt plus %0.5Fpt minus %0.5F%s"]
393local f_p_m = formatters["%0.5Fpt plus %0.5Fpt minus %0.5Fpt"]
394local f_f_z = formatters["%0.5Fpt plus %0.5F%s"]
395local f_p_z = formatters["%0.5Fpt plus %0.5Fpt"]
396local f_z_f = formatters["%0.5Fpt minus %0.5F%s"]
397local f_z_m = formatters["%0.5Fpt minus %0.5Fpt"]
398local f_z_z = formatters["%0.5Fpt"]
399
400local tonut = nodes.tonut
401
402local function nodetodimen(n)
403 n = tonut(n)
404 local id = getid(n)
405 if id == kern_code then
406 local width = getwidth(n)
407 if width == 0 then
408 return "0pt"
409 else
410 return f_z_z(width)
411 end
412 elseif id ~= glue_code then
413 return "0pt"
414 end
415 local width, stretch, shrink, stretch_order, shrink_order = getglue(n)
416 stretch = stretch / 65536
417 shrink = shrink / 65536
418 width = width / 65536
419 if stretch_order ~= 0 then
420 if shrink_order ~= 0 then
421 return f_f_f(width,stretch,fillcodes[stretch_order],shrink,fillcodes[shrink_order])
422 elseif shrink ~= 0 then
423 return f_f_m(width,stretch,fillcodes[stretch_order],shrink)
424 else
425 return f_f_z(width,stretch,fillcodes[stretch_order])
426 end
427 elseif shrink_order ~= 0 then
428 if stretch ~= 0 then
429 return f_p_f(width,stretch,shrink,fillcodes[shrink_order])
430 else
431 return f_z_f(width,shrink,fillcodes[shrink_order])
432 end
433 elseif stretch ~= 0 then
434 if shrink ~= 0 then
435 return f_p_m(width,stretch,shrink)
436 else
437 return f_p_z(width,stretch)
438 end
439 elseif shrink ~= 0 then
440 return f_z_m(width,shrink)
441 elseif width == 0 then
442 return "0pt"
443 else
444 return f_z_z(width)
445 end
446end
447
448
449
450
451
452
453local f_pt = formatters["%p"]
454local f_un = formatters["%F%s"]
455
456dimenfactors[""] = dimenfactors.pt
457
458local function numbertodimen(d,unit,fmt)
459 if not d or d == 0 then
460 if fmt then
461 return formatters[fmt](0,unit or "pt")
462 elseif unit then
463 return 0 .. unit
464 else
465 return "0pt"
466 end
467 elseif fmt then
468 if not unit then
469 unit = "pt"
470 end
471 return formatters[fmt](d*dimenfactors[unit],unit)
472 elseif not unit or unit == "pt" then
473 return f_pt(d)
474 else
475 return f_un(d*dimenfactors[unit],unit)
476 end
477end
478
479number.todimen = numbertodimen
480nodes .todimen = nodetodimen
481
482
483
484function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end
485function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end
486function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
487function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
488function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end
489function number.toscaledpoints(n) return n .. "sp" end
490function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
491function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end
492function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end
493function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end
494
495
496
497function nodes.topoints (n,fmt) return nodetodimen(n,"pt",fmt) end
498function nodes.toinches (n,fmt) return nodetodimen(n,"in",fmt) end
499function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end
500function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end
501function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end
502function nodes.toscaledpoints(n) return n .. "sp" end
503function nodes.tobasepoints (n,fmt) return nodetodimen(n,"bp",fmt) end
504function nodes.topicas (n,fmt) return nodetodimen(n "pc",fmt) end
505function nodes.todidots (n,fmt) return nodetodimen(n,"dd",fmt) end
506function nodes.tociceros (n,fmt) return nodetodimen(n,"cc",fmt) end
507
508
509
510
511
512local points = function(n)
513 if not n or n == 0 then
514 return "0pt"
515 elseif type(n) == "number" then
516 return lpegmatch(stripper,format("%.5fpt",n*ptfactor))
517 else
518 return numbertodimen(n,"pt")
519 end
520end
521
522local basepoints = function(n)
523 if not n or n == 0 then
524 return "0bp"
525 elseif type(n) == "number" then
526 return lpegmatch(stripper,format("%.5fbp",n*bpfactor))
527 else
528 return numbertodimen(n,"bp")
529 end
530end
531
532local pts = function(n)
533 if not n or n == 0 then
534 return "0pt"
535 elseif type(n) == "number" then
536 return format("%.5fpt",n*ptfactor)
537 else
538 return numbertodimen(n,"pt")
539 end
540end
541
542local nopts = function(n)
543 if not n or n == 0 then
544 return "0"
545 else
546 return format("%.5f",n*ptfactor)
547 end
548end
549
550number.points = points
551number.basepoints = basepoints
552number.pts = pts
553number.nopts = nopts
554
555nodes.points = function(n) return numbertodimen(n,"pt") end
556nodes.basepoints = function(n) return numbertodimen(n,"bp") end
557nodes.pts = function(n) return numbertodimen(n,"pt") end
558nodes.nopts = function(n) return format("%.5f",n*ptfactor) end
559
560local colors = { }
561tracers.colors = colors
562
563local unsetvalue = attributes.unsetvalue
564
565local a_color = attributes.private('color')
566local a_colormodel = attributes.private('colormodel')
567local m_color = attributes.list[a_color] or { }
568
569function colors.set(n,c,s,t)
570 local mc = m_color[c]
571 local nn = tonut(n)
572 if mc then
573 local mm = s or texgetattribute(a_colormodel)
574 if mm <= 0 then
575 mm = 1
576 end
577
578 if t then
579 setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,m_transparency[t] or unsetvalue)
580 else
581 setattrs(nn,a_colormodel,mm,a_color,mc)
582 end
583 else
584 setattr(nn,a_color,unsetvalue)
585 end
586 return n
587end
588
589function colors.setlist(n,c,s,t)
590 local nn = tonut(n)
591 local mc = m_color[c] or unsetvalue
592 local mm = s or texgetattribute(a_colormodel)
593 if mm <= 0 then
594 mm = 1
595 end
596 if t then
597 t = m_transparency[t] or unsetvalue
598 while nn do
599 setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,t)
600 nn = getnext(nn)
601 end
602 else
603 while nn do
604 setattrs(nn,a_colormodel,mm,a_color,mc)
605 nn = getnext(nn)
606 end
607 end
608 return n
609end
610
611function colors.reset(n)
612 setattr(tonut(n),a_color,unsetvalue)
613 return n
614end
615
616
617
618local transparencies = { }
619tracers.transparencies = transparencies
620
621local a_transparency = attributes.private('transparency')
622local m_transparency = attributes.list[a_transparency] or { }
623
624function transparencies.set(n,t)
625 setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue)
626 return n
627end
628
629function transparencies.setlist(n,c,s)
630 local nn = tonut(n)
631 local mt = m_transparency[c] or unsetvalue
632 while nn do
633 setattr(nn,a_transparency,mt)
634 nn = getnext(nn)
635 end
636 return n
637end
638
639function transparencies.reset(n)
640 setattr(n,a_transparency,unsetvalue)
641 return n
642end
643
644
645
646local visualizers = nodes.visualizers or { }
647nodes.visualizers = visualizers
648
649function visualizers.handler(head)
650 return head, false
651end
652
653
654
655
656local function setproperties(n,c,s)
657 local nn = tonut(n)
658 local mm = texgetattribute(a_colormodel)
659 setattr(nn,a_colormodel,mm > 0 and mm or 1)
660 setattr(nn,a_color,m_color[c])
661 setattr(nn,a_transparency,m_transparency[c])
662 return n
663end
664
665tracers.setproperties = setproperties
666
667
668
669
670function tracers.setlist(n,c,s)
671 local nn = tonut(n)
672 local mc = m_color[c]
673 local mt = m_transparency[c]
674 local mm = texgetattribute(a_colormodel)
675 if mm <= 0 then
676 mm = 1
677 end
678 while nn do
679 setattr(nn,a_colormodel,mm)
680 setattr(nn,a_color,mc)
681 setattr(nn,a_transparency,mt)
682 nn = getnext(nn)
683 end
684 return n
685end
686
687function tracers.resetproperties(n)
688 local nn = tonut(n)
689 setattr(nn,a_color,unsetvalue)
690 setattr(nn,a_transparency,unsetvalue)
691 return n
692end
693
694
695
696local nodestracerpool = { }
697local nutstracerpool = { }
698
699tracers.pool = {
700 nodes = nodestracerpool,
701 nuts = nutstracerpool,
702}
703
704table.setmetatableindex(nodestracerpool,function(t,k,v)
705 local f = nutstracerpool[k]
706 local v = function(...)
707 return tonode(f(...))
708 end
709 t[k] = v
710 return v
711end)
712
713function nutstracerpool.rule(w,h,d,c,s)
714 return setproperties(new_rule(w,h,d),c,s)
715end
716
717tracers.rule = nodestracerpool.rule
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734function nodes.handlers.show(n)
735 if n then
736 shownodes(tonut(n))
737 else
738
739 end
740 return n
741end
742
743trackers.register("fonts.result.show", function(v)
744 nodes.tasks.setaction("processors","nodes.handlers.show",v)
745end)
746
747
748
749local getattributes = nuts.getattributes
750local patchattributes = nuts.patchattributes
751
752local texgetattribute = tex.getattribute
753
754function recolor(head,colormodel,color,transparency)
755
756 for n, id in nextnode, head do
757 if id == glyph_code or id == rule_code then
758 local m, c, t = getattributes(n,a_colormodel,a_color,a_transparency)
759 if not c or c == 1 then
760 if not t then
761 patchattributes(n,a_colormodel,colormodel,a_color,color,a_transparency,transparency)
762 else
763 patchattributes(n,a_colormodel,colormodel,a_color,color)
764 end
765 else
766 if not t then
767 patchattributes(n,a_transparency,transparency)
768 end
769 end
770
771 elseif id == hlist_node or id == vlist_node then
772 recolor(getlist(n),colormodel,color,transparency)
773 end
774 end
775end
776
777local function recolorbox(head)
778 recolor(getlist(head),texgetattribute(a_colormodel),texgetattribute(a_color),texgetattribute(a_transparency))
779end
780
781nodes.handlers.recolor = recolorbox
782
783local getbox = nodes.nuts.getbox
784
785interfaces.implement {
786 name = "recolorbox",
787 public = true,
788 protected = true,
789 arguments = { "integer" },
790 actions = function(n)
791 recolor(getbox(n))
792 end
793}
794 |