page-str.lua /size: 9610 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['page-str'] = {
2    version   = 1.001,
3    comment   = "companion to page-str.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-- streams -> managers.streams
10
11-- work in progresss .. unfinished .. non-optimized
12
13local concat, insert, remove = table.concat, table.insert, table.remove
14
15local nodes, node = nodes, node
16
17local tasks             = nodes.tasks
18
19local implement         = interfaces.implement
20
21local nodecodes         = nodes.nodecodes
22
23local nuts              = nodes.nuts
24local tonut             = nodes.tonut
25local slide_node_list   = nuts.slide
26local write_node        = nuts.write
27local flushnode         = nuts.flush
28local copy_node_list    = nuts.copylist
29local vpack_node_list   = nuts.vpack
30local new_rule          = nuts.pool.rule
31
32----- getbox            = nuts.getbox
33local setlink           = nuts.setlink
34local getlist           = nuts.getlist
35local setlist           = nuts.setlist
36local getwhd            = nuts.getwhd
37local setwhd            = nuts.setwhd
38
39local settings_to_array = utilities.parsers.settings_to_array
40
41local enableaction      = nodes.tasks.enableaction
42
43local texgetdimen       = tex.getdimen
44
45local trace_collecting = false  trackers.register("streams.collecting", function(v) trace_collecting = v end)
46local trace_flushing   = false  trackers.register("streams.flushing",   function(v) trace_flushing   = v end)
47
48local report_streams = logs.reporter("streams")
49
50streams       = streams or { } -- might move to the builders namespace
51local streams = streams
52
53-- maybe store head and tail ... first we need usage
54
55local data    = { }
56local name    = nil
57local stack   = { }
58
59function streams.enable(newname)
60    if newname == "default" then
61        name = nil
62    else
63        name = newname
64    end
65end
66
67function streams.disable()
68    name = stack[#stack]
69end
70
71function streams.start(newname)
72    insert(stack,name)
73    name = newname
74end
75
76function streams.stop(newname)
77    name = remove(stack)
78end
79
80function streams.collect(head,where)
81    if name and head and name ~= "default" then
82        local head = tonut(head)
83        local dana = data[name]
84        if not dana then
85            dana = { }
86            data[name] = dana
87        end
88        local last = dana[#dana]
89        if last then
90            local tail = slide_node_list(last)
91            setlink(tail,head)
92        elseif last == false then
93            dana[#dana] = head
94        else
95            dana[1] = head
96        end
97        if trace_collecting then
98            report_streams("appending snippet %a to slot %s",name,#dana)
99        end
100        return nil
101    else
102        return head
103    end
104end
105
106function streams.push(thename)
107    if not thename or thename == "" then
108        thename = name
109    end
110    if thename and thename ~= "" then
111        local dana = data[thename]
112        if dana then
113            dana[#dana+1] = false
114            if trace_collecting then
115                report_streams("pushing snippet %a",thename)
116            end
117        end
118    end
119end
120
121function streams.flush(name,copy) -- problem: we need to migrate afterwards
122    local dana = data[name]
123    if dana then
124        local dn = #dana
125        if dn == 0 then
126            -- nothing to flush
127        elseif copy then
128            if trace_flushing then
129                report_streams("flushing copies of %s slots of %a",dn,name)
130            end
131            for i=1,dn do
132                local di = dana[i]
133                if di then
134                    write_node(copy_node_list(getlist(di))) -- list, will be option
135                end
136            end
137            if copy then
138                data[name] = nil
139            end
140        else
141            if trace_flushing then
142                report_streams("flushing %s slots of %a",dn,name)
143            end
144            for i=1,dn do
145                local di = dana[i]
146                if di then
147                    write_node(getlist(di)) -- list, will be option
148                    setlist(di)
149                    flushnode(di)
150                end
151            end
152        end
153    end
154end
155
156function streams.synchronize(list) -- this is an experiment !
157    -- we don't optimize this as we want to trace in detail
158    list = settings_to_array(list)
159    local max = 0
160    if trace_flushing then
161        report_streams("synchronizing list: % t",list)
162    end
163    for i=1,#list do
164        local dana = data[list[i]]
165        if dana then
166            local n = #dana
167            if n > max then
168                max = n
169            end
170        end
171    end
172    if trace_flushing then
173        report_streams("maximum number of slots: %s",max)
174    end
175    for m=1,max do
176        local height, depth = 0, 0
177        for i=1,#list do
178            local name = list[i]
179            local dana = data[name]
180            if dana then
181                local slot = dana[m]
182                if slot then
183                    local vbox = vpack_node_list(slot)
184                    local wd, ht, dp = getwhd(vbox)
185                    if ht > height then
186                        height = ht
187                    end
188                    if dp > depth then
189                        depth = dp
190                    end
191                    dana[m] = vbox
192                    if trace_flushing then
193                        report_streams("slot %s of %a is packed to height %p and depth %p",m,name,ht,dp)
194                    end
195                end
196            end
197        end
198        if trace_flushing then
199            report_streams("slot %s has max height %p and max depth %p",m,height,depth)
200        end
201        local strutht = texgetdimen("globalbodyfontstrutheight")
202        local strutdp = texgetdimen("globalbodyfontstrutdepth")
203        local struthtdp = strutht + strutdp
204        for i=1,#list do
205            local name = list[i]
206            local dana = data[name]
207            if dana then
208                local vbox = dana[m]
209                if vbox then
210                    local wd, ht, dp = getwhd(vbox)
211                    local delta_height = height - ht
212                    local delta_depth  = depth  - dp
213                    if delta_height > 0 or delta_depth > 0 then
214                        if false then
215                            -- actually we need to add glue and repack
216                            setwhd(vbox,false,height,depth)
217                            if trace_flushing then
218                                report_streams("slot %s of %a with delta (%p,%p) is compensated",m,i,delta_height,delta_depth)
219                            end
220                        else
221                            -- this is not yet ok as we also need to keep an eye on vertical spacing
222                            -- so we might need to do some splitting or whatever
223                            local list  = getlist(vbox)
224                            local tail  = list and slide_node_list(list)
225                            local n     = 0
226                            local delta = delta_height -- for tracing
227                            while delta > 0 do
228                                -- we need to add some interline penalties
229                             -- local line = copy_node_list(getbox("strutbox"))
230                             -- setwhd(line,false,strutht,strutdp)
231                                local line = new_rule(0,strutht,strutdp) -- no tracing
232                                if tail then
233                                    -- todo: inject at a better place
234                                    setlink(tail,line)
235                                end
236                                tail  = line
237                                n     = n + 1
238                                delta = delta - struthtdp
239                            end
240                            dana[m] = vpack_node_list(getlist(vbox))
241                            setlist(vbox)
242                            flushnode(vbox)
243                            if trace_flushing then
244                                report_streams("slot %s:%s with delta (%p,%p) is compensated by %s lines",m,i,delta_height,delta_depth,n)
245                            end
246                        end
247                    end
248                end
249            else
250                -- make dummy
251            end
252        end
253    end
254end
255
256-- hm, nut or node
257
258tasks.appendaction("mvlbuilders", "normalizers", "streams.collect")
259
260tasks.disableaction("mvlbuilders", "streams.collect")
261
262function streams.initialize()
263    enableaction("mvlbuilders","streams.collect")
264    function streams.initialize() end
265end
266
267-- todo: remove empty last { }'s
268-- todo: better names, enable etc
269
270implement {
271    name     = "initializestream",
272    actions  = streams.initialize,
273    onlyonce = true,
274}
275
276implement {
277    name      = "enablestream",
278    actions   = streams.enable,
279    arguments = "string"
280}
281
282implement {
283    name      = "disablestream",
284    actions   = streams.disable
285}
286
287implement {
288    name      = "startstream",
289    actions   = streams.start,
290    arguments = "string"
291}
292
293implement {
294    name      = "stopstream",
295    actions   = streams.stop
296}
297
298implement {
299    name      = "flushstream",
300    actions   = streams.flush,
301    arguments = "string"
302}
303
304implement {
305    name      = "flushstreamcopy",
306    actions   = streams.flush,
307    arguments = { "string", true }
308}
309
310implement {
311    name      = "synchronizestream",
312    actions   = streams.synchronize,
313    arguments = "string"
314}
315
316implement {
317    name      = "pushstream",
318    actions   = streams.push,
319    arguments = "string"
320}
321