summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/anch-pgr.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/anch-pgr.lua')
-rw-r--r--tex/context/base/mkiv/anch-pgr.lua1348
1 files changed, 942 insertions, 406 deletions
diff --git a/tex/context/base/mkiv/anch-pgr.lua b/tex/context/base/mkiv/anch-pgr.lua
index b5c2ae628..8a63c5b54 100644
--- a/tex/context/base/mkiv/anch-pgr.lua
+++ b/tex/context/base/mkiv/anch-pgr.lua
@@ -6,236 +6,546 @@ if not modules then modules = { } end modules ['anch-pgr'] = {
license = "see context related readme files"
}
--- todo: we need to clean up lists (of previous pages)
+-- This is a bit messy module but backgrounds are messy anyway. Especially when we want to
+-- follow shapes. This will always be work in progress as it also depends on new features
+-- in context.
+--
+-- Alas, shapes and inline didn't work as expected end of 2016 so I had to pick up this
+-- thread again. But with regular excursions to listening to Brad Mehldau's Mehliana I
+-- could keep myself motivated. Some old stuff has been removed, some suboptimal code has
+-- been replaced. Background code is still not perfect, but some day ... the details manual
+-- will discuss this issue.
-local format = string.format
-local abs = math.abs
-local concat, sort = table.concat, table.sort
+local abs, div, floor, round, min, max = math.abs, math.div, math.floor, math.round, math.min, math.max
+local sort, concat = table.sort, table.concat
local splitter = lpeg.splitat(":")
local lpegmatch = lpeg.match
-local jobpositions = job.positions
-local formatters = string.formatters
+local jobpositions = job.positions
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
-local commands = commands
-local context = context
+local commands = commands
+local context = context
-local implement = interfaces.implement
+local implement = interfaces.implement
-local report_graphics = logs.reporter("graphics")
+local report_graphics = logs.reporter("backgrounds")
+local report_shapes = logs.reporter("backgrounds","shapes")
+local report_free = logs.reporter("backgrounds","free")
-local f_b_tag = formatters["b:%s"]
-local f_e_tag = formatters["e:%s"]
-local f_p_tag = formatters["p:%s"]
+local trace_shapes = false trackers.register("backgrounds.shapes", function(v) trace_shapes = v end)
+local trace_ranges = false trackers.register("backgrounds.shapes.ranges",function(v) trace_ranges = v end)
+local trace_free = false trackers.register("backgrounds.shapes.free", function(v) trace_free = v end)
-local f_tag_two = formatters["%s:%s"]
+local f_b_tag = formatters["b:%s"]
+local f_e_tag = formatters["e:%s"]
+local f_p_tag = formatters["p:%s"]
-local f_point = formatters["%p"]
-local f_pair = formatters["(%p,%p)"]
-local f_path = formatters["%--t--cycle"]
+local f_tag_two = formatters["%s:%s"]
-local function regionarea(r)
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- return {
- f_pair(rx, rh - ry),
- f_pair(rw, rh - ry),
- f_pair(rw, rd - ry),
- f_pair(rx, rd - ry),
- }
-end
+local f_point = formatters["%p"]
+local f_pair = formatters["(%p,%p)"]
+local f_path = formatters["%--t--cycle"]
+local f_pair_i = formatters["(%r,%r)"] -- rounded
+
+graphics = graphics or { }
+local backgrounds = { }
+graphics.backgrounds = backgrounds
+
+-- -- --
+
+local texsetattribute = tex.setattribute
+local pdfgetpos = pdf.getpos -- why not a generic name !
+
+local a_textbackground = attributes.private("textbackground")
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local new_latelua = nuts.pool.latelua
+local new_rule = nuts.pool.rule
+local new_kern = nuts.pool.kern
+local new_hlist = nuts.pool.hlist
+
+local getbox = nuts.getbox
+local getid = nuts.getid
+----- getlist = nuts.getlist
+local setlink = nuts.setlink
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+
+local nodecodes = nodes.nodecodes
+local localpar_code = nodecodes.localpar
--- we can use a 'local t, n' and reuse the table
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
-local eps = 2
+local processranges = nodes.processranges
-local function add(t,x,y,last,direction)
- local n = #t
- if n == 0 then
- t[n+1] = { x, y }
+local unsetvalue = attributes.unsetvalue
+
+local jobpositions = job.positions
+
+local data = { }
+local realpage = 1
+local recycle = 1000 -- only tables can overflow this
+local enabled = false
+
+-- Freeing the data is somewhat tricky as we can have backgrounds spanning
+-- many pages but for an arbitrary background shape that is not so common.
+
+local function check(a,index,depth,d,where,ht,dp)
+ -- this is not yet r2l ready
+ local w = d.shapes[realpage]
+ local x, y = pdfgetpos()
+ if trace_ranges then
+ report_shapes("attribute %i, index %i, depth %i, location %s, position (%p,%p)",
+ a,index,depth,where,x,y)
+ end
+ local n = #w
+ if d.index ~= index then
+ n = n + 1
+ d.index = index
+ d.depth = depth
+-- w[n] = { x, x, y, ht, dp }
+ w[n] = { y, ht, dp, x, x }
else
- local tn = t[n]
- local lx = tn[1]
- local ly = tn[2]
- if x == lx and y == ly then
- -- quick skip
- elseif n == 1 then
- -- if abs(lx-x) <= eps or abs(ly-y) <= eps then
- if abs(lx-x) > eps or abs(ly-y) > eps then
- t[n+1] = { x, y }
+ local wn = w[n]
+ local wh = wn[2]
+ local wd = wn[3]
+ if depth < d.depth then
+ local wy = wn[1]
+ wn[1] = y
+ d.depth = depth
+ local dy = wy - y
+ wh = wh - dy
+ wd = wd - dy
+ end
+ if where == "r" then
+ if x > wn[5] then
+ wn[5] = x
end
else
- local tm = t[n-1]
- local px = tm[1]
- local py = tm[2]
- if (direction == "down" and y > ly) or (direction == "up" and y < ly) then
- -- move back from too much hang
- elseif abs(lx-px) <= eps and abs(lx-x) <= eps then
- if abs(ly-y) > eps then
- tn[2] = y
- end
- elseif abs(ly-py) <= eps and abs(ly-y) <= eps then
- if abs(lx-x) > eps then
- tn[1] = x
- end
- elseif not last then
- t[n+1] = { x, y }
+ if x < wn[4] then
+ wn[4] = x
end
end
+ if ht > wh then
+ wn[2] = ht
+ end
+ if dp > wd then
+ wn[3] = dp
+ end
end
+ -- inspect(w)
end
--- local function add(t,x,y,last)
--- t[#t+1] = { x, y }
+local index = 0
+
+local function flush(head,f,l,a,parent,depth)
+ local d = data[a]
+ if d then
+ local ix = index
+ local ht = getheight(parent)
+ local dp = getdepth(parent)
+ local ln = new_latelua(function() check(a,ix,depth,d,"l",ht,dp) end)
+ local rn = new_latelua(function() check(a,ix,depth,d,"r",ht,dp) end)
+ if trace_ranges then
+ ln = new_hlist(setlink(new_rule(65536,65536*4,0),new_kern(-65536),ln))
+ rn = new_hlist(setlink(new_rule(65536,0,65536*4),new_kern(-65536),rn))
+ end
+ if getid(f) == localpar_code then -- we need to clean this mess
+ insert_after(head,f,ln)
+ else
+ head, f = insert_before(head,f,ln)
+ end
+ insert_after(head,l,rn)
+ end
+ return head, true
+end
+
+local function registerbackground(name)
+ local n = #data + 1
+ if n > recycle then
+ -- we could also free all e: that are beyond a page but we don't always
+ -- know the page so a recycle is nicer and the s lists are kept anyway
+ -- so the amount of kept data is not that large
+ n = 1
+ end
+ local b = jobpositions.tobesaved["b:"..name]
+ if b then
+ local s = setmetatableindex("table")
+ b.s = s
+ data[n] = {
+ bpos = b,
+ name = name,
+ n = n,
+ shapes = s,
+ count = 0,
+ sindex = 0,
+ }
+ texsetattribute(a_textbackground,n)
+ if not enabled then
+ nodes.tasks.enableaction("contributers", "nodes.handlers.textbackgrounds")
+ enabled = true
+ end
+ else
+ texsetattribute(a_textbackground,unsetvalue)
+ end
+end
+
+-- local function collectbackgrounds(r,n)
+-- if enabled then
+-- local parent = getbox(n)
+-- local head = getlist(parent)
+-- realpage = r
+-- processranges(a_textbackground,flush,head) -- ,parent)
+-- end
-- end
+--
+-- interfaces.implement {
+-- name = "collectbackgrounds",
+-- actions = collectbackgrounds,
+-- arguments = { "integer", "integer" }
+-- }
+
+nodes.handlers.textbackgrounds = function(head,where,parent) -- we have hlistdir and local dir
+ -- todo enable action in register
+ head = tonut(head)
+ index = index + 1
+ local head, done = processranges(a_textbackground,flush,head,parent)
+ return tonode(head), done
+end
+
+interfaces.implement {
+ name = "registerbackground",
+ actions = registerbackground,
+ arguments = { "string" }
+}
+
+-- optimized already but we can assume a cycle i.e. prune the last point and then
+-- even less code .. we could merge some loops but his is more robust
+
+local function topairs(t,n)
+ local r = { }
+ for i=1,n do
+ local ti = t[i]
+ r[i] = f_pair_i(ti[1]/65556,ti[2]/65536)
+ end
+ return concat(r," ")
+end
+
+local eps = 65536 / 4 -- 2
+local pps = eps
+local nps = - pps
+
+local function unitvector(x,y)
+ if x < pps and x > nps then
+ x = 0
+ elseif x < 0 then
+ x = -1
+ else
+ x = 1
+ end
+ if y < pps and y > nps then
+ y = 0
+ elseif y < 0 then
+ y = -1
+ else
+ y = 1
+ end
+ return x, y
+end
local function finish(t)
- local n = #t
- if n > 1 then
- local first = t[1]
- local last = t[n]
- if abs(first[1]-last[1]) <= eps and abs(first[2]-last[2]) <= eps then
- t[n] = nil
+ local tm = #t
+ if tm < 2 then
+ return
+ end
+ if trace_ranges then
+ report_shapes("initial list: %s",topairs(t,tm))
+ end
+ -- remove similar points
+ local n = 1
+ local tn = tm
+ local tf = t[1]
+ local tx = tf[1]
+ local ty = tf[2]
+ for i=2,#t do
+ local ti = t[i]
+ local ix = ti[1]
+ local iy = ti[2]
+ local dx = ix - tx
+ local dy = iy - ty
+ if dx > eps or dx < - eps or dy > eps or dy < - eps then
+ n = n + 1
+ t[n] = ti
+ tx = ix
+ ty = iy
end
end
-end
+ if trace_shapes then
+ report_shapes("removing similar points: %s",topairs(t,n))
+ end
+ if n > 2 then
+ -- remove redundant points
+ repeat
+ tn = n
+ n = 0
+ local tm = t[tn]
+ local tmx = tm[1]
+ local tmy = tm[2]
+ local tp = t[1]
+ local tpx = tp[1]
+ local tpy = tp[2]
+ for i=1,tn do -- while and only step when done
+ local ti = tp
+ local tix = tpx
+ local tiy = tpy
+ if i == tn then
+ tp = t[1]
+ else
+ tp = t[i+1]
+ end
+ tpx = tp[1]
+ tpy = tp[2]
-local function clip(t,ytop,ybot)
- local first, last = 1, #t
- for i=first,last do
- local y = t[i][2]
- if ytop < y then
- first = i
+ local vx1, vx2 = unitvector(tix - tmx,tpx - tix)
+ if vx1 ~= vx2 then
+ n = n + 1
+ t[n] = ti
+ else
+ local vy1, vy2 = unitvector(tiy - tmy,tpy - tiy)
+ if vy1 ~= vy2 then
+ n = n + 1
+ t[n] = ti
+ end
+ end
+
+ tmx = tix
+ tmy = tiy
+ end
+ until n == tn or n <= 2
+ if trace_shapes then
+ report_shapes("removing redundant points: %s",topairs(t,n))
end
- if ybot > y then
- last = i
- break
+ -- remove spikes
+ if n > 2 then
+ repeat
+ tn = n
+ n = 0
+ local tm = t[tn]
+ local tmx = tm[1]
+ local tmy = tm[2]
+ local tp = t[1]
+ local tpx = tp[1]
+ local tpy = tp[2]
+ for i=1,tn do -- while and only step when done
+ local ti = tp
+ local tix = tpx
+ local tiy = tpy
+ if i == tn then
+ tp = t[1]
+ else
+ tp = t[i+1]
+ end
+ tpx = tp[1]
+ tpy = tp[2]
+
+ local vx1, vx2 = unitvector(tix - tmx,tpx - tix)
+ if vx1 ~= - vx2 then
+ n = n + 1
+ t[n] = ti
+ else
+ local vy1, vy2 = unitvector(tiy - tmy,tpy - tiy)
+ if vy1 ~= - vy2 then
+ n = n + 1
+ t[n] = ti
+ end
+ end
+
+ tmx = tix
+ tmy = tiy
+ end
+ until n == tn or n <= 2
+ if trace_shapes then
+ report_shapes("removing spikes: %s",topairs(t,n))
+ end
+ end
+ end
+ -- prune trailing points
+ if tm > n then
+ for i=tm,n+1,-1 do
+ t[i] = nil
end
end
- local lp = { }
- lp[#lp+1] = { t[first][1], ytop }
- for i=first+1,last-1 do
- lp[#lp+1] = { t[i][1], t[i][2] }
+ if n > 1 then
+ local tf = t[1]
+ local tl = t[n]
+ local dx = tf[1] - tl[1]
+ local dy = tf[2] - tl[2]
+ if dx > eps or dx < - eps or dy > eps or dy < - eps then
+ -- different points
+ else
+ -- saves a point (as we -- cycle anyway)
+ t[n] = nil
+ n = n -1
+ end
+ if trace_shapes then
+ report_shapes("removing cyclic endpoints: %s",topairs(t,n))
+ end
end
- lp[#lp+1] = { t[last][1], ybot }
- return lp
+ return t
end
--- todo: mark regions and free paragraphs in collected
+local eps = 65536
-local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
- -- we assume that we only hang per page and not cross pages
- -- which makes sense as hanging is only uses in special cases
+-- The next function can introduce redundant points but these are removed later on
+-- in the unspiker. It makes checking easier.
+
+local function shape(kind,b,p,realpage,xmin,xmax,ymin,ymax,fh,ld)
+ local s = b.s
+ if not s then
+ if trace_shapes then
+ report_shapes("calculating %s area, no shape",kind)
+ end
+ return
+ end
+ s = s[realpage]
+ if not s then
+ if trace_shapes then
+ report_shapes("calculating %s area, no shape for page %s",kind,realpage)
+ end
+ return
+ end
+ local ns = #s
+ if ns == 0 then
+ if trace_shapes then
+ report_shapes("calculating %s area, empty shape for page %s",kind,realpage)
+ end
+ return
+ end
--
- -- we can remove data as soon as a page is done so we could
- -- remember per page and discard areas after each shipout
- local leftshape, rightshape
- leftshape = { { rx, rh } } -- spikes get removed so we can start at the edge
- rightshape = { { rw, rh } } -- even if we hang next
- local paragraphs = r.paragraphs
- local extending = false
- if paragraphs then
- for i=1,#paragraphs do
- local p = paragraphs[i]
- local ha = p.ha
- if obeyhang and ha and ha ~= 0 then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local hi = p.hi
- local hang = ha * (ph + pd)
- local py_ph = py + ph
- -- ha < 0 hi < 0 : right top
- -- ha < 0 hi > 0 : left top
- if ha < 0 then
- if hi < 0 then -- right
- add(rightshape,rw, py_ph,"up")
- add(rightshape,rw + hi,py_ph,"up")
- add(rightshape,rw + hi,py_ph + hang,"up")
- add(rightshape,rw, py_ph + hang,"up")
- else
- -- left
- add(leftshape,rx,py_ph,"down")
- add(leftshape,rx + hi,py_ph,"down")
- add(leftshape,rx + hi,py_ph + hang,"down")
- add(leftshape,rx,py_ph + hang,"down")
+ if trace_shapes then
+ report_shapes("calculating %s area, using shape for page %s",kind,realpage)
+ end
+ -- it's a bit inefficient to use the par values and later compensate for b and
+ -- e but this keeps the code (loop) cleaner
+ local ph = p and p.h or 0
+ local pd = p and p.d or 0
+ --
+ xmax = xmax + eps
+ xmin = xmin - eps
+ ymax = ymax + eps
+ ymin = ymin - eps
+ local ls = { } -- left shape
+ local rs = { } -- right shape
+ local pl = nil -- previous left x
+ local pr = nil -- previous right x
+ local n = 0
+ local xl = nil
+ local xr = nil
+ local mh = ph -- min
+ local md = pd -- min
+ for i=1,ns do
+ local si = s[i]
+ local y = si[1]
+ local ll = si[4] -- can be sparse
+ if ll then
+ xl = ll
+ local rr = si[5] -- can be sparse
+ if rr then
+ xr = rr
+ end
+ end
+ if trace_ranges then
+ report_shapes("original : [%02i] xl=%p xr=%p y=%p",i,xl,xr,y)
+ end
+ if xl ~= xr then -- could be catched in the finalizer
+ local xm = xl + (xr - xl)/2 -- midpoint should be in region
+ if xm >= xmin and xm <= xmax and y >= ymin and y <= ymax then
+ local ht = si[2] -- can be sparse
+ if ht then
+ ph = ht
+ local dp = si[3] -- can be sparse
+ if dp then
+ pd = dp
end
- else
- -- maybe some day
end
- extending = true -- false
- else -- we need to clip to the next par
- local ps = p.ps
- if ps then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local step = ph + pd
- local size = #ps * step
- local py_ph = py + ph
- add(leftshape,rx,py_ph,"up")
- add(rightshape,rw,py_ph,"down")
- for i=1,#ps do
- local p = ps[i]
- local l = p[1]
- local w = p[2]
- add(leftshape,rx + l, py_ph,"up")
- add(rightshape,rx + l + w, py_ph,"down")
- py_ph = py_ph - step
- add(leftshape,rx + l, py_ph,"up")
- add(rightshape,rx + l + w, py_ph,"down")
+ local h = y + (ph < mh and mh or ph)
+ local d = y - (pd < md and md or pd)
+ if pl then
+ n = n + 1
+ ls[n] = { pl, h }
+ rs[n] = { pr, h }
+ if trace_ranges then
+ report_shapes("paragraph : [%02i] xl=%p xr=%p y=%p",i,pl,pr,h)
end
- extending = true
- elseif extending then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local py_ph = py + ph
- local py_pd = py - pd
- add(leftshape,leftshape[#leftshape][1],py_ph,"up")
- add(rightshape,rightshape[#rightshape][1],py_ph,"down")
- add(leftshape,rx,py_ph,"up") -- shouldn't this be py_pd
- add(rightshape,rw,py_ph,"down") -- shouldn't this be py_pd
- extending = false
+ end
+ n = n + 1
+ ls[n] = { xl, h }
+ rs[n] = { xr, h }
+ if trace_ranges then
+ report_shapes("height : [%02i] xl=%p xr=%p y=%p",i,xl,xr,h)
+ end
+ n = n + 1
+ ls[n] = { xl, d }
+ rs[n] = { xr, d }
+ if trace_ranges then
+ report_shapes("depth : [%02i] xl=%p xr=%p y=%p",i,xl,xr,d)
end
end
+ pl, pr = xl, xr
+ else
+ if trace_ranges then
+ report_shapes("ignored : [%02i] xl=%p xr=%p y=%p",i,xl,xr,y)
+ end
end
end
- -- we can have a simple variant when no paragraphs
- if extending then
- -- not ok
- leftshape[#leftshape][2] = rd
- rightshape[#rightshape][2] = rw
- else
- add(leftshape,rx,rd,"up")
- add(rightshape,rw,rd,"down")
+ --
+ if true and n > 0 then
+ -- use height of b and depth of e, maybe check for weird border
+ -- cases here
+ if fh then
+ local lsf, rsf = ls[1], rs[1]
+ if lsf[2] < fh then
+ lsf[2] = fh
+ end
+ if rsf[2] < fh then
+ rsf[2] = fh
+ end
+ end
+ if fd then
+ local lsl, rsl = ls[n], rs[n]
+ if lsl[2] > fd then
+ lsl[2] = fd
+ end
+ if rsl[2] > fd then
+ rsl[2] = fd
+ end
+ end
+ end
+ --
+ for i=n,1,-1 do
+ n = n + 1 rs[n] = ls[i]
end
- return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
+ return rs
end
--- local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
--- local leftshape = { { rx, rh }, { rx, rd } }
--- local rightshape = { { rw, rh }, { rw, rd } }
--- return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
--- end
-
-local function singlepart(b,e,r,left,right,obeyhang)
+local function singlepart(b,e,p,realpage,r,left,right)
local bx, by = b.x, b.y
local ex, ey = e.x, e.y
local rx, ry = r.x, r.y
+ local bh, bd = by + b.h, by - b.d
+ local eh, ed = ey + e.h, ey - e.d
+ local rh, rd = ry + r.h, ry - r.d
local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
if left then
rx = rx + left
rw = rw - right
end
- local bh = by + b.h
- local bd = by - b.d
- local eh = ey + e.h
- local ed = ey - e.d
if ex == rx then
-- We probably have a strut at the next line so we force a width
-- although of course it is better to move up. But as we have whitespace
@@ -244,169 +554,160 @@ local function singlepart(b,e,r,left,right,obeyhang)
end
local area
if by == ey then
+ if trace_shapes then
+ report_shapes("calculating single area, partial line")
+ end
+ area = {
+ { bx, bh },
+ { ex, eh },
+ { ex, ed },
+ { bx, bd },
+ }
+ elseif b.k == 2 then
area = {
- f_pair(bx,bh-ry),
- f_pair(ex,eh-ry),
- f_pair(ex,ed-ry),
- f_pair(bx,bd-ry),
+ { rx, bh },
+ { rw, bh },
+ { rw, ed },
+ { rx, ed },
}
else
- area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,ed,bh,eh,obeyhang)
- add(area,bx,bh-ry)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- add(area,ex,eh-ry)
- add(area,ex,ed-ry)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- add(area,bx,bd-ry)
- add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
- end
+ area = shape("single",b,p,realpage,rx,rw,rd,rh,bh,ed)
+ end
+ if not area then
+ area = {
+ { bx, bh },
+ { rw, bh },
+ { rw, eh },
+ { ex, eh },
+ { ex, ed },
+ { rx, ed },
+ { rx, bd },
+ { bx, bd },
+ }
end
return {
location = "single",
region = r,
- area = area,
+ area = finish(area),
}
end
-local function firstpart(b,r,left,right,obeyhang)
+local function firstpart(b,e,p,realpage,r,left,right)
local bx, by = b.x, b.y
local rx, ry = r.x, r.y
+ local bh, bd = by + b.h, by - b.d
+ local rh, rd = ry + r.h, ry - r.d
local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
if left then
rx = rx + left
rw = rw - right
end
- local bh = by + b.h
- local bd = by - b.d
- local area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,rd,bh,rd,obeyhang)
- add(area,bx,bh-ry)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- add(area,bx,bd-ry)
- add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
+ local area = shape("first",b,p,realpage,rx,rw,rd,rh,bh,false)
+ if not area then
+ if b.k == 2 then
+ area = {
+ { rx, bh },
+ { rw, bh },
+ { rw, rd },
+ { rx, rd },
+ }
+ else
+ area = {
+ { bx, bh },
+ { rw, bh },
+ { rw, rd }, -- { rw, eh },
+ { rx, rd }, -- { rx, ed },
+ { rx, bd },
+ { bx, bd },
+ }
+ end
end
return {
location = "first",
region = r,
- area = area,
+ area = finish(area),
}
end
-local function middlepart(r,left,right,obeyhang)
+local function middlepart(b,e,p,realpage,r,left,right)
local rx, ry = r.x, r.y
+ local rh, rd = ry + r.h, ry - r.d
local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
if left then
rx = rx + left
rw = rw - right
end
- local area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,rd,rh,rd,obeyhang)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
+ local area = shape("middle",b,p,realpage,rx,rw,rd,rh,false,false)
+ if not area then
+ area = {
+ { rw, rh },
+ { rw, rd },
+ { rx, rd },
+ { rx, rh },
+ }
end
return {
location = "middle",
region = r,
- area = area,
+ area = finish(area),
}
end
-local function lastpart(e,r,left,right,obeyhang)
+local function lastpart(b,e,p,realpage,r,left,right)
local ex, ey = e.x, e.y
local rx, ry = r.x, r.y
+ local eh, ed = ey + e.h, ey - e.d
+ local rh, rd = ry + r.h, ry - r.d
local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
if left then
rx = rx + left
rw = rw - right
end
- local eh = ey + e.h
- local ed = ey - e.d
- local area = { }
- -- two cases: till end and halfway e line
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,ed,rh,eh,obeyhang)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- add(area,ex,eh-ry)
- add(area,ex,ed-ry)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
+ local area = shape("last",b,p,realpage,rx,rw,rd,rh,false,ed)
+ if not area then
+ if b.k == 2 then
+ area = {
+ { rw, rh },
+ { rw, ed },
+ { rx, ed },
+ { rx, rh },
+ }
+ else
+ area = {
+ { rw, rh }, -- { rw, bh },
+ { rw, eh },
+ { ex, eh },
+ { ex, ed },
+ { rx, ed },
+ { rx, rh }, -- { rx, bd },
+ }
+ end
end
return {
location = "last",
region = r,
- area = area,
+ area = finish(area),
}
end
-graphics = graphics or { }
-local backgrounds = { }
-
-graphics.backgrounds = backgrounds
-
-local function calculatemultipar(tag,obeyhang)
+local function calculatemultipar(tag)
local collected = jobpositions.collected
local b = collected[f_b_tag(tag)]
local e = collected[f_e_tag(tag)]
if not b or not e then
- report_graphics("invalid tag %a",tag)
+ report_shapes("invalid tag %a",tag)
return { }
end
local br = b.r
local er = e.r
if not br or not er then
- report_graphics("invalid region for %a",tag)
+ report_shapes("invalid region for %a",tag)
return { }
end
local btag, bindex = lpegmatch(splitter,br)
local etag, eindex = lpegmatch(splitter,er)
if not bindex or not eindex or btag ~= etag then
- report_graphics("invalid indices for %a",tag)
+ report_shapes("invalid indices for %a",tag)
return { }
end
local bindex = tonumber(bindex)
@@ -415,40 +716,42 @@ local function calculatemultipar(tag,obeyhang)
-- entries and these are shared. We compensate left/right based on the columns
-- x and w but need to take the region into acount where the specification was
-- flushed and not the begin pos's region, because otherwise we get the wrong
- -- compensation for assymetrical doublesided layouts.
- local left = 0
+ -- compensation for asymetrical doublesided layouts.
+ local left = 0
local right = 0
- local rc = b.c
+ local bc = b.c
+ local rc = bc and collected[bc]
if rc then
- rc = collected[rc]
- if rc then
- local tb = collected[rc.r]
- if tb then
- left = -(tb.x - rc.x)
- right = (tb.w - rc.w - left) -- tb.x - rc.x
- end
+ local tb = collected[rc.r]
+ if tb then
+ left = -(tb.x - rc.x)
+ right = (tb.w - rc.w - left)
end
end
-- Obeying intermediate changes of left/rightskip makes no sense as it will
-- look bad, so we only look at the begin situation.
local bn = b.n
- if bn then
- local bp = collected[f_p_tag(bn)]
- if bp then
- left = left + bp.ls
- right = right + bp.rs
- end
+ local p = bn and collected[f_p_tag(bn)] -- par
+ if p then
+ left = left + (p.ls or 0)
+ right = right + (p.rs or 0)
+ end
+ --
+ local bp = b.p -- page
+ if trace_shapes then
+ report_shapes("tag %a, left %p, right %p, par %s, page %s, column %s",
+ left,right,bn or "-",bp or "-",bc or "-")
end
--
if bindex == eindex then
return {
- list = { [b.p] = { singlepart(b,e,collected[br],left,right,obeyhang) } },
+ list = { [bp] = { singlepart(b,e,p,bp,collected[br],left,right) } },
bpos = b,
epos = e,
}
else
local list = {
- [b.p] = { firstpart(b,collected[br],left,right,obeyhang) },
+ [bp] = { firstpart(b,e,p,bp,collected[br],left,right) },
}
for i=bindex+1,eindex-1 do
br = f_tag_two(btag,i)
@@ -456,21 +759,23 @@ local function calculatemultipar(tag,obeyhang)
if not r then
report_graphics("invalid middle for %a",br)
else
- local p = r.p
- local pp = list[p]
+ local rp = r.p -- page
+ local pp = list[rp]
+ local mp = middlepart(b,e,p,rp,r,left,right)
if pp then
- pp[#pp+1] = middlepart(r,left,right,obeyhang)
+ pp[#pp+1] = mp
else
- list[p] = { middlepart(r,left,right,obeyhang) }
+ list[rp] = { mp }
end
end
end
- local p = e.p
- local pp = list[p]
+ local ep = e.p -- page
+ local pp = list[ep]
+ local lp = lastpart(b,e,p,ep,collected[er],left,right)
if pp then
- pp[#pp+1] = lastpart(e,collected[er],left,right,obeyhang)
+ pp[#pp+1] = lp
else
- list[p] = { lastpart(e,collected[er],left,right,obeyhang) }
+ list[ep] = { lp }
end
return {
list = list,
@@ -480,41 +785,8 @@ local function calculatemultipar(tag,obeyhang)
end
end
--- local pending = { } -- needs gc
---
--- local function register(data,n,anchor)
--- local pa = pending[anchor]
--- if not pa then
--- pa = { }
--- pending[anchor] = pa
--- end
--- for page, pagedata in next, data do
--- local pap = pa[page]
--- if pap then
--- pap[#pap+1] = n
--- else
--- pa[page] = { n }
--- end
--- end
--- end
---
--- function backgrounds.registered(anchor,page)
--- local pa = pending[anchor]
--- if pa then
--- concat(pa,",")
--- else
--- return ""
--- end
--- end
-
local pbg = { } -- will move to pending
-function backgrounds.calculatemultipar(n)
- if not pbg[n] then
- pbg[n] = calculatemultipar("pbg",n) or { }
- end
-end
-
local multilocs = {
single = 1, -- maybe 0
first = 1,
@@ -524,7 +796,7 @@ local multilocs = {
-- if unknown context_abck : input mp-abck.mpiv ; fi ;
-local f_template_a = [[
+local f_template_a = formatters[ [[
path multiregs[], multipars[], multibox ;
string multikind[] ;
numeric multilocs[], nofmultipars ;
@@ -534,69 +806,340 @@ numeric par_strut_height, par_strut_depth, par_line_height ;
par_strut_height := %p ;
par_strut_depth := %p ;
par_line_height := %p ;
-]]
+]] ]
-local f_template_b = [[
+local f_template_b = formatters[ [[
multilocs[%s] := %s ;
multikind[%s] := "%s" ;
multipars[%s] := (%--t--cycle) shifted - (%p,%p) ;
-]]
+]] ]
-local f_template_c = [[
-multiregs[%s] := (%--t--cycle) shifted - %s ;
-]]
+-- unspiked(simplified(%--t--cycle)) shifted - (%p,%p) ;
-local f_template_d = [[
+local f_template_c = formatters[ [[
setbounds currentpicture to multibox ;
-]]
-
-f_template_a = formatters[f_template_a]
-f_template_b = formatters[f_template_b]
-f_template_c = formatters[f_template_c]
-f_template_d = formatters[f_template_d]
-
-local function fetchmultipar(n,anchor,page,obeyhang)
- local data = pbg[n]
- if not data then
- data = calculatemultipar(n,obeyhang)
- pbg[n] = data -- can be replaced by register
- -- register(data.list,n,anchor)
- end
- if data then
- local list = data.list
+]] ]
+
+local function freemultipar(pagedata,frees) -- ,k
+ -- if k == 3 then
+ -- -- tables have local regions
+ -- return
+ -- end
+ if not frees then
+ return
+ end
+ local nfree = #frees
+ if nfree == 0 then
+ return
+ end
+ for i=1,#pagedata do
+ local data = pagedata[i]
+ local area = data.area
+
+ if area then
+
+ local region = data.region
+ local y = 0 -- region.y
+ -- local x = region.x
+ local areas = { }
+ data.areas = areas
+
+ local f_1, n_1 = { }, 0
+ local f_2, n_2 = { }, 0
+ for i=1,#frees do
+ local f = frees[i]
+ local k = f.k
+ if k == 1 then -- pag
+ n_1 = n_1 + 1
+ f_1[n_1] = f
+ elseif k == 2 or k == 3 then -- par
+ n_2 = n_2 + 1
+ f_2[n_2] = f
+ end
+ end
+
+ local lineheight = tex.dimen.lineheight
+
+ -- page floats
+
+ local function check_one(free1,free2)
+ local temp = { }
+ local some = false
+ local top = (free2 and (y + free2.y + free2.h + (free2.to or 0))) or false
+ local bot = (free1 and (y + free1.y - free1.d - (free1.bo or 0))) or false
+ for i=1,#area do
+ local a = area[i]
+ local x = a[1]
+ local y = a[2]
+ if free2 and y <= top then
+ y = top
+ end
+ if free1 and y >= bot then
+ y = bot
+ end
+ if not some then
+ some = y
+ elseif some == true then
+ -- done
+ elseif y ~= some then
+ some = true
+ end
+ temp[i] = { x, y }
+ end
+ if some == true then
+ areas[#areas+1] = temp
+ end
+ end
+
+ if n_1 > 0 then
+ check_one(false,f_1[1])
+ for i=2,n_1 do
+ check_one(f_1[i-1],f_1[i])
+ end
+ check_one(f_1[n_1],false)
+ end
+
+ -- par floats
+
+ if #areas == 0 then
+ areas[1] = area
+ end
+
+ -- we can collect the coordinates first
+
+ local function check_two(area,frees)
+ local ul = area[1]
+ local ur = area[2]
+ local lr = area[3]
+ local ll = area[4]
+ local ulx, uly = ul[1], ul[2]
+ local urx, ury = ur[1], ur[2]
+ local lrx, lry = lr[1], lr[2]
+ local llx, lly = ll[1], ll[2]
+
+ local temp = { }
+ local n = 0
+ local done = false
+
+ for i=1,#frees do
+ local free = frees[i]
+ local fx = free.x
+ local fy = free.y
+ local ymax = y + fy + free.h + (free.to or 0)
+ local ymin = y + fy - free.d - (free.bo or 0)
+ local xmin = fx - (free.lo or 0)
+ local xmax = fx + free.w + (free.ro or 0)
+ if free.k == 3 then
+ if uly <= ymax and uly >= ymin and lly <= ymin then
+ if trace_free then
+ report_free("case 1, top, right") -- ok
+ end
+ n = n + 1 temp[n] = { xmin, ury }
+ n = n + 1 temp[n] = { xmin, ymin }
+ n = n + 1 temp[n] = { lrx, ymin }
+ n = n + 1 temp[n] = { lrx, lry }
+ done = true
+ elseif uly >= ymax and lly <= ymin then
+ if trace_free then
+ report_free("case 2, outside, right") -- ok
+ end
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmin, ury }
+ else
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { urx, ymax }
+ end
+ n = n + 1 temp[n] = { xmin, ymax }
+ n = n + 1 temp[n] = { xmin, ymin }
+ n = n + 1 temp[n] = { lrx, ymin }
+ n = n + 1 temp[n] = { lrx, lry }
+ done = true
+ elseif lly <= ymax and lly >= ymin and uly >= ymax then
+ if trace_free then
+ report_free("case 3, bottom, right")
+ end
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmin, ury }
+ else
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { urx, ymax }
+ end
+ n = n + 1 temp[n] = { xmin, ymax }
+ n = n + 1 temp[n] = { xmin, lry }
+ done = true
+ elseif uly <= ymax and lly >= ymin then
+ if trace_free then
+ report_free("case 4, inside, right")
+ end
+ n = n + 1 temp[n] = { xmin, lly }
+ n = n + 1 temp[n] = { xmin, uly }
+ done = true
+ else
+ -- case 0
+ if trace_free then
+ report_free("case 0, nothing")
+ end
+ end
+ end
+ end
+
+ if not done then
+ if trace_free then
+ report_free("no right shape")
+ end
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { lrx, lry }
+ n = n + 1 temp[n] = { llx, lly }
+ else
+ done = false
+ end
+
+ for i=#frees,1,-1 do
+ local free = frees[i]
+ local fx = free.x
+ local fy = free.y
+ local ymax = y + fy + free.h + (free.to or 0)
+ local ymin = y + fy - free.d - (free.bo or 0)
+ local xmin = fx - (free.lo or 0)
+ local xmax = fx + free.w + (free.ro or 0)
+ if free.k == 2 then
+ if uly <= ymax and uly >= ymin and lly <= ymin then
+ if trace_free then
+ report_free("case 1, top, left") -- ok
+ end
+ n = n + 1 temp[n] = { ulx, ymin }
+ n = n + 1 temp[n] = { xmax, ymin }
+ n = n + 1 temp[n] = { xmax, uly }
+ done = true
+ elseif uly >= ymax and lly <= ymin then
+ if trace_free then
+ report_free("case 2, outside, left") -- ok
+ end
+ n = n + 1 temp[n] = { llx, lly }
+ n = n + 1 temp[n] = { llx, ymin }
+ n = n + 1 temp[n] = { xmax, ymin }
+ n = n + 1 temp[n] = { xmax, ymax }
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmax, uly }
+ else
+ n = n + 1 temp[n] = { llx, ymax }
+ n = n + 1 temp[n] = { llx, uly }
+ end
+ done = true
+ elseif lly <= ymax and lly >= ymin and uly >= ymax then
+ if trace_free then
+ report_free("case 3, bottom, left")
+ end
+ n = n + 1 temp[n] = { xmax, lly }
+ n = n + 1 temp[n] = { xmax, ymax }
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmax, uly }
+ else
+ n = n + 1 temp[n] = { llx, ymax }
+ n = n + 1 temp[n] = { llx, uly }
+ end
+ done = true
+ elseif uly <= ymax and lly >= ymin then
+ if trace_free then
+ report_free("case 4, inside, left")
+ end
+ n = n + 1 temp[n] = { xmax, lly }
+ n = n + 1 temp[n] = { xmax, uly }
+ done = true
+ else
+ -- case 0
+ end
+ end
+ end
+
+ if not done then
+ if trace_free then
+ report_free("no left shape")
+ end
+ n = n + 1 temp[n] = { llx, lly }
+ end
+ n = n + 1 temp[n] = { ulx, uly }
+
+ return temp
+ end
+
+ if n_2 > 0 then
+ for i=1,#areas do
+ local area = areas[i]
+ if #area == 4 then -- and also check type, must be pargaraph
+ areas[i] = check_two(area,f_2)
+ else
+ -- message that not yet supported
+ end
+ end
+ end
+
+ for i=1,#areas do
+ finish(areas[i]) -- again
+ end
+
+ end
+
+ end
+end
+
+local function fetchmultipar(n,anchor,page)
+ local a = jobpositions.collected[anchor]
+ if not a then
+ report_graphics("missing anchor %a",anchor)
+ else
+ local data = pbg[n]
+ if not data then
+ data = calculatemultipar(n)
+ pbg[n] = data -- can be replaced by register
+ -- register(data.list,n,anchor)
+ end
+ local list = data and data.list
if list then
local pagedata = list[page]
if pagedata then
+ local k = data.bpos.k
+ if k ~= 3 then
+ -- to be checked: no need in txt mode
+ freemultipar(pagedata,jobpositions.free[page])
+ end
local nofmultipars = #pagedata
- -- report_graphics("fetching %a at page %s using anchor %a containing %s multipars",n,page,anchor,nofmultipars)
- local a = jobpositions.collected[anchor]
- if not a then
- report_graphics("missing anchor %a",anchor)
- else
- local trace = false
- local x, y, w, h, d = a.x, a.y, a.w, a.h, a.d
- local bpos = data.bpos
- local bh, bd = bpos.h, bpos.d
- local result = { f_template_a(nofmultipars,w,h+d,bh,bd,bh+bd) }
- for i=1,nofmultipars do
- local region = pagedata[i]
- result[#result+1] = f_template_b(
- i, multilocs[region.location],
- i, region.location,
- i, region.area, x, y-region.region.y)
- if trace then
- result[#result+1] = f_template_c(i, regionarea(region.region), offset)
+ if trace_shapes then
+ report_graphics("fetching %a at page %s using anchor %a containing %s multipars",
+ n,page,anchor,nofmultipars)
+ end
+ local x, y = a.x, a.y
+ local w, h, d = a.w, a.h, a.d
+ local bpos = data.bpos
+ local bh, bd = bpos.h, bpos.d
+ local result = { false } -- slot 1 will be set later
+ local n = 0
+ for i=1,nofmultipars do
+ local data = pagedata[i]
+ local location = data.location
+ local region = data.region
+ local areas = data.areas
+ if not areas then
+ areas = { data.area }
+ end
+ for i=1,#areas do
+ local area = areas[i]
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
end
+ n = n + 1
+ result[n+1] = f_template_b(n,multilocs[location],n,location,n,area,x,y)
end
- data[page] = nil
- result[#result+1] = f_template_d()
- result = concat(result,"\n")
- return result
end
+ data[page] = nil
+ result[1] = f_template_a(n,w,h+d,bh,bd,bh+bd) -- was delayed
+ result[n+2] = f_template_c()
+ return concat(result,"\n")
end
end
end
- return f_template_a(0,"origin",0,0,0)
+ return f_template_a(0,0,0,0,0,0);
end
backgrounds.fetchmultipar = fetchmultipar
@@ -613,28 +1156,19 @@ implement {
arguments = { "string", "string", "integer" }
}
-implement {
- name = "fetchmultishape",
- actions = { fetchmultipar, context },
- arguments = { "string", "string", "integer", true }
-}
-
-local f_template_a = [[
+local f_template_a = formatters[ [[
path posboxes[], posregions[] ;
numeric pospages[] ;
numeric nofposboxes ;
nofposboxes := %s ;
%t ;
-]]
+]] ]
-local f_template_b = [[
+local f_template_b = formatters[ [[
pospages[%s] := %s ;
posboxes[%s] := (%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle ;
posregions[%s] := (%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle ;
-]]
-
-f_template_a = formatters[f_template_a]
-f_template_b = formatters[f_template_b]
+]] ]
implement {
name = "fetchposboxes",
@@ -655,7 +1189,7 @@ implement {
if r then
local rx, ry, rw, rh, rd = r.x, r.y, r.w, r.h, r.d
local cx = c.x - rx
- local cy = c.y - ry
+ local cy = c.y
local cw = cx + c.w
local ch = cy + c.h
local cd = cy - c.d
@@ -668,7 +1202,7 @@ implement {
end
end
else
- print("\n missing",tag)
+ -- print("\n missing",tag)
end
end
context(f_template_a(nofboxes,list))
@@ -681,17 +1215,19 @@ implement {
name = "doifelserangeonpage",
arguments = { "string", "string", "integer" },
actions = function(first,last,page)
- local collected = jobpositions.collected
- local f = collected[first]
- if not f or f.p == true then
- doifelse(false)
- return
- end
- local l = collected[last]
- if not l or l.p == true then
- doifelse(false)
- return
+ local c = jobpositions.collected
+ local f = c[first]
+ if f then
+ f = f.p
+ if f and f ~= true and page >= f then
+ local l = c[last]
+ if l then
+ l = l.p
+ doifelse(l and l ~= true and page <= l)
+ return
+ end
+ end
end
- doifelse(page >= f.p and page <= l.p)
+ doifelse(false)
end
}