node-scn.lua /size: 11 Kb    last modification: 2021-10-28 13:50
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 leaders_code       = gluecodes.leaders
44
45local fontkern_code      = kerncodes.fontkern
46
47local variables          = interfaces.variables
48
49local privateattributes  = attributes.private
50
51local a_runningtext      = privateattributes('runningtext')
52
53local v_yes              = variables.yes
54local v_all              = variables.all
55
56local function striprange(first,last) -- todo: dir
57    if first and last then -- just to be sure
58        if first == last then
59            return first, last
60        end
61        while first and first ~= last do
62            local id = getid(first)
63            if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code
64                break
65            else
66                first = getnext(first)
67            end
68        end
69        if not first then
70            return nil, nil
71        elseif first == last then
72            return first, last
73        end
74        while last and last ~= first do
75            local id = getid(last)
76            if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code  then -- or id == rule_code
77                break
78            else
79                local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd
80                if prev then
81                    last = prev
82                else
83                    break
84                end
85            end
86        end
87        if not last then
88            return nil, nil
89        end
90    end
91    return first, last
92end
93
94nuts.striprange = striprange
95
96-- todo: order and maybe other dimensions
97
98-- we can use this one elsewhere too
99--
100-- todo: functions: word, sentence
101--
102-- glyph rule unset whatsit glue margin_kern kern math disc
103
104-- we assume {glyphruns} and no funny extra kerning, ok, maybe we need
105-- a dummy character as start and end; anyway we only collect glyphs
106--
107-- this one needs to take layers into account (i.e. we need a list of
108-- critical attributes)
109
110-- omkeren class en level -> scheelt functie call in analyze
111
112-- todo: switching inside math
113
114-- handlers
115
116local function processwords(attribute,data,flush,head,parent,skip) -- we have hlistdir and local dir
117    local n = head
118    if n then
119        local f, l, a, d, i, class
120        local continue, leaders, done, strip, level = false, false, false, true, -1
121        while n do
122            local id = getid(n)
123            if id == glyph_code or id == rule_code or (id == hlist_code and getattr(n,a_runningtext))
124or id == disc_code or id == boundary_code
125            then
126                local aa = getattr(n,attribute)
127                if aa and aa ~= skip then
128                    if aa == a then
129                        if not f then -- ?
130                            f = n
131                        end
132                        l = n
133                    else
134                        -- possible extensions: when in same class then keep spanning
135                        local newlevel, newclass = floor(aa/1000), aa%1000 -- will be configurable
136                     -- strip = not continue or level == 1 -- 0
137                        if f then
138                            if class == newclass then -- and newlevel > level then
139                                head, done = flush(head,f,l,d,level,parent,false), true
140                            else
141                                head, done = flush(head,f,l,d,level,parent,strip), true
142                            end
143                        end
144                        f, l, a = n, n, aa
145                        level, class = newlevel, newclass
146                        d = data[class]
147                        if d then
148                            local c = d.continue
149                            leaders = c == v_all
150                            continue = leaders or c == v_yes
151                        else
152                            continue = true
153                        end
154                    end
155                else
156                    if f then
157                        head, done = flush(head,f,l,d,level,parent,strip), true
158                    end
159                    f, l, a = nil, nil, nil
160                end
161                if id == hlist_code then
162                    local list = getlist(n)
163                    if list then
164                        setlist(n,(processwords(attribute,data,flush,list,n,aa))) -- watch ()
165                    end
166                end
167--             elseif id == disc_code or id == boundary_code then
168--                 if f then
169--                     l = n
170--                 end
171            elseif id == kern_code and getsubtype(n) == fontkern_code then
172                if f then
173                    l = n
174                end
175            elseif id == math_code then
176                -- otherwise not consistent: a $b$ c vs a $b+c$ d etc
177                -- we need a special (optional) go over math variant
178                if f then
179                    head, done = flush(head,f,l,d,level,parent,strip), true
180                    f, l, a = nil, nil, nil
181                end
182            elseif id == hlist_code or id == vlist_code then
183                if f then
184                    head, done = flush(head,f,l,d,level,parent,strip), true
185                    f, l, a = nil, nil, nil
186                end
187                local list = getlist(n)
188                if list then
189                    setlist(n,(processwords(attribute,data,flush,list,n,skip))) -- watch ()
190                end
191            elseif id == dir_code then -- only changes in dir, we assume proper boundaries
192                if f then
193                    l = n
194                end
195            elseif f then
196                if continue then
197                    if id == penalty_code then
198                        l = n
199                 -- elseif id == kern_code then
200                 --     l = n
201                    elseif id == glue_code then
202                        -- catch \underbar{a} \underbar{a} (subtype test is needed)
203                        local subtype = getsubtype(n)
204                        if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code or (leaders and subtype >= leaders_code)) then
205                            l = n
206                        else
207                            head, done = flush(head,f,l,d,level,parent,strip), true
208                            f, l, a = nil, nil, nil
209                        end
210                    end
211                else
212                    head, done = flush(head,f,l,d,level,parent,strip), true
213                    f, l, a = nil, nil, nil
214                end
215            end
216            n = getnext(n)
217        end
218        if f then
219            head, done = flush(head,f,l,d,level,parent,strip), true
220        end
221        return head, true -- todo: done
222    else
223        return head, false
224    end
225end
226
227nuts.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir
228    return processwords(attribute,data,flush,head,parent)
229end
230
231-- works on lines !
232-- todo: stack because skip can change when nested
233
234local function processranges(attribute,flush,head,parent,depth,skip)
235    local n = head
236    if n then
237        local f, l, a
238        local done = false
239        while n do
240            local id = getid(n)
241            if id == glyph_code or id == rule_code then
242                local aa = getattr(n,attribute)
243--                 if aa and (not skip or aa ~= skip) then
244                if aa then
245                    if aa == a then
246                        if not f then
247                            f = n
248                        end
249                        l = n
250                    else
251                        if f then
252                            head, done = flush(head,f,l,a,parent,depth), true
253                        end
254                        f, l, a = n, n, aa
255                    end
256                else
257                    if f then
258                        head, done = flush(head,f,l,a,parent,depth), true
259                    end
260                    f, l, a = nil, nil, nil
261                end
262            elseif id == disc_code or id == boundary_code then
263                if f then
264                    l = n
265                else
266                    -- weird
267                end
268            elseif id == kern_code and getsubtype(n) == fontkern_code then
269                if f then
270                    l = n
271                end
272         -- elseif id == penalty_code then
273            elseif id == glue_code then
274                -- todo: leaders
275            elseif id == hlist_code or id == vlist_code then
276                local aa = getattr(n,attribute)
277--                 if aa and (not skip or aa ~= skip) then
278                if aa then
279                    if aa == a then
280                        if not f then
281                            f = n
282                        end
283                        l = n
284                    else
285                        if f then
286                            head, done = flush(head,f,l,a,parent,depth), true
287                        end
288                        f, l, a = n, n, aa
289                    end
290                else
291                    if f then
292                        head, done = flush(head,f,l,a,parent,depth), true
293                    end
294                    f, l, a = nil, nil, nil
295                end
296                local list = getlist(n)
297                if list then
298                    setlist(n,(processranges(attribute,flush,list,n,depth+1,aa)))
299                end
300            end
301            n = getnext(n)
302        end
303        if f then
304            head, done = flush(head,f,l,a,parent,depth), true
305        end
306        return head, done
307    else
308        return head, false
309    end
310end
311
312nuts.processranges = function(attribute,flush,head,parent) -- we have hlistdir and local dir
313    return processranges(attribute,flush,head,parent,0)
314end
315