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"
}
--[[ldx--
We save positional information in the main utility table. Not only
can we store much more information in but it's also
more efficient.
--ldx]]--
-- 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 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",
public = true,
protected = true,
arguments = { "argument", "integerargument", "dimenargument", "dimenargument" },
actions = setall, -- name p x y
}
implement {
name = "dosavepositionwhd",
public = true,
protected = true,
arguments = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument" },
actions = setall, -- name p x y w h d
}
implement {
name = "dosavepositionplus",
public = true,
protected = true,
arguments = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "argument" },
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 box = getbox("strutbox")
local w, h, d = getwhd(box)
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 = "argument",
public = true,
protected = true,
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 = { "argument", "dimenargument", "dimenargument", "dimenargument" },
public = true,
protected = true,
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 = { "argument", "integerargument" },
public = true,
protected = true,
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 = { "argument", "dimenargument", "dimenargument", "dimenargument" },
public = true,
protected = true,
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 = "argument",
public = true,
protected = true,
actions = function(name)
local box = getbox("strutbox")
local w, h, d = getwhd(box)
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 = { "argument", "integerargument" },
public = true,
protected = true,
actions = function(name,kind)
local box = getbox("strutbox")
local w, h, d = getwhd(box)
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,"(, )") 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 = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument" },
public = true,
protected = true,
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 arguments",
public = true,
protected = true,
actions = function(target,source)
collected[target] = collected[source]
end
}
implement {
name = "MPp",
arguments = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
actions = MPpos
}
implement {
name = "MPn",
arguments = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
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 = "argument",
public = true,
actions = MPpardata
}
implement {
name = "MPposset",
arguments = "argument",
public = true,
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 = "argument",
public = true,
actions = function(name)
local jpi = collected[name]
if jpi then
context("%.5Fpt",jpi.ls*pt)
else
context("0pt")
end
end
}
implement {
name = "MPrs",
arguments = "argument",
public = true,
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 = { "argument", "integerargument", "argument" },
public = true,
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 = { "argument", "argument" },
public = true,
actions = function(name,default)
local jpi = collected[name]
context(jpi and jpi.e or default)
end
}
implement {
name = "MPxywhd",
arguments = "argument",
public = true,
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 = "argument",
public = true,
protected = true,
actions = function(name)
doifelse(collected[name])
end
}
implement {
name = "doifposition",
arguments = "argument",
public = true,
protected = true,
actions = function(name)
doif(collected[name])
end
}
implement {
name = "doifelsepositiononpage",
arguments = { "string", "integerargument" },
public = true,
protected = true,
actions = function(name,p)
local c = collected[name]
doifelse(c and c.p == p)
end
}
implement {
name = "doifelseoverlapping",
arguments = { "argument", "argument" },
public = true,
protected = true,
actions = function(one,two)
doifelse(overlapping(one,two))
end
}
implement {
name = "doifelsepositionsonsamepage",
arguments = "argument", -- string
public = true,
protected = true,
actions = function(list)
doifelse(onsamepage(list))
end
}
implement {
name = "doifelsepositionsonthispage",
arguments = "argument", -- string
public = true,
protected = true,
actions = function(list)
doifelse(onsamepage(list,tostring(texgetcount("realpageno"))))
end
}
implement {
name = "doifelsepositionsused",
public = true,
protected = true,
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",
public = true,
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
jobpositions.lastx = 0
jobpositions.lasty = 0
implement { name = "savepos", actions = function() context(newsavepos()) end }
implement { name = "lastxpos", actions = function() context(jobpositions.lastx) end }
implement { name = "lastypos", actions = function() context(jobpositions.lasty) end }