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 ----- leftskip_code = nodes.gluecodes.leftskip ----- 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 = nuts.getnormalizedline local getbox = nuts.getbox local setoffsets = nuts.setoffsets local addxoffset = nuts.addxoffset local setattrlist = nuts.setattrlist 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 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 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 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 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 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 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 }