diff options
Diffstat (limited to 'tex/context/base/mkiv/anch-pgr.lua')
-rw-r--r-- | tex/context/base/mkiv/anch-pgr.lua | 1348 |
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 } |