summaryrefslogtreecommitdiff
path: root/tex/context/base/mkxl/tabl-xtb.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkxl/tabl-xtb.lmt')
-rw-r--r--tex/context/base/mkxl/tabl-xtb.lmt1310
1 files changed, 1310 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/tabl-xtb.lmt b/tex/context/base/mkxl/tabl-xtb.lmt
new file mode 100644
index 000000000..32770141b
--- /dev/null
+++ b/tex/context/base/mkxl/tabl-xtb.lmt
@@ -0,0 +1,1310 @@
+if not modules then modules = { } end modules ['tabl-xtb'] = {
+ version = 1.001,
+ comment = "companion to tabl-xtb.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[
+
+This table mechanism is a combination between TeX and Lua. We do process
+cells at the TeX end and inspect them at the Lua end. After some analysis
+we have a second pass using the calculated widths, and if needed cells
+will go through a third pass to get the heights right. This last pass is
+avoided when possible which is why some code below looks a bit more
+complex than needed. The reason for such optimizations is that each cells
+is actually a framed instance and because tables like this can be hundreds
+of pages we want to keep processing time reasonable.
+
+To a large extend the behaviour is comparable with the way bTABLE/eTABLE
+works and there is a module that maps that one onto this one. Eventually
+this mechamism will be improved so that it can replace its older cousin.
+
+]]--
+
+-- todo: use linked list instead of r/c array
+-- todo: we can use the sum of previously forced widths for column spans
+
+local tonumber, next = tonumber, next
+
+local commands = commands
+local context = context
+local ctxnode = context.nodes.flush
+
+local implement = interfaces.implement
+
+local tex = tex
+local texgetcount = tex.getcount
+local texsetcount = tex.setcount
+local texgetdimen = tex.getdimen
+local texsetdimen = tex.setdimen
+local texget = tex.get
+
+local format = string.format
+local concat = table.concat
+local points = number.points
+
+local todimen = string.todimen
+
+local ctx_beginvbox = context.beginvbox
+local ctx_endvbox = context.endvbox
+local ctx_blank = context.blank
+local ctx_nointerlineskip = context.nointerlineskip
+local ctx_dummyxcell = context.dummyxcell
+
+local variables = interfaces.variables
+
+local setmetatableindex = table.setmetatableindex
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local nuts = nodes.nuts -- here nuts gain hardly nothing
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getlist = nuts.getlist
+local getwidth = nuts.getwidth
+local getbox = nuts.getbox
+local getwhd = nuts.getwhd
+
+local setlink = nuts.setlink
+local setdirection = nuts.setdirection
+local setshift = nuts.setshift
+
+local copynodelist = nuts.copylist
+local hpacknodelist = nuts.hpack
+local flushnodelist = nuts.flushlist
+local takebox = nuts.takebox
+
+local nodepool = nuts.pool
+
+local new_glue = nodepool.glue
+local new_kern = nodepool.kern
+local new_hlist = nodepool.hlist
+
+local lefttoright_code = nodes.dirvalues.lefttoright
+
+local v_stretch = variables.stretch
+local v_normal = variables.normal
+local v_width = variables.width
+local v_height = variables.height
+local v_repeat = variables["repeat"]
+local v_max = variables.max
+local v_fixed = variables.fixed
+----- v_auto = variables.auto
+local v_before = variables.before
+local v_after = variables.after
+local v_both = variables.both
+local v_samepage = variables.samepage
+local v_tight = variables.tight
+
+local xtables = { }
+typesetters.xtables = xtables
+
+local trace_xtable = false
+local report_xtable = logs.reporter("xtable")
+
+trackers.register("xtable.construct", function(v) trace_xtable = v end)
+
+local null_mode = 0
+local head_mode = 1
+local foot_mode = 2
+local more_mode = 3
+local body_mode = 4
+
+local namedmodes = { [0] =
+ "null",
+ "head",
+ "foot",
+ "next",
+ "body",
+}
+
+local stack, data = { }, nil
+
+function xtables.create(settings)
+ table.insert(stack,data)
+ local rows = { }
+ local widths = { }
+ local heights = { }
+ local depths = { }
+ -- local spans = { }
+ local distances = { }
+ local autowidths = { }
+ local modes = { }
+ local fixedrows = { }
+ local fixedcolumns = { }
+ -- local fixedcspans = { }
+ local frozencolumns = { }
+ local options = { }
+ local rowproperties = { }
+ data = {
+ rows = rows,
+ widths = widths,
+ heights = heights,
+ depths = depths,
+ -- spans = spans,
+ distances = distances,
+ modes = modes,
+ autowidths = autowidths,
+ fixedrows = fixedrows,
+ fixedcolumns = fixedcolumns,
+ -- fixedcspans = fixedcspans,
+ frozencolumns = frozencolumns,
+ options = options,
+ nofrows = 0,
+ nofcolumns = 0,
+ currentrow = 0,
+ currentcolumn = 0,
+ settings = settings or { },
+ rowproperties = rowproperties,
+ }
+ local function add_zero(t,k)
+ t[k] = 0
+ return 0
+ end
+ local function add_table(t,k)
+ local v = { }
+ t[k] = v
+ return v
+ end
+ local function add_cell(row,c)
+ local cell = {
+ nx = 0,
+ ny = 0,
+ list = false,
+ wd = 0,
+ ht = 0,
+ dp = 0,
+ }
+ row[c] = cell
+ if c > data.nofcolumns then
+ data.nofcolumns = c
+ end
+ return cell
+ end
+ local function add_row(rows,r)
+ local row = { }
+ setmetatableindex(row,add_cell)
+ rows[r] = row
+ if r > data.nofrows then
+ data.nofrows = r
+ end
+ return row
+ end
+ setmetatableindex(rows,add_row)
+ setmetatableindex(widths,add_zero)
+ setmetatableindex(heights,add_zero)
+ setmetatableindex(depths,add_zero)
+ setmetatableindex(distances,add_zero)
+ setmetatableindex(modes,add_zero)
+ setmetatableindex(fixedrows,add_zero)
+ setmetatableindex(fixedcolumns,add_zero)
+ setmetatableindex(options,add_table)
+ -- setmetatableindex(fixedcspans,add_table)
+ --
+ local globaloptions = settings_to_hash(settings.option)
+ --
+ settings.columndistance = tonumber(settings.columndistance) or 0
+ settings.rowdistance = tonumber(settings.rowdistance) or 0
+ settings.leftmargindistance = tonumber(settings.leftmargindistance) or 0
+ settings.rightmargindistance = tonumber(settings.rightmargindistance) or 0
+ settings.options = globaloptions
+ settings.textwidth = tonumber(settings.textwidth) or texget("hsize")
+ settings.lineheight = tonumber(settings.lineheight) or texgetdimen("lineheight")
+ settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
+ -- if #stack > 0 then
+ -- settings.textwidth = texget("hsize")
+ -- end
+ data.criterium_v = 2 * data.settings.lineheight
+ data.criterium_h = .75 * data.settings.textwidth
+ --
+ data.tight = globaloptions[v_tight] and true or false
+end
+
+function xtables.initialize_reflow_width(option,width)
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local drc = data.rows[r][c]
+ drc.nx = texgetcount("c_tabl_x_nx")
+ drc.ny = texgetcount("c_tabl_x_ny")
+ local distances = data.distances
+ local distance = texgetdimen("d_tabl_x_distance")
+ if distance > distances[c] then
+ distances[c] = distance
+ end
+ if option and option ~= "" then
+ local options = settings_to_hash(option)
+ data.options[r][c] = options
+ if options[v_fixed] then
+ data.frozencolumns[c] = true
+ end
+ end
+ data.currentcolumn = c
+end
+
+-- todo: we can better set the cell values in one go
+
+function xtables.set_reflow_width()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+ local cold = c
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ -- bah, we can have a span already
+ if c > cold then
+ local ro = row[cold]
+ local rx = ro.nx
+ local ry = ro.ny
+ if rx > 1 or ry > 1 then
+ local rn = row[c]
+ rn.nx = rx
+ rn.ny = ry
+ ro.nx = 1 -- or 0
+ ro.ny = 1 -- or 0
+ -- do we also need to set ro.span and rn.span
+ end
+ end
+ local tb = getbox("b_tabl_x")
+ local drc = row[c]
+ --
+ drc.list = true -- we don't need to keep the content around as we're in trial mode (no: copynodelist(tb))
+ --
+ local width, height, depth = getwhd(tb)
+ --
+ local widths = data.widths
+ local heights = data.heights
+ local depths = data.depths
+ local cspan = drc.nx
+ if cspan < 2 then
+ if width > widths[c] then
+ widths[c] = width
+ end
+ else
+ local options = data.options[r][c]
+ if data.tight then
+ -- no check
+ elseif not options then
+ if width > widths[c] then
+ widths[c] = width
+ end
+ elseif not options[v_tight] then
+ if width > widths[c] then
+ widths[c] = width
+ end
+ end
+ end
+ -- if cspan > 1 then
+ -- local f = data.fixedcspans[c]
+ -- local w = f[cspan] or 0
+ -- if width > w then
+ -- f[cspan] = width -- maybe some day a solution for autospanmax and so
+ -- end
+ -- end
+ if drc.ny < 2 then
+ -- report_xtable("set width, old: ht=%p, dp=%p",heights[r],depths[r])
+ -- report_xtable("set width, new: ht=%p, dp=%p",height,depth)
+ if height > heights[r] then
+ heights[r] = height
+ end
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ end
+ --
+ drc.wd = width
+ drc.ht = height
+ drc.dp = depth
+ --
+ local dimensionstate = texgetcount("frameddimensionstate")
+ local fixedcolumns = data.fixedcolumns
+ local fixedrows = data.fixedrows
+ if dimensionstate == 1 then
+ if cspan > 1 then
+ -- ignore width
+ elseif width > fixedcolumns[c] then -- how about a span here?
+ fixedcolumns[c] = width
+ end
+ elseif dimensionstate == 2 then
+ fixedrows[r] = height
+ elseif dimensionstate == 3 then
+ fixedrows[r] = height -- width
+ fixedcolumns[c] = width -- height
+ elseif width <= data.criterium_h and height >= data.criterium_v then
+ -- somewhat tricky branch
+ if width > fixedcolumns[c] then -- how about a span here?
+ -- maybe an image, so let's fix
+ fixedcolumns[c] = width
+ end
+ end
+--
+-- -- this fails so not good enough predictor
+--
+-- -- \startxtable
+-- -- \startxrow
+-- -- \startxcell knuth \stopxcell
+-- -- \startxcell \input knuth \stopxcell
+-- -- \stopxrow
+--
+-- else
+-- local o = data.options[r][c]
+-- if o and o[v_auto] then -- new per 5/5/2014 - removed per 15/07/2014
+-- data.autowidths[c] = true
+-- else
+-- -- no dimensions are set in the cell
+-- if width <= data.criterium_h and height >= data.criterium_v then
+-- -- somewhat tricky branch
+-- if width > fixedcolumns[c] then -- how about a span here?
+-- -- maybe an image, so let's fix
+-- fixedcolumns[c] = width
+-- end
+-- else
+-- -- safeguard as it could be text that can be recalculated
+-- -- and the previous branch could have happened in a previous
+-- -- row and then forces a wrong one-liner in a multiliner
+-- if width > fixedcolumns[c] then
+-- data.autowidths[c] = true -- new per 5/5/2014 - removed per 15/07/2014
+-- end
+-- end
+-- end
+-- end
+--
+ --
+ drc.dimensionstate = dimensionstate
+ --
+ local nx = drc.nx
+ local ny = drc.ny
+ if nx > 1 or ny > 1 then
+ -- local spans = data.spans -- not used
+ local self = true
+ for y=1,ny do
+ for x=1,nx do
+ if self then
+ self = false
+ else
+ local ry = r + y - 1
+ local cx = c + x - 1
+ -- if y > 1 then
+ -- spans[ry] = true -- not used
+ -- end
+ rows[ry][cx].span = true
+ end
+ end
+ end
+ c = c + nx - 1
+ end
+ if c > data.nofcolumns then
+ data.nofcolumns = c
+ end
+ data.currentcolumn = c
+end
+
+function xtables.initialize_reflow_height()
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local rows = data.rows
+ local row = rows[r]
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ data.currentcolumn = c
+ local widths = data.widths
+ local w = widths[c]
+ local drc = row[c]
+ for x=1,drc.nx-1 do
+ w = w + widths[c+x]
+ end
+ texsetdimen("d_tabl_x_width",w)
+ local dimensionstate = drc.dimensionstate or 0
+ if dimensionstate == 1 or dimensionstate == 3 then
+ -- width was fixed so height is known
+ texsetcount("c_tabl_x_skip_mode",1)
+ elseif dimensionstate == 2 then
+ -- height is enforced
+ texsetcount("c_tabl_x_skip_mode",1)
+ elseif data.autowidths[c] then
+ -- width has changed so we need to recalculate the height
+ texsetcount("c_tabl_x_skip_mode",0)
+ elseif data.fixedcolumns[c] then
+ texsetcount("c_tabl_x_skip_mode",0) -- new
+ else
+ texsetcount("c_tabl_x_skip_mode",1)
+ end
+end
+
+function xtables.set_reflow_height()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+ -- while row[c].span do -- we could adapt drc.nx instead
+ -- c = c + 1
+ -- end
+ local tb = getbox("b_tabl_x")
+ local drc = row[c]
+ --
+ local width, height, depth = getwhd(tb)
+ --
+ if drc.ny < 2 then
+ if data.fixedrows[r] == 0 then -- and drc.dimensionstate < 2
+ if drc.ht + drc.dp <= height + depth then -- new per 2017-12-15
+ local heights = data.heights
+ local depths = data.depths
+ -- report_xtable("set height, old: ht=%p, dp=%p",heights[r],depths[r])
+ -- report_xtable("set height, new: ht=%p, dp=%p",height,depth)
+ if height > heights[r] then
+ heights[r] = height
+ end
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ end
+ end
+ end
+ --
+ drc.wd = width
+ drc.ht = height
+ drc.dp = depth
+ --
+ -- c = c + drc.nx - 1
+ -- data.currentcolumn = c
+end
+
+function xtables.initialize_construct()
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local settings = data.settings
+ local rows = data.rows
+ local row = rows[r]
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ data.currentcolumn = c
+ local widths = data.widths
+ local heights = data.heights
+ local depths = data.depths
+ local distances = data.distances
+ --
+ local drc = row[c]
+ local wd = drc.wd
+ local ht = drc.ht
+ local dp = drc.dp
+ local nx = drc.nx - 1
+ local ny = drc.ny - 1
+ --
+ local width = widths[c]
+ local height = heights[r]
+ local depth = depths[r] -- problem: can be the depth of a one liner
+ --
+ local total = height + depth
+ --
+ if nx > 0 then
+ for x=1,nx do
+ width = width + widths[c+x] + distances[c+x-1]
+ end
+ local distance = settings.columndistance
+ if distance ~= 0 then
+ width = width + nx * distance
+ end
+ end
+ --
+ if ny > 0 then
+ for y=1,ny do
+ local nxt = r + y
+ total = total + heights[nxt] + depths[nxt]
+ end
+ local distance = settings.rowdistance
+ if distance ~= 0 then
+ total = total + ny * distance
+ end
+ end
+ --
+ texsetdimen("d_tabl_x_width",width)
+ texsetdimen("d_tabl_x_height",total)
+ texsetdimen("d_tabl_x_depth",0) -- for now
+end
+
+function xtables.set_construct()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+ -- while row[c].span do -- can also be previous row ones
+ -- c = c + 1
+ -- end
+ local drc = row[c]
+ -- this will change as soon as in luatex we can reset a box list without freeing
+ drc.list = takebox("b_tabl_x")
+ -- c = c + drc.nx - 1
+ -- data.currentcolumn = c
+end
+
+local function showwidths(where,widths,autowidths)
+ local result = { }
+ for i=1,#widths do
+ result[#result+1] = format("%12s%s",points(widths[i]),autowidths[i] and "*" or " ")
+ end
+ return report_xtable("%s widths: %s",where,concat(result," "))
+end
+
+function xtables.reflow_width()
+ local nofrows = data.nofrows
+ local nofcolumns = data.nofcolumns
+ local rows = data.rows
+ for r=1,nofrows do
+ local row = rows[r]
+ for c=1,nofcolumns do
+ local drc = row[c]
+ if drc.list then
+ -- flushnodelist(drc.list)
+ drc.list = false
+ end
+ end
+ end
+ -- spread
+ local settings = data.settings
+ local options = settings.options
+ local maxwidth = settings.maxwidth
+ -- calculate width
+ local widths = data.widths
+ local heights = data.heights
+ local depths = data.depths
+ local distances = data.distances
+ local autowidths = data.autowidths
+ local fixedcolumns = data.fixedcolumns
+ local frozencolumns = data.frozencolumns
+ local width = 0
+ local distance = 0
+ local nofwide = 0
+ local widetotal = 0
+ local available = settings.textwidth - settings.leftmargindistance - settings.rightmargindistance
+ if trace_xtable then
+ showwidths("stage 1",widths,autowidths)
+ end
+ local noffrozen = 0
+ -- here we can also check spans
+ if options[v_max] then
+ for c=1,nofcolumns do
+ width = width + widths[c]
+ if width > maxwidth then
+ autowidths[c] = true
+ nofwide = nofwide + 1
+ widetotal = widetotal + widths[c]
+ end
+ if c < nofcolumns then
+ distance = distance + distances[c]
+ end
+ if frozencolumns[c] then
+ noffrozen = noffrozen + 1 -- brr, should be nx or so
+ end
+ end
+ else
+ for c=1,nofcolumns do -- also keep track of forced
+ local fixedwidth = fixedcolumns[c]
+ if fixedwidth > 0 then
+ widths[c] = fixedwidth
+ width = width + fixedwidth
+ else
+ local wc = widths[c]
+ width = width + wc
+ -- if width > maxwidth then
+ if wc > maxwidth then -- per 2015-08-09
+ autowidths[c] = true
+ nofwide = nofwide + 1
+ widetotal = widetotal + wc
+ end
+ end
+ if c < nofcolumns then
+ distance = distance + distances[c]
+ end
+ if frozencolumns[c] then
+ noffrozen = noffrozen + 1 -- brr, should be nx or so
+ end
+ end
+ end
+ if trace_xtable then
+ showwidths("stage 2",widths,autowidths)
+ end
+ local delta = available - width - distance - (nofcolumns-1) * settings.columndistance
+ if delta == 0 then
+ -- nothing to be done
+ if trace_xtable then
+ report_xtable("perfect fit")
+ end
+ elseif delta > 0 then
+ -- we can distribute some
+ if not options[v_stretch] then
+ -- not needed
+ if trace_xtable then
+ report_xtable("too wide but no stretch, delta %p",delta)
+ end
+ elseif options[v_width] then
+ local factor = delta / width
+ if trace_xtable then
+ report_xtable("proportional stretch, delta %p, width %p, factor %a",delta,width,factor)
+ end
+ for c=1,nofcolumns do
+ widths[c] = widths[c] + factor * widths[c]
+ end
+ else
+ -- frozen -> a column with option=fixed will not stretch
+ local extra = delta / (nofcolumns - noffrozen)
+ if trace_xtable then
+ report_xtable("normal stretch, delta %p, extra %p",delta,extra)
+ end
+ for c=1,nofcolumns do
+ if not frozencolumns[c] then
+ widths[c] = widths[c] + extra
+ end
+ end
+ end
+ elseif nofwide > 0 then
+ while true do
+ done = false
+ local available = (widetotal + delta) / nofwide
+ if trace_xtable then
+ report_xtable("shrink check, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] and available >= widths[c] then
+ autowidths[c] = nil
+ nofwide = nofwide - 1
+ widetotal = widetotal - widths[c]
+ done = true
+ end
+ end
+ if not done then
+ break
+ end
+ end
+ -- maybe also options[v_width] here but tricky as width does not say
+ -- much about amount
+ if options[v_width] then -- not that much (we could have a clever vpack loop balancing .. no fun)
+ local factor = (widetotal + delta) / width
+ if trace_xtable then
+ report_xtable("proportional shrink used, total %p, delta %p, columns %s, factor %s",widetotal,delta,nofwide,factor)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] then
+ widths[c] = factor * widths[c]
+ end
+ end
+ else
+ local available = (widetotal + delta) / nofwide
+ if trace_xtable then
+ report_xtable("normal shrink used, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] then
+ widths[c] = available
+ end
+ end
+ end
+ end
+ if trace_xtable then
+ showwidths("stage 3",widths,autowidths)
+ end
+ --
+ data.currentrow = 0
+ data.currentcolumn = 0
+end
+
+function xtables.reflow_height()
+ data.currentrow = 0
+ data.currentcolumn = 0
+ local settings = data.settings
+ --
+ -- analyze ny
+ --
+ local nofrows = data.nofrows
+ local nofcolumns = data.nofcolumns
+ local widths = data.widths
+ local heights = data.heights
+ local depths = data.depths
+ --
+ for r=1,nofrows do
+ for c=1,nofcolumns do
+ local drc = data.rows[r][c]
+ if drc then
+ local ny = drc.ny
+ if ny > 1 then
+ local height = heights[r]
+ local depth = depths[r]
+ local total = height + depth
+ local htdp = drc.ht + drc.dp
+ for y=1,ny-1 do
+ local nxt = r + y
+ total = total + heights[nxt] + depths[nxt]
+ end
+ local delta = htdp - total
+ if delta > 0 then
+ delta = delta / ny
+ for y=0,ny-1 do
+ local nxt = r + y
+ heights[nxt] = heights[nxt] + delta
+ end
+ end
+ end
+ end
+ end
+ end
+ --
+ if settings.options[v_height] then
+ local totalheight = 0
+ local totaldepth = 0
+ for i=1,nofrows do
+ totalheight = totalheight + heights[i]
+ totalheight = totalheight + depths [i]
+ end
+ local total = totalheight + totaldepth
+ local leftover = settings.textheight - total
+ if leftover > 0 then
+ local leftheight = (totalheight / total) * leftover / #heights
+ local leftdepth = (totaldepth / total) * leftover / #depths
+ for i=1,nofrows do
+ heights[i] = heights[i] + leftheight
+ depths [i] = depths [i] + leftdepth
+ end
+ end
+ end
+end
+
+local function showspans(data)
+ local rows = data.rows
+ local modes = data.modes
+ local nofcolumns = data.nofcolumns
+ local nofrows = data.nofrows
+ for r=1,nofrows do
+ local line = { }
+ local row = rows[r]
+ for c=1,nofcolumns do
+ local cell =row[c]
+ if cell.list then
+ line[#line+1] = "list"
+ elseif cell.span then
+ line[#line+1] = "span"
+ else
+ line[#line+1] = "none"
+ end
+ end
+ report_xtable("%3d : %s : % t",r,namedmodes[modes[r]] or "----",line)
+ end
+end
+
+function xtables.construct()
+ local rows = data.rows
+ local heights = data.heights
+ local depths = data.depths
+ local widths = data.widths
+ -- local spans = data.spans
+ local distances = data.distances
+ local modes = data.modes
+ local settings = data.settings
+ local nofcolumns = data.nofcolumns
+ local nofrows = data.nofrows
+ local columndistance = settings.columndistance
+ local rowdistance = settings.rowdistance
+ local leftmargindistance = settings.leftmargindistance
+ local rightmargindistance = settings.rightmargindistance
+ local rowproperties = data.rowproperties
+ -- ranges can be mixes so we collect
+
+ if trace_xtable then
+ showspans(data)
+ end
+
+ local ranges = {
+ [head_mode] = { },
+ [foot_mode] = { },
+ [more_mode] = { },
+ [body_mode] = { },
+ }
+ for r=1,nofrows do
+ local m = modes[r]
+ if m == 0 then
+ m = body_mode
+ end
+ local range = ranges[m]
+ range[#range+1] = r
+ end
+ -- todo: hook in the splitter ... the splitter can ask for a chunk of
+ -- a certain size ... no longer a split memory issue then and header
+ -- footer then has to happen here too .. target height
+ local function packaged_column(r)
+ local row = rows[r]
+ local start = nil
+ local stop = nil
+ if leftmargindistance > 0 then
+ start = new_kern(leftmargindistance)
+ stop = start
+ end
+ local hasspan = false
+ for c=1,nofcolumns do
+ local drc = row[c]
+ if not hasspan then
+ hasspan = drc.span
+ end
+ local list = drc.list
+ if list then
+ local w, h, d = getwhd(list)
+ setshift(list,h+d)
+ -- list = hpacknodelist(list) -- is somehow needed
+ -- setwhd(list,0,0,0)
+ -- faster:
+ local h = new_hlist(list)
+ list = h
+ --
+ if start then
+ setlink(stop,list)
+ else
+ start = list
+ end
+ stop = list
+ end
+ local step = widths[c]
+ if c < nofcolumns then
+ step = step + columndistance + distances[c]
+ end
+ local kern = new_kern(step)
+ if stop then
+ setlink(stop,kern)
+ else -- can be first spanning next row (ny=...)
+ start = kern
+ end
+ stop = kern
+ end
+ if start then
+ if rightmargindistance > 0 then
+ local kern = new_kern(rightmargindistance)
+ setlink(stop,kern)
+ -- stop = kern
+ end
+ return start, heights[r] + depths[r], hasspan
+ end
+ end
+ local function collect_range(range)
+ local result, nofr = { }, 0
+ local nofrange = #range
+ for i=1,#range do
+ local r = range[i]
+ -- local row = rows[r]
+ local list, size, hasspan = packaged_column(r)
+ if list then
+ if hasspan and nofr > 0 then
+ result[nofr][4] = true
+ end
+ nofr = nofr + 1
+ local rp = rowproperties[r]
+ -- we have a direction issue here but hpacknodelist(list,0,"exactly") cannot be used
+ -- due to the fact that we need the width
+ local hbox = hpacknodelist(list)
+ setdirection(hbox,lefttoright_code)
+ result[nofr] = {
+ hbox,
+ size,
+ i < nofrange and rowdistance > 0 and rowdistance or false, -- might move
+ false,
+ rp or false,
+ }
+ end
+ end
+ if nofr > 0 then
+ -- the [5] slot gets the after break
+ result[1] [5] = false
+ result[nofr][5] = false
+ for i=2,nofr-1 do
+ local r = result[i][5]
+ if r == v_both or r == v_before then
+ result[i-1][5] = true
+ elseif r == v_after then
+ result[i][5] = true
+ end
+ end
+ end
+ return result
+ end
+ local body = collect_range(ranges[body_mode])
+ data.results = {
+ [head_mode] = collect_range(ranges[head_mode]),
+ [foot_mode] = collect_range(ranges[foot_mode]),
+ [more_mode] = collect_range(ranges[more_mode]),
+ [body_mode] = body,
+ }
+ if #body == 0 then
+ texsetcount("global","c_tabl_x_state",0)
+ texsetdimen("global","d_tabl_x_final_width",0)
+ else
+ texsetcount("global","c_tabl_x_state",1)
+ texsetdimen("global","d_tabl_x_final_width",getwidth(body[1][1]))
+ end
+end
+
+-- todo: join as that is as efficient as fushing multiple
+
+local function inject(row,copy,package)
+ local list = row[1]
+ if copy then
+ row[1] = copynodelist(list)
+ end
+ if package then
+ ctx_beginvbox()
+ ctxnode(tonode(list))
+ ctxnode(tonode(new_kern(row[2])))
+ ctx_endvbox()
+ ctx_nointerlineskip() -- figure out a better way
+ if row[4] then
+ -- nothing as we have a span
+ elseif row[5] then
+ if row[3] then
+ ctx_blank { v_samepage, row[3] .. "sp" }
+ else
+ ctx_blank { v_samepage }
+ end
+ elseif row[3] then
+ ctx_blank { row[3] .. "sp" } -- why blank ?
+ else
+ ctxnode(tonode(new_glue(0)))
+ end
+ else
+ ctxnode(tonode(list))
+ ctxnode(tonode(new_kern(row[2])))
+ if row[3] then
+ ctxnode(tonode(new_glue(row[3])))
+ end
+ end
+end
+
+local function total(row,distance)
+ local n = #row > 0 and rowdistance or 0
+ for i=1,#row do
+ local ri = row[i]
+ n = n + ri[2] + (ri[3] or 0)
+ end
+ return n
+end
+
+-- local function append(list,what)
+-- for i=1,#what do
+-- local l = what[i]
+-- list[#list+1] = l[1]
+-- local k = l[2] + (l[3] or 0)
+-- if k ~= 0 then
+-- list[#list+1] = new_kern(k)
+-- end
+-- end
+-- end
+
+local function spanheight(body,i)
+ local height, n = 0, 1
+ while true do
+ local bi = body[i]
+ if bi then
+ height = height + bi[2] + (bi[3] or 0)
+ if bi[4] then
+ n = n + 1
+ i = i + 1
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ return height, n
+end
+
+function xtables.flush(directives) -- todo split by size / no inbetween then .. glue list kern blank
+ local height = directives.height
+ local method = directives.method or v_normal
+ local settings = data.settings
+ local results = data.results
+ local rowdistance = settings.rowdistance
+ local head = results[head_mode]
+ local foot = results[foot_mode]
+ local more = results[more_mode]
+ local body = results[body_mode]
+ local repeatheader = settings.header == v_repeat
+ local repeatfooter = settings.footer == v_repeat
+ if height and height > 0 then
+ ctx_beginvbox()
+ local bodystart = data.bodystart or 1
+ local bodystop = data.bodystop or #body
+ if bodystart > 0 and bodystart <= bodystop then
+ local bodysize = height
+ local footsize = total(foot,rowdistance)
+ local headsize = total(head,rowdistance)
+ local moresize = total(more,rowdistance)
+ local firstsize, firstspans = spanheight(body,bodystart)
+ if bodystart == 1 then -- first chunk gets head
+ bodysize = bodysize - headsize - footsize
+ if headsize > 0 and bodysize >= firstsize then
+ for i=1,#head do
+ inject(head[i],repeatheader)
+ end
+ if rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ if not repeatheader then
+ results[head_mode] = { }
+ end
+ end
+ elseif moresize > 0 then -- following chunk gets next
+ bodysize = bodysize - footsize - moresize
+ if bodysize >= firstsize then
+ for i=1,#more do
+ inject(more[i],true)
+ end
+ if rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ end
+ elseif headsize > 0 and repeatheader then -- following chunk gets head
+ bodysize = bodysize - footsize - headsize
+ if bodysize >= firstsize then
+ for i=1,#head do
+ inject(head[i],true)
+ end
+ if rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ end
+ else -- following chunk gets nothing
+ bodysize = bodysize - footsize
+ end
+ if bodysize >= firstsize then
+ local i = bodystart
+ while i <= bodystop do -- room for improvement
+ local total, spans = spanheight(body,i)
+ local bs = bodysize - total
+ if bs > 0 then
+ bodysize = bs
+ for s=1,spans do
+ inject(body[i])
+ body[i] = nil
+ i = i + 1
+ end
+ bodystart = i
+ else
+ break
+ end
+ end
+ if bodystart > bodystop then
+ -- all is flushed and footer fits
+ if footsize > 0 then
+ if rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ for i=1,#foot do
+ inject(foot[i])
+ end
+ results[foot_mode] = { }
+ end
+ results[body_mode] = { }
+ texsetcount("global","c_tabl_x_state",0)
+ else
+ -- some is left so footer is delayed
+ -- todo: try to flush a few more lines
+ if repeatfooter and footsize > 0 then
+ if rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ for i=1,#foot do
+ inject(foot[i],true)
+ end
+ else
+ -- todo: try to fit more of body
+ end
+ texsetcount("global","c_tabl_x_state",2)
+ end
+ else
+ if firstsize > height then
+ -- get rid of the too large cell
+ for s=1,firstspans do
+ inject(body[bodystart])
+ body[bodystart] = nil
+ bodystart = bodystart + 1
+ end
+ end
+ texsetcount("global","c_tabl_x_state",2) -- 1
+ end
+ else
+ texsetcount("global","c_tabl_x_state",0)
+ end
+ data.bodystart = bodystart
+ data.bodystop = bodystop
+ ctx_endvbox()
+ else
+ if method == variables.split then
+ -- maybe also a non float mode with header/footer repeat although
+ -- we can also use a float without caption
+ for i=1,#head do
+ inject(head[i],false,true)
+ end
+ if #head > 0 and rowdistance > 0 then
+ ctx_blank { rowdistance .. "sp" }
+ end
+ for i=1,#body do
+ inject(body[i],false,true)
+ end
+ if #foot > 0 and rowdistance > 0 then
+ ctx_blank { rowdistance .. "sp" }
+ end
+ for i=1,#foot do
+ inject(foot[i],false,true)
+ end
+ else -- normal
+ ctx_beginvbox()
+ for i=1,#head do
+ inject(head[i])
+ end
+ if #head > 0 and rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ for i=1,#body do
+ inject(body[i])
+ end
+ if #foot > 0 and rowdistance > 0 then
+ ctxnode(tonode(new_glue(rowdistance)))
+ end
+ for i=1,#foot do
+ inject(foot[i])
+ end
+ ctx_endvbox()
+ end
+ results[head_mode] = { }
+ results[body_mode] = { }
+ results[foot_mode] = { }
+ texsetcount("global","c_tabl_x_state",0)
+ end
+end
+
+function xtables.cleanup()
+ for mode, result in next, data.results do
+ for _, r in next, result do
+ flushnodelist(r[1])
+ end
+ end
+
+ -- local rows = data.rows
+ -- for i=1,#rows do
+ -- local row = rows[i]
+ -- for i=1,#row do
+ -- local cell = row[i]
+ -- local list = cell.list
+ -- if list then
+ -- cell.width, cell.height, cell.depth = getwhd(list)
+ -- cell.list = true
+ -- end
+ -- end
+ -- end
+ -- data.result = nil
+
+ data = table.remove(stack)
+end
+
+function xtables.next_row(specification)
+ local r = data.currentrow + 1
+ data.modes[r] = texgetcount("c_tabl_x_mode")
+ data.currentrow = r
+ data.currentcolumn = 0
+ data.rowproperties[r] = specification
+end
+
+function xtables.finish_row()
+ local c = data.currentcolumn
+ local r = data.currentrow
+ local d = data.rows[r][c]
+ local n = data.nofcolumns - c
+ if d then
+ local nx = d.nx
+ if nx > 0 then
+ n = n - nx + 1
+ end
+ end
+ if n > 0 then
+ for i=1,n do
+ ctx_dummyxcell()
+ end
+ end
+end
+
+-- eventually we might only have commands
+
+implement {
+ name = "x_table_create",
+ actions = xtables.create,
+ arguments = {
+ {
+ { "option" },
+ { "textwidth", "dimen" },
+ { "textheight", "dimen" },
+ { "maxwidth", "dimen" },
+ { "lineheight", "dimen" },
+ { "columndistance", "dimen" },
+ { "leftmargindistance", "dimen" },
+ { "rightmargindistance", "dimen" },
+ { "rowdistance", "dimen" },
+ { "header" },
+ { "footer" },
+ }
+ }
+}
+
+implement {
+ name = "x_table_flush",
+ actions = xtables.flush,
+ arguments = {
+ {
+ { "method" },
+ { "height", "dimen" }
+ }
+ }
+}
+
+implement { name = "x_table_reflow_width", actions = xtables.reflow_width }
+implement { name = "x_table_reflow_height", actions = xtables.reflow_height }
+implement { name = "x_table_construct", actions = xtables.construct }
+implement { name = "x_table_cleanup", actions = xtables.cleanup }
+implement { name = "x_table_next_row", actions = xtables.next_row }
+implement { name = "x_table_next_row_option", actions = xtables.next_row, arguments = "string" }
+implement { name = "x_table_finish_row", actions = xtables.finish_row }
+implement { name = "x_table_init_reflow_width", actions = xtables.initialize_reflow_width }
+implement { name = "x_table_init_reflow_height", actions = xtables.initialize_reflow_height }
+implement { name = "x_table_init_reflow_width_option", actions = xtables.initialize_reflow_width, arguments = "string" }
+implement { name = "x_table_init_reflow_height_option", actions = xtables.initialize_reflow_height, arguments = "string" }
+implement { name = "x_table_init_construct", actions = xtables.initialize_construct }
+implement { name = "x_table_set_reflow_width", actions = xtables.set_reflow_width }
+implement { name = "x_table_set_reflow_height", actions = xtables.set_reflow_height }
+implement { name = "x_table_set_construct", actions = xtables.set_construct }
+implement { name = "x_table_r", actions = function() context(data.currentrow or 0) end }
+implement { name = "x_table_c", actions = function() context(data.currentcolumn or 0) end }
+
+-- experiment:
+
+do
+
+ local context = context
+ local ctxcore = context.core
+
+ local startxtable = ctxcore.startxtable
+ local stopxtable = ctxcore.stopxtable
+
+ local startcollecting = context.startcollecting
+ local stopcollecting = context.stopcollecting
+
+ function ctxcore.startxtable(...)
+ startcollecting()
+ startxtable(...)
+ end
+
+ function ctxcore.stopxtable()
+ stopxtable()
+ stopcollecting()
+ end
+
+end