page-str.lua /size: 9449 b    last modification: 2021-10-28 13:50
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
30
31local getbox            = nuts.getbox
32local setlink           = nuts.setlink
33local getlist           = nuts.getlist
34local setlist           = nuts.setlist
35local getwhd            = nuts.getwhd
36local setwhd            = nuts.setwhd
37
38local settings_to_array = utilities.parsers.settings_to_array
39
40local enableaction      = nodes.tasks.enableaction
41
42local texgetdimen       = tex.getdimen
43----- texgetbox         = tex.getbox
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                                if tail then
232                                    setlink(tail,line)
233                                end
234                                tail  = line
235                                n     = n + 1
236                                delta = delta - struthtdp
237                            end
238                            dana[m] = vpack_node_list(getlist(vbox))
239                            setlist(vbox)
240                            flushnode(vbox)
241                            if trace_flushing then
242                                report_streams("slot %s:%s with delta (%p,%p) is compensated by %s lines",m,i,delta_height,delta_depth,n)
243                            end
244                        end
245                    end
246                end
247            else
248                -- make dummy
249            end
250        end
251    end
252end
253
254-- hm, nut or node
255
256tasks.appendaction("mvlbuilders", "normalizers", "streams.collect")
257
258tasks.disableaction("mvlbuilders", "streams.collect")
259
260function streams.initialize()
261    enableaction("mvlbuilders","streams.collect")
262    function streams.initialize() end
263end
264
265-- todo: remove empty last { }'s
266-- todo: better names, enable etc
267
268implement {
269    name     = "initializestream",
270    actions  = streams.initialize,
271    onlyonce = true,
272}
273
274implement {
275    name      = "enablestream",
276    actions   = streams.enable,
277    arguments = "string"
278}
279
280implement {
281    name      = "disablestream",
282    actions   = streams.disable
283}
284
285implement {
286    name      = "startstream",
287    actions   = streams.start,
288    arguments = "string"
289}
290
291implement {
292    name      = "stopstream",
293    actions   = streams.stop
294}
295
296implement {
297    name      = "flushstream",
298    actions   = streams.flush,
299    arguments = "string"
300}
301
302implement {
303    name      = "flushstreamcopy",
304    actions   = streams.flush,
305    arguments = { "string", true }
306}
307
308implement {
309    name      = "synchronizestream",
310    actions   = streams.synchronize,
311    arguments = "string"
312}
313
314implement {
315    name      = "pushstream",
316    actions   = streams.push,
317    arguments = "string"
318}
319