pack-rul.lua /size: 8671 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['pack-rul'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to pack-rul.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10-- we need to be careful with display math as it uses shifts
11
12-- \framed[align={lohi,middle}]{$x$}
13-- \framed[align={lohi,middle}]{$ $}
14-- \framed[align={lohi,middle}]{\hbox{ }}
15-- \framed[align={lohi,middle}]{\hbox{}}
16-- \framed[align={lohi,middle}]{$\hskip2pt$}
17
18local type = type
19
20local context           = context
21
22local nodecodes         = nodes.nodecodes
23local listcodes         = nodes.listcodes
24
25local hlist_code        = nodecodes.hlist
26local vlist_code        = nodecodes.vlist
27
28local boxlist_code      = listcodes.box
29local linelist_code     = listcodes.line
30local equationlist_code = listcodes.equation
31
32local texsetdimen       = tex.setdimen
33local texsetcount       = tex.setcount
34
35local implement         = interfaces.implement
36
37local nuts              = nodes.nuts
38
39local getnext           = nuts.getnext
40local getprev           = nuts.getprev
41local getlist           = nuts.getlist
42local setlist           = nuts.setlist
43local getwhd            = nuts.getwhd
44local getid             = nuts.getid
45local getsubtype        = nuts.getsubtype
46local getbox            = nuts.getbox
47local getdirection      = nuts.getdirection
48local setshift          = nuts.setshift
49local setwidth          = nuts.setwidth
50local getwidth          = nuts.getwidth
51local setboxglue        = nuts.setboxglue
52local getboxglue        = nuts.getboxglue
53
54local hpack             = nuts.hpack
55local getdimensions     = nuts.dimensions
56local flushnode         = nuts.flush
57
58local traversers        = nuts.traversers
59local nexthlist         = traversers.hlist
60local nextvlist         = traversers.vlist
61local nextlist          = traversers.list
62
63local checkformath      = false
64
65directives.register("framed.checkmath",function(v) checkformath = v end) -- experiment
66
67-- beware: dir nodes and pseudostruts can end up on lines of their own
68
69local function doreshapeframedbox(n)
70    local box            = getbox(n)
71    local noflines       = 0
72    local nofnonzero     = 0
73    local firstheight    = nil
74    local lastdepth      = nil
75    local lastlinelength = 0
76    local minwidth       = 0
77    local maxwidth       = 0
78    local totalwidth     = 0
79    local averagewidth   = 0
80    local boxwidth       = getwidth(box)
81    if boxwidth ~= 0 then -- and h.subtype == vlist_code
82        local list = getlist(box)
83        if list then
84            local hdone = false
85            for n, id, subtype, list in nextlist, list do -- no dir etc needed
86                local width, height, depth = getwhd(n)
87                if not firstheight then
88                    firstheight = height
89                end
90                lastdepth = depth
91                noflines  = noflines + 1
92                if list then
93                    if id == hlist_code then
94                        if subtype == boxlist_code or subtype == linelist_code then
95                            lastlinelength = getdimensions(list)
96                        else
97                            lastlinelength = width
98                        end
99                        hdone = true
100                    else
101                        lastlinelength = width
102                     -- vdone = true
103                    end
104                    if lastlinelength > maxwidth then
105                        maxwidth = lastlinelength
106                    end
107                    if lastlinelength < minwidth or minwidth == 0 then
108                        minwidth = lastlinelength
109                    end
110                    if lastlinelength > 0 then
111                        nofnonzero = nofnonzero + 1
112                    end
113                    totalwidth = totalwidth + lastlinelength
114                end
115            end
116            if not firstheight then
117                -- done)
118            elseif maxwidth ~= 0 then
119                if hdone then
120                    for h, id, subtype, list in nextlist, list do
121                        if list and id == hlist_code then
122                            -- called a lot so maybe a simple case is needed
123                            if subtype == boxlist_code or subtype == linelist_code then
124                                -- getdirection is irrelevant here so it will go
125                                -- somehow a parfillskip also can get influenced
126                                local p = hpack(list,maxwidth,'exactly',getdirection(h)) -- multiple return value
127                                local set, order, sign = getboxglue(p)
128                                setboxglue(h,set,order,sign)
129                                setlist(p)
130                                flushnode(p)
131                            elseif checkformath and subtype == equationlist_code then
132                             -- display formulas use a shift
133                                if nofnonzero == 1 then
134                                    setshift(h,0)
135                                end
136                            end
137                            setwidth(h,maxwidth)
138                        end
139                    end
140                end
141             -- if vdone then
142             --     for v in nextvlist, list do
143             --         local width = getwidth(n)
144             --         if width > maxwidth then
145             --             setwidth(v,maxwidth)
146             --         end
147             --     end
148             -- end
149                setwidth(box,maxwidth)
150                averagewidth = noflines > 0 and totalwidth/noflines or 0
151            else -- e.g. empty math {$ $} or \hbox{} or ...
152                setwidth(box,0)
153            end
154        end
155    end
156    texsetcount("global","framednoflines",noflines)
157    texsetdimen("global","framedfirstheight",firstheight or 0) -- also signal
158    texsetdimen("global","framedlastdepth",lastdepth or 0)
159    texsetdimen("global","framedminwidth",minwidth)
160    texsetdimen("global","framedmaxwidth",maxwidth)
161    texsetdimen("global","framedaveragewidth",averagewidth)
162end
163
164local function doanalyzeframedbox(n)
165    local box         = getbox(n)
166    local noflines    = 0
167    local firstheight = nil
168    local lastdepth   = nil
169    if getwidth(box) ~= 0 then
170        local list = getlist(box)
171        if list then
172            for n in nexthlist, list do
173                local width, height, depth = getwhd(n)
174                if not firstheight then
175                    firstheight = height
176                end
177                lastdepth = depth
178                noflines  = noflines + 1
179            end
180            for n in nextvlist, list do
181                local width, height, depth = getwhd(n)
182                if not firstheight then
183                    firstheight = height
184                end
185                lastdepth = depth
186                noflines  = noflines + 1
187            end
188        end
189    end
190    texsetcount("global","framednoflines",noflines)
191    texsetdimen("global","framedfirstheight",firstheight or 0)
192    texsetdimen("global","framedlastdepth",lastdepth or 0)
193end
194
195implement { name = "doreshapeframedbox", actions = doreshapeframedbox, arguments = "integer" }
196implement { name = "doanalyzeframedbox", actions = doanalyzeframedbox, arguments = "integer" }
197
198local function maxboxwidth(box)
199    local boxwidth = getwidth(box)
200    if boxwidth == 0 then
201        return 0
202    end
203    local list = getlist(box)
204    if not list then
205        return 0
206    end
207    if getid(box) == hlist_code then
208        return boxwidth
209    end
210    local lastlinelength = 0
211    local maxwidth       = 0
212    for n, subtype in nexthlist, list do -- no dir etc needed
213        local l = getlist(n)
214        if l then
215            if subtype == boxlist_code or subtype == linelist_code then
216                lastlinelength = getdimensions(l)
217            else
218                lastlinelength = getwidth(n)
219            end
220            if lastlinelength > maxwidth then
221                maxwidth = lastlinelength
222            end
223        end
224    end
225    for n, subtype in nextvlist, list do -- no dir etc needed
226        local l = getlist(n)
227        if l then
228            lastlinelength = getwidth(n)
229            if lastlinelength > maxwidth then
230                maxwidth = lastlinelength
231            end
232        end
233    end
234    return maxwidth
235end
236
237nodes.maxboxwidth = maxboxwidth
238
239implement {
240    name      = "themaxboxwidth",
241    actions   = function(n) context("%rsp",maxboxwidth(getbox(n))) end, -- r = rounded
242    arguments = "integer"
243}
244