page-str.lmt /size: 9557 b    last modification: 2024-01-16 10:22
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_strut         = nuts.pool.strutrule
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
38local setattrlist       = nuts.setattrlist
39
40local settings_to_array = utilities.parsers.settings_to_array
41
42local enableaction      = nodes.tasks.enableaction
43
44local texgetdimen       = tex.getdimen
45
46local trace_collecting = false  trackers.register("streams.collecting", function(v) trace_collecting = v end)
47local trace_flushing   = false  trackers.register("streams.flushing",   function(v) trace_flushing   = v end)
48
49local report_streams = logs.reporter("streams")
50
51streams       = streams or { } -- might move to the builders namespace
52local streams = streams
53
54-- maybe store head and tail ... first we need usage
55
56local data    = { }
57local name    = nil
58local stack   = { }
59
60function streams.enable(newname)
61    if newname == "default" then
62        name = nil
63    else
64        name = newname
65    end
66end
67
68function streams.disable()
69    name = stack[#stack]
70end
71
72function streams.start(newname)
73    insert(stack,name)
74    name = newname
75end
76
77function streams.stop(newname)
78    name = remove(stack)
79end
80
81function streams.collect(head,where)
82    if name and head and name ~= "default" then
83        local head = tonut(head)
84        local dana = data[name]
85        if not dana then
86            dana = { }
87            data[name] = dana
88        end
89        local last = dana[#dana]
90        if last then
91            local tail = slide_node_list(last)
92            setlink(tail,head)
93        elseif last == false then
94            dana[#dana] = head
95        else
96            dana[1] = head
97        end
98        if trace_collecting then
99            report_streams("appending snippet %a to slot %s",name,#dana)
100        end
101        return nil
102    else
103        return head
104    end
105end
106
107function streams.push(thename)
108    if not thename or thename == "" then
109        thename = name
110    end
111    if thename and thename ~= "" then
112        local dana = data[thename]
113        if dana then
114            dana[#dana+1] = false
115            if trace_collecting then
116                report_streams("pushing snippet %a",thename)
117            end
118        end
119    end
120end
121
122function streams.flush(name,copy) -- problem: we need to migrate afterwards
123    local dana = data[name]
124    if dana then
125        local dn = #dana
126        if dn == 0 then
127            -- nothing to flush
128        elseif copy then
129            if trace_flushing then
130                report_streams("flushing copies of %s slots of %a",dn,name)
131            end
132            for i=1,dn do
133                local di = dana[i]
134                if di then
135                    write_node(copy_node_list(getlist(di))) -- list, will be option
136                end
137            end
138            if copy then
139                data[name] = nil
140            end
141        else
142            if trace_flushing then
143                report_streams("flushing %s slots of %a",dn,name)
144            end
145            for i=1,dn do
146                local di = dana[i]
147                if di then
148                    write_node(getlist(di)) -- list, will be option
149                    setlist(di)
150                    flushnode(di)
151                end
152            end
153        end
154    end
155end
156
157function streams.synchronize(list) -- this is an experiment !
158    -- we don't optimize this as we want to trace in detail
159    list = settings_to_array(list)
160    local max = 0
161    if trace_flushing then
162        report_streams("synchronizing list: % t",list)
163    end
164    for i=1,#list do
165        local dana = data[list[i]]
166        if dana then
167            local n = #dana
168            if n > max then
169                max = n
170            end
171        end
172    end
173    if trace_flushing then
174        report_streams("maximum number of slots: %s",max)
175    end
176    for m=1,max do
177        local height, depth = 0, 0
178        for i=1,#list do
179            local name = list[i]
180            local dana = data[name]
181            if dana then
182                local slot = dana[m]
183                if slot then
184                    local vbox = vpack_node_list(slot)
185                    local wd, ht, dp = getwhd(vbox)
186                    if ht > height then
187                        height = ht
188                    end
189                    if dp > depth then
190                        depth = dp
191                    end
192                    dana[m] = vbox
193                    if trace_flushing then
194                        report_streams("slot %s of %a is packed to height %p and depth %p",m,name,ht,dp)
195                    end
196                end
197            end
198        end
199        if trace_flushing then
200            report_streams("slot %s has max height %p and max depth %p",m,height,depth)
201        end
202        local strutht = texgetdimen("globalbodyfontstrutheight")
203        local strutdp = texgetdimen("globalbodyfontstrutdepth")
204        local struthtdp = strutht + strutdp
205        for i=1,#list do
206            local name = list[i]
207            local dana = data[name]
208            if dana then
209                local vbox = dana[m]
210                if vbox then
211                    local wd, ht, dp = getwhd(vbox)
212                    local delta_height = height - ht
213                    local delta_depth  = depth  - dp
214                    if delta_height > 0 or delta_depth > 0 then
215                        if false then
216                            -- actually we need to add glue and repack
217                            setwhd(vbox,false,height,depth)
218                            if trace_flushing then
219                                report_streams("slot %s of %a with delta (%p,%p) is compensated",m,i,delta_height,delta_depth)
220                            end
221                        else
222                            -- this is not yet ok as we also need to keep an eye on vertical spacing
223                            -- so we might need to do some splitting or whatever
224                            local list  = getlist(vbox)
225                            local tail  = list and slide_node_list(list)
226                            local n     = 0
227                            local delta = delta_height -- for tracing
228                            while delta > 0 do
229                                -- we need to add some interline penalties
230                                local strut = new_strut(0,strutht,strutdp)
231                                setattrlist(strut,vbox)
232                                if tail then
233                                    -- todo: inject at a better place
234                                    setlink(tail,strut)
235                                end
236                                tail  = strut
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