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 boundary_code = nodes.nodecodes.boundary local hlist_code = nodes.nodecodes.hlist local kern_code = nodes.nodecodes.kern local penalty_code = nodes.nodecodes.penalty local glue_code = nodes.nodecodes.glue local line_code = nodes.listcodes.line local ghost_code = nodes.listcodes.ghost local middle_code = nodes.listcodes.middle local leftskip_code = nodes.gluecodes.leftskip local rightskip_code = nodes.gluecodes.rightskip 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 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 = node.direct.getnormalizedline local getbox = nuts.getbox local setoffsets = nuts.setoffsets local addxoffset = nuts.addxoffset 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 newkern = nuts.pool.kern 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 c_strc_math_n_of_lines = texiscount("c_strc_math_n_of_lines") local d_strc_math_max_width = texisdimen("d_strc_math_max_width") local d_strc_math_first_width = texisdimen("d_strc_math_first_width") local d_strc_math_last_width = texisdimen("d_strc_math_last_width") 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 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 d = distance + getdimensions(p,n) f[2] = d f[3] = p if d > max then max = d end break end p = getprev(p) end end -- we use a hangindent so we need to treat the first one local f = found[1] local delta = f[2] - max if delta ~= 0 then insertafter(head,moveon(head),newkern(-delta)) end for i=2,#found do local f = found[i] insertafter(head,moveon(f[3]),newkern(-f[2])) -- check head 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 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 maxwidth = false local cnt = 0 local lastwidth = 0 local firstheight = 0 local lastdepth = 0 for n, id, subtype, list in nextlist, head do if subtype == line_code then local t = getnormalizedline(n) local m = t.rightskip + t.parfillrightskip 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 if stage == 2 and (align == 2 or align == 3) then for n, id, subtype, list in nextlist, head do if subtype == line_code then if align == 1 then -- flushleft -- todo elseif align == 2 then -- middle reposition(n,maxwidth/2) elseif align == 3 then -- flushright reposition(n,maxwidth) end end end end texsetcount("global",c_strc_math_n_of_lines,cnt) texsetdimen("global",d_strc_math_max_width,maxwidth) texsetdimen("global",d_strc_math_first_width,firstwidth) texsetdimen("global",d_strc_math_last_width,lastwidth) texsetdimen("global",d_strc_math_first_height,firstheight) texsetdimen("global",d_strc_math_last_depth,lastdepth) 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" }, } }, 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 }