diff options
Diffstat (limited to 'tex/context/base/mkiv/anch-pgr.lua')
-rw-r--r-- | tex/context/base/mkiv/anch-pgr.lua | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/anch-pgr.lua b/tex/context/base/mkiv/anch-pgr.lua new file mode 100644 index 000000000..b5c2ae628 --- /dev/null +++ b/tex/context/base/mkiv/anch-pgr.lua @@ -0,0 +1,697 @@ +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 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 commands = commands +local context = context + +local implement = interfaces.implement + +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 + 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 } + 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] + +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 + 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.fetchmultipar = fetchmultipar + +backgrounds.point = f_point +backgrounds.pair = f_pair +backgrounds.path = f_path + +-- n anchor page + +implement { + name = "fetchmultipar", + actions = { fetchmultipar, context }, + arguments = { "string", "string", "integer" } +} + +implement { + name = "fetchmultishape", + actions = { fetchmultipar, context }, + arguments = { "string", "string", "integer", true } +} + +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] + +implement { + name = "fetchposboxes", + arguments = { "string", "string", "integer" }, + actions = function(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 + +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 + end + doifelse(page >= f.p and page <= l.p) + end +} |