Repository: Kozea/WeasyPrint (python)

Issue:
v63.0 margin is ignored for block inside inline-block
In v63.0 the margin is ignored for a div block if it is inside an inline-block element:

```
<html>
<head>
<style>
      .coverpage .coverpage-wrapper {
        display: table;
        width: 132.0mm;
        height: 271.0mm;
      }

      .coverpage .coverpage-cell {
        display: table-cell;
        width: 132.0mm;
        height: 271.0mm;
        vertical-align: middle;
      }

      .coverpage .coverpage-content {
        display: inline-block;
        width: 132.0mm;
      }

      .page-top {
        margin-top: 170.0mm;
        margin-bottom: 3.0mm;
      }
    </style>
</head>
<body>
    <div class="main">
      <div class="coverpage">
        <div class="coverpage-wrapper">
          <div class="coverpage-cell">
            <div class="coverpage-content">
              <div class="page-top">
                Speisekarte
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
```

Pull request description:
Include floats in calculation of minimum cell height
We calculate the height of a cell by rendering the cell as a block after setting an auto height to the cell. This way, it can grow if needed.

But setting the height attribute is not enough: we take care of floats in finish_block_formatting_context, that checks for the style value: we have to fake this value too.

Related to #2216. Fix #2293.

Patch:
diff --git a/pyproject.toml b/pyproject.toml
index c3996ffb..58fe8c53 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,14 +12,14 @@ requires-python = '>=3.9'
 readme = {file = 'README.rst', content-type = 'text/x-rst'}
 license = {file = 'LICENSE'}
 dependencies = [
-  'pydyf >=0.11.0',
-  'cffi >=0.6',
-  'tinyhtml5 >=2.0.0b1',
-  'tinycss2 >=1.4.0',
-  'cssselect2 >=0.1',
-  'Pyphen >=0.9.1',
-  'Pillow >=9.1.0',
-  'fonttools[woff] >=4.0.0',
+  'pydyf >=0.11.0,<0.12',
+  'cffi >=0.6,<2',
+  'tinyhtml5 >=2.0.0b1,<3',
+  'tinycss2 >=1.4.0,<2',
+  'cssselect2 >=0.1,<0.8',
+  'Pyphen >=0.9.1,<0.16',
+  'Pillow >=9.1.0,<12',
+  'fonttools[woff] >=4.0.0,<5',
 ]
 classifiers = [
   'Development Status :: 5 - Production/Stable',
diff --git a/weasyprint/layout/table.py b/weasyprint/layout/table.py
index 2a8992c8..d08d54ed 100644
--- a/weasyprint/layout/table.py
+++ b/weasyprint/layout/table.py
@@ -144,9 +144,6 @@ def table_layout(context, table, bottom_space, skip_stack, containing_block,
                     sum(spanned_widths) +
                     border_spacing_x * (cell.colspan - 1) -
                     borders_plus_padding)
-                # Set computed height as a minimum.
-                cell.computed_height = cell.height
-                cell.height = 'auto'
                 if skip_stack:
                     if index_cell in skip_stack:
                         cell_skip_stack = skip_stack[index_cell]
@@ -175,11 +172,20 @@ def table_layout(context, table, bottom_space, skip_stack, containing_block,
                 # force to render something if the page is actually empty, or
                 # just draw an empty cell otherwise. See
                 # test_table_break_children_margin.
+                # Pretend that height is not set, keeping computed height as a minimum.
+                cell.computed_height = cell.height
+                cell.height = 'auto'
+                original_style = cell.style
+                if cell.style['height'] != 'auto':
+                    style_copy = cell.style.copy()
+                    style_copy['height'] = 'auto'
+                    cell.style = style_copy
                 new_cell, cell_resume_at, _, _, _, _ = block_container_layout(
                     context, cell, bottom_space, cell_skip_stack,
                     page_is_empty=page_is_empty, absolute_boxes=absolute_boxes,
                     fixed_boxes=fixed_boxes, adjoining_margins=None,
                     discard=False, max_lines=None)
+                cell.style = original_style
                 if new_cell is None:
                     cell = cell.copy_with_children([])
                     cell, _, _, _, _, _ = block_container_layout(
@@ -289,10 +295,7 @@ def table_layout(context, table, bottom_space, skip_stack, containing_block,
                             cell.computed_height - cell.content_height)
                     if vertical_align_shift > 0:
                         for child in cell.children:
-                            child_shift = child.margin_height()
-                            if cell.vertical_align == 'middle':
-                                child_shift /= 2
-                            child.translate(dy=vertical_align_shift - child_shift)
+                            child.translate(dy=vertical_align_shift)
 
             next_position_y = row.position_y + row.height
             if resume_at is None:


Test patch:
diff --git a/tests/layout/test_table.py b/tests/layout/test_table.py
index 270d4b4e..06044033 100644
--- a/tests/layout/test_table.py
+++ b/tests/layout/test_table.py
@@ -1966,17 +1966,50 @@ def test_table_vertical_align(assert_pixels):
 
 @assert_no_logs
 def test_table_vertical_align_float():
-    # Test regression: https://github.com/Kozea/WeasyPrint/issues/2216
+    # Test regressions:
+    # https://github.com/Kozea/WeasyPrint/issues/2216
+    # https://github.com/Kozea/WeasyPrint/issues/2293
     page, = render_pages('''
       <style>
-        @page { size: 100px }
-        td { width: 50px; height: 100px }
-        div { width: 25px; height: 20px }
+        @page { size: 100px 400px }
+        td { width: 50px; height: 100px; line-height: 0 }
       </style>
       <table>
         <tr>
-          <td style="vertical-align: middle"><div style="float: left"></div></td>
-          <td style="vertical-align: bottom"><div style="float: right"></div></td>
+          <td style="vertical-align: middle">
+            <div style="float: left; height: 20px; width: 20px"></div>
+            <div style="display: inline-block; height: 30px; width: 30px"></div>
+          </td>
+          <td style="vertical-align: bottom">
+            <div style="display: inline-block; height: 30px; width: 30px"></div>
+            <div style="float: right; height: 20px; width: 20px"></div>
+          </td>
+        </tr>
+        <tr>
+          <td style="vertical-align: middle">
+            <div style="float: left; height: 20px; width: 20px"></div>
+            <div style="display: inline-block; height: 10px; width: 10px"></div>
+          </td>
+          <td style="vertical-align: bottom">
+            <div style="display: inline-block; height: 10px; width: 10px"></div>
+            <div style="float: right; height: 20px; width: 20px"></div>
+          </td>
+        </tr>
+        <tr>
+          <td style="vertical-align: middle">
+            <div style="display: inline-block; height: 10px; width: 10px"></div>
+          </td>
+          <td style="vertical-align: bottom">
+            <div style="display: inline-block; height: 10px; width: 10px"></div>
+          </td>
+        </tr>
+        <tr>
+          <td style="vertical-align: middle">
+            <div style="float: left; height: 20px; width: 20px"></div>
+          </td>
+          <td style="vertical-align: bottom">
+            <div style="float: right; height: 20px; width: 20px"></div>
+          </td>
         </tr>
       </table>
     ''')
@@ -1986,14 +2019,52 @@ def test_table_vertical_align_float():
     table, = wrapper.children
     table, = wrapper.children
     row_group, = table.children
-    row, = row_group.children
-    td_1, td_2 = row.children
+    row_1, row_2, row_3, row_4, = row_group.children
+
+    td_1, td_2 = row_1.children
+    div_1, line_wrapper = td_1.children
+    assert div_1.position_x == 0
+    assert div_1.position_y == (100 - 30) / 2
+    div_2 = line_wrapper.children[0].children[0]
+    assert div_2.position_x == 20
+    assert div_2.position_y == (100 - 30) / 2
+    line_box, = td_2.children
+    div_1, _, div_2 = line_box.children
+    assert div_1.position_x == 50
+    assert div_1.position_y == 100 - 30
+    assert div_2.position_x == 80
+    assert div_2.position_y == 100 - 30
+
+    td_1, td_2 = row_2.children
+    div_1, line_wrapper = td_1.children
+    assert div_1.position_x == 0
+    assert div_1.position_y == 100 + (100 - 20) / 2
+    div_2 = line_wrapper.children[0].children[0]
+    assert div_2.position_x == 20
+    assert div_2.position_y == 100 + (100 - 20) / 2
+    line_box, = td_2.children
+    div_1, _, div_2 = line_box.children
+    assert div_1.position_x == 50
+    assert div_1.position_y == 100 + (100 - 20)
+    assert div_2.position_x == 80
+    assert div_2.position_y == 100 + (100 - 20)
+
+    td_1, td_2 = row_3.children
+    line_box, = td_1.children
+    div, _ = line_box.children
+    assert div.position_x == 0
+    assert div.position_y == 200 + (100 - 10) / 2
+    div, = td_2.children
+    assert div.position_x == 50
+    assert div.position_y == 200 + (100 - 10)
+
+    td_1, td_2 = row_4.children
     div, = td_1.children
     assert div.position_x == 0
-    assert div.position_y == 40  # (100 - 20) / 2
+    assert div.position_y == 300 + (100 - 20) / 2
     div, = td_2.children
-    assert div.position_x == 75
-    assert div.position_y == 80  # 100 - 20
+    assert div.position_x == 80
+    assert div.position_y == 300 + (100 - 20)
 
 
 @assert_no_logs