node-tra.lmt /size: 23 Kb    last modification: 2023-12-21 09:44
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-- Some of the code here might become a runtime module instead. This old module will
10-- be cleaned up anyway!
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 -- should go in environment
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-- this will be reorganized:
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) -- tonut needed?
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        -- nothing to report
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 -- = fonts.hashes.descriptions
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] -- id == font id
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 .. ">]" -- todo l2r etc
157            elseif id == par_code and startofpar(current) then
158                n = n + 1 ; t[n] = "[<" .. getdirection(start) .. ">]" -- todo l2r etc
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-- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks
255--     head = tonut(head)
256--     tail = tonut(tail)
257--     local n = head
258--     while n.next do
259--         n = n.next
260--     end
261--     local t, last_id, last_n = { }, nil, 0
262--     while n do
263--         local id = n.id
264--         if not last_id then
265--             last_id, last_n = id, 1
266--         elseif last_id == id then
267--             last_n = last_n + 1
268--         else
269--             if last_n > 1 then
270--                 t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?")
271--             else
272--                 t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?")
273--             end
274--             last_id, last_n = id, 1
275--         end
276--         if n == head then
277--             break
278--         end
279--         n = getprev(n)
280--     end
281--     if not last_id then
282--         t[#t+1] = "no nodes"
283--     elseif last_n > 1 then
284--         t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?")
285--     else
286--         t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?")
287--     end
288--     return table.concat(table.reversed(t)," ")
289-- end
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 -- zwnj
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-- number.todimen(123)
450-- number.todimen(123,"cm")
451-- number.todimen(123,false,"%F))
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-- todo: dk
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-------- number.tonewdidots   (n,fmt) return numbertodimen(n,"nd",fmt) end
495-------- number.tonewciceros  (n,fmt) return numbertodimen(n,"nc",fmt) end
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-------- nodes.tonewdidots   (n,fmt) return nodetodimen(n,"nd",fmt) end
508-------- nodes.tonewciceros  (n,fmt) return nodetodimen(n,"nc",fmt) end
509
510-- stop redefinition
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)) -- faster than formatter
517    else
518        return numbertodimen(n,"pt") -- also deals with nodes
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)) -- faster than formatter
527    else
528        return numbertodimen(n,"bp") -- also deals with nodes
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) -- faster than formatter
537    else
538        return numbertodimen(n,"pt") -- also deals with nodes
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) -- faster than formatter
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) -- also unsets !
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        -- only here t is dealt with
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-- maybe
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-- for the moment here
645
646local visualizers = nodes.visualizers or { }
647nodes.visualizers = visualizers
648
649function visualizers.handler(head)
650    return head, false
651end
652
653-- we could cache attribute lists and set attr (copy will increment count) .. todo ..
654-- although tracers are used seldom
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-- setting attrlist entries instead of attr for successive entries doesn't
668-- speed up much (this function is only used in tracers anyway)
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-- this one returns a nut
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 -- for a while
718
719-- local function show(head,n,message)
720--     print("START",message or "")
721--     local i = 0
722--     for current in traverse(head) do
723--         local prev = getprev(current)
724--         local next = getnext(current)
725--         i = i + 1
726--         print(i, prev and nodecodes[getid(prev)],nodecodes[getid(current)],next and nodecodes[getid(next)])
727--         if i == n then
728--             break
729--         end
730--     end
731--     print("STOP", message or "")
732-- end
733
734function nodes.handlers.show(n)
735    if n then
736        shownodes(tonut(n))
737    else
738        -- maybe a message
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-- This is a weird spot but it's just a compatibility hack:
748
749local getattributes   = nuts.getattributes
750local patchattributes = nuts.patchattributes
751
752local texgetattribute = tex.getattribute
753
754function recolor(head,colormodel,color,transparency)
755    -- todo loop over content
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            -- maybe skip the visuals
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