View Issue Details

IDProjectCategoryView StatusLast Update
0001010contextbug reportpublic2019-07-22 17:10
Reporteritoijala Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Summary0001010: xtables stretch width
DescriptionUsing the options stretch and width can result in a table that does not take up the full \textwidth.
This means that the content is shrunk further than necessary.

I think this is due to a bug in xtables.reflow_width.
I think the "width" in line 674 of tabl-xtb.lua should be widetotal instead, giving:

local factor = (widetotal + delta) / widetotal

This makes sense as the equation we want to solve is

widetotal + delta = factor * widetotal
Steps To ReproduceSee the attached file.
This includes a copy of the patched function that can be activated by removing some comment markers.

It should be run as

context xtables-demo.tex --trackers=xtable.construct --debug
TagsNo tags attached.

Activities

itoijala

2019-07-22 17:10

reporter  

xtables-demo.tex (6,818 bytes)   
\startluacode
xtables = typesetters.xtables
local orig = xtables.reflow_width

local data = nil
local trace_xtable = nil
local showwidths = nil
local v_max = nil
local report_xtable = nil
local v_stretch = nil
local v_width = nil

function 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 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) / widetotal -- PATCHED!
            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

for i = 1, debug.getinfo(orig).nups do
    debug.upvaluejoin(reflow_width, i, orig, i)
end

-- Run with or without these lines to compare
-- xtables.reflow_width = reflow_width
-- interfaces.implement { name = "x_table_reflow_width", actions = xtables.reflow_width }
\stopluacode

\starttext

\showframe

\startxtable[option={stretch}]
  \startxrow
    \startxcell
      a
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
  \stopxrow
\stopxtable

\startxtable[option={stretch,width}]
  \startxrow
    \startxcell
      a
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
    \startxcell
      abcdefghijklmnopqrstuvwxyz
    \stopxcell
  \stopxrow
\stopxtable

\stoptext
xtables-demo.tex (6,818 bytes)   
xtables-demo.pdf (9,042 bytes)

Issue History

Date Modified Username Field Change
2019-07-22 17:10 itoijala New Issue
2019-07-22 17:10 itoijala File Added: xtables-demo.tex
2019-07-22 17:10 itoijala File Added: xtables-demo.pdf