1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-03 23:48:07 +03:00

Simplify (and fix) extra width distribution for auto table layout

Fix #2174.
This commit is contained in:
Guillaume Ayoub 2024-06-04 22:54:40 +02:00
parent af118df140
commit 4a5326edbf
2 changed files with 70 additions and 106 deletions

View File

@ -1298,11 +1298,14 @@ def test_layout_table_auto_41():
def test_layout_table_auto_42():
# Cell width as percentage in auto-width table
page, = render_pages('''
<table>
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
</style>
<table style="font-family: weasyprint">
<tbody>
<tr>
<td style="width: 70px">a a a a a a a a</td>
<td style="width: 30%">a a a a a a a a</td>
<td style="width: 70px">aaa</td>
<td style="width: 25%">aaa</td>
</tr>
</tbody>
</table>
@ -1314,9 +1317,9 @@ def test_layout_table_auto_42():
row_group, = table.children
row, = row_group.children
td_1, td_2 = row.children
assert td_1.width == 70
assert td_2.width == 30
assert table.width == 100
assert td_2.width == 16 * 3 # Percentage column is set to max-width
assert td_1.width == (16 * 3) * 3 # Pixel column constraint is ignored
assert table.width == (16 * 3) * 4
@assert_no_logs
@ -1554,6 +1557,32 @@ def test_layout_table_auto_50():
assert td_21.width == 240 # 15 * font_size
@assert_no_logs
def test_layout_table_auto_51():
# Test regression:
# https://github.com/Kozea/WeasyPrint/issues/2174
page, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
</style>
<table style="font-family: weasyprint; width: 100px">
<tr>
<td style="width: 29.9999%">a</td>
<td style="width: 70%">a</td>
</tr>
</table>
''')
html, = page.children
body, = html.children
table_wrapper, = body.children
table, = table_wrapper.children
row_group, = table.children
row_1, = row_group.children
td_1, td_2 = row_1.children
assert abs(td_1.width - 30) < 0.1
assert abs(td_2.width - 70) < 0.1
@assert_no_logs
@pytest.mark.parametrize(
'body_width, table_width, check_width, positions, widths', (

View File

@ -7,7 +7,7 @@ import tinycss2.color3
from ..formatting_structure import boxes
from ..logger import LOGGER
from .percent import resolve_one_percentage, resolve_percentages
from .preferred import max_content_width, table_and_columns_preferred_widths
from .preferred import table_and_columns_preferred_widths
def table_layout(context, table, bottom_space, skip_stack, containing_block,
@ -797,19 +797,9 @@ def auto_table_layout(context, box, containing_block):
else:
table.column_widths = max_content_guess
excess_width = assignable_width - sum(max_content_guess)
excess_width = distribute_excess_width(
distribute_excess_width(
context, grid, excess_width, table.column_widths, constrainedness,
column_intrinsic_percentages, column_max_content_widths)
if excess_width:
if table_min_content_width < table.width - excess_width:
# Reduce the width of the size from the excess width that has
# not been distributed.
table.width -= excess_width
else:
# Break rules
columns = [i for i, column in enumerate(grid) if any(column)]
for i in columns:
table.column_widths[i] += excess_width / len(columns)
def table_wrapper_width(context, wrapper, containing_block):
@ -866,38 +856,26 @@ def distribute_excess_width(context, grid, excess_width, column_widths,
column_slice=slice(0, None)):
"""Distribute available width to columns.
Return excess width left when it's impossible without breaking rules.
See https://dbaron.org/css/intrinsic/#distributetocols
See https://www.w3.org/TR/css-tables-3/#distributing-width-to-columns
"""
# First group
columns = [
(i + column_slice.start, column)
for i, column in enumerate(grid[column_slice])
if not constrainedness[i + column_slice.start] and
column_intrinsic_percentages[i + column_slice.start] == 0 and
column_max_content_widths[i + column_slice.start] > 0]
i for i, _ in enumerate(grid[column_slice], start=column_slice.start)
if not constrainedness[i] and
column_intrinsic_percentages[i] == 0 and
column_max_content_widths[i] > 0]
if columns:
current_widths = [column_widths[i] for i, column in columns]
differences = [
max(0, width[0] - width[1])
for width in zip(column_max_content_widths, current_widths)]
if sum(differences) > excess_width:
differences = [
difference / sum(differences) * excess_width
for difference in differences]
excess_width -= sum(differences)
for i, difference in enumerate(differences):
column_widths[columns[i][0]] += difference
if excess_width <= 0:
sum_max_content_widths = sum(column_max_content_widths[i] for i in columns)
ratio = excess_width / sum_max_content_widths
for i in columns:
column_widths[i] += column_max_content_widths[i] * ratio
return
# Second group
columns = [
i + column_slice.start for i, column in enumerate(grid[column_slice])
if not constrainedness[i + column_slice.start] and
column_intrinsic_percentages[i + column_slice.start] == 0]
i for i, _ in enumerate(grid[column_slice], start=column_slice.start)
if not constrainedness[i] and column_intrinsic_percentages[i] == 0]
if columns:
for i in columns:
column_widths[i] += excess_width / len(columns)
@ -905,85 +883,42 @@ def distribute_excess_width(context, grid, excess_width, column_widths,
# Third group
columns = [
(i + column_slice.start, column)
for i, column in enumerate(grid[column_slice])
if constrainedness[i + column_slice.start] and
column_intrinsic_percentages[i + column_slice.start] == 0 and
column_max_content_widths[i + column_slice.start] > 0]
i for i, _ in enumerate(grid[column_slice], start=column_slice.start)
if constrainedness[i] and
column_intrinsic_percentages[i] == 0 and
column_max_content_widths[i] > 0]
if columns:
current_widths = [column_widths[i] for i, column in columns]
differences = [
max(0, width[0] - width[1])
for width in zip(column_max_content_widths, current_widths)]
if sum(differences) > excess_width:
differences = [
difference / sum(differences) * excess_width
for difference in differences]
excess_width -= sum(differences)
for i, difference in enumerate(differences):
column_widths[columns[i][0]] += difference
if excess_width <= 0:
sum_max_content_widths = sum(column_max_content_widths[i] for i in columns)
ratio = excess_width / sum_max_content_widths
for i in columns:
column_widths[i] += column_max_content_widths[i] * ratio
return
# Fourth group
columns = [
(i + column_slice.start, column)
for i, column in enumerate(grid[column_slice])
if column_intrinsic_percentages[i + column_slice.start] > 0]
i for i, _ in enumerate(grid[column_slice], start=column_slice.start)
if column_intrinsic_percentages[i] > 0 and column_max_content_widths[i] > 0]
if columns:
fixed_width = sum(
column_widths[j] for j in range(len(grid))
if j not in [i for i, column in columns])
percentage_width = sum(
column_intrinsic_percentages[i]
for i, column in columns)
if fixed_width and percentage_width >= 100:
# Sum of the percentages are greater than 100%
ratio = excess_width
elif fixed_width == 0:
# No fixed width, let's take the whole excess width
ratio = excess_width
else:
ratio = fixed_width / (100 - percentage_width)
widths = [
column_intrinsic_percentages[i] * ratio for i, column in columns]
current_widths = [column_widths[i] for i, column in columns]
# Allow to reduce the size of the columns to respect the percentage
differences = [
width[0] - width[1]
for width in zip(widths, current_widths)]
if sum(differences) > excess_width:
differences = [
difference / sum(differences) * excess_width
for difference in differences]
excess_width -= sum(differences)
for i, difference in enumerate(differences):
column_widths[columns[i][0]] += difference
if excess_width <= 0:
sum_intrinsic_percentages = sum(
column_intrinsic_percentages[i] for i in columns)
ratio = excess_width / sum_intrinsic_percentages
for i in columns:
column_widths[i] += column_intrinsic_percentages[i] * ratio
return
# Bonus: we've tried our best to distribute the extra size, but we
# failed. Instead of blindly distributing the size among all the colums
# and breaking all the rules (as said in the draft), let's try to
# change the columns with no constraint at all, then resize the table,
# and at least break the rules to make the columns fill the table.
# Fifth group, part 1
# Fifth group
columns = [
i + column_slice.start for i, column in enumerate(grid[column_slice])
if any(column) and
column_intrinsic_percentages[i + column_slice.start] == 0 and
not any(
max_content_width(context, cell)
for cell in column if cell)]
i for i, column in enumerate(grid[column_slice], start=column_slice.start)
if column]
if columns:
for i in columns:
column_widths[i] += excess_width / len(columns)
return
# Fifth group, part 2, aka abort
return excess_width
# Sixth group
columns = [i for i, _ in enumerate(grid[column_slice], start=column_slice.start)]
for i in columns:
column_widths[i] += excess_width / len(columns)
TRANSPARENT = tinycss2.color3.parse_color('transparent')