From 910efca562e6db4eace228c6d634104e7b479a96 Mon Sep 17 00:00:00 2001 From: Kesara Rathnayake Date: Tue, 10 Dec 2024 22:54:27 +1300 Subject: [PATCH 1/4] Fix table overflow edge cases Fixes #2325 --- weasyprint/layout/preferred.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/weasyprint/layout/preferred.py b/weasyprint/layout/preferred.py index e3ab421d1..7294cf0b2 100644 --- a/weasyprint/layout/preferred.py +++ b/weasyprint/layout/preferred.py @@ -13,7 +13,7 @@ from math import inf from ..formatting_structure import boxes -from ..text.line_break import split_first_line +from ..text.line_break import split_first_line, get_log_attrs from .replaced import default_image_sizing @@ -314,7 +314,8 @@ def inline_line_widths(context, box, outer, is_line_start, minimum, skip_stack=N child_text = child.text.encode()[(skip or 0):] if is_line_start and space_collapse: child_text = child_text.lstrip(b' ') - if minimum and child_text == b' ': + _, log_attrs = get_log_attrs(child_text.strip(b' ').decode(), child.style['lang']) + if minimum and (child_text == b' ' or (len(child_text.strip(b' ')) == 1 and log_attrs[0].is_word_boundary)): lines = [0, 0] else: max_width = 0 if minimum else None From 5d36cf53af5b878c088d5367844c5d5af4d8ae3d Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Tue, 10 Dec 2024 20:21:10 +0100 Subject: [PATCH 2/4] Improve workaround for line break detection Related to #2325. --- weasyprint/layout/preferred.py | 41 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/weasyprint/layout/preferred.py b/weasyprint/layout/preferred.py index 7294cf0b2..042031879 100644 --- a/weasyprint/layout/preferred.py +++ b/weasyprint/layout/preferred.py @@ -13,7 +13,7 @@ from math import inf from ..formatting_structure import boxes -from ..text.line_break import split_first_line, get_log_attrs +from ..text.line_break import can_break_text, split_first_line from .replaced import default_image_sizing @@ -306,6 +306,7 @@ def inline_line_widths(context, box, outer, is_line_start, minimum, skip_stack=N elif isinstance(child, boxes.TextBox): space_collapse = child.style['white_space'] in ( 'normal', 'nowrap', 'pre-line') + text_wrap = child.style['white_space'] in ('normal', 'pre-wrap', 'pre-line') if skip_stack is None: skip = 0 else: @@ -314,26 +315,26 @@ def inline_line_widths(context, box, outer, is_line_start, minimum, skip_stack=N child_text = child.text.encode()[(skip or 0):] if is_line_start and space_collapse: child_text = child_text.lstrip(b' ') - _, log_attrs = get_log_attrs(child_text.strip(b' ').decode(), child.style['lang']) - if minimum and (child_text == b' ' or (len(child_text.strip(b' ')) == 1 and log_attrs[0].is_word_boundary)): - lines = [0, 0] - else: - max_width = 0 if minimum else None - lines = [] - resume_index = new_resume_index = 0 - while new_resume_index is not None: - resume_index += new_resume_index - _, _, new_resume_index, width, _, _ = ( - split_first_line( - child_text[resume_index:].decode(), child.style, - context, max_width, child.justification_spacing, - is_line_start=is_line_start, minimum=True)) - lines.append(width) - if first_line: - break - if first_line and new_resume_index: - current_line += lines[0] + max_width = 0 if minimum else None + lines = [] + resume_index = new_resume_index = 0 + while new_resume_index is not None: + resume_index += new_resume_index + _, _, new_resume_index, width, _, _ = split_first_line( + child_text[resume_index:].decode(), child.style, context, max_width, + child.justification_spacing, is_line_start=is_line_start, + minimum=True) + lines.append(width) + if first_line: break + if first_line and new_resume_index: + current_line += lines[0] + break + # TODO: use the real next character instead of 'a' to detect line breaks. + can_break = can_break_text( + child_text.decode()[-1:] + 'a', child.style['lang']) + if minimum and text_wrap and can_break: + lines.append(0) else: # https://www.w3.org/TR/css-text-3/#overflow-wrap # "The line breaking behavior of a replaced element From 37ce9ed7df78f4c9471fb45cfb503e6997dc42e7 Mon Sep 17 00:00:00 2001 From: Kesara Rathnayake Date: Thu, 12 Dec 2024 01:58:05 +1300 Subject: [PATCH 3/4] Test for table overflow --- tests/layout/test_table.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/layout/test_table.py b/tests/layout/test_table.py index 721a6e7c6..482391fd9 100644 --- a/tests/layout/test_table.py +++ b/tests/layout/test_table.py @@ -1558,6 +1558,40 @@ def test_layout_table_auto_51(): assert abs(td_2.width - 70) < 0.1 +@assert_no_logs +def test_layout_table_auto_52(): + # Test regression: + # https://github.com/Kozea/WeasyPrint/issues/2325 + page, = render_pages(''' + + + + + + +
+ foobar, + foobar +
+ ''') + html, = page.children + body, = html.children + table_wrapper, = body.children + table, = table_wrapper.children + row_group, = table.children + row, = row_group.children + td_1, td_2 = row.children + assert table_wrapper.position_x == 0 + assert table.position_x == 0 + assert td_1.position_x == 1 + assert td_1.width == 4 # spacing + assert td_2.position_x == 6 # 1 + 5 + sp + assert td_2.width == 112 + assert table.width == td_2.width + td_1.width + 3 + + @assert_no_logs @pytest.mark.parametrize( 'body_width, table_width, check_width, positions, widths', ( From 84beeaa06ab4fd4b15652c03a6fc8a67e69c3c05 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sun, 15 Dec 2024 19:41:50 +0100 Subject: [PATCH 4/4] Minor fixes for test --- tests/layout/test_table.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/layout/test_table.py b/tests/layout/test_table.py index 482391fd9..94601dbd1 100644 --- a/tests/layout/test_table.py +++ b/tests/layout/test_table.py @@ -1564,14 +1564,16 @@ def test_layout_table_auto_52(): # https://github.com/Kozea/WeasyPrint/issues/2325 page, = render_pages(''' - +
- foobar, - foobar + foo, + foo, + foo
@@ -1583,13 +1585,14 @@ def test_layout_table_auto_52(): row_group, = table.children row, = row_group.children td_1, td_2 = row.children + assert table.width == 20 assert table_wrapper.position_x == 0 assert table.position_x == 0 - assert td_1.position_x == 1 - assert td_1.width == 4 # spacing - assert td_2.position_x == 6 # 1 + 5 + sp - assert td_2.width == 112 - assert table.width == td_2.width + td_1.width + 3 + assert td_1.position_x == 1 # spacing + assert td_1.width == 4 # image width + assert td_2.position_x == td_1.width + 2 * 1 # 2 * spacing + assert td_2.width == table.width - td_1.width - 3 * 1 # 3 * spacing + assert td_2.height == 3 * 2 # 3 lines * line height @assert_no_logs