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