def fixed_table_layout(box):
    """Run the fixed table layout and return a list of column widths.

    https://www.w3.org/TR/CSS21/tables.html#fixed-table-layout

    """
    table = box.get_wrapped_table()
    assert table.width != 'auto'

    all_columns = [
        column for column_group in table.column_groups
        for column in column_group.children]
    if table.children and table.children[0].children:
        first_rowgroup = table.children[0]
        first_row_cells = first_rowgroup.children[0].children
    else:
        first_row_cells = []
    num_columns = max(len(all_columns), sum(cell.colspan for cell in first_row_cells))
    # ``None`` means not know yet.
    column_widths = [None] * num_columns

    # Set width on column boxes.
    for i, column in enumerate(all_columns):
        resolve_one_percentage(column, 'width', table.width)
        if column.width != 'auto':
            column_widths[i] = column.width

    if table.style['border_collapse'] == 'separate':
        border_spacing_x, _ = table.style['border_spacing']
    else:
        border_spacing_x = 0

    # Set width on cells of the first row.
    i = 0
    for cell in first_row_cells:
        resolve_percentages(cell, table)
        if cell.width != 'auto':
            width = cell.border_width()
            width -= border_spacing_x * (cell.colspan - 1)
            # In the general case, this width affects several columns (through
            # colspan) some of which already have a width. Subtract these
            # known widths and divide among remaining columns.
            columns_without_width = []  # and occupied by this cell
            for j in range(i, i + cell.colspan):
                if column_widths[j] is None:
                    columns_without_width.append(j)
                else:
                    width -= column_widths[j]
            if columns_without_width:
                width_per_column = width / len(columns_without_width)
                for j in columns_without_width:
                    column_widths[j] = width_per_column
        i += cell.colspan

    # Distribute the remaining space equally on columns that do not have
    # a width yet.
    all_border_spacing = border_spacing_x * (num_columns + 1)
    min_table_width = (sum(w for w in column_widths if w is not None) +
                       all_border_spacing)
    columns_without_width = [i for i, w in enumerate(column_widths)
                             if w is None]
    if columns_without_width and table.width >= min_table_width:
        remaining_width = table.width - min_table_width
        width_per_column = remaining_width / len(columns_without_width)
        for i in columns_without_width:
            column_widths[i] = width_per_column
    else:
        # This is bad, but we were given a broken table.
        for i in columns_without_width:
            column_widths[i] = 0

    # If the sum is less than the table width, distribute the remaining space
    # equally.
    extra_width = table.width - sum(column_widths) - all_border_spacing
    if extra_width <= 0:
        # Substract a negative: widen the table.
        table.width -= extra_width
    elif num_columns:
        extra_per_column = extra_width / num_columns
        column_widths = [w + extra_per_column for w in column_widths]

    # Now we have table.width == sum(column_widths) + all_border_spacing
    # with possible floating point rounding errors (unless there is zero column).
    table.column_widths = column_widths
Run the fixed table layout and return a list of column widths.

https://www.w3.org/TR/CSS21/tables.html#fixed-table-layout
weasyprint/layout/table.py