def table_and_columns_preferred_widths(context, box, outer=True):
    """Return content widths for the auto layout table and its columns.

    The tuple returned is
    ``(table_min_content_width, table_max_content_width,
       column_min_content_widths, column_max_content_widths,
       column_intrinsic_percentages, constrainedness,
       total_horizontal_border_spacing, grid)``

    https://dbaron.org/css/intrinsic/

    """
    from .table import distribute_excess_width

    table = box.get_wrapped_table()
    result = context.tables.get(table)
    if result:
        return result[outer]

    # Create the grid
    grid_width, grid_height = 0, 0
    row_number = 0
    for row_group in table.children:
        for row in row_group.children:
            for cell in row.children:
                grid_width = max(cell.grid_x + cell.colspan, grid_width)
                grid_height = max(row_number + cell.rowspan, grid_height)
            row_number += 1
    grid = [[None] * grid_width for i in range(grid_height)]
    row_number = 0
    for row_group in table.children:
        for row in row_group.children:
            for cell in row.children:
                grid[row_number][cell.grid_x] = cell
            row_number += 1

    zipped_grid = list(zip(*grid))

    # Define the total horizontal border spacing
    if table.style['border_collapse'] == 'separate' and grid_width > 0:
        total_horizontal_border_spacing = (
            table.style['border_spacing'][0] *
            (1 + len([column for column in zipped_grid if any(column)])))
    else:
        total_horizontal_border_spacing = 0

    if grid_width == 0 or grid_height == 0:
        table.children = []
        min_width = block_min_content_width(context, table, outer=False)
        max_width = block_max_content_width(context, table, outer=False)
        outer_min_width = adjust(
            box, outer=True, width=block_min_content_width(context, table))
        outer_max_width = adjust(
            box, outer=True, width=block_max_content_width(context, table))
        result = ([], [], [], [], total_horizontal_border_spacing, [])
        context.tables[table] = result = {
            False: (min_width, max_width, *result),
            True: (outer_min_width, outer_max_width, *result),
        }
        return result[outer]

    column_groups = [None] * grid_width
    columns = [None] * grid_width
    column_number = 0
    for column_group in table.column_groups:
        for column in column_group.children:
            column_groups[column_number] = column_group
            columns[column_number] = column
            column_number += 1
            if column_number == grid_width:
                break
        else:
            continue
        break

    colspan_cells = []
    colspans = set()

    # Define the intermediate content widths
    min_content_widths = [0] * grid_width
    max_content_widths = [0] * grid_width
    intrinsic_percentages = [0] * grid_width

    # Intermediate content widths for span 1
    for i in range(grid_width):
        for groups in (column_groups, columns):
            if group := groups[i]:
                min_content_widths[i] = max(
                    min_content_widths[i], min_content_width(context, group))
                max_content_widths[i] = max(
                    max_content_widths[i], max_content_width(context, group))
                intrinsic_percentages[i] = max(
                    intrinsic_percentages[i], _percentage_contribution(group))
        for cell in zipped_grid[i]:
            if not cell:
                continue
            if cell.colspan == 1:
                min_width, max_width = table_cell_min_max_content_width(context, cell)
                min_content_widths[i] = max(min_content_widths[i], min_width)
                max_content_widths[i] = max(max_content_widths[i], max_width)
                intrinsic_percentages[i] = max(
                    intrinsic_percentages[i], _percentage_contribution(cell))
            else:
                colspan_cells.append(cell)
                colspans.add(cell.colspan - 1)

    # Intermediate content widths for span > 1 is wrong in the 4.1 section, as
    # explained in its third issue. Min- and max-content widths are handled by
    # the excess width distribution method, and percentages do not distribute
    # widths to columns that have originating cells.

    # Intermediate intrinsic percentage widths for span > 1
    rows_origins = []
    for y, row in enumerate(grid):
        origin = None
        rows_origins.append(row_origins := [])
        for x, cell in enumerate(row):
            if cell:
                origin = x
            row_origins.append(origin)

    @cache
    def get_percentage_contribution(origin_cell, origin, max_content_width):
        # Cached for big colspan values, see #1155.
        cell_slice = slice(origin, origin + origin_cell.colspan)
        baseline_percentage = sum(intrinsic_percentages[cell_slice])
        cell_percentage_contribution = _percentage_contribution(origin_cell)
        diff = max(0, cell_percentage_contribution - baseline_percentage)
        other_columns_contributions = [
            max_content_widths[j]
            for j in range(origin, origin + origin_cell.colspan)
            if intrinsic_percentages[j] == 0]
        other_columns_contributions_sum = sum(other_columns_contributions)
        if other_columns_contributions_sum == 0:
            ratio = 1 / (len(other_columns_contributions) or 1)
        else:
            ratio = max_content_width / other_columns_contributions_sum
        return diff * ratio

    for span in sorted(colspans):
        percentage_contributions = []
        for i in range(grid_width):
            if percentage_contribution := intrinsic_percentages[i]:
                percentage_contributions.append(percentage_contribution)
                continue
            for row, row_origins in zip(grid, rows_origins):
                if (origin := row_origins[i]) is None:
                    continue
                origin_cell = row[origin]
                if origin_cell.colspan - 1 != span:
                    continue
                cell_percentage_contribution = get_percentage_contribution(
                    origin_cell, origin, max_content_widths[i])
                percentage_contribution = max(
                    percentage_contribution, cell_percentage_contribution)

            percentage_contributions.append(percentage_contribution)

        intrinsic_percentages = percentage_contributions

    # Define constrainedness
    constrainedness = [False for i in range(grid_width)]
    for i in range(grid_width):
        if (column_groups[i] and column_groups[i].style['width'] != 'auto' and
                column_groups[i].style['width'].unit != '%'):
            constrainedness[i] = True
            continue
        if (columns[i] and columns[i].style['width'] != 'auto' and
                columns[i].style['width'].unit != '%'):
            constrainedness[i] = True
            continue
        for cell in zipped_grid[i]:
            if (cell and cell.colspan == 1 and
                    cell.style['width'] != 'auto' and
                    cell.style['width'].unit != '%'):
                constrainedness[i] = True
                break

    intrinsic_percentages = [
        min(percentage, 100 - sum(intrinsic_percentages[:i]))
        for i, percentage in enumerate(intrinsic_percentages)]

    # Max- and min-content widths for span > 1
    for cell in colspan_cells:
        min_content = min_content_width(context, cell)
        max_content = max_content_width(context, cell)
        column_slice = slice(cell.grid_x, cell.grid_x + cell.colspan)
        columns_min_content = sum(min_content_widths[column_slice])
        columns_max_content = sum(max_content_widths[column_slice])
        if table.style['border_collapse'] == 'separate':
            spacing = (cell.colspan - 1) * table.style['border_spacing'][0]
        else:
            spacing = 0

        if min_content > columns_min_content + spacing:
            excess_width = min_content - (columns_min_content + spacing)
            distribute_excess_width(
                context, zipped_grid, excess_width, min_content_widths,
                constrainedness, intrinsic_percentages, max_content_widths,
                column_slice)

        if max_content > columns_max_content + spacing:
            excess_width = max_content - (columns_max_content + spacing)
            distribute_excess_width(
                context, zipped_grid, excess_width, max_content_widths,
                constrainedness, intrinsic_percentages, max_content_widths,
                column_slice)

    # Calculate the max- and min-content widths of table and columns
    small_percentage_contributions = [
        max_content_widths[i] / (intrinsic_percentages[i] / 100)
        for i in range(grid_width)
        if intrinsic_percentages[i]]
    large_percentage_contribution_numerator = sum(
        max_content_widths[i] for i in range(grid_width)
        if intrinsic_percentages[i] == 0)
    large_percentage_contribution_denominator = (
        (100 - sum(intrinsic_percentages)) / 100)
    if large_percentage_contribution_denominator == 0:
        if large_percentage_contribution_numerator == 0:
            large_percentage_contribution = 0
        else:
            # "the large percentage contribution of the table [is] an
            # infinitely large number if the numerator is nonzero [and] the
            # denominator of that ratio is 0."
            #
            # https://dbaron.org/css/intrinsic/#autotableintrinsic
            #
            # Please note that "an infinitely large number" is not "infinite",
            # and that's probably not a coincindence: putting 'inf' here breaks
            # some cases (see #305).
            large_percentage_contribution = sys.maxsize
    else:
        large_percentage_contribution = (
            large_percentage_contribution_numerator /
            large_percentage_contribution_denominator)

    table_min_content_width = (
        total_horizontal_border_spacing + sum(min_content_widths))
    table_max_content_width = (
        total_horizontal_border_spacing + max([
            sum(max_content_widths), large_percentage_contribution,
            *small_percentage_contributions]))

    if table.style['width'] != 'auto' and table.style['width'].unit == 'px':
        # "percentages on the following properties are treated instead as
        # though they were the following: width: auto"
        # https://dbaron.org/css/intrinsic/#outer-intrinsic
        table_min_width = table_max_width = table.style['width'].value
    else:
        table_min_width = table_min_content_width
        table_max_width = table_max_content_width

    table_min_content_width = max(
        table_min_content_width, adjust(
            table, outer=False, width=table_min_width))
    table_max_content_width = max(
        table_max_content_width, adjust(
            table, outer=False, width=table_max_width))
    table_outer_min_content_width = margin_width(
        table, margin_width(box, table_min_content_width))
    table_outer_max_content_width = margin_width(
        table, margin_width(box, table_max_content_width))

    result = (
        min_content_widths, max_content_widths, intrinsic_percentages,
        constrainedness, total_horizontal_border_spacing, zipped_grid)
    context.tables[table] = result = {
        False: (table_min_content_width, table_max_content_width, *result),
        True: (table_outer_min_content_width, table_outer_max_content_width, *result),
    }
    return result[outer]
Return content widths for the auto layout table and its columns.

The tuple returned is
``(table_min_content_width, table_max_content_width,
column_min_content_widths, column_max_content_widths,
column_intrinsic_percentages, constrainedness,
total_horizontal_border_spacing, grid)``

https://dbaron.org/css/intrinsic/
weasyprint/layout/preferred.py