if not modules then modules = { } end modules ['anch-pos'] = { version = 1.001, comment = "companion to anch-pos.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- We save positional information in the main utility table. Not only can we store -- much more information in Lua but it's also more efficient. -- -- plus (extra) is obsolete but we will keep it for a while -- -- maybe replace texsp by our own converter (stay at the lua end) -- eventually mp will have large numbers so we can use sp there too -- -- this is one of the first modules using scanners and we need to replace it by -- implement and friends -- -- we could have namespaces, like p, page, region, columnarea, textarea but then -- we need virtual table accessors as well as have tag/id accessors ... we don't -- save much here (at least not now) -- -- This was the last module that got rid of directly setting scanners, with a little -- performance degradation but not that noticeable. local tostring, next, setmetatable, tonumber = tostring, next, setmetatable, tonumber local sort = table.sort local format, gmatch = string.format, string.gmatch local lpegmatch = lpeg.match local insert, remove = table.insert, table.remove local allocate = utilities.storage.allocate local report = logs.reporter("positions") local scanners = tokens.scanners local scanstring = scanners.string local scaninteger = scanners.integer local scandimen = scanners.dimen local implement = interfaces.implement local commands = commands local context = context local ctx_latelua = context.latelua local tex = tex local texgetdimen = tex.getdimen local texgetcount = tex.getcount local texgetinteger = tex.getintegervalue or tex.getcount local texsetcount = tex.setcount local texget = tex.get local texsp = tex.sp ----- texsp = string.todimen -- because we cache this is much faster but no rounding local setmetatableindex = table.setmetatableindex local setmetatablenewindex = table.setmetatablenewindex local nuts = nodes.nuts local setlink = nuts.setlink local getlist = nuts.getlist local setlist = nuts.setlist local getbox = nuts.getbox local getid = nuts.getid local getwhd = nuts.getwhd local hlist_code = nodes.nodecodes.hlist local find_tail = nuts.tail ----- hpack = nuts.hpack local new_latelua = nuts.pool.latelua local variables = interfaces.variables local v_text = variables.text local v_column = variables.column local pt = number.dimenfactors.pt local pts = number.pts local formatters = string.formatters local collected = allocate() local tobesaved = allocate() local jobpositions = { collected = collected, tobesaved = tobesaved, } job.positions = jobpositions local default = { -- not r and paragraphs etc __index = { x = 0, -- x position baseline y = 0, -- y position baseline w = 0, -- width h = 0, -- height d = 0, -- depth p = 0, -- page n = 0, -- paragraph ls = 0, -- leftskip rs = 0, -- rightskip hi = 0, -- hangindent ha = 0, -- hangafter hs = 0, -- hsize pi = 0, -- parindent ps = false, -- parshape dir = 0, } } local f_b_tag = formatters["b:%s"] local f_e_tag = formatters["e:%s"] local f_p_tag = formatters["p:%s"] local f_w_tag = formatters["w:%s"] local f_region = formatters["region:%s"] local f_tag_three = formatters["%s:%s:%s"] local f_tag_two = formatters["%s:%s"] local nofregular = 0 local nofspecial = 0 local splitter = lpeg.splitat(":",true) local pagedata = { } local columndata = setmetatableindex("table") -- per page local freedata = setmetatableindex("table") -- per page local function initializer() tobesaved = jobpositions.tobesaved collected = jobpositions.collected for tag, data in next, collected do local prefix, rest = lpegmatch(splitter,tag) if prefix == "p" then nofregular = nofregular + 1 elseif prefix == "page" then nofregular = nofregular + 1 pagedata[tonumber(rest) or 0] = data elseif prefix == "free" then nofspecial = nofspecial + 1 local t = freedata[data.p or 0] t[#t+1] = data elseif prefix == "columnarea" then columndata[data.p or 0][data.c or 0] = data end setmetatable(data,default) end -- local pages = structures.pages.collected if pages then local last = nil for p=1,#pages do local region = "page:" .. p local data = pagedata[p] local free = freedata[p] if free then sort(free,function(a,b) return b.y < a.y end) -- order matters ! end if data then last = data last.free = free elseif last then local t = setmetatableindex({ free = free, p = p },last) if not collected[region] then collected[region] = t else -- something is wrong end pagedata[p] = t end end end jobpositions.pagedata = pagedata end function jobpositions.used() return next(collected) -- we can safe it end function jobpositions.getfree(page) return freedata[page] end -- we can gain a little when we group positions but then we still have to -- deal with regions and cells so we either end up with lots of extra small -- tables pointing to them and/or assembling/disassembling so in the end -- it makes no sense to do it (now) and still have such a mix -- -- proof of concept code removed ... see archive local function finalizer() -- We make the (possible extensive) shape lists sparse working -- from the end. We could also drop entries here that have l and -- r the same which saves testing later on. for k, v in next, tobesaved do local s = v.s if s then for p, data in next, s do local n = #data if n > 1 then local ph = data[1][2] local pd = data[1][3] local xl = data[1][4] local xr = data[1][5] for i=2,n do local di = data[i] local h = di[2] local d = di[3] local l = di[4] local r = di[5] if r == xr then di[5] = nil if l == xl then di[4] = nil if d == pd then di[3] = nil if h == ph then di[2] = nil else ph = h end else pd, ph = d, h end else ph, pd, xl = h, d, l end else ph, pd, xl, xr = h, d, l, r end end end end end end end job.register('job.positions.collected', tobesaved, initializer, finalizer) local regions = { } local nofregions = 0 local region = nil local columns = { } local nofcolumns = 0 local column = nil local nofpages = nil -- beware ... we're not sparse here as lua will reserve slots for the nilled local getpos, gethpos, getvpos, getrpos function jobpositions.registerhandlers(t) getpos = t and t.getpos or function() return 0, 0 end getrpos = t and t.getrpos or function() return 0, 0, 0 end gethpos = t and t.gethpos or function() return 0 end getvpos = t and t.getvpos or function() return 0 end end function jobpositions.getpos () return getpos () end function jobpositions.getrpos() return getrpos() end function jobpositions.gethpos() return gethpos() end function jobpositions.getvpos() return getvpos() end -------- jobpositions.getcolumn() return column end jobpositions.registerhandlers() local function setall(name,p,x,y,w,h,d,extra) tobesaved[name] = { p = p, x = x ~= 0 and x or nil, y = y ~= 0 and y or nil, w = w ~= 0 and w or nil, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, e = extra ~= "" and extra or nil, r = region, c = column, r2l = texgetinteger("inlinelefttoright") == 1 and true or nil, } end local function enhance(data) if not data then return nil end if data.r == true then -- or "" data.r = region end if data.x == true then if data.y == true then local x, y = getpos() data.x = x ~= 0 and x or nil data.y = y ~= 0 and y or nil else local x = gethpos() data.x = x ~= 0 and x or nil end elseif data.y == true then local y = getvpos() data.y = y ~= 0 and y or nil end if data.p == true then data.p = texgetcount("realpageno") -- we should use a variable set in otr end if data.c == true then data.c = column end if data.w == 0 then data.w = nil end if data.h == 0 then data.h = nil end if data.d == 0 then data.d = nil end return data end -- analyze some files (with lots if margindata) and then when one key optionally -- use that one instead of a table (so, a 3rd / 4th argument: key, e.g. "x") local function set(name,index,value) -- ,key -- officially there should have been a settobesaved local data = enhance(value or {}) if value then container = tobesaved[name] if not container then tobesaved[name] = { [index] = data } else container[index] = data end else tobesaved[name] = data end end local function setspec(specification) local name = specification.name local index = specification.index local value = specification.value local data = enhance(value or {}) if value then container = tobesaved[name] if not container then tobesaved[name] = { [index] = data } else container[index] = data end else tobesaved[name] = data end end local function get(id,index) if index then local container = collected[id] return container and container[index] else return collected[id] end end ------------.setdim = setdim jobpositions.setall = setall jobpositions.set = set jobpositions.setspec = setspec jobpositions.get = get implement { name = "dosaveposition", arguments = { "string", "integer", "dimen", "dimen" }, actions = setall, -- name p x y } implement { name = "dosavepositionwhd", arguments = { "string", "integer", "dimen", "dimen", "dimen", "dimen", "dimen" }, actions = setall, -- name p x y w h d } implement { name = "dosavepositionplus", arguments = { "string", "integer", "dimen", "dimen", "dimen", "dimen", "dimen", "string" }, actions = setall, -- name p x y w h d extra } -- will become private table (could also become attribute driven but too nasty -- as attributes can bleed e.g. in margin stuff) -- not much gain in keeping stack (inc/dec instead of insert/remove) local function b_column(specification) local tag = specification.tag local x = gethpos() tobesaved[tag] = { r = true, x = x ~= 0 and x or nil, -- w = 0, } insert(columns,tag) column = tag end local function e_column() local t = tobesaved[column] if not t then -- something's wrong else local x = gethpos() - t.x t.w = x ~= 0 and x or nil t.r = region end remove(columns) column = columns[#columns] end jobpositions.b_column = b_column jobpositions.e_column = e_column implement { name = "bposcolumn", arguments = "string", actions = function(tag) insert(columns,tag) column = tag end } implement { name = "bposcolumnregistered", arguments = "string", actions = function(tag) insert(columns,tag) column = tag ctx_latelua { action = b_column, tag = tag } end } implement { name = "eposcolumn", actions = function() remove(columns) column = columns[#columns] end } implement { name = "eposcolumnregistered", actions = function() ctx_latelua { action = e_column } remove(columns) column = columns[#columns] end } -- regions local function b_region(specification) local tag = specification.tag or specification local last = tobesaved[tag] local x, y = getpos() last.x = x ~= 0 and x or nil last.y = y ~= 0 and y or nil last.p = texgetcount("realpageno") insert(regions,tag) -- todo: fast stack region = tag end local function e_region(specification) local last = tobesaved[region] local y = getvpos() local x, y = getpos() if specification.correct then local h = (last.y or 0) - y last.h = h ~= 0 and h or nil end last.y = y ~= 0 and y or nil remove(regions) -- todo: fast stack region = regions[#regions] end jobpositions.b_region = b_region jobpositions.e_region = e_region local lastregion local function setregionbox(n,tag,k,lo,ro,to,bo,column) -- kind if not tag or tag == "" then nofregions = nofregions + 1 tag = f_region(nofregions) end local box = getbox(n) local w, h, d = getwhd(box) tobesaved[tag] = { -- p = texgetcount("realpageno"), -- we copy them x = 0, y = 0, w = w ~= 0 and w or nil, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, k = k ~= 0 and k or nil, lo = lo ~= 0 and lo or nil, ro = ro ~= 0 and ro or nil, to = to ~= 0 and to or nil, bo = bo ~= 0 and bo or nil, c = column or nil, } lastregion = tag return tag, box end local function markregionbox(n,tag,correct,...) -- correct needs checking local tag, box = setregionbox(n,tag,...) -- todo: check if tostring is needed with formatter local push = new_latelua { action = b_region, tag = tag } local pop = new_latelua { action = e_region, correct = correct } -- maybe we should construct a hbox first (needs experimenting) so that we can avoid some at the tex end local head = getlist(box) -- no, this fails with \framed[region=...] .. needs thinking -- if getid(box) ~= hlist_code then -- -- report("mark region box assumes a hlist, fix this for %a",tag) -- head = hpack(head) -- end if head then local tail = find_tail(head) setlink(push,head) setlink(tail,pop) else -- we can have a simple push/pop setlink(push,pop) end setlist(box,push) end jobpositions.markregionbox = markregionbox jobpositions.setregionbox = setregionbox function jobpositions.enhance(name) enhance(tobesaved[name]) end function jobpositions.gettobesaved(name,tag) local t = tobesaved[name] if t and tag then return t[tag] else return t end end function jobpositions.settobesaved(name,tag,data) local t = tobesaved[name] if t and tag and data then t[tag] = data end end local nofparagraphs = 0 implement { name = "parpos", actions = function() nofparagraphs = nofparagraphs + 1 texsetcount("global","c_anch_positions_paragraph",nofparagraphs) local h = texgetdimen("strutht") local d = texgetdimen("strutdp") local t = { p = true, c = true, r = true, x = true, y = true, h = h, d = d, hs = texget("hsize"), -- never 0 } local leftskip = texget("leftskip",false) local rightskip = texget("rightskip",false) local hangindent = texget("hangindent") local hangafter = texget("hangafter") local parindent = texget("parindent") local parshape = texget("parshape") if leftskip ~= 0 then t.ls = leftskip end if rightskip ~= 0 then t.rs = rightskip end if hangindent ~= 0 then t.hi = hangindent end if hangafter ~= 1 and hangafter ~= 0 then -- can not be zero .. so it needs to be 1 if zero t.ha = hangafter end if parindent ~= 0 then t.pi = parindent end if parshape and #parshape > 0 then t.ps = parshape end local name = f_p_tag(nofparagraphs) tobesaved[name] = t ctx_latelua { action = enhance, specification = t } end } implement { name = "dosetposition", arguments = "string", actions = function(name) local spec = { p = true, c = column, r = true, x = true, y = true, n = nofparagraphs > 0 and nofparagraphs or nil, r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } implement { name = "dosetpositionwhd", arguments = { "string", "dimen", "dimen", "dimen" }, actions = function(name,w,h,d) local spec = { p = true, c = column, r = true, x = true, y = true, w = w ~= 0 and w or nil, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, n = nofparagraphs > 0 and nofparagraphs or nil, r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } implement { name = "dosetpositionbox", arguments = { "string", "integer" }, actions = function(name,n) local box = getbox(n) local w, h, d = getwhd(box) local spec = { p = true, c = column, r = true, x = true, y = true, w = w ~= 0 and w or nil, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, n = nofparagraphs > 0 and nofparagraphs or nil, r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } implement { name = "dosetpositionplus", arguments = { "string", "dimen", "dimen", "dimen" }, actions = function(name,w,h,d) local spec = { p = true, c = column, r = true, x = true, y = true, w = w ~= 0 and w or nil, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, n = nofparagraphs > 0 and nofparagraphs or nil, e = scanstring(), r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } implement { name = "dosetpositionstrut", arguments = "string", actions = function(name) local h = texgetdimen("strutht") local d = texgetdimen("strutdp") local spec = { p = true, c = column, r = true, x = true, y = true, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, n = nofparagraphs > 0 and nofparagraphs or nil, r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } implement { name = "dosetpositionstrutkind", arguments = { "string", "integer" }, actions = function(name,kind) local h = texgetdimen("strutht") local d = texgetdimen("strutdp") local spec = { k = kind, p = true, c = column, r = true, x = true, y = true, h = h ~= 0 and h or nil, d = d ~= 0 and d or nil, n = nofparagraphs > 0 and nofparagraphs or nil, r2l = texgetinteger("inlinelefttoright") == 1 or nil, } tobesaved[name] = spec ctx_latelua { action = enhance, specification = spec } end } function jobpositions.getreserved(tag,n) if tag == v_column then local fulltag = f_tag_three(tag,texgetcount("realpageno"),n or 1) local data = collected[fulltag] if data then return data, fulltag end tag = v_text end if tag == v_text then local fulltag = f_tag_two(tag,texgetcount("realpageno")) return collected[fulltag] or false, fulltag end return collected[tag] or false, tag end function jobpositions.copy(target,source) collected[target] = collected[source] end function jobpositions.replace(id,p,x,y,w,h,d) collected[id] = { p = p, x = x, y = y, w = w, h = h, d = d } -- c g end local function getpage(id) local jpi = collected[id] return jpi and jpi.p end local function getcolumn(id) local jpi = collected[id] return jpi and jpi.c or false end local function getparagraph(id) local jpi = collected[id] return jpi and jpi.n end local function getregion(id) local jpi = collected[id] if jpi then local r = jpi.r if r then return r end local p = jpi.p if p then return "page:" .. p end end return false end jobpositions.page = getpage jobpositions.column = getcolumn jobpositions.paragraph = getparagraph jobpositions.region = getregion jobpositions.p = getpage -- not used, kind of obsolete jobpositions.c = getcolumn -- idem jobpositions.n = getparagraph -- idem jobpositions.r = getregion -- idem function jobpositions.x(id) local jpi = collected[id] return jpi and jpi.x end function jobpositions.y(id) local jpi = collected[id] return jpi and jpi.y end function jobpositions.width(id) local jpi = collected[id] return jpi and jpi.w end function jobpositions.height(id) local jpi = collected[id] return jpi and jpi.h end function jobpositions.depth(id) local jpi = collected[id] return jpi and jpi.d end function jobpositions.whd(id) local jpi = collected[id] if jpi then return jpi.h, jpi.h, jpi.d end end function jobpositions.leftskip(id) local jpi = collected[id] return jpi and jpi.ls end function jobpositions.rightskip(id) local jpi = collected[id] return jpi and jpi.rs end function jobpositions.hsize(id) local jpi = collected[id] return jpi and jpi.hs end function jobpositions.parindent(id) local jpi = collected[id] return jpi and jpi.pi end function jobpositions.hangindent(id) local jpi = collected[id] return jpi and jpi.hi end function jobpositions.hangafter(id) local jpi = collected[id] return jpi and jpi.ha or 1 end function jobpositions.xy(id) local jpi = collected[id] if jpi then return jpi.x, jpi.y else return 0, 0 end end function jobpositions.lowerleft(id) local jpi = collected[id] if jpi then return jpi.x, jpi.y - jpi.d else return 0, 0 end end function jobpositions.lowerright(id) local jpi = collected[id] if jpi then return jpi.x + jpi.w, jpi.y - jpi.d else return 0, 0 end end function jobpositions.upperright(id) local jpi = collected[id] if jpi then return jpi.x + jpi.w, jpi.y + jpi.h else return 0, 0 end end function jobpositions.upperleft(id) local jpi = collected[id] if jpi then return jpi.x, jpi.y + jpi.h else return 0, 0 end end function jobpositions.position(id) local jpi = collected[id] if jpi then return jpi.p, jpi.x, jpi.y, jpi.w, jpi.h, jpi.d else return 0, 0, 0, 0, 0, 0 end end local splitter = lpeg.splitat(",") function jobpositions.extra(id,n,default) -- assume numbers local jpi = collected[id] if jpi then local e = jpi.e if e then local split = jpi.split if not split then split = lpegmatch(splitter,jpi.e) jpi.split = split end return texsp(split[n]) or default -- watch the texsp here end end return default end local function overlapping(one,two,overlappingmargin) -- hm, strings so this is wrong .. texsp one = collected[one] two = collected[two] if one and two and one.p == two.p then if not overlappingmargin then overlappingmargin = 2 end local x_one = one.x local x_two = two.x local w_two = two.w local llx_one = x_one - overlappingmargin local urx_two = x_two + w_two + overlappingmargin if llx_one > urx_two then return false end local w_one = one.w local urx_one = x_one + w_one + overlappingmargin local llx_two = x_two - overlappingmargin if urx_one < llx_two then return false end local y_one = one.y local y_two = two.y local d_one = one.d local h_two = two.h local lly_one = y_one - d_one - overlappingmargin local ury_two = y_two + h_two + overlappingmargin if lly_one > ury_two then return false end local h_one = one.h local d_two = two.d local ury_one = y_one + h_one + overlappingmargin local lly_two = y_two - d_two - overlappingmargin if ury_one < lly_two then return false end return true end end local function onsamepage(list,page) for id in gmatch(list,"([^,%s]+)") do local jpi = collected[id] if jpi then local p = jpi.p if not p then return false elseif not page then page = p elseif page ~= p then return false end end end return page end local function columnofpos(realpage,xposition) local p = columndata[realpage] if p then for i=1,#p do local c = p[i] local x = c.x or 0 local w = c.w or 0 if xposition >= x and xposition <= (x + w) then return i end end end return 1 end jobpositions.overlapping = overlapping jobpositions.onsamepage = onsamepage jobpositions.columnofpos = columnofpos -- interface implement { name = "replacepospxywhd", arguments = { "string", "integer", "dimen", "dimen", "dimen", "dimen", "dimen" }, actions = function(name,page,x,y,w,h,d) collected[name] = { p = page, x = x, y = y, w = w, h = h, d = d, } end } implement { name = "copyposition", arguments = "2 strings", actions = function(target,source) collected[target] = collected[source] end } implement { name = "MPp", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local p = jpi.p if p and p ~= true then context(p) return end end context('0') end } implement { name = "MPx", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local x = jpi.x if x and x ~= true and x ~= 0 then context("%.5Fpt",x*pt) return end end context('0pt') end } implement { name = "MPy", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local y = jpi.y if y and y ~= true and y ~= 0 then context("%.5Fpt",y*pt) return end end context('0pt') end } implement { name = "MPw", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local w = jpi.w if w and w ~= 0 then context("%.5Fpt",w*pt) return end end context('0pt') end } implement { name = "MPh", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local h = jpi.h if h and h ~= 0 then context("%.5Fpt",h*pt) return end end context('0pt') end } implement { name = "MPd", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local d = jpi.d if d and d ~= 0 then context("%.5Fpt",d*pt) return end end context('0pt') end } implement { name = "MPxy", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context('(%.5Fpt,%.5Fpt)', jpi.x*pt, jpi.y*pt ) else context('(0,0)') end end } implement { name = "MPwhd", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local w = jpi.w or 0 local h = jpi.h or 0 local d = jpi.d or 0 if w ~= 0 or h ~= 0 or d ~= 0 then context("%.5Fpt,%.5Fpt,%.5Fpt",w*pt,h*pt,d*pt) return end end context('0pt,0pt,0pt') end } implement { name = "MPll", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context('(%.5Fpt,%.5Fpt)', jpi.x *pt, (jpi.y-jpi.d)*pt ) else context('(0,0)') -- for mp only end end } implement { name = "MPlr", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context('(%.5Fpt,%.5Fpt)', (jpi.x + jpi.w)*pt, (jpi.y - jpi.d)*pt ) else context('(0,0)') -- for mp only end end } implement { name = "MPur", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context('(%.5Fpt,%.5Fpt)', (jpi.x + jpi.w)*pt, (jpi.y + jpi.h)*pt ) else context('(0,0)') -- for mp only end end } implement { name = "MPul", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context('(%.5Fpt,%.5Fpt)', jpi.x *pt, (jpi.y + jpi.h)*pt ) else context('(0,0)') -- for mp only end end } local function MPpos(id) local jpi = collected[id] if jpi then local p = jpi.p if p then context("%s,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt", p, jpi.x*pt, jpi.y*pt, jpi.w*pt, jpi.h*pt, jpi.d*pt ) return end end context('0,0,0,0,0,0') -- for mp only end implement { name = "MPpos", arguments = "string", actions = MPpos } implement { name = "MPn", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local n = jpi.n if n then context(n) return end end context(0) end } implement { name = "MPc", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local c = jpi.c if c and c ~= true then context(c) return end end context('0') -- okay ? end } implement { name = "MPr", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then local r = jpi.r if r and r ~= true then context(r) return end local p = jpi.p if p and p ~= true then context("page:" .. p) end end end } local function MPpardata(id) local t = collected[id] if not t then local tag = f_p_tag(id) t = collected[tag] end if t then context("%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%s,%.5Fpt", t.hs*pt, t.ls*pt, t.rs*pt, t.hi*pt, t.ha, t.pi*pt ) else context("0,0,0,0,0,0") -- for mp only end end implement { name = "MPpardata", arguments = "string", actions = MPpardata } implement { name = "MPposset", arguments = "string", actions = function(name) local b = f_b_tag(name) local e = f_e_tag(name) local w = f_w_tag(name) local p = f_p_tag(getparagraph(b)) MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p) end } implement { name = "MPls", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context("%.5Fpt",jpi.ls*pt) else context("0pt") end end } implement { name = "MPrs", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context("%.5Fpt",jpi.rs*pt) else context("0pt") end end } local splitter = lpeg.tsplitat(",") implement { name = "MPplus", arguments = { "string", "integer", "string" }, actions = function(name,n,default) local jpi = collected[name] if jpi then local e = jpi.e if e then local split = jpi.split if not split then split = lpegmatch(splitter,jpi.e) jpi.split = split end context(split[n] or default) return end end context(default) end } implement { name = "MPrest", arguments = { "string", "string" }, actions = function(name,default) local jpi = collected[name] context(jpi and jpi.e or default) end } implement { name = "MPxywhd", arguments = "string", actions = function(name) local jpi = collected[name] if jpi then context("%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt", jpi.x*pt, jpi.y*pt, jpi.w*pt, jpi.h*pt, jpi.d*pt ) else context("0,0,0,0,0") -- for mp only end end } local doif = commands.doif local doifelse = commands.doifelse implement { name = "doifelseposition", arguments = "string", actions = function(name) doifelse(collected[name]) end } implement { name = "doifposition", arguments = "string", actions = function(name) doif(collected[name]) end } implement { name = "doifelsepositiononpage", arguments = { "string", "integer" }, actions = function(name,p) local c = collected[name] doifelse(c and c.p == p) end } implement { name = "doifelseoverlapping", arguments = { "string", "string" }, actions = function(one,two) doifelse(overlapping(one,two)) end } implement { name = "doifelsepositionsonsamepage", arguments = "string", actions = function(list) doifelse(onsamepage(list)) end } implement { name = "doifelsepositionsonthispage", arguments = "string", actions = function(list) doifelse(onsamepage(list,tostring(texgetcount("realpageno")))) end } implement { name = "doifelsepositionsused", actions = function() doifelse(next(collected)) end } implement { name = "markregionbox", arguments = "integer", actions = markregionbox } implement { name = "setregionbox", arguments = "integer", actions = setregionbox } implement { name = "markregionboxtagged", arguments = { "integer", "string" }, actions = markregionbox } implement { name = "markregionboxtaggedn", arguments = { "integer", "string", "integer" }, actions = function(box,tag,n) markregionbox(box,tag,nil,nil,nil,nil,nil,nil,n) end } implement { name = "setregionboxtagged", arguments = { "integer", "string" }, actions = setregionbox } implement { name = "markregionboxcorrected", arguments = { "integer", "string", true }, actions = markregionbox } implement { name = "markregionboxtaggedkind", arguments = { "integer", "string", "integer", "dimen", "dimen", "dimen", "dimen" }, actions = function(box,tag,n,d1,d2,d3,d4) markregionbox(box,tag,nil,n,d1,d2,d3,d4) end } implement { name = "reservedautoregiontag", actions = function() nofregions = nofregions + 1 context(f_region(nofregions)) end } -- statistics (at least for the moment, when testing) -- statistics.register("positions", function() -- local total = nofregular + nofusedregions + nofmissingregions -- if total > 0 then -- return format("%s collected, %s regulars, %s regions, %s unresolved regions", -- total, nofregular, nofusedregions, nofmissingregions) -- else -- return nil -- end -- end) statistics.register("positions", function() local total = nofregular + nofspecial if total > 0 then return format("%s collected, %s regular, %s special",total,nofregular,nofspecial) else return nil end end) -- We support the low level positional commands too: local newsavepos = nodes.pool.savepos implement { name = "savepos", actions = function() context(newsavepos()) end } implement { name = "lastxpos", actions = function() context(gethpos()) end } implement { name = "lastypos", actions = function() context(getvpos()) end }