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