if not modules then modules = { } end modules ['math-spa'] = { version = 1.001, comment = "companion to math-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- for the moment (when testing) we use a penalty 1 local setmetatableindex = table.setmetatableindex local nodecodes = nodes.nodecodes local listcodes = nodes.listcodes local boundary_code = nodecodes.boundary local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local kern_code = nodecodes.kern local penalty_code = nodecodes.penalty local glue_code = nodecodes.glue local line_code = listcodes.line local ghost_code = listcodes.ghost local middle_code = listcodes.middle local wrapped_code = listcodes.wrapped local mathpack_code = listcodes.mathpack local construct_code = listcodes.construct local alignment_code = listcodes.alignment local row_code = listcodes.row local fence_code = listcodes.fence local nuts = nodes.nuts local tonut = nodes.tonut local tonode = nodes.tonode local getid = nuts.getid local getsubtype = nuts.getsubtype local getnext = nuts.getnext local getprev = nuts.getprev local getattr = nuts.getattr local getprop = nuts.getprop local getwidth = nuts.getwidth local getdata = nuts.getdata local getdepth = nuts.getdepth local getheight = nuts.getheight local getlist = nuts.getlist local setglue = nuts.setglue local setwhd = nuts.setwhd local getdimensions = nuts.dimensions local getnormalizedline = nuts.getnormalizedline local getbox = nuts.getbox local setoffsets = nuts.setoffsets local addxoffset = nuts.addxoffset local setattrlist = nuts.setattrlist local rangedimensions = nuts.rangedimensions local nextglue = nuts.traversers.glue local nextlist = nuts.traversers.list local nextboundary = nuts.traversers.boundary local nextnode = nuts.traversers.node local insertafter = nuts.insertafter local insertbefore = nuts.insertbefore local newkern = nuts.pool.kern local newstrutrule = nuts.pool.strutrule local texsetdimen = tex.setdimen local texgetdimen = tex.getdimen local texsetcount = tex.setcount local texisdimen = tex.isdimen local texiscount = tex.iscount local boundary = tex.boundaries.system("mathalign") local stages = { } local initial = { } local a_location = attributes.system("mathnumberlocation") local c_strc_math_n_of_lines = texiscount("c_strc_math_n_of_lines") local d_strc_math_max_right = texisdimen("d_strc_math_max_right") local d_strc_math_first_right = texisdimen("d_strc_math_first_right") local d_strc_math_last_right = texisdimen("d_strc_math_last_right") local d_strc_math_max_left = texisdimen("d_strc_math_max_left") local d_strc_math_first_left = texisdimen("d_strc_math_first_left") local d_strc_math_last_left = texisdimen("d_strc_math_last_left") local d_strc_math_first_height = texisdimen("d_strc_math_first_height") local d_strc_math_last_depth = texisdimen("d_strc_math_last_depth") local d_strc_math_indent = texisdimen("d_strc_math_indent") local report = logs.reporter("mathalign") local trace = false trackers.register("math.align",function(v) trace = v end ) local function moveon(s) for n, id, subtype in nextnode, getnext(s) do s = n if id == kern_code then -- move on (s_2 case) elseif id == glue_code then -- move on elseif id == penalty_code then -- move on (untested) elseif id == hlist_code and subtype == ghost_code then -- move on else break end end return s end -- -- todo: skip over ghost, maybe penalty, maybe glues all in one loop -- -- local n = getnext(s) -- if n and getid(n) == kern_code then -- also needed -- n = getnext(n) -- end -- while n and getid(n) == hlist_code and getsubtype(n) == ghost_code do -- n = getnext(n) -- end -- while n and getid(n) == glue_code do -- if n and getid(n) == glue_code then -- n = getnext(n) -- end local getpenalty = nuts.getpenalty stages[1] = function(specification,stage) local box = getbox(specification.box) local head = getlist(box) local align = specification.alignstate local distance = specification.distance local found = { } local max = 0 for s in nextboundary, head do local data = getdata(s) if data == boundary then s = moveon(s) found[#found+1] = { s, 0, head } end end if #found > 0 then if found[1] then max = distance + getdimensions(head,found[1][1]) found[1][2] = max end for i=2,#found do local f = found[i] local n = f[1] local p = n while p do if getid(p) == penalty_code and getpenalty(p) == -10000 then local w = getdimensions(p,n) local d = distance + w f[2] = d f[3] = p if d > max then max = d end break end p = getprev(p) end end for i=1,#found do local f = found[i] local w = f[2] local d = i == 1 and (max-w) or -w local k = newkern(d) local r = newstrutrule(0,2*65536,2*65536) -- hm, this adds height and depth ! local s = moveon(f[3]) if trace then report("row %i, width %p, delta %p",i,w,d) end setattrlist(r,head) setattrlist(k,head) insertbefore(head,s,r) insertafter(head,r,k) end end texsetdimen("global",d_strc_math_indent,max) if align == 2 then for n in nextglue, head do setglue(n,getwidth(n),0,0,0,0) end end end local function handlepacked(parent,plist,position) if plist then local current = plist -- print("") -- nuts.show(current) -- print("") while current do if getid(current) == hlist_code and getsubtype(current) == wrapped_code then local packed = ((getattr(current,a_location) or 0) >> 8) & 0xF -- print(packed,a_location,getattr(current,a_location)) -- packed = 1 if packed > 0 then -- print("") -- nuts.show(current) -- print("") local linewidth, wrapwidth, leading, trailing local wlist = getlist(current) if wlist and getid(wlist) == hlist_code and getsubtype(wlist) == fence_code then wlist = getlist(wlist) -- left=none demands this end for wrapped, id, subtype, list in nextlist, wlist do if id == hlist_code and subtype == construct_code then -- mathpack_code while getid(list) == vlist_code do list = getlist(list) end for a, id, subtype, alist in nextlist, list do if subtype == alignment_code then for cell, id, subtype, clist in nextlist, alist do for n, id, subtype in nextlist, clist do local p = getprop(n,"mathalignshift") if p == "right" then if not leading then linewidth = getwidth(parent) wrapwidth = getwidth(current) leading = rangedimensions(parent,plist,current) end local shift = rangedimensions(parent,getnext(wrapped)) addxoffset(n,shift + linewidth - wrapwidth - leading - position) elseif p == "left" then if not leading then linewidth = getwidth(parent) wrapwidth = getwidth(current) leading = rangedimensions(parent,plist,current) end local shift = nuts.rangedimensions(parent,wlist,wrapped) addxoffset(n,- shift - leading - position) end end end end end end wrapped = getnext(wrapped) end end end current = getnext(current) end end end local function reposition(n,offset) -- We need to relocate the local boxes that we use to push something left or -- right ... quite horrible and one needs a bit twisted mindset for this. for n, id, subtype, list in nextlist, getlist(n) do if subtype == middle_code then addxoffset(n,-offset) end end -- this is tricky ... see line numbering so it might become a real shift -- inside the box: setprops(n,"repositioned",true) addxoffset(n,offset) end stages[2] = function(specification,stage) local head = getlist(getbox(specification.box)) local align = specification.alignstate local cnt = 0 local maxwidth = false local firstwidth = 0 local lastwidth = 0 local maxright = false local firstright = false local lastright = false local maxleft = false local firstleft = false local lastleft = false local firstheight = 0 local lastdepth = 0 local linenumber = 0 local leftmargin = specification.leftmargin local rightmargin = specification.rightmargin if trace then report("stage 2") end for n, id, subtype, list in nextlist, head do if subtype == line_code then local t = getnormalizedline(n) -- local m = t.rightskip + t.parfillrightskip local l = t.leftskip + t.lefthangskip + t.parinitleftskip + t.parfillleftskip + t.indent local r = t.rightskip + t.righthangskip + t.parinitrightskip + t.parfillrightskip local w = getwidth(n) local m = r linenumber = linenumber + 1 if trace then report("line %i, width %p, left %p, right %p, used %p",linenumber,w,l,r,w-l-r) end if not maxleft or m > maxleft then maxleft = l end if not maxright or m > maxright then maxright = r end if not firstleft then firstleft = l end if not firstright then firstright = r end lastleft = l lastright = r if not maxwidth then maxwidth = m firstheight = getheight(n) firstwidth = m elseif m < maxwidth then maxwidth = m end cnt = cnt + 1 lastwidth = m lastdepth = getdepth(n) end end local position = 0 if align == 1 then -- flushleft if trace then report("reposition %p, %s",0, "flush left") -- todo end -- position = leftmargin elseif align == 2 then -- middle position = (maxwidth-rightmargin)/2 if trace then report("reposition %p, %s",position, "center") end elseif align == 3 then -- flushright position = maxwidth - rightmargin -- a nasty interplay with the tex end (TODO !!!) if trace then report("reposition %p, %s",maxwidth, "flush right") end end if stage == 2 and position ~= 0 then for n, id, subtype, list in nextlist, head do reposition(n,position) end firstleft = firstleft + position lastleft = lastleft + position maxleft = maxleft + position firstright = firstright - position lastright = lastright - position maxright = maxright - position else position = 0 end for n, id, subtype, list in nextlist, head do handlepacked(n,list,position) end texsetcount("global",c_strc_math_n_of_lines,cnt) texsetdimen("global",d_strc_math_first_height,firstheight) texsetdimen("global",d_strc_math_last_depth,lastdepth) texsetdimen("global",d_strc_math_first_left,firstleft) texsetdimen("global",d_strc_math_first_right,firstright) texsetdimen("global",d_strc_math_last_left,lastleft) texsetdimen("global",d_strc_math_last_right,lastright) texsetdimen("global",d_strc_math_max_left,maxleft) texsetdimen("global",d_strc_math_max_right,maxright) end stages[3] = stages[2] stages[4] = function(specification,stage) local box = getbox(specification.box) nuts.openup(specification,getlist(box)) local w, h, d = getdimensions(getlist(box),true) -- vertical setwhd(box,w,h,d) end interfaces.implement { name = "handlemathhang", arguments = { { { "stage", "integer" }, -- { "method" }, { "alignstate", "integer" }, { "box", "integer" }, { "distance", "dimension" }, { "inbetween", "dimension" }, { "height", "dimension" }, { "depth", "dimension" }, { "splitmethod" }, { "leftmargin", "dimension" }, { "rightmargin", "dimension" }, } }, actions = function(specification) local stage = specification.stage if stage == 1 then initial = specification else setmetatableindex(specification,initial) end if stage > 0 and stage <= #stages then stages[stage](specification,stage) end end }