summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/page-cst.lua
diff options
context:
space:
mode:
authorContext Git Mirror Bot <phg42.2a@gmail.com>2016-03-01 15:15:08 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2016-03-01 15:15:08 +0100
commit2a958dcf22dd71ba1e4408648676d44c16d7e3bf (patch)
tree8a118b195ac47f9e926bb5083f3d0f91c352d281 /tex/context/base/mkiv/page-cst.lua
parent48c3ce21b30a886099e9afc2edf683e8a47ba29e (diff)
downloadcontext-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.lua1454
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",
+}