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
13
14local next = next
15local utfchar = utf.char
16local format, match, gmatch, concat, rep = string.format, string.match, string.gmatch, table.concat, string.rep
17local lpegmatch = lpeg.match
18local clock = os.gettimeofday or os.clock
19
20local report_nodes = logs.reporter("nodes","tracing")
21
22local nodes, node, context = nodes, node, context
23
24local texgetattribute = tex.getattribute
25
26local tracers = nodes.tracers or { }
27nodes.tracers = tracers
28
29local tasks = nodes.tasks or { }
30nodes.tasks = tasks
31
32local handlers = nodes.handlers or {}
33nodes.handlers = handlers
34
35local injections = nodes.injections or { }
36nodes.injections = injections
37
38local nuts = nodes.nuts
39local tonut = nuts.tonut
40local tonode = nuts.tonode
41
42local getnext = nuts.getnext
43local getprev = nuts.getprev
44local getid = nuts.getid
45local getsubtype = nuts.getsubtype
46local getlist = nuts.getlist
47local getdisc = nuts.getdisc
48local setattr = nuts.setattr
49local getglue = nuts.getglue
50local isglyph = nuts.isglyph
51local getdirection = nuts.getdirection
52local getwidth = nuts.getwidth
53local count_nodes = nuts.countall
54local used_nodes = nuts.usedlist
55
56local nextnode = nuts.traversers.node
57local nextglyph = nuts.traversers.glyph
58
59local d_tostring = nuts.tostring
60
61local nutpool = nuts.pool
62local new_rule = nutpool.rule
63
64local nodecodes = nodes.nodecodes
65local whatsitcodes = nodes.whatsitcodes
66local fillcodes = nodes.fillcodes
67
68local subtypes = nodes.subtypes
69
70local glyph_code = nodecodes.glyph
71local hlist_code = nodecodes.hlist
72local vlist_code = nodecodes.vlist
73local disc_code = nodecodes.disc
74local glue_code = nodecodes.glue
75local kern_code = nodecodes.kern
76local rule_code = nodecodes.rule
77local dir_code = nodecodes.dir
78local par_code = nodecodes.par
79local whatsit_code = nodecodes.whatsit
80local passive_code = nodecodes.passive
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",status.output_active,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 local w = { }
309 local n = 0
310 local g = formatters["<%i>"]
311 local d = formatters["[%s|%s|%s]"]
312 while h do
313 local c, id = isglyph(h)
314 if c then
315 n = n + 1 ; w[n] = c >= 0 and utfchar(c) or g(c)
316 if joiner then
317 n = n + 1 ; w[n] = joiner
318 end
319 elseif id == disc_code then
320 local pre, pos, rep = getdisc(h)
321 if not nodisc then
322 n = n + 1 ; w[n] = d(
323 pre and listtoutf(pre,joiner,textonly) or "",
324 pos and listtoutf(pos,joiner,textonly) or "",
325 rep and listtoutf(rep,joiner,textonly) or ""
326 )
327 elseif rep then
328 n = n + 1 ; w[n] = listtoutf(rep,joiner,textonly) or ""
329 end
330 if joiner then
331 n = n + 1 ; w[n] = joiner
332 end
333 elseif id == passive_code then
334
335 print("weird: passive node in listtoutf")
336 return ""
337 elseif textonly then
338 if id == glue_code then
339 if getwidth(h) > 0 then
340 n = n + 1 ; w[n] = " "
341 end
342 elseif id == hlist_code or id == vlist_code then
343 local l = getlist(h)
344 n = n + 1 ; w[n] = "["
345 if l then
346 n = n + 1 ; w[n] = listtoutf(l,joiner,textonly,last,nodisc)
347 end
348 n = n + 1 ; w[n] = "]"
349 end
350 else
351 n = n + 1 ; w[n] = "[-]"
352 end
353 if h == last then
354 break
355 else
356 h = getnext(h)
357 end
358 end
359 return concat(w,"",1,(w[n] == joiner) and (n-1) or n)
360 else
361 return ""
362 end
363end
364
365function nodes.listtoutf(h,joiner,textonly,last,nodisc)
366 if h then
367 local joiner = joiner == true and utfchar(0x200C) or joiner
368 return listtoutf(tonut(h),joiner,textonly,last and tonut(last) or nil,nodisc)
369 else
370 return ""
371 end
372end
373
374local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }
375
376local function showboxes(n,symbol,depth)
377 depth = depth or 0
378 symbol = symbol or "."
379 for n, id, subtype in nextnode, tonut(n) do
380 if id == hlist_code or id == vlist_code then
381 report_nodes(rep(symbol,depth) .. what[subtype] or subtype)
382 showboxes(getlist(n),symbol,depth+1)
383 end
384 end
385end
386
387nodes.showboxes = showboxes
388
389local ptfactor = dimenfactors.pt
390local bpfactor = dimenfactors.bp
391local stripper = lpeg.patterns.stripzeros
392
393local f_f_f = formatters["%0.5Fpt plus %0.5F%s minus %0.5F%s"]
394local f_f_m = formatters["%0.5Fpt plus %0.5F%s minus %0.5Fpt"]
395local f_p_f = formatters["%0.5Fpt plus %0.5Fpt minus %0.5F%s"]
396local f_p_m = formatters["%0.5Fpt plus %0.5Fpt minus %0.5Fpt"]
397local f_f_z = formatters["%0.5Fpt plus %0.5F%s"]
398local f_p_z = formatters["%0.5Fpt plus %0.5Fpt"]
399local f_z_f = formatters["%0.5Fpt minus %0.5F%s"]
400local f_z_m = formatters["%0.5Fpt minus %0.5Fpt"]
401local f_z_z = formatters["%0.5Fpt"]
402
403local tonut = nodes.tonut
404
405local function nodetodimen(n)
406 n = tonut(n)
407 local id = getid(n)
408 if id == kern_code then
409 local width = getwidth(n)
410 if width == 0 then
411 return "0pt"
412 else
413 return f_z_z(width)
414 end
415 elseif id ~= glue_code then
416 return "0pt"
417 end
418 local width, stretch, shrink, stretch_order, shrink_order = getglue(n)
419 stretch = stretch / 65536
420 shrink = shrink / 65536
421 width = width / 65536
422 if stretch_order ~= 0 then
423 if shrink_order ~= 0 then
424 return f_f_f(width,stretch,fillcodes[stretch_order],shrink,fillcodes[shrink_order])
425 elseif shrink ~= 0 then
426 return f_f_m(width,stretch,fillcodes[stretch_order],shrink)
427 else
428 return f_f_z(width,stretch,fillcodes[stretch_order])
429 end
430 elseif shrink_order ~= 0 then
431 if stretch ~= 0 then
432 return f_p_f(width,stretch,shrink,fillcodes[shrink_order])
433 else
434 return f_z_f(width,shrink,fillcodes[shrink_order])
435 end
436 elseif stretch ~= 0 then
437 if shrink ~= 0 then
438 return f_p_m(width,stretch,shrink)
439 else
440 return f_p_z(width,stretch)
441 end
442 elseif shrink ~= 0 then
443 return f_z_m(width,shrink)
444 elseif width == 0 then
445 return "0pt"
446 else
447 return f_z_z(width)
448 end
449end
450
451
452
453
454
455
456local f_pt = formatters["%p"]
457local f_un = formatters["%F%s"]
458
459dimenfactors[""] = dimenfactors.pt
460
461local function numbertodimen(d,unit,fmt)
462 if not d or d == 0 then
463 if fmt then
464 return formatters[fmt](0,unit or "pt")
465 elseif unit then
466 return 0 .. unit
467 else
468 return "0pt"
469 end
470 elseif fmt then
471 if not unit then
472 unit = "pt"
473 end
474 return formatters[fmt](d*dimenfactors[unit],unit)
475 elseif not unit or unit == "pt" then
476 return f_pt(d)
477 else
478 return f_un(d*dimenfactors[unit],unit)
479 end
480end
481
482number.todimen = numbertodimen
483nodes .todimen = nodetodimen
484
485function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end
486function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end
487function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
488function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
489function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end
490function number.toscaledpoints(n) return n .. "sp" end
491function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
492function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end
493function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end
494function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end
495
496
497
498function nodes.topoints (n,fmt) return nodetodimen(n,"pt",fmt) end
499function nodes.toinches (n,fmt) return nodetodimen(n,"in",fmt) end
500function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end
501function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end
502function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end
503function nodes.toscaledpoints(n) return n .. "sp" end
504function nodes.tobasepoints (n,fmt) return nodetodimen(n,"bp",fmt) end
505function nodes.topicas (n,fmt) return nodetodimen(n "pc",fmt) end
506function nodes.todidots (n,fmt) return nodetodimen(n,"dd",fmt) end
507function nodes.tociceros (n,fmt) return nodetodimen(n,"cc",fmt) end
508
509
510
511
512
513local points = function(n)
514 if not n or n == 0 then
515 return "0pt"
516 elseif type(n) == "number" then
517 return lpegmatch(stripper,format("%.5fpt",n*ptfactor))
518 else
519 return numbertodimen(n,"pt")
520 end
521end
522
523local basepoints = function(n)
524 if not n or n == 0 then
525 return "0bp"
526 elseif type(n) == "number" then
527 return lpegmatch(stripper,format("%.5fbp",n*bpfactor))
528 else
529 return numbertodimen(n,"bp")
530 end
531end
532
533local pts = function(n)
534 if not n or n == 0 then
535 return "0pt"
536 elseif type(n) == "number" then
537 return format("%.5fpt",n*ptfactor)
538 else
539 return numbertodimen(n,"pt")
540 end
541end
542
543local nopts = function(n)
544 if not n or n == 0 then
545 return "0"
546 else
547 return format("%.5f",n*ptfactor)
548 end
549end
550
551number.points = points
552number.basepoints = basepoints
553number.pts = pts
554number.nopts = nopts
555
556nodes.points = function(n) return numbertodimen(n,"pt") end
557nodes.basepoints = function(n) return numbertodimen(n,"bp") end
558nodes.pts = function(n) return numbertodimen(n,"pt") end
559nodes.nopts = function(n) return format("%.5f",n*ptfactor) end
560
561local colors = { }
562tracers.colors = colors
563
564local unsetvalue = attributes.unsetvalue
565
566local a_color = attributes.private('color')
567local a_colormodel = attributes.private('colormodel')
568local m_color = attributes.list[a_color] or { }
569
570function colors.set(n,c,s)
571 local mc = m_color[c]
572 local nn = tonut(n)
573 if mc then
574 local mm = s or texgetattribute(a_colormodel)
575 setattr(nn,a_colormodel,mm <= 0 and mm or 1)
576 setattr(nn,a_color,mc)
577 else
578 setattr(nn,a_color,unsetvalue)
579 end
580 return n
581end
582
583function colors.setlist(n,c,s)
584 local nn = tonut(n)
585 local mc = m_color[c] or unsetvalue
586 local mm = s or texgetattribute(a_colormodel)
587 if mm <= 0 then
588 mm = 1
589 end
590 while nn do
591 setattr(nn,a_colormodel,mm)
592 setattr(nn,a_color,mc)
593 nn = getnext(nn)
594 end
595 return n
596end
597
598function colors.reset(n)
599 setattr(tonut(n),a_color,unsetvalue)
600 return n
601end
602
603
604
605local transparencies = { }
606tracers.transparencies = transparencies
607
608local a_transparency = attributes.private('transparency')
609local m_transparency = attributes.list[a_transparency] or { }
610
611function transparencies.set(n,t)
612 setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue)
613 return n
614end
615
616function transparencies.setlist(n,c,s)
617 local nn = tonut(n)
618 local mt = m_transparency[c] or unsetvalue
619 while nn do
620 setattr(nn,a_transparency,mt)
621 nn = getnext(nn)
622 end
623 return n
624end
625
626function transparencies.reset(n)
627 setattr(n,a_transparency,unsetvalue)
628 return n
629end
630
631
632
633local visualizers = nodes.visualizers or { }
634nodes.visualizers = visualizers
635
636function visualizers.handler(head)
637 return head, false
638end
639
640
641
642
643local function setproperties(n,c,s)
644 local nn = tonut(n)
645 local mm = texgetattribute(a_colormodel)
646 setattr(nn,a_colormodel,mm > 0 and mm or 1)
647 setattr(nn,a_color,m_color[c])
648 setattr(nn,a_transparency,m_transparency[c])
649 return n
650end
651
652tracers.setproperties = setproperties
653
654
655
656
657function tracers.setlist(n,c,s)
658 local nn = tonut(n)
659 local mc = m_color[c]
660 local mt = m_transparency[c]
661 local mm = texgetattribute(a_colormodel)
662 if mm <= 0 then
663 mm = 1
664 end
665 while nn do
666 setattr(nn,a_colormodel,mm)
667 setattr(nn,a_color,mc)
668 setattr(nn,a_transparency,mt)
669 nn = getnext(nn)
670 end
671 return n
672end
673
674function tracers.resetproperties(n)
675 local nn = tonut(n)
676 setattr(nn,a_color,unsetvalue)
677 setattr(nn,a_transparency,unsetvalue)
678 return n
679end
680
681
682
683local nodestracerpool = { }
684local nutstracerpool = { }
685
686tracers.pool = {
687 nodes = nodestracerpool,
688 nuts = nutstracerpool,
689}
690
691table.setmetatableindex(nodestracerpool,function(t,k,v)
692 local f = nutstracerpool[k]
693 local v = function(...)
694 return tonode(f(...))
695 end
696 t[k] = v
697 return v
698end)
699
700function nutstracerpool.rule(w,h,d,c,s)
701 return setproperties(new_rule(w,h,d),c,s)
702end
703
704tracers.rule = nodestracerpool.rule
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720 |