if not modules then modules = { } end modules ['anch-pgr'] = { version = 1.001, comment = "companion to anch-pgr.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- todo: we need to clean up lists (of previous pages) local commands, context = commands, context local format = string.format local abs = math.abs local concat, sort = table.concat, table.sort local splitter = lpeg.splitat(":") local lpegmatch = lpeg.match local jobpositions = job.positions local formatters = string.formatters local report_graphics = logs.reporter("graphics") local f_b_tag = formatters["b:%s"] local f_e_tag = formatters["e:%s"] local f_p_tag = formatters["p:%s"] local f_tag_two = formatters["%s:%s"] local f_point = formatters["%p"] local f_pair = formatters["(%p,%p)"] local f_path = formatters["%--t--cycle"] 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 -- we can use a 'local t, n' and reuse the table local eps = 2 local function add(t,x,y,last,direction) local n = #t if n == 0 then t[n+1] = { x, y } 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 } 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 else if 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 } end end end end end -- local function add(t,x,y,last) -- t[#t+1] = { 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 end end end 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 end if ybot > y then last = i break 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] } end lp[#lp+1] = { t[last][1], ybot } return lp end -- todo: mark regions and free paragraphs in collected 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 -- -- 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") 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") 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 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") end return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot) 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 bx, by = b.x, b.y local ex, ey = e.x, e.y local rx, ry = r.x, r.y 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 -- (at least visually) injected then it's best to stress the issue. ex = rw end local area if by == ey then area = { f_pair(bx,bh-ry), f_pair(ex,eh-ry), f_pair(ex,ed-ry), f_pair(bx,bd-ry), } 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 end return { location = "single", region = r, area = area, } end local function firstpart(b,r,left,right,obeyhang) local bx, by = b.x, b.y local rx, ry = r.x, r.y 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]) end return { location = "first", region = r, area = area, } end local function middlepart(r,left,right,obeyhang) local rx, ry = r.x, r.y 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]) end return { location = "middle", region = r, area = area, } end local function lastpart(e,r,left,right,obeyhang) local ex, ey = e.x, e.y local rx, ry = r.x, r.y 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]) end return { location = "last", region = r, area = area, } end graphics = graphics or { } local backgrounds = { } graphics.backgrounds = backgrounds local function calculatemultipar(tag,obeyhang) 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) return { } end local br = b.r local er = e.r if not br or not er then report_graphics("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) return { } end local bindex = tonumber(bindex) local eindex = tonumber(eindex) -- Here we compensate for columns (in tables): a table can have a set of column -- 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 local right = 0 local rc = b.c 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 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 end -- if bindex == eindex then return { list = { [b.p] = { singlepart(b,e,collected[br],left,right,obeyhang) } }, bpos = b, epos = e, } else local list = { [b.p] = { firstpart(b,collected[br],left,right,obeyhang) }, } for i=bindex+1,eindex-1 do br = f_tag_two(btag,i) local r = collected[br] if not r then report_graphics("invalid middle for %a",br) else local p = r.p local pp = list[p] if pp then pp[#pp+1] = middlepart(r,left,right,obeyhang) else list[p] = { middlepart(r,left,right,obeyhang) } end end end local p = e.p local pp = list[p] if pp then pp[#pp+1] = lastpart(e,collected[er],left,right,obeyhang) else list[p] = { lastpart(e,collected[er],left,right,obeyhang) } end return { list = list, bpos = b, epos = e, } 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, middle = 2, last = 3, } -- if unknown context_abck : input mp-abck.mpiv ; fi ; local f_template_a = [[ path multiregs[], multipars[], multibox ; string multikind[] ; numeric multilocs[], nofmultipars ; nofmultipars := %s ; multibox := unitsquare xyscaled (%p,%p) ; 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 = [[ multilocs[%s] := %s ; multikind[%s] := "%s" ; multipars[%s] := (%--t--cycle) shifted - (%p,%p) ; ]] local f_template_c = [[ multiregs[%s] := (%--t--cycle) shifted - %s ; ]] local f_template_d = [[ 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] function backgrounds.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 if list then local pagedata = list[page] if pagedata then 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) end end data[page] = nil result[#result+1] = f_template_d() result = concat(result,"\n") return result end end end end return f_template_a(0,"origin",0,0,0) end backgrounds.point = f_point backgrounds.pair = f_pair backgrounds.path = f_path function commands.fetchmultipar(n,anchor,page) context(backgrounds.fetchmultipar(n,anchor,page)) end function commands.fetchmultishape(n,anchor,page) context(backgrounds.fetchmultipar(n,anchor,page,true)) end local f_template_a = [[ path posboxes[], posregions[] ; numeric pospages[] ; numeric nofposboxes ; nofposboxes := %s ; %t ; ]] local f_template_b = [[ 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] function commands.fetchposboxes(tags,anchor,page) -- no caching (yet) / todo: anchor, page local collected = jobpositions.collected if type(tags) == "string" then tags = utilities.parsers.settings_to_array(tags) end local list, nofboxes = { }, 0 for i=1,#tags do local tag= tags[i] local c = collected[tag] if c then local r = c.r if r then r = collected[r] 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 cw = cx + c.w local ch = cy + c.h local cd = cy - c.d nofboxes = nofboxes + 1 list[nofboxes] = f_template_b( nofboxes,c.p, nofboxes,cx,ch,cw,ch,cw,cd,cx,cd, nofboxes,0,rh,rw,rh,rw,rd,0,rd ) end end else print("\n missing",tag) end end context(f_template_a(nofboxes,list)) end local doifelse = commands.doifelse function commands.doifelsemultipar(n,page,obeyhang) local data = pbg[n] if not data then data = calculatemultipar(n,obeyhang) pbg[n] = data end if page then doifelse(data and data[page] and true) else doifelse(data and next(data) and true) end end function commands.doifelserangeonpage(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 end doifelse(page >= f.p and page <= l.p) end