typo-lbx.lmt /size: 10 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['typo-lbx'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to typo-lbx.mkxl",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10local context      = context
11
12local tostring = tostring
13
14local nuts              = nodes.nuts
15local tonut             = nodes.tonut
16local tonode            = nodes.tonode
17
18local left_box_code     = nodes.listcodes.left
19local right_box_code    = nodes.listcodes.right
20local local_box_code    = nodes.listcodes["local"]
21local hlist_code        = nodes.nodecodes.hlist
22
23-- some can go:
24
25local getlist           = nuts.getlist
26local getprev           = nuts.getprev
27local getnext           = nuts.getnext
28local getattribute      = nuts.getattribute
29local gettail           = nuts.tail
30local getwidth          = nuts.getwidth
31local getboth           = nuts.getboth
32local getindex          = nuts.getindex
33local setlist           = nuts.setlist
34local flushlist         = nuts.flushlist
35local takebox           = nuts.takebox
36local setbox            = nuts.setbox
37local copynode          = nuts.copynode
38local rangedimensions   = nuts.rangedimensions
39local traverse_list     = nuts.traversers.list
40
41----- setlocalbox       = tex.setlocalbox -- todo: also in node.direct namespace
42----- getlocalbox       = tex.getlocalbox -- todo: also in node.direct namespace
43
44local expandmacro       = token.expandmacro
45
46local dimension_value   = tokens.values.dimension
47local integer_value     = tokens.values.integer
48
49local implement         = interfaces.implement
50
51typesetters             = typesetters or { }
52local typesetters       = typesetters
53
54typesetters.localboxes  = typesetters.localboxes or { }
55local localboxes        = typesetters.localboxes
56
57local a_localboxesmark  = attributes.private("localboxesmark")
58
59local starttiming       = statistics.starttiming
60local stoptiming        = statistics.stoptiming
61
62do
63    local lb_found            = nil
64    local lb_index            = 0
65    local lb_linenumber       = 0
66    local lb_linewidth        = 0
67    local lb_localwidth       = 0
68    local lb_progress         = 0
69    local lb_leftoffset       = 0
70    local lb_rightoffset      = 0
71    local lb_leftskip         = 0
72    local lb_rightskip        = 0
73    local lb_lefthang         = 0
74    local lb_righthang        = 0
75    local lb_indent           = 0
76    local lb_parfillleftskip  = 0
77    local lb_parfillrightskip = 0
78    local lb_parinitleftskip  = 0
79    local lb_parinitrightskip = 0
80    local lb_overshoot        = 0
81
82    implement { name = "localboxindex",            public = true, usage = "value", actions = function() return integer_value,   lb_index            end }
83    implement { name = "localboxlinenumber",       public = true, usage = "value", actions = function() return integer_value,   lb_linenumber       end }
84    implement { name = "localboxlinewidth",        public = true, usage = "value", actions = function() return dimension_value, lb_linewidth        end }
85    implement { name = "localboxlocalwidth",       public = true, usage = "value", actions = function() return dimension_value, lb_localwidth       end }
86    implement { name = "localboxprogress",         public = true, usage = "value", actions = function() return dimension_value, lb_progress         end }
87    implement { name = "localboxleftoffset",       public = true, usage = "value", actions = function() return dimension_value, lb_leftoffset       end }
88    implement { name = "localboxrightoffset",      public = true, usage = "value", actions = function() return dimension_value, lb_rightoffset      end }
89    implement { name = "localboxleftskip",         public = true, usage = "value", actions = function() return dimension_value, lb_leftskip         end }
90    implement { name = "localboxrightskip",        public = true, usage = "value", actions = function() return dimension_value, lb_rightskip        end }
91    implement { name = "localboxlefthang",         public = true, usage = "value", actions = function() return dimension_value, lb_lefthang         end }
92    implement { name = "localboxrighthang",        public = true, usage = "value", actions = function() return dimension_value, lb_righthang        end }
93    implement { name = "localboxindent",           public = true, usage = "value", actions = function() return dimension_value, lb_indent           end }
94    implement { name = "localboxparfillleftskip",  public = true, usage = "value", actions = function() return dimension_value, lb_parfillleftskip  end }
95    implement { name = "localboxparfillrightskip", public = true, usage = "value", actions = function() return dimension_value, lb_parfillrightskip end }
96    implement { name = "localboxparinitleftskip",  public = true, usage = "value", actions = function() return dimension_value, lb_parinitleftskip  end }
97    implement { name = "localboxparinitrightskip", public = true, usage = "value", actions = function() return dimension_value, lb_parinitrightskip end }
98    implement { name = "localboxovershoot",        public = true, usage = "value", actions = function() return dimension_value, lb_overshoot        end }
99
100    local cache = table.setmetatableindex(function(t,k)
101        local v = { n = 0, m = 0 }
102        t[k] = v
103        return v
104    end)
105
106    -- todo: use a simple usernode that refers to a cache so that we don't need to
107    -- make copies
108
109    implement {
110        name      = "localboxmarkonce",
111        public    = true,
112        arguments =  "integer",
113        actions   = function(attr)
114            local c = cache[attr]
115            local n = c.n
116-- first test this:
117--             if n == c.m then
118-- print("all false",attr,n)
119--                 -- all false
120--                 n = 1
121--                 c.m = 0
122--             else
123                n = n + 1
124--             end
125            c[n] = true
126            c.n = n
127            context(n)
128        end
129    }
130
131    local function action(current)
132        local list = getlist(current)
133        if list then
134            local attr = getattribute(list,a_localboxesmark) or 0
135            local cach = attr and cache[lb_index]
136            local once = cach and cach[attr]
137            if once == false then
138                setlist(current)
139                flushlist(list)
140            else
141                setlist(current)
142                local head = copynode(current)
143                setlist(head,list)
144                setbox("localboxcontentbox",head)
145                expandmacro("localboxcommand") -- no longer pass arguments
146                local box = takebox("localboxcontentbox")
147                setlist(current,box)
148                if once and cach[attr] == true then
149                    cach[attr] = false
150                    cach.m = cach.m + 1
151                end
152            end
153        end
154    end
155
156    -- these two are now more or less the same so ... todo: add
157    -- warning about non zero width
158
159    local function processleftbox(box)
160        local list = getlist(box)
161        for current, id, subtype in traverse_list, list do
162            if subtype == local_box_code and getwidth(current) == 0 then
163                local index = getindex(current)
164                if index then
165                    lb_found    = current
166                    lb_index    = index
167                    lb_progress = rangedimensions(box,list,current)
168                    action(current)
169                end
170            end
171        end
172    end
173
174    local function processrightbox(box)
175        local list = getlist(box)
176        for current, id, subtype in traverse_list, list do
177            if subtype == local_box_code and getwidth(current) == 0 then
178                local index = getindex(current)
179                if index then
180                    lb_found    = current
181                    lb_index    = index
182                    lb_progress = rangedimensions(box,list,current)
183                    action(current)
184                end
185            end
186        end
187    end
188
189    local function processmiddlebox(box,line)
190        local list = getlist(box)
191        for current, id, subtype in traverse_list, list do
192            if subtype == local_box_code and getwidth(current) == 0 then
193                local index = getindex(current)
194                if index then
195                    lb_found    = current
196                    lb_index    = index
197                    lb_progress = rangedimensions(line,getlist(line),box)
198                    action(current)
199                end
200            end
201        end
202    end
203
204    local function processlocalboxes(line,leftbox,rightbox,middlebox,linenumber,leftskip,rightskip,lefthang,righthang,indent,parinitleftskip,parinitrightskip,parfillleftskip,parfillrightskip,overshoot)
205        --
206        lb_found            = nil
207        lb_index            = 0
208        lb_linenumber       = linenumber
209        lb_progress         = 0
210        lb_leftskip         = leftskip
211        lb_rightskip        = rightskip
212        lb_lefthang         = lefthang
213        lb_righthang        = righthang
214        lb_indent           = indent
215        lb_parfillleftskip  = parfillleftskip
216        lb_parfillrightskip = parfillrightskip
217        lb_parinitleftskip  = parinitleftskip
218        lb_parinitrightskip = parinitrightskip
219        lb_overshoot        = overshoot
220        lb_linewidth        = getwidth(line)
221        -- only true for some cases
222        lb_leftoffset       = leftskip  + lefthang  + parfillleftskip  + parinitleftskip  + indent
223        lb_rightoffset      = rightskip + righthang + parfillrightskip + parinitrightskip - overshoot
224        --
225        if leftbox then
226            lb_localwidth = getwidth(leftbox)
227            processleftbox(leftbox)
228        end
229        if middlebox then
230            lb_localwidth = getwidth(middlebox)
231            processmiddlebox(middlebox,line)
232        end
233        if rightbox then
234            lb_localwidth = getwidth(rightbox)
235            processrightbox(rightbox)
236        end
237    end
238
239    typesetters.localboxes.handler = processlocalboxes
240
241end
242
243local localboxactions = nodes.tasks.actions("localboxes")
244
245function builders.local_box_filter(...)
246    starttiming(builders)
247    localboxactions(...)
248    stoptiming(builders)
249end
250
251callbacks.register("local_box_filter", builders.local_box_filter, "process local boxes")
252