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 getglue = nuts.getglue
48local isglyph = nuts.isglyph
49local getdirection = nuts.getdirection
50local getwidth = nuts.getwidth
51local count_nodes = nuts.countall
52
53local nextnode = nuts.traversers.node
54local nextglyph = nuts.traversers.glyph
55
56local d_tostring = nuts.tostring
57
58local nutpool = nuts.pool
59local new_rule = nutpool.rule
60
61local nodecodes = nodes.nodecodes
62local whatsitcodes = nodes.whatsitcodes
63local fillcodes = nodes.fillcodes
64
65local subtypes = nodes.subtypes
66
67local glyph_code = nodecodes.glyph
68local hlist_code = nodecodes.hlist
69local vlist_code = nodecodes.vlist
70local disc_code = nodecodes.disc
71local glue_code = nodecodes.glue
72local kern_code = nodecodes.kern
73local rule_code = nodecodes.rule
74local dir_code = nodecodes.dir
75local par_code = nodecodes.par
76local whatsit_code = nodecodes.whatsit
77local passive_code = nodecodes.passive
78
79local dimenfactors = number.dimenfactors
80local formatters = string.formatters
81
82local startofpar = nuts.startofpar
83
84
85
86function nodes.showlist(head, message)
87 if message then
88 report_nodes(message)
89 end
90 for n in nextnode, tonut(head) do
91 report_nodes(d_tostring(n))
92 end
93end
94
95function nodes.handlers.checkglyphs(head,message)
96 local h = tonut(head)
97 local t = { }
98 local n = 0
99 local f = formatters["%U:%s"]
100 for g, char, font in nextglyph, h do
101 n = n + 1
102 t[n] = f(char,getsubtype(g))
103 end
104 if n == 0 then
105
106 elseif message and message ~= "" then
107 report_nodes("%s, %s glyphs: % t",message,n,t)
108 else
109 report_nodes("%s glyphs: % t",n,t)
110 end
111 return false
112end
113
114local fontcharacters
115
116local function tosequence(start,stop,compact)
117 if start then
118 if not fontcharacters then
119 fontcharacters = fonts.hashes.descriptions
120 if not fontcharacters then
121 return "[no char data]"
122 end
123 end
124 local f_sequence = formatters["U+%04X:%s"]
125 local f_subrange = formatters["[[ %s ][ %s ][ %s ]]"]
126 start = tonut(start)
127 stop = stop and tonut(stop)
128 local t = { }
129 local n = 0
130 while start do
131 local c, id = isglyph(start)
132 if c then
133 local u = fontcharacters[id][c]
134 u = u and u.unicode or c
135 if type(u) == "table" then
136 local tt = { }
137 for i=1,#u do
138 local c = u[i]
139 tt[i] = compact and utfchar(c) or f_sequence(c,utfchar(c))
140 end
141 n = n + 1 ; t[n] = "(" .. concat(tt," ") .. ")"
142 else
143 n = n + 1 ; t[n] = compact and utfchar(c) or f_sequence(c,utfchar(c))
144 end
145 elseif id == disc_code then
146 local pre, post, replace = getdisc(start)
147 t[#t+1] = f_subrange(pre and tosequence(pre),post and tosequence(post),replace and tosequence(replace))
148 elseif id == rule_code then
149 n = n + 1 ; t[n] = compact and "|" or nodecodes[id] or "?"
150 elseif id == dir_code then
151 local d, p = getdirection(start)
152 n = n + 1 ; t[n] = "[<" .. (p and "-" or "+") .. d .. ">]"
153 elseif id == par_code and startofpar(current) then
154 n = n + 1 ; t[n] = "[<" .. getdirection(start) .. ">]"
155 elseif compact then
156 n = n + 1 ; t[n] = "[]"
157 else
158 n = n + 1 ; t[n] = nodecodes[id]
159 end
160 if start == stop then
161 break
162 else
163 start = getnext(start)
164 end
165 end
166 if compact then
167 return concat(t)
168 else
169 return concat(t," ")
170 end
171 else
172 return "[empty]"
173 end
174end
175
176nodes.tosequence = tosequence
177nuts .tosequence = tosequence
178
179function nodes.report(t)
180 report_nodes("output %a, %s nodes",status.output_active,count_nodes(t))
181end
182
183function nodes.packlist(head)
184 local t = { }
185 for n in nextnode, tonut(head) do
186 t[#t+1] = d_tostring(n)
187 end
188 return t
189end
190
191function nodes.idstostring(head,tail)
192 head = tonut(head)
193 tail = tail and tonut(tail)
194 local t = { }
195 local last_id = nil
196 local last_n = 0
197 local f_two = formatters["[%s*%s]"]
198 local f_one = formatters["[%s]"]
199 for n, id, subtype in nextnode, head do
200 if id == whatsit_code then
201 id = whatsitcodes[subtype]
202 else
203 id = nodecodes[id]
204 end
205 if not last_id then
206 last_id = id
207 last_n = 1
208 elseif last_id == id then
209 last_n = last_n + 1
210 else
211 if last_n > 1 then
212 t[#t+1] = f_two(last_n,last_id)
213 else
214 t[#t+1] = f_one(last_id)
215 end
216 last_id = id
217 last_n = 1
218 end
219 if n == tail then
220 break
221 end
222 end
223 if not last_id then
224 t[#t+1] = "no nodes"
225 else
226 if last_n > 1 then
227 t[#t+1] = f_two(last_n,last_id)
228 else
229 t[#t+1] = f_one(last_id)
230 end
231 end
232 return concat(t," ")
233end
234
235function nodes.idsandsubtypes(head)
236 local h = tonut(head)
237 local t = { }
238 local f = formatters["%s:%s"]
239 for n, id, subtype in nextnode, h do
240 local c = nodecodes[id]
241 if subtype then
242 t[#t+1] = f(c,subtypes[id][subtype])
243 else
244 t[#t+1] = c
245 end
246 end
247 return concat(t, " ")
248end
249
250
251
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
287local function showsimplelist(h,depth,n)
288 h = h and tonut(h)
289 while h do
290 report_nodes("% w%s",n,d_tostring(h))
291 if not depth or n < depth then
292 local id = getid(h)
293 if id == hlist_code or id == vlist_code then
294 showsimplelist(getlist(h),depth,n+1)
295 end
296 end
297 h = getnext(h)
298 end
299end
300
301nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end
302
303local function listtoutf(h,joiner,textonly,last,nodisc)
304 if h then
305 local w = { }
306 local n = 0
307 local g = formatters["<%i>"]
308 local d = formatters["[%s|%s|%s]"]
309 while h do
310 local c, id = isglyph(h)
311 if c then
312 n = n + 1 ; w[n] = c >= 0 and utfchar(c) or g(c)
313 if joiner then
314 n = n + 1 ; w[n] = joiner
315 end
316 elseif id == disc_code then
317 local pre, pos, rep = getdisc(h)
318 if not nodisc then
319 n = n + 1 ; w[n] = d(
320 pre and listtoutf(pre,joiner,textonly) or "",
321 pos and listtoutf(pos,joiner,textonly) or "",
322 rep and listtoutf(rep,joiner,textonly) or ""
323 )
324 elseif rep then
325 n = n + 1 ; w[n] = listtoutf(rep,joiner,textonly) or ""
326 end
327 if joiner then
328 n = n + 1 ; w[n] = joiner
329 end
330 elseif id == passive_code then
331
332 print("weird: passive node in listtoutf")
333 return ""
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
482function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end
483function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end
484function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
485function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
486function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end
487function number.toscaledpoints(n) return n .. "sp" end
488function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
489function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end
490function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end
491function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end
492
493
494
495function nodes.topoints (n,fmt) return nodetodimen(n,"pt",fmt) end
496function nodes.toinches (n,fmt) return nodetodimen(n,"in",fmt) end
497function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end
498function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end
499function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end
500function nodes.toscaledpoints(n) return n .. "sp" end
501function nodes.tobasepoints (n,fmt) return nodetodimen(n,"bp",fmt) end
502function nodes.topicas (n,fmt) return nodetodimen(n "pc",fmt) end
503function nodes.todidots (n,fmt) return nodetodimen(n,"dd",fmt) end
504function nodes.tociceros (n,fmt) return nodetodimen(n,"cc",fmt) end
505
506
507
508
509
510local points = function(n)
511 if not n or n == 0 then
512 return "0pt"
513 elseif type(n) == "number" then
514 return lpegmatch(stripper,format("%.5fpt",n*ptfactor))
515 else
516 return numbertodimen(n,"pt")
517 end
518end
519
520local basepoints = function(n)
521 if not n or n == 0 then
522 return "0bp"
523 elseif type(n) == "number" then
524 return lpegmatch(stripper,format("%.5fbp",n*bpfactor))
525 else
526 return numbertodimen(n,"bp")
527 end
528end
529
530local pts = function(n)
531 if not n or n == 0 then
532 return "0pt"
533 elseif type(n) == "number" then
534 return format("%.5fpt",n*ptfactor)
535 else
536 return numbertodimen(n,"pt")
537 end
538end
539
540local nopts = function(n)
541 if not n or n == 0 then
542 return "0"
543 else
544 return format("%.5f",n*ptfactor)
545 end
546end
547
548number.points = points
549number.basepoints = basepoints
550number.pts = pts
551number.nopts = nopts
552
553nodes.points = function(n) return numbertodimen(n,"pt") end
554nodes.basepoints = function(n) return numbertodimen(n,"bp") end
555nodes.pts = function(n) return numbertodimen(n,"pt") end
556nodes.nopts = function(n) return format("%.5f",n*ptfactor) end
557
558local colors = { }
559tracers.colors = colors
560
561local unsetvalue = attributes.unsetvalue
562
563local a_color = attributes.private('color')
564local a_colormodel = attributes.private('colormodel')
565local m_color = attributes.list[a_color] or { }
566
567function colors.set(n,c,s)
568 local mc = m_color[c]
569 local nn = tonut(n)
570 if mc then
571 local mm = s or texgetattribute(a_colormodel)
572 setattr(nn,a_colormodel,mm <= 0 and mm or 1)
573 setattr(nn,a_color,mc)
574 else
575 setattr(nn,a_color,unsetvalue)
576 end
577 return n
578end
579
580function colors.setlist(n,c,s)
581 local nn = tonut(n)
582 local mc = m_color[c] or unsetvalue
583 local mm = s or texgetattribute(a_colormodel)
584 if mm <= 0 then
585 mm = 1
586 end
587 while nn do
588 setattr(nn,a_colormodel,mm)
589 setattr(nn,a_color,mc)
590 nn = getnext(nn)
591 end
592 return n
593end
594
595function colors.reset(n)
596 setattr(tonut(n),a_color,unsetvalue)
597 return n
598end
599
600
601
602local transparencies = { }
603tracers.transparencies = transparencies
604
605local a_transparency = attributes.private('transparency')
606local m_transparency = attributes.list[a_transparency] or { }
607
608function transparencies.set(n,t)
609 setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue)
610 return n
611end
612
613function transparencies.setlist(n,c,s)
614 local nn = tonut(n)
615 local mt = m_transparency[c] or unsetvalue
616 while nn do
617 setattr(nn,a_transparency,mt)
618 nn = getnext(nn)
619 end
620 return n
621end
622
623function transparencies.reset(n)
624 setattr(n,a_transparency,unsetvalue)
625 return n
626end
627
628
629
630local visualizers = nodes.visualizers or { }
631nodes.visualizers = visualizers
632
633function visualizers.handler(head)
634 return head, false
635end
636
637
638
639
640local function setproperties(n,c,s)
641 local nn = tonut(n)
642 local mm = texgetattribute(a_colormodel)
643 setattr(nn,a_colormodel,mm > 0 and mm or 1)
644 setattr(nn,a_color,m_color[c])
645 setattr(nn,a_transparency,m_transparency[c])
646 return n
647end
648
649tracers.setproperties = setproperties
650
651
652
653
654function tracers.setlist(n,c,s)
655 local nn = tonut(n)
656 local mc = m_color[c]
657 local mt = m_transparency[c]
658 local mm = texgetattribute(a_colormodel)
659 if mm <= 0 then
660 mm = 1
661 end
662 while nn do
663 setattr(nn,a_colormodel,mm)
664 setattr(nn,a_color,mc)
665 setattr(nn,a_transparency,mt)
666 nn = getnext(nn)
667 end
668 return n
669end
670
671function tracers.resetproperties(n)
672 local nn = tonut(n)
673 setattr(nn,a_color,unsetvalue)
674 setattr(nn,a_transparency,unsetvalue)
675 return n
676end
677
678
679
680local nodestracerpool = { }
681local nutstracerpool = { }
682
683tracers.pool = {
684 nodes = nodestracerpool,
685 nuts = nutstracerpool,
686}
687
688table.setmetatableindex(nodestracerpool,function(t,k,v)
689 local f = nutstracerpool[k]
690 local v = function(...)
691 return tonode(f(...))
692 end
693 t[k] = v
694 return v
695end)
696
697function nutstracerpool.rule(w,h,d,c,s)
698 return setproperties(new_rule(w,h,d),c,s)
699end
700
701tracers.rule = nodestracerpool.rule
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 |