diff options
author | Context Git Mirror Bot <phg42.2a@gmail.com> | 2016-03-01 15:15:08 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg42.2a@gmail.com> | 2016-03-01 15:15:08 +0100 |
commit | 2a958dcf22dd71ba1e4408648676d44c16d7e3bf (patch) | |
tree | 8a118b195ac47f9e926bb5083f3d0f91c352d281 /tex/context/base/mkiv/page-cst.lua | |
parent | 48c3ce21b30a886099e9afc2edf683e8a47ba29e (diff) | |
download | context-2a958dcf22dd71ba1e4408648676d44c16d7e3bf.tar.gz |
2016-03-01 14:06:00
Diffstat (limited to 'tex/context/base/mkiv/page-cst.lua')
-rw-r--r-- | tex/context/base/mkiv/page-cst.lua | 1454 |
1 files changed, 1454 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/page-cst.lua b/tex/context/base/mkiv/page-cst.lua new file mode 100644 index 000000000..782bbebfc --- /dev/null +++ b/tex/context/base/mkiv/page-cst.lua @@ -0,0 +1,1454 @@ +if not modules then modules = { } end modules ["page-cst"] = { + version = 1.001, + comment = "companion to page-cst.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: check what is used + +local next, type = next, type +local ceil, floor, odd, round = math.ceil, math.floor, math.odd, math.round +local lower = string.lower +local copy = table.copy + +local trace_state = false trackers.register("columnsets.trace", function(v) trace_state = v end) +local trace_detail = false trackers.register("columnsets.detail", function(v) trace_detail = v end) +local trace_cells = false trackers.register("columnsets.cells", function(v) trace_cells = v end) + +local report = logs.reporter("column sets") + +local setmetatableindex = table.setmetatableindex + +local properties = nodes.properties + +local nodecodes = nodes.nodecodes +local gluecodes = nodes.gluecodes +local rulecodes = nodes.rulecodes + +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local penalty_code = nodecodes.penalty +local insert_code = nodecodes.ins +local mark_code = nodecodes.mark +local rule_code = nodecodes.rule + +local topskip_code = gluecodes.topskip +local lineskip_code = gluecodes.lineskip +local baselineskip_code = gluecodes.baselineskip +local userskip_code = gluecodes.userskip + +local nuts = nodes.nuts +local tonode = nuts.tonode +local tonut = nuts.tonut + +local hpack = nuts.hpack +local vpack = nuts.vpack +local freenode = nuts.free +local flushlist = nuts.flush_list +local removenode = nuts.remove + +local getfield = nuts.getfield +local setfield = nuts.setfield +local setlink = nuts.setlink +local setlist = nuts.setlist +local setnext = nuts.setnext +local setprev = nuts.setprev +local setsubtype = nuts.setsubtype +local setbox = nuts.setbox + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getlist = nuts.getlist +local getsubtype = nuts.getsubtype +local takebox = nuts.takebox +local takelist = nuts.takelist +local splitbox = nuts.splitbox +local getskip = nuts.getskip +local getattribute = nuts.getattribute +local copylist = nuts.copy_list + +local getbox = nuts.getbox +local getcount = tex.getcount +local getdimen = tex.getdimen + +local texsetbox = tex.setbox +local texsetcount = tex.setcount +local texsetdimen = tex.setdimen + +local theprop = nuts.theprop + +local nodepool = nuts.pool + +local new_hlist = nodepool.hlist +local new_vlist = nodepool.vlist +local new_kern = nodepool.kern +local new_trace_rule = nodepool.rule +local new_empty_rule = nodepool.emptyrule + +local context = context +local implement = interfaces.implement + +local variables = interfaces.variables +local v_here = variables.here +local v_fixed = variables.fixed +local v_top = variables.top +local v_bottom = variables.bottom +local v_repeat = variables["repeat"] +local v_left = variables.left +local v_right = variables.right +local v_yes = variables.yes +local v_page = variables.page +local v_first = variables.first +local v_last = variables.last +local v_wide = variables.wide + +pagebuilders = pagebuilders or { } -- todo: pages.builders +pagebuilders.columnsets = pagebuilders.columnsets or { } +local columnsets = pagebuilders.columnsets + +local data = { [""] = { } } + +-- todo: use state + +local function setstate(t,start) + if start or not t.firstcolumn then + t.firstcolumn = odd(getcount("realpageno")) and 1 or 2 + end + if t.firstcolumn > 1 then + t.firstcolumn = 1 + t.lastcolumn = t.nofleft + t.state = "left" + else + t.firstcolumn = t.nofleft + 1 + t.lastcolumn = t.firstcolumn + t.nofright - 1 + t.state = "right" + end + t.currentcolumn = t.firstcolumn + t.currentrow = 1 +end + +function columnsets.define(t) + local name = t.name + local nofleft = t.nofleft or 1 + local nofright = t.nofright or 1 + local nofcolumns = nofleft + nofright + local dataset = data[name] or { } + data[name] = dataset + dataset.nofleft = nofleft + dataset.nofright = nofright + dataset.nofcolumns = nofcolumns + dataset.nofrows = t.nofrows or 1 + dataset.distance = t.distance or getdimen("bodyfontsize") + dataset.maxwidth = t.maxwidth or getdimen("makeupwidth") + dataset.lineheight = t.lineheight or getdimen("globalbodyfontstrutheight") + dataset.linedepth = t.linedepth or getdimen("globalbodyfontstrutdepth") + -- + dataset.cells = { } + dataset.currentcolumn = 1 + dataset.currentrow = 1 + -- + dataset.lines = dataset.lines or setmetatableindex("table") + dataset.start = dataset.start or setmetatableindex("table") + -- + dataset.page = 1 + -- + local distances = dataset.distances or setmetatableindex(function(t,k) + return dataset.distance + end) + dataset.distances = distances + -- + local widths = dataset.widths or setmetatableindex(function(t,k) + return dataset.width + end) + dataset.widths = widths + -- + local width = t.width + if not width or width == 0 then + local dl = 0 + local dr = 0 + for i=1,nofleft-1 do + dl = dl + distances[i] + end + for i=1,nofright-1 do + dr = dr + distances[nofleft+i] + end + local nl = nofleft + local nr = nofright + local wl = dataset.maxwidth + local wr = wl + for i=1,nofleft do + local w = rawget(widths,i) + if w then + nl = nl - 1 + wl = wl - w + end + end + for i=1,nofright do + local w = rawget(widths,nofleft+i) + if w then + nr = nr - 1 + wr = wr - w + end + end + dl = (wl - dl) / nl + dr = (wr - dr) / nr + if dl > dr then + report("using %s page column width %p in columnset %a","right",dr,name) + width = dr + elseif dl < dr then + report("using %s page column width %p in columnset %a","left",dl,name) + width = dl + else + width = dl + end + end + -- report("width %p, nleft %i, nright %i",width,nofleft,nofright) + width = round(width) + dataset.width = width + local spans = { } + dataset.spans = spans + for i=1,nofleft do + local s = { } + local d = 0 + for j=1,nofleft-i+1 do + d = d + width + s[j] = round(d) + d = d + distances[j] + end + spans[i] = s + end + for i=1,nofright do + local s = { } + local d = 0 + for j=1,nofright-i+1 do + d = d + width + s[j] = round(d) + d = d + distances[j] + end + spans[nofleft+i] = s + end + -- + local spreads = copy(spans) + dataset.spreads = spreads + local gap = 2 * getdimen("backspace") + for l=1,nofleft do + local s = spreads[l] + local n = #s + local o = s[n] + gap + for r=1,nofright do + n = n + 1 + s[n] = s[r] + o + end + end + -- + texsetdimen("d_page_grid_column_width",dataset.width) + -- + setstate(dataset,true) + -- + return dataset +end + +local function check(dataset) + local cells = dataset.cells + local page = dataset.page + local offset = odd(page) and dataset.nofleft or 0 + local start = dataset.start + local list = rawget(start,page) + if list then + for c, n in next, list do + local column = cells[offset + c] + if column then + for r=1,n do + column[r] = true + end + end + end + start[page] = nil + end + local lines = dataset.lines + local list = rawget(lines,page) + local rows = dataset.nofrows + if list then + for c, n in next, list do + local column = cells[offset + c] + if column then + if n > 0 then + for r=n+1,rows do + column[r] = true + end + elseif n < 0 then + for r=rows,rows+n+1,-1 do + column[r] = true + end + end + end + end + lines[page] = nil + end +end + +local function erase(dataset,all) + local cells = dataset.cells + local nofrows = dataset.nofrows + local first = 1 + local last = dataset.nofcolumns + -- + if not all then + first = dataset.firstcolumn or first + last = dataset.lastcolumn or last + end + for c=first,last do + local column = { } + for r=1,nofrows do + if column[r] then + report("slot (%i,%i) is not empty",c,r) + end + column[r] = false -- not used + end + cells[c] = column + end +end + +function columnsets.reset(t) + local dataset = columnsets.define(t) + erase(dataset,true) + check(dataset) +end + +function columnsets.prepareflush(name) + local dataset = data[name] + local cells = dataset.cells + local firstcolumn = dataset.firstcolumn + local lastcolumn = dataset.lastcolumn + local nofrows = dataset.nofrows + local lineheight = dataset.lineheight + local linedepth = dataset.linedepth + local widths = dataset.widths + local height = (lineheight+linedepth)*nofrows -- - linedepth + -- + local columns = { } + dataset.columns = columns + -- + for c=firstcolumn,lastcolumn do + local column = cells[c] + for r=1,nofrows do + local cell = column[r] + if (cell == false) or (cell == true) then + if trace_cells then + column[r] = new_trace_rule(65536*2,lineheight,linedepth) + else + column[r] = new_empty_rule(0,lineheight,linedepth) + end + end + end + for r=1,nofrows-1 do + setlink(column[r],column[r+1]) + end + local v = new_vlist(column[1]) + setfield(v,"height",height) +-- setfield(v,"depth",linedepth) + setfield(v,"width",widths[c]) + columns[c] = v + end + -- + texsetcount("c_page_grid_first_column",firstcolumn) + texsetcount("c_page_grid_last_column",lastcolumn) +end + +function columnsets.flushcolumn(name,column) + local dataset = data[name] + local columns = dataset.columns + local packed = columns[column] + setbox("b_page_grid_column",packed) + columns[column] = nil +end + +function columnsets.finishflush(name) + local dataset = data[name] + local cells = dataset.cells + local firstcolumn = dataset.firstcolumn + local lastcolumn = dataset.lastcolumn + local nofrows = dataset.nofrows + for c=firstcolumn,lastcolumn do + local column = { } + for r=1,nofrows do + column[r] = false -- not used + end + cells[c] = column + end + dataset.page = dataset.page + 1 + check(dataset) + setstate(dataset) +end + +function columnsets.block(t) + local dataset = data[t.name] + local cells = dataset.cells + local nofcolumns = dataset.nofcolumns + local nofrows = dataset.nofrows + -- + local c = t.c or 0 + local r = t.r or 0 + if c == 0 or r == 0 or c > nofcolumns or r > nofrows then + return + end + local nc = t.nc or 0 + local nr = t.nr or 0 + if nc == 0 then + return + end + if nr == 0 then + return + end + local rr = r + nr - 1 + local cc = c + nc - 1 + if rr > nofrows then + rr = nofrows + end + if cc > nofcolumns then + cc = nofcolumns + end + for i=c,cc do + local column = cells[i] + for j=r,rr do + column[j] = true + end + end +end + +local function here(c,r,nr,nofcolumns,nofrows,cells,width,spans) + local rr = r + nr - 1 + if rr > nofrows then + return false + end + local cc = 0 + local wd = spans[c] + local wc = 0 + local nc = 0 + for i=c,nofcolumns do + nc = nc + 1 + wc = wd[nc] + if not wc then + break + elseif wc >= width then + cc = i + break + end + end + if cc == 0 or cc > nofcolumns then + -- report("needed %p, no slot free at (%i,%i)",width,c,r) + return false + end + for i=c,cc do + local column = cells[i] + for j=r,rr do + if column[j] then + -- report("width %p, needed %p, checking (%i,%i) x (%i,%i), %s",width,wc,c,r,nc,nr,"quit") + return false + end + end + end + -- report("width %p, needed %p, checking (%i,%i) x (%i,%i), %s",width,wc,c,r,nc,nr,"match") + return c, r, nc +end + +local methods = { + [v_here] = here, + [v_fixed] = here, + tblr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for j=1,nofrows-nr+1 do + for i=c,nofcolumns do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + lrtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=1,nofrows-nr+1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + tbrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for j=1,nofrows-nr+1 do + for i=nofcolumns,c,-1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + rltb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=nofcolumns,c,-1 do + for j=1,nofrows-nr+1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + btlr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for j=nofrows-nr+1,1,-1 do + for i=c,nofcolumns do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + lrbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=nofrows-nr+1,1,-1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + btrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for j=nofrows-nr+1,1,-1 do + for i=nofcolumns,c,-1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + rlbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=nofcolumns,c,-1 do + for j=nofrows-nr+1,1,-1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + fxtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=r,nofrows-nr+1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + r = 1 + end + end + end, + fxbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=nofrows-nr+1,r,-1 do + if not cells[i][j] then + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + r = 1 + end + end, + [v_top] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=1,nofrows-nr+1 do + if cells[i][j] then + break + else + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, + [v_bottom] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans) + for i=c,nofcolumns do + for j=1,nofrows-nr+1 do + if cells[i][j] then + break + else + local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans) + if c then + return c, r, cc + end + end + end + end + end, +} + +local threshold = 50 + +function columnsets.check(t) + local dataset = data[t.name] + local cells = dataset.cells + local nofcolumns = dataset.nofcolumns + local nofrows = dataset.nofrows + local widths = dataset.widths + local lineheight = dataset.lineheight + local linedepth = dataset.linedepth + local distances = dataset.distances + local spans = dataset.spans + -- + local method = lower(t.method or "tblr") + local boxwidth = t.width or 0 + local boxheight = t.height or 0 + local boxnumber = t.box + local box = boxnumber and getbox(boxnumber) + -- + if boxwidth > 0 and boxheight > 0 then + -- we're ok + elseif box then + boxwidth = getfield(box,"width") + boxheight = getfield(box,"height") + getfield(box,"depth") + else + report("empty box") + return + end + -- + local c = t.c or 0 + local r = t.r or 0 + if c == 0 then + c = dataset.currentcolumn + end + if r == 0 then + r = dataset.currentrow + end + if c == 0 or r == 0 or c > nofcolumns or r > nofrows then + texsetcount("c_page_grid_reserved_state",5) + return + end +-- report("checking width %p, height %p, depth %p, slot (%i,%i)",boxwidth,boxheight,boxdepth,c,r) + local nr = ceil(boxheight/(lineheight+linedepth)) + -- + local action = methods[method] + local cfound = false + local rfound = false + local lastcolumn = dataset.lastcolumn + -- if t.option == v_wide then + -- lastcolumn = nofcolumns + -- spans = dataset.spreads + -- end + if action then + cfound, rfound, nc = action(c,r,nr,lastcolumn,nofrows,cells,boxwidth-threshold,spans) + end + if not cfound and method ~= v_here then + cfound, rfound, nc = here(c,r,nr,lastcolumn,nofrows,cells,boxwidth-threshold,spans) + end + if cfound then + local ht = nr*(lineheight+linedepth) + local wd = spans[cfound][nc] + dataset.reserved_ht = ht + dataset.reserved_wd = wd + dataset.reserved_c = cfound + dataset.reserved_r = rfound + dataset.reserved_nc = nc + dataset.reserved_nr = nr + texsetcount("c_page_grid_reserved_state",0) + texsetdimen("d_page_grid_reserved_height",ht) + texsetdimen("d_page_grid_reserved_width",wd) + -- report("using (%i,%i) x (%i,%i) @ (%p,%p)",cfound,rfound,nc,nr,wd,ht) + else + dataset.reserved_ht = false + dataset.reserved_wd = false + dataset.reserved_c = false + dataset.reserved_r = false + dataset.reserved_nc = false + dataset.reserved_nr = false + texsetcount("c_page_grid_reserved_state",4) + -- texsetdimen("d_page_grid_reserved_height",0) + -- texsetdimen("d_page_grid_reserved_width",0) + -- report("no slot found") + end +end + +function columnsets.put(t) + local dataset = data[t.name] + local cells = dataset.cells + local widths = dataset.widths + local lineheight = dataset.lineheight + local linedepth = dataset.linedepth + local boxnumber = t.box + local box = boxnumber and takebox(boxnumber) + -- + local c = t.c or dataset.reserved_c + local r = t.r or dataset.reserved_r + if not c or not r then + -- report("no reserved slot (%i,%i)",c,r) + return + end + local lastc = c + dataset.reserved_nc - 1 + local lastr = r + dataset.reserved_nr - 1 + -- + for i=c,lastc do + local column = cells[i] + for j=r,lastr do + column[j] = true + end + end + cells[c][r] = box + setfield(box,"height",lineheight) + setfield(box,"depth",linedepth) + setfield(box,"width",widths[c]) + dataset.reserved_c = false + dataset.reserved_r = false + dataset.reserved_nc = false + dataset.reserved_nr = false + -- +end + +local function findgap(dataset) + local cells = dataset.cells + local nofcolumns = dataset.nofcolumns + local nofrows = dataset.nofrows + local currentrow = dataset.currentrow + local currentcolumn = dataset.currentcolumn + -- + local foundc = 0 + local foundr = 0 + local foundn = 0 + for c=currentcolumn,dataset.lastcolumn do + local column = cells[c] +foundn = 0 + for r=currentrow,nofrows do + if not column[r] then + if foundc == 0 then + foundc = c + foundr = r + end + foundn = foundn + 1 + elseif foundn > 0 then + return foundc, foundr, foundn + end + end + if foundn > 0 then + return foundc, foundr, foundn + end + currentrow = 1 + end +end + +-- we can enforce grid snapping + +-- local function checkroom(head,available,row) +-- if row == 1 then +-- while head do +-- local id = getid(head) +-- if id == glue_code then +-- head = getnext(head) +-- else +-- break +-- end +-- end +-- end +-- local used = 0 +-- local line = false +-- while head do +-- local id = getid(head) +-- if id == hlist_code or id == vlist_code or id == rule_code then -- <= rule_code +-- used = used + getfield(head,"height") + getfield(head,"depth") +-- line = true +-- elseif id == glue_code then +-- if line then +-- break +-- end +-- used = used + getfield(head,"width") +-- elseif id == kern_code then +-- used = used + getfield(head,"kern") +-- elseif id == penalty_code then +-- end +-- if used > available then +-- break +-- end +-- head = getnext(head) +-- end +-- return line, used +-- end + +local function checkroom(head,available,row) + if row == 1 then + while head do + local id = getid(head) + if id == glue_code then + head = getnext(head) + else + break + end + end + end + local used = 0 + local line = false + while head do + local id = getid(head) + if id == hlist_code or id == vlist_code or id == rule_code then -- <= rule_code + used = used + getfield(head,"height") + getfield(head,"depth") + line = true + if used > available then + break + end + elseif id == glue_code then + if line then + break + end + used = used + getfield(head,"width") + if used > available then + break + end + elseif id == kern_code then + used = used + getfield(head,"kern") + if used > available then + break + end + elseif id == penalty_code then + -- not good enough ... we need to look bakck too + if getfield(head,"penalty") >= 10000 then + line = false + else + break + end + end + head = getnext(head) + end + return line, used +end + +-- we could preroll on a cheap copy .. in fact, a split loop normally works on +-- a copy ... then we could also stepsise make the height smaller .. slow but nice + +-- local function findslice(dataset,head,available,column,row) +-- local used = 0 +-- local first = nil +-- local last = nil +-- local line = false +-- local lineheight = dataset.lineheight +-- local linedepth = dataset.linedepth +-- if row == 1 then +-- while head do +-- local id = getid(head) +-- if id == glue_code then +-- head = removenode(head,head,true) +-- else +-- break +-- end +-- end +-- end +-- while head do +-- -- no direction yet, if so use backend code +-- local id = getid(head) +-- local hd = 0 +-- if id == hlist_code or id == vlist_code or id == rule_code then -- <= rule_code +-- hd = getfield(head,"height") + getfield(head,"depth") +-- elseif id == glue_code then +-- hd = getfield(head,"width") +-- elseif id == kern_code then +-- hd = getfield(head,"kern") +-- elseif id == penalty_code then +-- end +-- if used + hd > available then +-- if first then +-- setnext(last) +-- setprev(head) +-- return used, first, head +-- else +-- return 0 +-- end +-- else +-- if not first then +-- first = head +-- end +-- used = used + hd +-- last = head +-- head = getnext(head) +-- end +-- end +-- return used, first +-- end + +-- todo +-- +-- first = takelist(done) +-- head = takelist(rest) +-- local tail = nuts.tail(first) +-- if false then +-- local disc = tex.lists.split_discards_head +-- if disc then +-- disc = tonut(disc) +-- setlink(tail,disc) +-- tail = nuts.tail(disc) +-- tex.lists.split_discards_head = nil +-- end +-- end +-- setlink(tail,head) + +-- We work on a copy because we need to keep properties. We can make faster copies +-- by only doing a one-level deep copy. + +local function findslice(dataset,head,available,column,row) + local first = nil + local lineheight = dataset.lineheight + local linedepth = dataset.linedepth + local linetotal = lineheight + linedepth + local slack = 65536 -- 1pt + local copy = copylist(head) + local attempts = 0 + local usedsize = available + while true do + attempts = attempts + 1 + texsetbox("scratchbox",tonode(new_vlist(copy))) + local done = splitbox("scratchbox",usedsize,"additional") + local used = getfield(done,"height") + local rest = takebox("scratchbox") + if used > (usedsize+slack) then + if trace_detail then + report("at (%i,%i) available %p, used %p, overflow %p",column,row,usedsize,used,used-usedsize) + end + -- flush copy + flushlist(takelist(done)) + flushlist(takelist(rest)) + -- check it we can try again + usedsize = usedsize - linetotal + if usedsize > linetotal then + copy = copylist(head) + else + return 0, nil, head + end + else + -- flush copied box + flushlist(takelist(done)) + flushlist(takelist(rest)) + -- deal with real data + texsetbox("scratchbox",tonode(new_vlist(head))) + done = splitbox("scratchbox",usedsize,"additional") + rest = takebox("scratchbox") + used = getfield(done,"height") + if attempts > 1 then + used = available + end + first = takelist(done) + head = takelist(rest) + -- return result + return used, first, head + end + end +end + +local nofcolumngaps = 0 + +function columnsets.add(name,box) + local dataset = data[name] + local cells = dataset.cells + local nofcolumns = dataset.nofcolumns + local nofrows = dataset.nofrows + local currentrow = dataset.currentrow + local currentcolumn = dataset.currentcolumn + local lineheight = dataset.lineheight + local linedepth = dataset.linedepth + local widths = dataset.widths + -- + local b = getbox(box) + local l = getlist(b) +-- dataset.rest = l + if l then + setlist(b,nil) + local hd = lineheight + linedepth + while l do + local foundc, foundr, foundn = findgap(dataset) + if foundc then + local available = foundn * hd + local used, first, last = findslice(dataset,l,available,foundc,foundr) + if first then + local v + if used == available or (foundr+foundn > nofrows) then + v = vpack(first,available,"exactly") + else + v = new_vlist(first) + end + nofcolumngaps = nofcolumngaps + 1 + -- getmetatable(v).columngap = nofcolumngaps + properties[v] = { columngap = nofcolumngaps } + -- report("setting gap %a at (%i,%i)",nofcolumngaps,foundc,foundr) + setfield(v,"height",lineheight) + setfield(v,"depth",linedepth) + setfield(v,"width",widths[currentcolumn]) + local column = cells[foundc] + -- + column[foundr] = v + used = used - hd + if used > 0 then + for r=foundr+1,foundr+foundn-1 do + used = used - hd + foundr = foundr + 1 + column[r] = true + if used <= 0 then + break + end + end + end + currentcolumn = foundc + currentrow = foundr + dataset.currentcolumn = currentcolumn + dataset.currentrow = currentrow + l = last + dataset.rest = l + else + local column = cells[foundc] + for i=foundr,foundr+foundn-1 do + column[i] = true + end + l = last + end + else + dataset.rest = l + return -- save and flush + end + end + end +end + +do + + -- A split approach is more efficient than a context(followup) inside + -- followup itself as we need less (internal) housekeeping. + + local followup = nil + local splitter = lpeg.splitter("*",tonumber) + + columnsets["noto"] = function(t) + return followup() + end + + columnsets["goto"] = function(name,target) + local dataset = data[name] + local nofcolumns = dataset.nofcolumns + if target == v_yes or target == "" then + local currentcolumn = dataset.currentcolumn + followup = function() + context(dataset.currentcolumn == currentcolumn and 1 or 0) + end + return followup() + end + if target == v_first then + if dataset.currentcolumn > 1 then + target = v_page + else + return context(0) + end + end + if target == v_page then + if dataset.currentcolumn == 1 and dataset.currentrow == 1 then + return context(0) + else + local currentpage = dataset.page + followup = function() + context(dataset.page == currentpage and 1 or 0) + end + return followup() + end + end + if target == v_last then + target = dataset.nofcolumns + if dataset.currentcolumn ~= target then + followup = function() + context(dataset.currentcolumn ~= target and 1 or 0) + end + return followup() + end + return + end + local targetpage = tonumber(target) + if targetpage then + followup = function() + context(dataset.currentcolumn ~= targetpage and 1 or 0) + end + return followup() + end + local targetcolumn, targetrow = lpeg.match(splitter,target) + if targetcolumn and targetrow then + if dataset.currentcolumn ~= targetcolumn and dataset.currentrow ~= targetrow then + followup = function() + if dataset.currentcolumn ~= targetcolumn then + context(1) + return + end + if dataset.currentcolumn == targetcolumn then + context(dataset.currentrow ~= targetrow and 1 or 0) + else + context(0) + end + end + return followup() + end + end + end + +end + +function columnsets.currentcolumn(name) + local dataset = data[name] + context(dataset.currentcolumn) +end + +function columnsets.flushrest(name,box) + local dataset = data[name] + local rest = dataset.rest + if rest then + dataset.rest = nil + setbox("global",box,new_vlist(rest)) + end +end + +function columnsets.setvsize(name) + local dataset = data[name] + local c, r, n = findgap(dataset) + if n then + dataset.currentcolumn = c + dataset.currentrow = r + else + dataset.currentcolumn = 1 + dataset.currentrow = 1 + n = 0 + end + local gap = n*(dataset.lineheight+dataset.linedepth) + texsetdimen("d_page_grid_gap_height",gap) + -- can be integrated + -- report("state %a, n %a, column %a, row %a",dataset.state,n,dataset.currentcolumn,dataset.currentrow) +end + +function columnsets.sethsize(name) + local dataset = data[name] + texsetdimen("d_page_grid_column_width",dataset.widths[dataset.currentcolumn]) +end + +function columnsets.sethspan(name,span) + -- no checking if there is really space, so we assume it can be + -- placed which makes spans a very explicit feature + local dataset = data[name] + local column = dataset.currentcolumn + local available = dataset.lastcolumn - column + 1 + if span > available then + span = available + end + local width = dataset.spans[column][span] + texsetdimen("d_page_grid_span_width",width) +end + +function columnsets.setlines(t) + local dataset = data[t.name] + dataset.lines[t.page][t.column] = t.value +end + +function columnsets.setstart(t) + local dataset = data[t.name] + dataset.start[t.page][t.column] = t.value +end + +function columnsets.setproperties(t) + local dataset = data[t.name] + local column = t.column + dataset.distances[column] = t.distance + dataset.widths[column] = t.width +end + +local areas = { } + +function columnsets.registerarea(t) + -- maybe metatable with values + areas[#areas+1] = t +end + +-- state : repeat | start + +local ctx_page_grid_set_area = context.protected.page_grid_set_area + +function columnsets.flushareas(name) + local nofareas = #areas + if nofareas == 0 then + return + end + local dataset = data[name] + local page = dataset.page + if odd(page) then + -- report("checking %i areas",#areas) + local kept = { } + for i=1,nofareas do + local area = areas[i] + -- local page = area.page -- maybe use page counter in columnset + -- local type = area.type + local okay = false + -- + local nofcolumns = area.nc + local nofrows = area.nr + local column = area.c + local row = area.r + columnsets.block { + name = name, + c = column, + r = row, + nc = nofcolumns, + nr = nofrows, + } + local left = 0 + local start = dataset.nofleft + 1 + local overflow = (column + nofcolumns - 1) - dataset.nofleft + local height = nofrows * (dataset.lineheight + dataset.linedepth) + local width = dataset.spreads[column][nofcolumns] + -- report("span, width %p, overflow %i",width,overflow) + if overflow > 0 then + local used = nofcolumns - overflow + left = dataset.spreads[column][used] + getdimen("backspace") + end + ctx_page_grid_set_area(name,area.name,column,row,width,height,start,left) -- or via counters / dimens + if area.state ~= v_repeat then + area = nil + end + if area then + kept[#kept+1] = area + end + end + areas = kept + end +end + +function columnsets.setarea(t) + local dataset = data[t.name] + local cells = dataset.cells + local box = takebox(t.box) + local column = t.c + local row = t.r + if column and row then + setfield(box,"height",dataset.lineheight) + setfield(box,"depth",dataset.linedepth) + setfield(box,"width",dataset.widths[column]) + cells[column][row] = box + end +end + +-- The interface. + +interfaces.implement { + name = "definecolumnset", + actions = columnsets.define, + arguments = { { + { "name", "string" }, + } } +} + +interfaces.implement { + name = "resetcolumnset", + actions = columnsets.reset, + arguments = { { + { "name", "string" }, + { "nofleft", "integer" }, + { "nofright", "integer" }, + { "nofrows", "integer" }, + { "lineheight", "dimension" }, + { "linedepth", "dimension" }, + { "width", "dimension" }, + { "distance", "dimension" }, + { "maxwidth", "dimension" }, + } } +} + +interfaces.implement { + name = "preparecolumnsetflush", + actions = columnsets.prepareflush, + arguments = { "string" }, +} + +interfaces.implement { + name = "finishcolumnsetflush", + actions = columnsets.finishflush, + arguments = { "string" }, +} + +interfaces.implement { + name = "flushcolumnsetcolumn", + actions = columnsets.flushcolumn, + arguments = { "string" ,"integer" }, +} + +interfaces.implement { + name = "setvsizecolumnset", + actions = columnsets.setvsize, + arguments = { "string" }, +} + +interfaces.implement { + name = "sethsizecolumnset", + actions = columnsets.sethsize, + arguments = { "string" }, +} + +interfaces.implement { + name = "sethsizecolumnspan", + actions = columnsets.sethspan, + arguments = { "string" ,"integer" }, +} + +interfaces.implement { + name = "flushcolumnsetrest", + actions = columnsets.flushrest, + arguments = { "string", "integer" }, +} + +interfaces.implement { + name = "blockcolumnset", + actions = columnsets.block, + arguments = { { + { "name", "string" }, + { "c", "integer" }, + { "r", "integer" }, + { "nc", "integer" }, + { "nr", "integer" }, + { "box", "integer" }, + } } +} + +interfaces.implement { + name = "checkcolumnset", + actions = columnsets.check, + arguments = { { + { "name", "string" }, + { "method", "string" }, + { "c", "integer" }, + { "r", "integer" }, + { "box", "integer" }, + { "width", "dimension" }, + { "height", "dimension" }, + { "option", "string" }, + } } +} + +interfaces.implement { + name = "putincolumnset", + actions = columnsets.put, + arguments = { { + { "name", "string" }, + { "c", "integer" }, + { "r", "integer" }, + { "box", "integer" }, + } } +} + +interfaces.implement { + name = "addtocolumnset", + actions = columnsets.add, + arguments = { "string", "integer" }, +} + +interfaces.implement { + name = "setcolumnsetlines", + actions = columnsets.setlines, + arguments = { { + { "name", "string" }, + { "page", "integer" }, + { "column", "integer" }, + { "value", "integer" }, + } } +} + +interfaces.implement { + name = "setcolumnsetstart", + actions = columnsets.setstart, + arguments = { { + { "name", "string" }, + { "page", "integer" }, + { "column", "integer" }, + { "value", "integer" }, + } } +} + +interfaces.implement { + name = "setcolumnsetproperties", + actions = columnsets.setproperties, + arguments = { { + { "name", "string" }, + { "column", "integer" }, + { "distance", "dimension" }, + { "width", "dimension" }, + } } +} + +interfaces.implement { + name = "registercolumnsetarea", + actions = columnsets.registerarea, + arguments = { { + { "name", "string" }, + { "type", "string" }, + { "page", "integer" }, + { "state", "string" }, + { "c", "integer" }, + { "r", "integer" }, + { "nc", "integer" }, + { "nr", "integer" }, + } } +} + +interfaces.implement { + name = "flushcolumnsetareas", + actions = columnsets.flushareas, + arguments = "string", +} + +interfaces.implement { + name = "setcolumnsetarea", + actions = columnsets.setarea, + arguments = { { + { "name", "string" }, + { "c", "integer" }, + { "r", "integer" }, + { "box", "integer" }, + } } +} + +interfaces.implement { + name = "columnsetgoto", + actions = columnsets["goto"], + arguments = { "string" , "string" }, +} + +interfaces.implement { + name = "columnsetnoto", + actions = columnsets["noto"], +} + +interfaces.implement { + name = "columnsetcurrentcolumn", + actions = columnsets.currentcolumn, + arguments = "string", +} |