diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.lmt')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.lmt | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.lmt b/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.lmt new file mode 100644 index 00000000000..b69bfd87346 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.lmt @@ -0,0 +1,1325 @@ +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 texiscount = tex.iscount +local texgetdimen = tex.getdimen +local texsetdimen = tex.setdimen +local texisdimen = tex.isdimen +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 c_tabl_x_nx = texiscount("c_tabl_x_nx") +local c_tabl_x_ny = texiscount("c_tabl_x_ny") +local c_tabl_x_state = texiscount("c_tabl_x_state") +local c_tabl_x_mode = texiscount("c_tabl_x_mode") +local c_tabl_x_skip_mode = texiscount("c_tabl_x_skip_mode") +local d_tabl_x_final_width = texisdimen("d_tabl_x_final_width") +local d_tabl_x_distance = texisdimen("d_tabl_x_distance") +local d_tabl_x_width = texisdimen("d_tabl_x_width") +local d_tabl_x_height = texisdimen("d_tabl_x_height") +local d_tabl_x_depth = texisdimen("d_tabl_x_depth") +local d_lineheight = texisdimen("lineheight") + +local c_frameddimensionstate = texiscount("frameddimensionstate") + +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(d_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(c_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 |