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