1if not modules then modules = { } end modules ['math-spa'] = {
2 version = 1.001,
3 comment = "companion to math-ini.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
10
11local setmetatableindex = table.setmetatableindex
12
13local nodecodes = nodes.nodecodes
14local listcodes = nodes.listcodes
15local boundary_code = nodecodes.boundary
16local hlist_code = nodecodes.hlist
17local vlist_code = nodecodes.vlist
18local kern_code = nodecodes.kern
19local penalty_code = nodecodes.penalty
20local glue_code = nodecodes.glue
21local line_code = listcodes.line
22local ghost_code = listcodes.ghost
23local middle_code = listcodes.middle
24local wrapped_code = listcodes.wrapped
25local mathpack_code = listcodes.mathpack
26local construct_code = listcodes.construct
27local alignment_code = listcodes.alignment
28local row_code = listcodes.row
29local fence_code = listcodes.fence
30
31local nuts = nodes.nuts
32local tonut = nodes.tonut
33local tonode = nodes.tonode
34
35local getid = nuts.getid
36local getsubtype = nuts.getsubtype
37local getnext = nuts.getnext
38local getprev = nuts.getprev
39local getattr = nuts.getattr
40local getprop = nuts.getprop
41local getwidth = nuts.getwidth
42local getdata = nuts.getdata
43local getdepth = nuts.getdepth
44local getheight = nuts.getheight
45local getlist = nuts.getlist
46local setglue = nuts.setglue
47local setwhd = nuts.setwhd
48local getdimensions = nuts.dimensions
49local getnormalizedline = nuts.getnormalizedline
50local getbox = nuts.getbox
51local setoffsets = nuts.setoffsets
52local addxoffset = nuts.addxoffset
53local setattrlist = nuts.setattrlist
54local rangedimensions = nuts.rangedimensions
55
56local nextglue = nuts.traversers.glue
57local nextlist = nuts.traversers.list
58local nextboundary = nuts.traversers.boundary
59local nextnode = nuts.traversers.node
60
61local insertafter = nuts.insertafter
62local insertbefore = nuts.insertbefore
63local newkern = nuts.pool.kern
64local newstrutrule = nuts.pool.strutrule
65
66local texsetdimen = tex.setdimen
67local texgetdimen = tex.getdimen
68local texsetcount = tex.setcount
69local texisdimen = tex.isdimen
70local texiscount = tex.iscount
71
72local boundary = tex.boundaries.system("mathalign")
73local stages = { }
74local initial = { }
75local a_location = attributes.system("mathnumberlocation")
76
77local c_strc_math_n_of_lines = texiscount("c_strc_math_n_of_lines")
78local d_strc_math_max_right = texisdimen("d_strc_math_max_right")
79local d_strc_math_first_right = texisdimen("d_strc_math_first_right")
80local d_strc_math_last_right = texisdimen("d_strc_math_last_right")
81local d_strc_math_max_left = texisdimen("d_strc_math_max_left")
82local d_strc_math_first_left = texisdimen("d_strc_math_first_left")
83local d_strc_math_last_left = texisdimen("d_strc_math_last_left")
84local d_strc_math_first_height = texisdimen("d_strc_math_first_height")
85local d_strc_math_last_depth = texisdimen("d_strc_math_last_depth")
86local d_strc_math_indent = texisdimen("d_strc_math_indent")
87
88local report = logs.reporter("mathalign")
89
90local trace = false trackers.register("math.align",function(v) trace = v end )
91
92local function moveon(s)
93 for n, id, subtype in nextnode, getnext(s) do
94 s = n
95 if id == kern_code then
96
97 elseif id == glue_code then
98
99 elseif id == penalty_code then
100
101 elseif id == hlist_code and subtype == ghost_code then
102
103 else
104 break
105 end
106 end
107 return s
108end
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124local getpenalty = nuts.getpenalty
125
126stages[1] = function(specification,stage)
127 local box = getbox(specification.box)
128 local head = getlist(box)
129 local align = specification.alignstate
130 local distance = specification.distance
131 local found = { }
132 local max = 0
133 for s in nextboundary, head do
134 local data = getdata(s)
135 if data == boundary then
136 s = moveon(s)
137 found[#found+1] = { s, 0, head }
138 end
139 end
140 if #found > 0 then
141 if found[1] then
142 max = distance + getdimensions(head,found[1][1])
143 found[1][2] = max
144 end
145 for i=2,#found do
146 local f = found[i]
147 local n = f[1]
148 local p = n
149 while p do
150 if getid(p) == penalty_code and getpenalty(p) == -10000 then
151 local w = getdimensions(p,n)
152 local d = distance + w
153 f[2] = d
154 f[3] = p
155 if d > max then
156 max = d
157 end
158 break
159 end
160 p = getprev(p)
161 end
162 end
163 for i=1,#found do
164 local f = found[i]
165 local w = f[2]
166 local d = i == 1 and (max-w) or -w
167 local k = newkern(d)
168 local r = newstrutrule(0,2*65536,2*65536)
169 local s = moveon(f[3])
170 if trace then
171 report("row %i, width %p, delta %p",i,w,d)
172 end
173 setattrlist(r,head)
174 setattrlist(k,head)
175 insertbefore(head,s,r)
176 insertafter(head,r,k)
177 end
178 end
179 texsetdimen("global",d_strc_math_indent,max)
180 if align == 2 then
181 for n in nextglue, head do
182 setglue(n,getwidth(n),0,0,0,0)
183 end
184 end
185end
186
187local function handlepacked(parent,plist,position)
188 if plist then
189 local current = plist
190
191
192
193 while current do
194 if getid(current) == hlist_code and getsubtype(current) == wrapped_code then
195 local packed = ((getattr(current,a_location) or 0) >> 8) & 0xF
196
197
198 if packed > 0 then
199
200
201
202 local linewidth, wrapwidth, leading, trailing
203 local wlist = getlist(current)
204 if wlist and getid(wlist) == hlist_code and getsubtype(wlist) == fence_code then
205 wlist = getlist(wlist)
206 end
207 for wrapped, id, subtype, list in nextlist, wlist do
208 if id == hlist_code and subtype == construct_code then
209 while getid(list) == vlist_code do
210 list = getlist(list)
211 end
212 for a, id, subtype, alist in nextlist, list do
213 if subtype == alignment_code then
214 for cell, id, subtype, clist in nextlist, alist do
215 for n, id, subtype in nextlist, clist do
216 local p = getprop(n,"mathalignshift")
217 if p == "right" then
218 if not leading then
219 linewidth = getwidth(parent)
220 wrapwidth = getwidth(current)
221 leading = rangedimensions(parent,plist,current)
222 end
223 local shift = rangedimensions(parent,getnext(wrapped))
224 addxoffset(n,shift + linewidth - wrapwidth - leading - position)
225 elseif p == "left" then
226 if not leading then
227 linewidth = getwidth(parent)
228 wrapwidth = getwidth(current)
229 leading = rangedimensions(parent,plist,current)
230 end
231 local shift = nuts.rangedimensions(parent,wlist,wrapped)
232 addxoffset(n,- shift - leading - position)
233 end
234 end
235 end
236 end
237 end
238 end
239 wrapped = getnext(wrapped)
240 end
241 end
242 end
243 current = getnext(current)
244 end
245 end
246end
247
248local function reposition(n,offset)
249
250
251 for n, id, subtype, list in nextlist, getlist(n) do
252 if subtype == middle_code then
253 addxoffset(n,-offset)
254 end
255 end
256
257
258 addxoffset(n,offset)
259end
260
261stages[2] = function(specification,stage)
262 local head = getlist(getbox(specification.box))
263 local align = specification.alignstate
264 local cnt = 0
265 local maxwidth = false
266 local firstwidth = 0
267 local lastwidth = 0
268 local maxright = false
269 local firstright = false
270 local lastright = false
271 local maxleft = false
272 local firstleft = false
273 local lastleft = false
274 local firstheight = 0
275 local lastdepth = 0
276 local linenumber = 0
277 local leftmargin = specification.leftmargin
278 local rightmargin = specification.rightmargin
279 if trace then
280 report("stage 2")
281 end
282 for n, id, subtype, list in nextlist, head do
283 if subtype == line_code then
284 local t = getnormalizedline(n)
285
286 local l = t.leftskip + t.lefthangskip + t.parinitleftskip + t.parfillleftskip + t.indent
287 local r = t.rightskip + t.righthangskip + t.parinitrightskip + t.parfillrightskip
288 local w = getwidth(n)
289 local m = r
290 linenumber = linenumber + 1
291 if trace then
292 report("line %i, width %p, left %p, right %p, used %p",linenumber,w,l,r,w-l-r)
293 end
294 if not maxleft or m > maxleft then
295 maxleft = l
296 end
297 if not maxright or m > maxright then
298 maxright = r
299 end
300 if not firstleft then
301 firstleft = l
302 end
303 if not firstright then
304 firstright = r
305 end
306 lastleft = l
307 lastright = r
308 if not maxwidth then
309 maxwidth = m
310 firstheight = getheight(n)
311 firstwidth = m
312 elseif m < maxwidth then
313 maxwidth = m
314 end
315 cnt = cnt + 1
316 lastwidth = m
317 lastdepth = getdepth(n)
318 end
319 end
320
321 local position = 0
322 if align == 1 then
323 if trace then
324 report("reposition %p, %s",0, "flush left")
325
326 end
327
328 elseif align == 2 then
329 position = (maxwidth-rightmargin)/2
330 if trace then
331 report("reposition %p, %s",position, "center")
332 end
333 elseif align == 3 then
334 position = maxwidth - rightmargin
335 if trace then
336 report("reposition %p, %s",maxwidth, "flush right")
337 end
338 end
339 if stage == 2 and position ~= 0 then
340 for n, id, subtype, list in nextlist, head do
341 reposition(n,position)
342 end
343 firstleft = firstleft + position
344 lastleft = lastleft + position
345 maxleft = maxleft + position
346 firstright = firstright - position
347 lastright = lastright - position
348 maxright = maxright - position
349 else
350 position = 0
351 end
352
353 for n, id, subtype, list in nextlist, head do
354 handlepacked(n,list,position)
355 end
356
357 texsetcount("global",c_strc_math_n_of_lines,cnt)
358 texsetdimen("global",d_strc_math_first_height,firstheight)
359 texsetdimen("global",d_strc_math_last_depth,lastdepth)
360 texsetdimen("global",d_strc_math_first_left,firstleft)
361 texsetdimen("global",d_strc_math_first_right,firstright)
362 texsetdimen("global",d_strc_math_last_left,lastleft)
363 texsetdimen("global",d_strc_math_last_right,lastright)
364 texsetdimen("global",d_strc_math_max_left,maxleft)
365 texsetdimen("global",d_strc_math_max_right,maxright)
366end
367
368stages[3] = stages[2]
369
370stages[4] = function(specification,stage)
371 local box = getbox(specification.box)
372 nuts.openup(specification,getlist(box))
373 local w, h, d = getdimensions(getlist(box),true)
374 setwhd(box,w,h,d)
375end
376
377interfaces.implement {
378 name = "handlemathhang",
379 arguments = {
380 {
381 { "stage", "integer" },
382
383 { "alignstate", "integer" },
384 { "box", "integer" },
385 { "distance", "dimension" },
386 { "inbetween", "dimension" },
387 { "height", "dimension" },
388 { "depth", "dimension" },
389 { "splitmethod" },
390 { "leftmargin", "dimension" },
391 { "rightmargin", "dimension" },
392 }
393 },
394 actions = function(specification)
395 local stage = specification.stage
396 if stage == 1 then
397 initial = specification
398 else
399 setmetatableindex(specification,initial)
400 end
401 if stage > 0 and stage <= #stages then
402 stages[stage](specification,stage)
403 end
404 end
405}
406 |