From 5b431c66308f747b5dc8b8711f18a1ab53d61e62 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Wed, 4 Mar 2026 18:21:53 +0100 Subject: [PATCH 1/2] Avoid endless loops in grids Fix #2691. --- tests/layout/test_grid.py | 37 +++++++++++++++++++++++++++++++++++++ weasyprint/layout/grid.py | 10 ++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/tests/layout/test_grid.py b/tests/layout/test_grid.py index 8ff75908f..a4bff2f48 100644 --- a/tests/layout/test_grid.py +++ b/tests/layout/test_grid.py @@ -1863,3 +1863,40 @@ def test_grid_in_flex_after_full_height(): grid, = flex.children grid_item, = grid.children assert not grid_item.children + + +@assert_no_logs +def test_grid_in_columns(): + # Regression test for #2691. + page1, page2, = render_pages(''' + +
+
+
+
+
+
+
+
+
+ ''') + html, = page1.children + body, = html.children + div, = body.children + column1, column2 = div.children + grid, = column1.children + assert len(grid.children) == 1 + grid, = column2.children + assert len(grid.children) == 1 + + html, = page2.children + body, = html.children + div, = body.children + column1, column2 = div.children + grid, = column1.children + assert len(grid.children) == 1 + grid, = column2.children + assert len(grid.children) == 0 diff --git a/weasyprint/layout/grid.py b/weasyprint/layout/grid.py index b5a041962..d371cf5a7 100644 --- a/weasyprint/layout/grid.py +++ b/weasyprint/layout/grid.py @@ -1315,7 +1315,6 @@ def _add_page_children(max_row=inf): context, child, bottom_space, child_skip_stack, parent, page_is_empty, absolute_boxes, fixed_boxes)[:3] if new_child: - page_is_empty = False broken_child = False span = _get_span(child.style['grid_row_start']) if child_resume_at: @@ -1337,7 +1336,10 @@ def _add_page_children(max_row=inf): # last child. assert isinstance(new_child, boxes.ParentBox) previous_skip_child = max(child_skip_stack) if child_skip_stack else 0 - resume_at[y][i] = {previous_skip_child + len(new_child.children): None} + if not page_is_empty: + resume_at[y][i] = { + previous_skip_child + len(new_child.children): None} + page_is_empty = False else: if resume_at is None: resume_at = {} @@ -1418,10 +1420,10 @@ def _add_page_children(max_row=inf): broken_child = y + span >= next_page_last_row + 1 if broken_child and child.style['box_decoration_break'] != 'clone': child.remove_decoration(start=False, end=True) - child.height = ( + child.height = max(0, ( context.page_bottom - bottom_space - child.position_y - child.margin_top - child.border_top_width - child.padding_top - - child.margin_bottom - child.border_bottom_width - child.padding_bottom) + child.margin_bottom - child.border_bottom_width - child.padding_bottom)) if broken_child: # Child not fully drawn, keep advancement. advancements[x, y] = child.margin_height() From d74fb34e466c2906b8cb840d358f0bfd606417f7 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Fri, 6 Mar 2026 15:47:27 +0100 Subject: [PATCH 2/2] Add failing test for #2695 --- tests/layout/test_grid.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/layout/test_grid.py b/tests/layout/test_grid.py index a4bff2f48..cd1297f32 100644 --- a/tests/layout/test_grid.py +++ b/tests/layout/test_grid.py @@ -2,6 +2,8 @@ from math import isclose +import pytest + from ..testing_utils import assert_no_logs, render_pages @@ -1900,3 +1902,40 @@ def test_grid_in_columns(): assert len(grid.children) == 1 grid, = column2.children assert len(grid.children) == 0 + + +@pytest.mark.xfail +@assert_no_logs +def test_grid_in_columns_with_break(): + # Regression test for #2695. + page1, page2, = render_pages(''' + +
+
+
+
+
+
+
+
+
+ ''') + html, = page1.children + body, = html.children + div, = body.children + column1, = div.children + grid, = column1.children + section1, = grid.children + assert section1.position_y == 6 + + html, = page2.children + body, = html.children + div, = body.children + column1, = div.children + grid, = column1.children + section2, section3 = grid.children + assert section2.position_y == 0 + assert section3.position_y == 4