summaryrefslogtreecommitdiff
path: root/tex/context/base/tabl-xtb.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/tabl-xtb.lua')
-rw-r--r--tex/context/base/tabl-xtb.lua1976
1 files changed, 988 insertions, 988 deletions
diff --git a/tex/context/base/tabl-xtb.lua b/tex/context/base/tabl-xtb.lua
index 3ffe8a219..5b47bf705 100644
--- a/tex/context/base/tabl-xtb.lua
+++ b/tex/context/base/tabl-xtb.lua
@@ -1,988 +1,988 @@
-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
-
-local commands, context, tex, node = commands, context, tex, node
-
-local texdimen = tex.dimen
-local texcount = tex.count
-local texbox = tex.box
-local texsetcount = tex.setcount
-local texsetdimen = tex.setdimen
-
-local format = string.format
-local concat = table.concat
-local points = number.points
-
-local context = context
-local context_beginvbox = context.beginvbox
-local context_endvbox = context.endvbox
-local context_blank = context.blank
-local context_nointerlineskip = context.nointerlineskip
-
-local variables = interfaces.variables
-
-local setmetatableindex = table.setmetatableindex
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local copy_node_list = node.copy_list
-local hpack_node_list = node.hpack
-local vpack_node_list = node.vpack
-local slide_node_list = node.slide
-local flush_node_list = node.flush_list
-
-local nodepool = nodes.pool
-
-local new_glue = nodepool.glue
-local new_kern = nodepool.kern
-local new_penalty = nodepool.penalty
-local new_hlist = nodepool.hlist
-
-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
-
-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 frozencolumns = { }
- local options = { }
- data = {
- rows = rows,
- widths = widths,
- heights = heights,
- depths = depths,
- spans = spans,
- distances = distances,
- modes = modes,
- autowidths = autowidths,
- fixedrows = fixedrows,
- fixedcolumns = fixedcolumns,
- frozencolumns = frozencolumns,
- options = options,
- nofrows = 0,
- nofcolumns = 0,
- currentrow = 0,
- currentcolumn = 0,
- settings = settings or { },
- }
- 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,
- }
- 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)
- --
- 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 = settings_to_hash(settings.option)
- settings.textwidth = tonumber(settings.textwidth) or tex.hsize
- settings.lineheight = tonumber(settings.lineheight) or texdimen.lineheight
- settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
- -- if #stack > 0 then
- -- settings.textwidth = tex.hsize
- -- end
- data.criterium_v = 2 * data.settings.lineheight
- data.criterium_h = .75 * data.settings.textwidth
-
-end
-
-function xtables.initialize_reflow_width(option)
- local r = data.currentrow
- local c = data.currentcolumn + 1
- local drc = data.rows[r][c]
- drc.nx = texcount.c_tabl_x_nx
- drc.ny = texcount.c_tabl_x_ny
- local distances = data.distances
- local distance = texdimen.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
-
--- local function rather_fixed(n)
--- for n in node.
-
-function xtables.set_reflow_width()
- 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 tb = texbox.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: copy_node_list(tb))
- --
- local widths, width = data.widths, tb.width
- if width > widths[c] then
- widths[c] = width
- end
- local heights, height = data.heights, tb.height
- if height > heights[r] then
- heights[r] = height
- end
- local depths, depth = data.depths, tb.depth
- if depth > depths[r] then
- depths[r] = depth
- end
- --
- local dimensionstate = texcount.frameddimensionstate
- local fixedcolumns = data.fixedcolumns
- local fixedrows = data.fixedrows
- if dimensionstate == 1 then
- if 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
- else -- probably something frozen, like an image -- we could parse the list
- if width <= data.criterium_h and height >= data.criterium_v then
- if width > fixedcolumns[c] then -- how about a span here?
- fixedcolumns[c] = width
- end
- end
- end
- drc.dimensionstate = dimensionstate
- --
- local nx, ny = drc.nx, drc.ny
- if nx > 1 or ny > 1 then
- local spans = data.spans
- 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
- 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
- texdimen.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
- texcount.c_tabl_x_skip_mode = 1
- elseif dimensionstate == 2 then
- -- height is enforced
- texcount.c_tabl_x_skip_mode = 1
- elseif data.autowidths[c] then
- -- width has changed so we need to recalculate the height
- texcount.c_tabl_x_skip_mode = 0
- else
- texcount.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 = texbox.b_tabl_x
- local drc = row[c]
- if data.fixedrows[r] == 0 then -- and drc.dimensionstate < 2
- local heights, height = data.heights, tb.height
- if height > heights[r] then
- heights[r] = height
- end
- local depths, depth = data.depths, tb.depth
- if depth > depths[r] then
- depths[r] = depth
- end
- end
--- c = c + drc.nx - 1
--- data.currentcolumn = c
-end
-
-function xtables.initialize_construct()
- 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 heights = data.heights
- local depths = data.depths
- local w = widths[c]
- local h = heights[r]
- local d = depths[r]
- local drc = row[c]
- for x=1,drc.nx-1 do
- w = w + widths[c+x]
- end
- for y=1,drc.ny-1 do
- h = h + heights[r+y]
- d = d + depths[r+y]
- end
- texdimen.d_tabl_x_width = w
- texdimen.d_tabl_x_height = h + d
- texdimen.d_tabl_x_depth = 0
-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 = copy_node_list(texbox.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 : %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
- -- flush_node_list(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 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
- 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
- width = width + widths[c]
- if width > maxwidth then
- autowidths[c] = true
- nofwide = nofwide + 1
- widetotal = widetotal + widths[c]
- 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
- if settings.options[v_height] then
- local heights = data.heights
- local depths = data.depths
- local nofrows = data.nofrows
- 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
- -- 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
- list.shift = list.height + list.depth
- -- list = hpack_node_list(list) -- is somehow needed
- -- list.width = 0
- -- list.height = 0
- -- list.depth = 0
- -- faster:
- local h = new_hlist()
- h.list = list
- list = h
- --
- if start then
- stop.next = list
- list.prev = stop
- else
- start = list
- end
- stop = list -- one node anyway, so not needed: slide_node_list(list)
- end
- local step = widths[c]
- if c < nofcolumns then
- step = step + columndistance + distances[c]
- end
- local kern = new_kern(step)
- if stop then
- stop.prev = kern
- stop.next = 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)
- stop.next = kern
- kern.prev = stop
- -- 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
- result[nofr] = {
- hpack_node_list(list),
- size,
- i < nofrange and rowdistance > 0 and rowdistance or false, -- might move
- false
- }
- 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",body[1][1].width)
- end
-end
-
-local function inject(row,copy,package)
- local list = row[1]
- if copy then
- row[1] = copy_node_list(list)
- end
- if package then
- context_beginvbox()
- context(list)
- context(new_kern(row[2]))
- context_endvbox()
- context_nointerlineskip() -- figure out a better way
- if row[4] then
- -- nothing as we have a span
- elseif row[3] then
- context_blank(row[3] .. "sp") -- why blank ?
- else
- context(new_glue(0))
- end
- else
- context(list)
- context(new_kern(row[2]))
- if row[3] then
- context(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 vsize = directives.vsize
- 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 vsize and vsize > 0 then
- context_beginvbox()
- local bodystart = data.bodystart or 1
- local bodystop = data.bodystop or #body
- if bodystart > 0 and bodystart <= bodystop then
- local bodysize = vsize
- 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
- context(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
- context(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
- context(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
- context(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
- context(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 > vsize 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
- context_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
- context_blank(rowdistance .. "sp")
- end
- for i=1,#body do
- inject(body[i],false,true)
- end
- if #foot > 0 and rowdistance > 0 then
- context_blank(rowdistance .. "sp")
- end
- for i=1,#foot do
- inject(foot[i],false,true)
- end
- else -- normal
- context_beginvbox()
- for i=1,#head do
- inject(head[i])
- end
- if #head > 0 and rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#body do
- inject(body[i])
- end
- if #foot > 0 and rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#foot do
- inject(foot[i])
- end
- context_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
- flush_node_list(r[1])
- end
- end
- data = table.remove(stack)
-end
-
-function xtables.next_row()
- local r = data.currentrow + 1
- data.modes[r] = texcount.c_tabl_x_mode
- data.currentrow = r
- data.currentcolumn = 0
-end
-
--- eventually we might only have commands
-
-commands.x_table_create = xtables.create
-commands.x_table_reflow_width = xtables.reflow_width
-commands.x_table_reflow_height = xtables.reflow_height
-commands.x_table_construct = xtables.construct
-commands.x_table_flush = xtables.flush
-commands.x_table_cleanup = xtables.cleanup
-commands.x_table_next_row = xtables.next_row
-commands.x_table_init_reflow_width = xtables.initialize_reflow_width
-commands.x_table_init_reflow_height = xtables.initialize_reflow_height
-commands.x_table_init_construct = xtables.initialize_construct
-commands.x_table_set_reflow_width = xtables.set_reflow_width
-commands.x_table_set_reflow_height = xtables.set_reflow_height
-commands.x_table_set_construct = xtables.set_construct
+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
+
+local commands, context, tex, node = commands, context, tex, node
+
+local texdimen = tex.dimen
+local texcount = tex.count
+local texbox = tex.box
+local texsetcount = tex.setcount
+local texsetdimen = tex.setdimen
+
+local format = string.format
+local concat = table.concat
+local points = number.points
+
+local context = context
+local context_beginvbox = context.beginvbox
+local context_endvbox = context.endvbox
+local context_blank = context.blank
+local context_nointerlineskip = context.nointerlineskip
+
+local variables = interfaces.variables
+
+local setmetatableindex = table.setmetatableindex
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local copy_node_list = node.copy_list
+local hpack_node_list = node.hpack
+local vpack_node_list = node.vpack
+local slide_node_list = node.slide
+local flush_node_list = node.flush_list
+
+local nodepool = nodes.pool
+
+local new_glue = nodepool.glue
+local new_kern = nodepool.kern
+local new_penalty = nodepool.penalty
+local new_hlist = nodepool.hlist
+
+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
+
+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 frozencolumns = { }
+ local options = { }
+ data = {
+ rows = rows,
+ widths = widths,
+ heights = heights,
+ depths = depths,
+ spans = spans,
+ distances = distances,
+ modes = modes,
+ autowidths = autowidths,
+ fixedrows = fixedrows,
+ fixedcolumns = fixedcolumns,
+ frozencolumns = frozencolumns,
+ options = options,
+ nofrows = 0,
+ nofcolumns = 0,
+ currentrow = 0,
+ currentcolumn = 0,
+ settings = settings or { },
+ }
+ 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,
+ }
+ 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)
+ --
+ 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 = settings_to_hash(settings.option)
+ settings.textwidth = tonumber(settings.textwidth) or tex.hsize
+ settings.lineheight = tonumber(settings.lineheight) or texdimen.lineheight
+ settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
+ -- if #stack > 0 then
+ -- settings.textwidth = tex.hsize
+ -- end
+ data.criterium_v = 2 * data.settings.lineheight
+ data.criterium_h = .75 * data.settings.textwidth
+
+end
+
+function xtables.initialize_reflow_width(option)
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local drc = data.rows[r][c]
+ drc.nx = texcount.c_tabl_x_nx
+ drc.ny = texcount.c_tabl_x_ny
+ local distances = data.distances
+ local distance = texdimen.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
+
+-- local function rather_fixed(n)
+-- for n in node.
+
+function xtables.set_reflow_width()
+ 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 tb = texbox.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: copy_node_list(tb))
+ --
+ local widths, width = data.widths, tb.width
+ if width > widths[c] then
+ widths[c] = width
+ end
+ local heights, height = data.heights, tb.height
+ if height > heights[r] then
+ heights[r] = height
+ end
+ local depths, depth = data.depths, tb.depth
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ --
+ local dimensionstate = texcount.frameddimensionstate
+ local fixedcolumns = data.fixedcolumns
+ local fixedrows = data.fixedrows
+ if dimensionstate == 1 then
+ if 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
+ else -- probably something frozen, like an image -- we could parse the list
+ if width <= data.criterium_h and height >= data.criterium_v then
+ if width > fixedcolumns[c] then -- how about a span here?
+ fixedcolumns[c] = width
+ end
+ end
+ end
+ drc.dimensionstate = dimensionstate
+ --
+ local nx, ny = drc.nx, drc.ny
+ if nx > 1 or ny > 1 then
+ local spans = data.spans
+ 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
+ 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
+ texdimen.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
+ texcount.c_tabl_x_skip_mode = 1
+ elseif dimensionstate == 2 then
+ -- height is enforced
+ texcount.c_tabl_x_skip_mode = 1
+ elseif data.autowidths[c] then
+ -- width has changed so we need to recalculate the height
+ texcount.c_tabl_x_skip_mode = 0
+ else
+ texcount.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 = texbox.b_tabl_x
+ local drc = row[c]
+ if data.fixedrows[r] == 0 then -- and drc.dimensionstate < 2
+ local heights, height = data.heights, tb.height
+ if height > heights[r] then
+ heights[r] = height
+ end
+ local depths, depth = data.depths, tb.depth
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ end
+-- c = c + drc.nx - 1
+-- data.currentcolumn = c
+end
+
+function xtables.initialize_construct()
+ 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 heights = data.heights
+ local depths = data.depths
+ local w = widths[c]
+ local h = heights[r]
+ local d = depths[r]
+ local drc = row[c]
+ for x=1,drc.nx-1 do
+ w = w + widths[c+x]
+ end
+ for y=1,drc.ny-1 do
+ h = h + heights[r+y]
+ d = d + depths[r+y]
+ end
+ texdimen.d_tabl_x_width = w
+ texdimen.d_tabl_x_height = h + d
+ texdimen.d_tabl_x_depth = 0
+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 = copy_node_list(texbox.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 : %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
+ -- flush_node_list(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 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
+ 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
+ width = width + widths[c]
+ if width > maxwidth then
+ autowidths[c] = true
+ nofwide = nofwide + 1
+ widetotal = widetotal + widths[c]
+ 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
+ if settings.options[v_height] then
+ local heights = data.heights
+ local depths = data.depths
+ local nofrows = data.nofrows
+ 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
+ -- 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
+ list.shift = list.height + list.depth
+ -- list = hpack_node_list(list) -- is somehow needed
+ -- list.width = 0
+ -- list.height = 0
+ -- list.depth = 0
+ -- faster:
+ local h = new_hlist()
+ h.list = list
+ list = h
+ --
+ if start then
+ stop.next = list
+ list.prev = stop
+ else
+ start = list
+ end
+ stop = list -- one node anyway, so not needed: slide_node_list(list)
+ end
+ local step = widths[c]
+ if c < nofcolumns then
+ step = step + columndistance + distances[c]
+ end
+ local kern = new_kern(step)
+ if stop then
+ stop.prev = kern
+ stop.next = 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)
+ stop.next = kern
+ kern.prev = stop
+ -- 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
+ result[nofr] = {
+ hpack_node_list(list),
+ size,
+ i < nofrange and rowdistance > 0 and rowdistance or false, -- might move
+ false
+ }
+ 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",body[1][1].width)
+ end
+end
+
+local function inject(row,copy,package)
+ local list = row[1]
+ if copy then
+ row[1] = copy_node_list(list)
+ end
+ if package then
+ context_beginvbox()
+ context(list)
+ context(new_kern(row[2]))
+ context_endvbox()
+ context_nointerlineskip() -- figure out a better way
+ if row[4] then
+ -- nothing as we have a span
+ elseif row[3] then
+ context_blank(row[3] .. "sp") -- why blank ?
+ else
+ context(new_glue(0))
+ end
+ else
+ context(list)
+ context(new_kern(row[2]))
+ if row[3] then
+ context(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 vsize = directives.vsize
+ 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 vsize and vsize > 0 then
+ context_beginvbox()
+ local bodystart = data.bodystart or 1
+ local bodystop = data.bodystop or #body
+ if bodystart > 0 and bodystart <= bodystop then
+ local bodysize = vsize
+ 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
+ context(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
+ context(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
+ context(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
+ context(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
+ context(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 > vsize 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
+ context_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
+ context_blank(rowdistance .. "sp")
+ end
+ for i=1,#body do
+ inject(body[i],false,true)
+ end
+ if #foot > 0 and rowdistance > 0 then
+ context_blank(rowdistance .. "sp")
+ end
+ for i=1,#foot do
+ inject(foot[i],false,true)
+ end
+ else -- normal
+ context_beginvbox()
+ for i=1,#head do
+ inject(head[i])
+ end
+ if #head > 0 and rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#body do
+ inject(body[i])
+ end
+ if #foot > 0 and rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#foot do
+ inject(foot[i])
+ end
+ context_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
+ flush_node_list(r[1])
+ end
+ end
+ data = table.remove(stack)
+end
+
+function xtables.next_row()
+ local r = data.currentrow + 1
+ data.modes[r] = texcount.c_tabl_x_mode
+ data.currentrow = r
+ data.currentcolumn = 0
+end
+
+-- eventually we might only have commands
+
+commands.x_table_create = xtables.create
+commands.x_table_reflow_width = xtables.reflow_width
+commands.x_table_reflow_height = xtables.reflow_height
+commands.x_table_construct = xtables.construct
+commands.x_table_flush = xtables.flush
+commands.x_table_cleanup = xtables.cleanup
+commands.x_table_next_row = xtables.next_row
+commands.x_table_init_reflow_width = xtables.initialize_reflow_width
+commands.x_table_init_reflow_height = xtables.initialize_reflow_height
+commands.x_table_init_construct = xtables.initialize_construct
+commands.x_table_set_reflow_width = xtables.set_reflow_width
+commands.x_table_set_reflow_height = xtables.set_reflow_height
+commands.x_table_set_construct = xtables.set_construct