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