node-scn.lmt /size: 13 Kb    last modification: 2024-01-16 09:03
1if not modules then modules = { } end modules ['node-scn'] = {
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
9local floor = math.floor
10
11local attributes         = attributes
12local nodes              = nodes
13
14local nuts               = nodes.nuts
15
16local getnext            = nuts.getnext
17local getprev            = nuts.getprev
18local getid              = nuts.getid
19local getattr            = nuts.getattr
20local getsubtype         = nuts.getsubtype
21local getlist            = nuts.getlist
22local setlist            = nuts.setlist
23
24local nodecodes          = nodes.nodecodes
25local gluecodes          = nodes.gluecodes
26local kerncodes          = nodes.kerncodes
27
28local glyph_code         = nodecodes.glyph
29local disc_code          = nodecodes.disc
30local rule_code          = nodecodes.rule
31local boundary_code      = nodecodes.boundary
32local dir_code           = nodecodes.dir
33local math_code          = nodecodes.math
34local glue_code          = nodecodes.glue
35local penalty_code       = nodecodes.penalty
36local kern_code          = nodecodes.kern
37local hlist_code         = nodecodes.hlist
38local vlist_code         = nodecodes.vlist
39
40local userskip_code      = gluecodes.userskip
41local spaceskip_code     = gluecodes.spaceskip
42local xspaceskip_code    = gluecodes.xspaceskip
43local intermathkip_code  = gluecodes.intermathskip
44local leaders_code       = gluecodes.leaders
45local rightfillskip_code = gluecodes.rightparfillskip
46local righthangskip_code = gluecodes.righthangskip
47
48local fontkern_code      = kerncodes.fontkern
49
50local variables          = interfaces.variables
51
52local privateattributes  = attributes.private
53
54local a_runningtext      = privateattributes('runningtext')
55
56local v_yes              = variables.yes
57local v_all              = variables.all
58
59local function striprange(first,last) -- todo: dir
60    if first and last then -- just to be sure
61        if first == last then
62            return first, last
63        end
64        while first and first ~= last do
65            local id = getid(first)
66            if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code
67                break
68            else
69                first = getnext(first)
70            end
71        end
72        if not first then
73            return nil, nil
74        elseif first == last then
75            return first, last
76        end
77        while last and last ~= first do
78            local id = getid(last)
79            if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code  then -- or id == rule_code
80                break
81            else
82                local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd
83                if prev then
84                    last = prev
85                else
86                    break
87                end
88            end
89        end
90        if not last then
91            return nil, nil
92        end
93    end
94    return first, last
95end
96
97nuts.striprange = striprange
98
99-- todo: order and maybe other dimensions
100
101-- we can use this one elsewhere too
102--
103-- todo: functions: word, sentence
104--
105-- glyph rule unset whatsit glue margin_kern kern math disc
106
107-- we assume {glyphruns} and no funny extra kerning, ok, maybe we need
108-- a dummy character as start and end; anyway we only collect glyphs
109--
110-- this one needs to take layers into account (i.e. we need a list of
111-- critical attributes)
112
113-- omkeren class en level -> scheelt functie call in analyze
114
115-- todo: switching inside math
116
117-- handlers (some are very specialized and demanding)
118
119local maxlevel = 1
120
121local function processwords(nesting,attribute,data,flush,head,parent,skip) -- we have hlistdir and local dir
122    local n = head
123    if n then
124        local f, l, a, d, class
125        local continue, leaders, strip, level = false, false, true, -1
126        while n do
127            local id = getid(n)
128            if id == glyph_code
129                or id == disc_code                                 -- can indeed be a start
130                or id == rule_code                                 -- can be a strut
131                or id == boundary_code                             -- can be between quote and text
132                or (id == hlist_code and getattr(n,a_runningtext)) -- well ...
133            then
134                local aa = getattr(n,attribute)
135                -- new approach
136                local dd, ll
137                if aa then
138                    dd = data[aa]
139                    ll = dd.level or 1
140                    if nesting == 1 and ll > maxlevel then
141                        maxlevel = ll
142                    end
143                    if ll == nesting then
144                        -- we're okay
145                    else
146                        while true do
147                            local nestingvalue = dd.nestingvalue
148                            if nestingvalue then
149                                dd = dd.nestingdata
150                                ll = dd.level
151                                if ll == nesting then
152                                    aa = nestingvalue
153                                    break
154                                end
155                            else
156                                -- no matching level found
157                                aa = nil
158                                dd = nil
159                                break
160                            end
161                        end
162                    end
163                end
164                -- old approach
165                if aa and aa ~= skip then
166                    if aa == a then
167                        if not f then -- ?
168                            f = n
169                        end
170                        l = n
171                    else
172                        if f then
173                            if class == aa then -- and newlevel > level then
174                                head = flush(head,f,l,d,level,parent,false)
175                            else
176                                head = flush(head,f,l,d,level,parent,strip)
177                            end
178                        end
179                        f, l, a = n, n, aa
180                        d     = dd
181                        class = aa
182                        if d then
183                            continue = d.continue
184                            level    = d.level or 1
185                            leaders  = continue == v_all
186                            continue = leaders or continue == v_yes
187                        else
188                            continue = true
189                            level    = 1
190                            leaders  = false
191                        end
192                    end
193                else
194                    if f then
195                        head = flush(head,f,l,d,level,parent,strip)
196                    end
197                    f, l, a = nil, nil, nil
198                end
199                if id == hlist_code then
200                    local list = getlist(n)
201                    if list then
202                        setlist(n,processwords(nesting,attribute,data,flush,list,n,aa))
203                    end
204                end
205                goto next
206         -- elseif id == disc_code or id == boundary_code then
207         --     if f then
208         --         l = n
209         --     end
210         --     goto next
211            elseif id == kern_code then
212                if getsubtype(n) == fontkern_code then
213                    if f then
214                        l = n
215                    end
216                    goto next
217                else
218                    goto rest
219                end
220            elseif id == math_code then
221                -- otherwise not consistent: a $b$ c vs a $b+c$ d etc
222                -- we need a special (optional) go over math variant
223                if f then
224                    head = flush(head,f,l,d,level,parent,strip)
225                    f, l, a = nil, nil, nil
226                end
227                goto next
228            elseif id == hlist_code or id == vlist_code then
229                if f then
230                    head = flush(head,f,l,d,level,parent,strip)
231                    f, l, a = nil, nil, nil
232                end
233                local list = getlist(n)
234                if list then
235                    setlist(n,processwords(nesting,attribute,data,flush,list,n,skip))
236                end
237                goto next
238            elseif id == dir_code then -- only changes in dir, we assume proper boundaries
239                if f then
240                    l = n
241                end
242                goto next
243            end
244          ::rest::
245            if f then
246                if continue then
247                    if id == penalty_code then
248                        l = n
249                        goto next
250                 -- elseif id == kern_code then
251                 --     l = n
252                 -- goto next
253                    elseif id == glue_code then
254                        -- catch \underbar{a} \underbar{a} (subtype test is needed)
255                        local subtype = getsubtype(n)
256                        if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code or subtype == intermathkip_code or (leaders and subtype >= leaders_code)) then
257                            l = n
258                        else
259                            head = flush(head,f,l,d,level,parent,strip)
260                            f, l, a = nil, nil, nil
261                        end
262                    end
263                else
264                    head = flush(head,f,l,d,level,parent,strip)
265                    f, l, a = nil, nil, nil
266                end
267            end
268          ::next::
269            n = getnext(n)
270        end
271        if f then
272            head = flush(head,f,l,d,level,parent,strip)
273        end
274    end
275    return head
276end
277
278nuts.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir
279 -- print("processing words at level "..1)io.flush()
280    maxlevel = 1
281    head = processwords(1,attribute,data,flush,head,parent)
282    for i=2,maxlevel do
283     -- print("processing words at level "..i)io.flush()
284        head = processwords(i,attribute,data,flush,head,parent)
285    end
286    return head
287end
288
289-- works on lines !
290-- todo: stack because skip can change when nested
291
292local function processranges(attribute,flush,head,parent,depth,skip)
293    local n = head
294    if n then
295        local f, l, a
296        while n do
297            local id = getid(n)
298            if id == glyph_code or id == rule_code then
299                local aa = getattr(n,attribute)
300             -- if aa and (not skip or aa ~= skip) then
301                if aa then
302                    if aa == a then
303                        if not f then
304                            f = n
305                        end
306                        l = n
307                    else
308                        if f then
309                            head = flush(head,f,l,a,parent,depth)
310                        end
311                        f, l, a = n, n, aa
312                    end
313                else
314                    if f then
315                        head = flush(head,f,l,a,parent,depth)
316                    end
317                    f, l, a = nil, nil, nil
318                end
319            elseif id == disc_code or id == boundary_code then
320                if f then
321                    l = n
322                else
323                    -- weird
324                end
325            elseif id == kern_code then
326                if getsubtype(n) == fontkern_code then
327                    if f then
328                        l = n
329                    end
330                end
331         -- elseif id == penalty_code then
332            elseif id == glue_code then
333                -- todo: leaders
334--                 if getsubtype(n) == rightfillskip_code or getsubtype(n) == righthangskip_code then
335--                     break
336--                 end
337            elseif id == hlist_code or id == vlist_code then
338                local aa = getattr(n,attribute)
339             -- if aa and (not skip or aa ~= skip) then
340                if aa then
341                    if aa == a then
342                        if not f then
343                            f = n
344                        end
345                        l = n
346                    else
347                        if f then
348                            head = flush(head,f,l,a,parent,depth), true
349                        end
350                        f, l, a = n, n, aa
351                    end
352                else
353                    if f then
354                        head = flush(head,f,l,a,parent,depth), true
355                    end
356                    f, l, a = nil, nil, nil
357                end
358                local list = getlist(n)
359                if list then
360                    setlist(n,processranges(attribute,flush,list,n,depth+1,aa))
361                end
362            end
363            n = getnext(n)
364        end
365        if f then
366            head = flush(head,f,l,a,parent,depth)
367        end
368    end
369    return head
370end
371
372nuts.processranges = function(attribute,flush,head,parent) -- we have hlistdir and local dir
373    return processranges(attribute,flush,head,parent,0)
374end
375