node-aux.lmt /size: 11 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['node-aux'] = {
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-- todo: n1 .. n2 : __concat metatable
10
11local type, tostring = type, tostring
12
13local nodes          = nodes
14local context        = context
15
16local utfvalues      = utf.values
17
18local nodecodes      = nodes.nodecodes
19
20local glyph_code     = nodecodes.glyph
21local hlist_code     = nodecodes.hlist
22local vlist_code     = nodecodes.vlist
23local attribute_code = nodecodes.attribute -- temporary
24local par_code       = nodecodes.par
25
26local nuts           = nodes.nuts
27local tonut          = nuts.tonut
28local tonode         = nuts.tonode
29local vianuts        = nuts.vianuts
30
31local getbox         = nuts.getbox
32local getnext        = nuts.getnext
33local getid          = nuts.getid
34local getsubtype     = nuts.getsubtype
35local getlist        = nuts.getlist
36local getattr        = nuts.getattr
37local getboth        = nuts.getboth
38local getprev        = nuts.getprev
39local getwidth       = nuts.getwidth
40local setwidth       = nuts.setwidth
41local getboxglue     = nuts.getboxglue
42local setboxglue     = nuts.setboxglue
43
44local setfield       = nuts.setfield
45local setattr        = nuts.setattr
46local setlink        = nuts.setlink
47local setlist        = nuts.setlist
48local setnext        = nuts.setnext
49local setprev        = nuts.setprev
50local setattrlist    = nuts.setattrlist
51
52local traversers     = nuts.traversers
53local nextnode       = traversers.node
54local nextglyph      = traversers.glyph
55
56local flushnode      = nuts.flush
57local flushlist      = nuts.flushlist
58local hpack_nodes    = nuts.hpack
59local vpack_nodes    = nuts.vpack
60local unsetattribute = nuts.unsetattribute
61----- firstglyphnode = nuts.firstglyphnode
62----- firstglyph     = nuts.firstglyph
63----- firstchar      = nuts.firstchar
64local copy_node      = nuts.copy
65local find_tail      = nuts.tail
66local getbox         = nuts.getbox
67local count          = nuts.count
68local isglyph        = nuts.isglyph
69
70local nodepool       = nuts.pool
71local new_glue       = nodepool.glue
72local new_glyph      = nodepool.glyph
73
74local unsetvalue     = attributes.unsetvalue
75
76local current_font   = font.current
77
78local texsetbox      = tex.setbox
79
80local report_error   = logs.reporter("node-aux:error")
81
82local function takebox(id)
83    local box = getbox(id)
84    if box then
85        local list = getlist(box)
86        setlist(box,nil)
87        local copy = copy_node(box)
88        if list then
89            setlist(copy,list)
90        end
91        texsetbox(id,false)
92        return copy
93    end
94end
95
96function nodes.takebox(id)
97    local b = takebox(id)
98    if b then
99        return tonode(b)
100    end
101end
102
103if not nuts.migratebox then -- todo: add to nodes.direct, or maybe getmigrate, setmigrate
104
105    local setpre  = nuts.setpre
106    local setpost = nuts.setpost
107    local getpre  = nuts.getpre
108    local getpost = nuts.getpost
109
110    function nuts.migratebox(source,target)
111        setpre(target,getpre(source))
112        setpost(target,getpost(source))
113        setpre(source)
114        setpost(source)
115    end
116
117end
118
119local splitbox = tex.splitbox
120nodes.splitbox = splitbox
121
122function nuts.splitbox(id,height)
123    return tonut(splitbox(id,height))
124end
125
126-- function nodes.takelist(n)
127--     -- when we need it
128-- end
129
130function nuts.takelist(n)
131    local l = getlist(n)
132    setlist(n)
133    flushnode(n)
134    return l
135end
136
137nuts.takebox = takebox
138tex.takebox  = nodes.takebox -- sometimes more clear
139
140-- so far
141
142local function repackhlist(list,...)
143    local temp, b = hpack_nodes(list,...)
144    list = getlist(temp)
145    setlist(temp)
146    flushnode(temp)
147    return list, b
148end
149
150nuts.repackhlist = repackhlist
151
152function nodes.repackhlist(list,...)
153    local list, b = repackhlist(tonut(list),...)
154    return tonode(list), b
155end
156
157local function setattributes(head,attr,value)
158    for n, id in nextnode, head do
159        setattr(n,attr,value)
160        if id == hlist_node or id == vlist_node then
161            setattributes(getlist(n),attr,value)
162        end
163    end
164end
165
166local function setunsetattributes(head,attr,value)
167    for n, id in nextnode, head do
168        if not getattr(n,attr) then
169            setattr(n,attr,value)
170        end
171        if id == hlist_code or id == vlist_code then
172            setunsetattributes(getlist(n),attr,value)
173        end
174    end
175end
176
177local function unsetattributes(head,attr)
178    for n, id in nextnode, head do
179        setattr(n,attr,unsetvalue)
180        if id == hlist_code or id == vlist_code then
181            unsetattributes(getlist(n),attr)
182        end
183    end
184end
185
186-- function nuts.firstcharacter(n,untagged) -- tagged == subtype > 255
187--     if untagged then
188--         return firstchar(n)
189--     else
190--         return firstglyph(n)
191--     end
192-- end
193--
194-- nodes.firstcharacter = vianuts(firstcharacter)
195--
196-- local function firstcharinbox(n,untagged)
197--     local l = getlist(getbox(n))
198--     if l then
199--         return firstglyphnode(l)
200--     end
201-- end
202--
203-- nuts .firstcharinbox = firstcharinbox
204
205-- this depends on fonts, so we have a funny dependency ... will be
206-- sorted out .. we could make tonodes a plugin into this
207
208local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-ini
209    if not str or str == "" then
210        return
211    end
212    local head, tail, space, fnt, template = nil, nil, nil, nil, nil
213    if not fnt then
214        fnt = current_font()
215    elseif type(fnt) ~= "number" and getid(fnt) == glyph_code then -- so it has to be a real node
216        fnt, template = nil, tonut(fnt)
217    end
218    for s in utfvalues(str) do
219        local n
220        if s == 32 then
221            if space then
222                n = copy_node(space)
223            elseif fonts then -- depedency
224                local parameters = fonts.hashes.identifiers[fnt].parameters
225                space = new_glue(parameters.space,parameters.spacestretch,parameters.spaceshrink)
226                n = space
227            end
228        elseif template then
229            n = copy_node(template)
230            setvalue(n,"char",s)
231        else
232            n = new_glyph(fnt,s)
233        end
234        if attr then -- normally false when template
235            setattrlist(n,attr)
236        end
237        if head then
238            setlink(tail,n)
239        else
240            head = n
241        end
242        tail = n
243    end
244    return head, tail
245end
246
247nuts.tonodes = tonodes
248
249nodes.tonodes = function(str,fnt,attr)
250    local head, tail = tonodes(str,fnt,attr)
251    return tonode(head), tonode(tail)
252end
253
254local function link(list,currentfont,currentattr,head,tail) -- an oldie, might be replaced
255    for i=1,#list do
256        local n = list[i]
257        if n then
258            local tn = type(n)
259            if tn == "string" then
260                if #tn > 0 then
261                    if not currentfont then
262                        currentfont = current_font()
263                    end
264                    local h, t = tonodes(n,currentfont,currentattr)
265                    if not h then
266                        -- skip
267                    elseif not head then
268                        head, tail = h, t
269                    else
270                        setnext(tail,h)
271                        setprev(h,t)
272                        tail = t
273                    end
274                end
275            elseif tn == "table" then
276                if #tn > 0 then
277                    if not currentfont then
278                        currentfont = current_font()
279                    end
280                    head, tail = link(n,currentfont,currentattr,head,tail)
281                end
282            elseif not head then
283                head = n
284                tail = find_tail(n)
285            elseif getid(n) == attribute_code then
286                -- weird case
287                report_error("weird node type in list at index %s:",i)
288                for i=1,#list do
289                    local l = list[i]
290                    report_error("%3i: %s %S",i,getid(l) == attribute_code and "!" or ">",l)
291                end
292                os.exit()
293            else
294                setlink(tail,n)
295                if getnext(n) then
296                    tail = find_tail(n)
297                else
298                    tail = n
299                end
300            end
301        else
302            -- permitting nil is convenient
303        end
304    end
305    return head, tail
306end
307
308nuts.link = link
309
310nodes.link = function(list,currentfont,currentattr,head,tail)
311    local head, tail = link(list,currentfont,currentattr,tonut(head),tonut(tail))
312    return tonode(head), tonode(tail)
313end
314
315local function locate(start,wantedid,wantedsubtype)
316    for n, id, subtype in nextnode, start do
317        if id == wantedid then
318            if not wantedsubtype or subtype == wantedsubtype then
319                return n
320            end
321        elseif id == hlist_code or id == vlist_code then
322            local found = locate(getlist(n),wantedid,wantedsubtype)
323            if found then
324                return found
325            end
326        end
327    end
328end
329
330nuts.locate = locate
331
332function nodes.locate(start,wantedid,wantedsubtype)
333    local found = locate(tonut(start),wantedid,wantedsubtype)
334    return found and tonode(found)
335end
336
337local function rehpack(n,width)
338    local head = getlist(n)
339    local size = width or getwidth(n)
340    local temp = hpack_nodes(head,size,"exactly")
341    setwidth(n,size)
342    local set, order, sign = getboxglue(temp)
343    setboxglue(n,set,order,sign)
344    setlist(temp)
345    flushnode(temp)
346    return n
347end
348
349nuts.rehpack = rehpack
350
351function nodes.rehpack(n,...)
352    rehpack(tonut(n),...)
353end
354
355do
356
357    local parcodes      = nodes.parcodes
358    local hmodepar_code = parcodes.hmodepar
359    local vmodepar_code = parcodes.vmodepar
360
361    local getnest       = tex.getnest
362    local getsubtype    = nuts.getsubtype
363
364    function nuts.setparproperty(action,...)
365        local tail = tonut(getnest().tail)
366        while tail do
367            if getid(tail) == par_code then
368                local s = getsubtype(tail)
369                if s == hmodepar_code or s == vmodepar_code then
370                    return action(tail,...)
371                else
372                    -- something is wrong here
373                end
374            end
375            tail = getprev(tail)
376        end
377    end
378
379    local startofpar = nuts.startofpar
380
381    function nodes.startofpar(n)
382        return startofpar(tonut(n))
383    end
384
385end
386
387function nodes.hpack(h,...) return tonode(hpack_nodes(tonut(h),...)) end
388function nodes.vpack(h,...) return tonode(vpack_nodes(tonut(h),...)) end
389
390--
391
392local report_slide = logs.reporter("nodes", "slide")
393
394function nuts.checkslide(head,banner)
395    local c = head
396    local x = nil
397    local b = false
398    while c do
399        local p, n = getboth(c)
400        if p and p ~= x then
401            b = true
402        end
403        x = c
404        c = n
405    end
406    if b then
407        report_slide("")
408        report_slide(banner or "?")
409        report_slide("")
410        c = head
411        x = nil
412        while c do
413            local p, n = getboth(c)
414            report_slide("%s %s",(p and p ~= x and "!") or " ",tostring(tonode(c)))
415            x = c
416            c = getnext(c)
417        end
418        report_slide("")
419        nuts.show(head)
420        report_slide("")
421        nuts.slide(head)
422    end
423end
424