if not modules then modules = { } end modules ['driv-shp'] = { version = 1.001, optimize = true, comment = "companion to driv-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local nuts = nodes.nuts local tonut = nodes.tonut local getdirection = nuts.getdirection local getlist = nuts.getlist local getoffsets = nuts.getoffsets local getorientation = nuts.getorientation local getanchors = nuts.getanchors local getgeometry = nuts.getgeometry local getwhd = nuts.getwhd local getkern = nuts.getkern local getwidth = nuts.getwidth local getheight = nuts.getheight local getdepth = nuts.getdepth local getnext = nuts.getnext local getid = nuts.getid local getshift = nuts.getshift local getprop = nuts.getprop local getreplace = nuts.getreplace local setreplace = nuts.setreplace ----- getkerndimension = nuts.getkerndimension ----- getglyphdimensions = nuts.getglyphdimensions local setdirection = nuts.setdirection local setlink = nuts.setlink local nextnode = nuts.traversers.node local effectiveglue = nuts.effectiveglue local dirdimensions = nuts.dirdimensions local nodecodes = nodes.nodecodes local whatsitcodes = nodes.whatsitcodes local directioncodes = tex.directioncodes local lefttoright_code = directioncodes.lefttoright local righttoleft_code = directioncodes.righttoleft local glyph_code = nodecodes.glyph local kern_code = nodecodes.kern local glue_code = nodecodes.glue local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local dir_code = nodecodes.dir local disc_code = nodecodes.disc local math_code = nodecodes.math local rule_code = nodecodes.rule local whatsit_code = nodecodes.whatsit local userdefined_code = nodes.whatsitcodes.userdefined local drivers = drivers local report = logs.reporter("drivers") local magicconstants = tex.magicconstants local maxdimen = magicconstants.maxdimen local pos_h = 0 local pos_v = 0 local pos_r = lefttoright_code local applyorientation = drivers.applyorientation local applyanchor = drivers.applyanchor local initialize local finalize local userdefined local function reset_state() pos_h = 0 pos_v = 0 pos_r = lefttoright_code end local dirstack = { } local function reset_dir_stack() dirstack = { } end local function handlewhatsit(current,pos_h,pos_v) local action = userdefined[getprop(current,"id")] if action then action(current,pos_h,pos_v) end end local hlist_out, vlist_out -- todo: some can be combined hlist_out = function(this_box,current) local ref_h = pos_h local ref_v = pos_v local ref_r = pos_r pos_r = getdirection(this_box) local cur_h = 0 local cur_b for current, id, subtype in nextnode, current do if id == glyph_code then -- or id == kern_code local width, factor = getwidth(current,true) if width ~= 0 then if factor ~= 0 then cur_h = cur_h + (1.0 + factor/1000000.0) * width else cur_h = cur_h + width end end elseif id == glue_code then -- cur_h = cur_h + effectiveglue(current,this_box) cur_h = cur_h + effectiveglue(current,this_box,true) elseif id == hlist_code or id == vlist_code then local width, height, depth = getwhd(current) local list = getlist(current) if list then local boxdir = getdirection(current) or lefttoright_code local shift = getshift(current) local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true) local anchor, source, target, targetdata, s_anchor, t_anchor local anc_h, anc_v local usedorientation = false if hasanchor then anchor, source, target, s_anchor, t_anchor = getanchors(current) end if hasorientation then local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current) local orientation, basepoint_h, basepoint_v = applyorientation(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset) if orientation == 1 then basepoint_h = basepoint_h + doffset if boxdir == pos_r then basepoint_v = basepoint_v - height end usedorientation = orientation elseif orientation == 2 then if boxdir == pos_r then basepoint_h = basepoint_h + width end usedorientation = orientation elseif orientation == 3 then basepoint_h = basepoint_h + hoffset if boxdir ~= pos_r then basepoint_v = basepoint_v - height end usedorientation = orientation end if target then targetdata = anchors[target] if targetdata then anc_h = basepoint_h anc_v = - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end -- pos_v = ref_v - (cur_v + basepoint_v) pos_v = ref_v - basepoint_v elseif hasoffset then local orientation, xoffset, yoffset = getorientation(current) local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if target then targetdata = anchors[target] if targetdata then anc_h = xoffset + basepoint_h anc_v = yoffset - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h + xoffset) else pos_h = ref_h + (cur_h + basepoint_h + xoffset) end pos_v = ref_v - (basepoint_v - yoffset) elseif hasanchor then local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if target then targetdata = anchors[target] if targetdata then anc_h = basepoint_h anc_v = - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end pos_v = ref_v - basepoint_v else local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end pos_v = ref_v - basepoint_v end goto process ::posdone:: if pos_r == righttoleft_code then pos_h = targetdata[1] - anc_h else pos_h = targetdata[1] + anc_h end pos_v = targetdata[2] + anc_v if anchor and anchor > 0 then pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5]) pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth) end ::process:: if source then local anchor_h = pos_h local anchor_v = pos_v if usedorientation then if usedorientation == 1 then anchor_v = anchor_v - (width - height) elseif usedorientation == 2 then anchor_v = anchor_v - (depth - height) elseif usedorientation == 3 then -- weird anchor_v = anchor_v + (height - width) end end anchors[source] = { anchor_h, anchor_v, width, height, depth } end -- if usedorientation then -- pushorientation(usedorientation,pos_h,pos_v,pos_r) -- end if id == vlist_code then vlist_out(current,list) else hlist_out(current,list) end -- if usedorientation then -- poporientation(usedorientation,pos_h,pos_v,pos_r) -- end end cur_h = cur_h + width elseif id == kern_code then local kern, factor = getkern(current,true) if kern ~= 0 then if factor ~= 0 then cur_h = cur_h + (1.0 + factor/1000000.0) * kern else cur_h = cur_h + kern end end elseif id == rule_code then cur_h = cur_h + getwidth(current) elseif id == math_code then -- cur_h = cur_h + effectiveglue(current,this_box) cur_h = cur_h + effectiveglue(current,this_box,true) elseif id == dir_code then local dir, cancel = getdirection(current) if cancel then local ds = dirstack[current] if ds then ref_h = ds.ref_h ref_v = ds.ref_v cur_h = ds.cur_h end pos_r = dir else local width, enddir = dirdimensions(this_box,current) local new_h = cur_h + width if dir ~= pos_r then cur_h = new_h end if enddir ~= current then dirstack[enddir] = { cur_h = new_h, ref_h = ref_h, ref_v = ref_v, } setdirection(enddir,pos_r) end if pos_r == righttoleft_code then pos_h = ref_h - cur_h else pos_h = ref_h + cur_h end pos_v = ref_v ref_h = pos_h ref_v = pos_v cur_h = 0 pos_r = dir goto synced end elseif id == whatsit_code then if subtype == userdefined_code then local action = userdefined[getprop(current,"id")] if action then if not cur_b then local wd, ht, dp = getwhd(this_box) cur_b = { wd, ht, dp, ref_h, ref_v, ref_r } end action(current, pos_h,pos_v, cur_b) end end elseif id == disc_code then local replace, tail = getreplace(current) if replace then setlink(tail,getnext(current)) setlink(current,replace) setreplace(current) end else goto synced end if pos_r == righttoleft_code then pos_h = ref_h - cur_h else pos_h = ref_h + cur_h end pos_v = ref_v ::synced:: end pos_h = ref_h pos_v = ref_v pos_r = ref_r end vlist_out = function(this_box,current) local ref_h = pos_h local ref_v = pos_v local ref_r = pos_r pos_r = getdirection(this_box) local cur_v = - getheight(this_box) local top_edge = cur_v pos_h = ref_h pos_v = ref_v - cur_v local cur_b for current, id, subtype in nextnode, current do if id == glue_code then -- cur_v = cur_v + effectiveglue(current,this_box) cur_v = cur_v + effectiveglue(current,this_box,true) elseif id == hlist_code or id == vlist_code then local width, height, depth = getwhd(current) local list = getlist(current) if list then local boxdir = getdirection(current) or lefttoright_code local shift = getshift(current) local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true) local anchor, source, target, targetdata, s_anchor, t_anchor local usedorientation = false if hasanchor then anchor, source, target, s_anchor, t_anchor = getanchors(current) end if hasorientation then local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current) local orientation, basepoint_h, basepoint_v = applyorientation(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset) if orientation == 1 then basepoint_h = basepoint_h + width - height -- hm basepoint_v = basepoint_v - height usedorientation = orientation elseif orientation == 2 then basepoint_h = basepoint_h + width basepoint_v = basepoint_v + depth - height usedorientation = orientation elseif orientation == 3 then -- weird basepoint_h = basepoint_h + height usedorientation = orientation end if target then targetdata = anchors[target] if targetdata then if pos_r == righttoleft_code then pos_h = targetdata[1] - basepoint_h else pos_h = targetdata[1] + basepoint_h end pos_v = targetdata[2] - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - basepoint_h else pos_h = ref_h + basepoint_h end pos_v = ref_v - (cur_v + basepoint_v) elseif hasoffset then local orientation, xoffset, yoffset = getorientation(current) -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if target then targetdata = anchors[target] if targetdata then if pos_r == righttoleft_code then pos_h = targetdata[1] - (shift + xoffset) else pos_h = targetdata[1] + (shift + xoffset) end pos_v = targetdata[2] - (height - yoffset) goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (shift + xoffset) else pos_h = ref_h + (shift + xoffset) end pos_v = ref_v - (cur_v + height - yoffset) elseif hasanchor then -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if target then local a = anchors[target] if a then if pos_r == righttoleft_code then pos_h = targetdata[1] - shift else pos_h = targetdata[1] + shift end pos_v = targetdata[2] - height goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - shift else pos_h = ref_h + shift end pos_v = ref_v - (cur_v + height) else -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if pos_r == righttoleft_code then pos_h = ref_h - shift else pos_h = ref_h + shift end pos_v = ref_v - (cur_v + height) end goto process ::posdone:: if anchor and anchor > 0 then pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5]) pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth) end ::process:: if source then -- move this into apply_anchor local anchor_h = pos_h local anchor_v = pos_v if usedorientation then if usedorientation == 1 then anchor_v = anchor_v - (width - height) elseif usedorientation == 2 then anchor_v = anchor_v - (depth - height) elseif usedorientation == 3 then -- weird anchor_v = anchor_v + (height - width) end end anchors[source] = { anchor_h, anchor_v, width, height, depth } end -- if usedorientation then -- pushorientation(usedorientation,pos_h,pos_v,pos_r) -- end -- if source then -- flushstored(current,source,true) -- end if id == vlist_code then vlist_out(current,list) else hlist_out(current,list) end -- if source then -- flushstored(current,source,false) -- end -- if usedorientation then -- poporientation(usedorientation,pos_h,pos_v,pos_r) -- end end cur_v = cur_v + height + depth elseif id == kern_code then cur_v = cur_v + getkern(current) elseif id == rule_code then local width, height, depth = getwhd(current) cur_v = cur_v + height + depth elseif id == whatsit_code then if subtype == userdefined_code then local action = userdefined[getprop(current,"id")] if action then if not cur_b then local wd, ht, dp = getwhd(this_box) cur_b = { wd, ht, dp, ref_h, ref_v, ref_r } end action(current,pos_h,pos_v) end end else goto synced end pos_h = ref_h ::synced:: end pos_h = ref_h pos_v = ref_v pos_r = ref_r end function drivers.converters.analyze(driver,box) if not driver then report("error in converter, no driver") return elseif box then box = tonut(box) else report("error in converter, no box") return end local width, height, depth = getwhd(box) local total = height + depth if height > maxdimen or depth > maxdimen or width > maxdimen or total > maxdimen then report("error in converter, overflow") return end local actions = driver.actions local flushers = driver.flushers initialize = actions.initialize finalize = actions.finalize userdefined = flushers.userdefined reset_dir_stack() reset_state() pos_r = getdirection(box) pos_v = depth pos_h = pos_r == righttoleft_code and width or 0 local details = { boundingbox = { 0, 0, width, total }, } if initialize then initialize(driver,details) end if getid(box) == vlist_code then vlist_out(box,getlist(box)) else hlist_out(box,getlist(box)) end if finalize then finalize(driver,details) end end