Skip to content

Commit

Permalink
Split absolute boxes
Browse files Browse the repository at this point in the history
Fix #36.
  • Loading branch information
liZe committed Dec 12, 2021
1 parent c22b211 commit f5d6d54
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 55 deletions.
72 changes: 63 additions & 9 deletions tests/draw/test_absolute.py
Expand Up @@ -12,14 +12,13 @@
from . import assert_pixels


@pytest.mark.xfail
@assert_no_logs
def test_absolute_split_1():
expected_pixels = '''
BBBBRRRRRRRR____
BBBBRRRRRRRR____
BBBB____________
BBBB____________
BBBBRR__________
BBBBRR__________
'''
html = '''
<style>
Expand All @@ -33,22 +32,23 @@ def test_absolute_split_1():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
left: 0;
position: absolute;
top: 0;
left: 0;
width: 4px;
}
</style>
<div class="split">aa aa</div>
<div>bbbbbb bb</div>
<div>bbbbbb bbb</div>
'''
assert_pixels('absolute_split_1', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_absolute_split_2():
expected_pixels = '''
Expand All @@ -69,6 +69,8 @@ def test_absolute_split_2():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand Down Expand Up @@ -104,6 +106,8 @@ def test_absolute_split_3():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand Down Expand Up @@ -139,6 +143,8 @@ def test_absolute_split_4():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand All @@ -154,7 +160,6 @@ def test_absolute_split_4():
assert_pixels('absolute_split_4', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_absolute_split_5():
expected_pixels = '''
Expand All @@ -175,6 +180,8 @@ def test_absolute_split_5():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand All @@ -197,7 +204,6 @@ def test_absolute_split_5():
assert_pixels('absolute_split_5', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_absolute_split_6():
expected_pixels = '''
Expand All @@ -218,6 +224,8 @@ def test_absolute_split_6():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand All @@ -238,7 +246,6 @@ def test_absolute_split_6():
assert_pixels('absolute_split_6', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_absolute_split_7():
expected_pixels = '''
Expand All @@ -259,6 +266,8 @@ def test_absolute_split_7():
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
Expand All @@ -281,3 +290,48 @@ def test_absolute_split_7():
<div class="push">bbbb bb</div>
'''
assert_pixels('absolute_split_7', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_absolute_next_page():
# TODO: currently, the layout of absolute boxes forces to render a box,
# even when it doesn’t fit in the page. This workaround avoids placeholders
# with no box. Instead, we should remove these placeholders, or avoid
# crashes when they’re rendered.
expected_pixels = '''
RRRRRRRRRR______
RRRRRRRRRR______
RRRRRRRRRR______
RRRRRRRRRR______
BBBBBBRRRR______
BBBBBBRRRR______
BBBBBB__________
________________
'''
html = '''
<style>
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
@page {
background: white;
size: 16px 4px;
}
body {
color: red;
font-family: weasyprint;
font-size: 2px;
line-height: 1;
orphans: 1;
widows: 1;
}
div.split {
color: blue;
position: absolute;
font-size: 3px;
}
</style>
aaaaa aaaaa
<div class="split">bb</div>
aaaaa
'''
assert_pixels('absolute_next_page', 16, 8, expected_pixels, html)
1 change: 0 additions & 1 deletion tests/draw/test_float.py
Expand Up @@ -393,7 +393,6 @@ def test_float_split_1():
assert_pixels('float_split_1', 16, 4, expected_pixels, html)


@pytest.mark.xfail
@assert_no_logs
def test_float_split_2():
expected_pixels = '''
Expand Down
9 changes: 6 additions & 3 deletions weasyprint/layout/__init__.py
Expand Up @@ -91,13 +91,16 @@ def layout_fixed_boxes(context, pages, containing_page):
# Absolute boxes in fixed boxes are rendered as fixed boxes'
# children, even when they are fixed themselves.
absolute_boxes = []
yield absolute_box_layout(
context, box, containing_page, absolute_boxes)
absolute_box, _ = absolute_box_layout(
context, box, containing_page, absolute_boxes,
bottom_space=-inf, skip_stack=None)
yield absolute_box
while absolute_boxes:
new_absolute_boxes = []
for box in absolute_boxes:
absolute_layout(
context, box, containing_page, new_absolute_boxes)
context, box, containing_page, new_absolute_boxes,
bottom_space=-inf, skip_stack=None)
absolute_boxes = new_absolute_boxes


Expand Down
79 changes: 47 additions & 32 deletions weasyprint/layout/absolute.py
Expand Up @@ -187,7 +187,8 @@ def absolute_height(box, context, containing_block):
return translate_box_height, translate_y


def absolute_block(context, box, containing_block, fixed_boxes):
def absolute_block(context, box, containing_block, fixed_boxes, bottom_space,
skip_stack):
from .block import block_container_layout

cb_x, cb_y, cb_width, cb_height = containing_block
Expand All @@ -203,13 +204,15 @@ def absolute_block(context, box, containing_block, fixed_boxes):
if box.is_table_wrapper:
table_wrapper_width(context, box, (cb_width, cb_height))

new_box, _, _, _, _ = block_container_layout(
context, box, bottom_space=-float('inf'), skip_stack=None,
page_is_empty=False, absolute_boxes=absolute_boxes,
fixed_boxes=fixed_boxes, adjoining_margins=None, discard=False)
new_box, resume_at, _, _, _ = block_container_layout(
context, box, bottom_space, skip_stack, page_is_empty=True,
absolute_boxes=absolute_boxes, fixed_boxes=fixed_boxes,
adjoining_margins=None, discard=False)

for child_placeholder in absolute_boxes:
absolute_layout(context, child_placeholder, new_box, fixed_boxes)
absolute_layout(
context, child_placeholder, new_box, fixed_boxes, bottom_space,
skip_stack)

if translate_box_width:
translate_x -= new_box.width
Expand All @@ -218,11 +221,11 @@ def absolute_block(context, box, containing_block, fixed_boxes):

new_box.translate(translate_x, translate_y)

return new_box
return new_box, resume_at


def absolute_flex(context, box, containing_block_sizes, fixed_boxes,
containing_block):
def absolute_flex(context, box, containing_block, fixed_boxes, bottom_space,
skip_stack, containing_block_sizes):
from .flex import flex_layout

# TODO: this function is really close to absolute_block, we should have
Expand All @@ -241,13 +244,15 @@ def absolute_flex(context, box, containing_block_sizes, fixed_boxes,
if box.is_table_wrapper:
table_wrapper_width(context, box, (cb_width, cb_height))

new_box, _, _, _, _ = flex_layout(
context, box, bottom_space=-float('inf'), skip_stack=None,
containing_block=containing_block, page_is_empty=False,
absolute_boxes=absolute_boxes, fixed_boxes=fixed_boxes)
new_box, resume_at, _, _, _ = flex_layout(
context, box, bottom_space, skip_stack, containing_block,
page_is_empty=True, absolute_boxes=absolute_boxes,
fixed_boxes=fixed_boxes)

for child_placeholder in absolute_boxes:
absolute_layout(context, child_placeholder, new_box, fixed_boxes)
absolute_layout(
context, child_placeholder, new_box, fixed_boxes, bottom_space,
skip_stack)

if translate_box_width:
translate_x -= new_box.width
Expand All @@ -256,31 +261,37 @@ def absolute_flex(context, box, containing_block_sizes, fixed_boxes,

new_box.translate(translate_x, translate_y)

return new_box
return new_box, resume_at


def absolute_layout(context, placeholder, containing_block, fixed_boxes):
def absolute_layout(context, placeholder, containing_block, fixed_boxes,
bottom_space, skip_stack):
"""Set the width of absolute positioned ``box``."""
assert not placeholder._layout_done
box = placeholder._box
placeholder.set_laid_out_box(
absolute_box_layout(context, box, containing_block, fixed_boxes))
new_box, resume_at = absolute_box_layout(
context, box, containing_block, fixed_boxes, bottom_space, skip_stack)
placeholder.set_laid_out_box(new_box)
if resume_at:
context.broken_out_of_flow.append(
(box, containing_block, resume_at))


def absolute_box_layout(context, box, containing_block, fixed_boxes):
cb = containing_block
def absolute_box_layout(context, box, containing_block, fixed_boxes,
bottom_space, skip_stack):
# TODO: handle inline boxes (point 10.1.4.1)
# http://www.w3.org/TR/CSS2/visudet.html#containing-block-details
if isinstance(containing_block, boxes.PageBox):
cb_x = cb.content_box_x()
cb_y = cb.content_box_y()
cb_width = cb.width
cb_height = cb.height
cb_x = containing_block.content_box_x()
cb_y = containing_block.content_box_y()
cb_width = containing_block.width
cb_height = containing_block.height
else:
cb_x = cb.padding_box_x()
cb_y = cb.padding_box_y()
cb_width = cb.padding_width()
cb_height = cb.padding_height()
cb_x = containing_block.padding_box_x()
cb_y = containing_block.padding_box_y()
cb_width = containing_block.padding_width()
cb_height = containing_block.padding_height()
original_containing_block = containing_block
containing_block = cb_x, cb_y, cb_width, cb_height

resolve_percentages(box, (cb_width, cb_height))
Expand All @@ -289,15 +300,19 @@ def absolute_box_layout(context, box, containing_block, fixed_boxes):
context.create_block_formatting_context()
# Absolute tables are wrapped into block boxes
if isinstance(box, boxes.BlockBox):
new_box = absolute_block(context, box, containing_block, fixed_boxes)
new_box, resume_at = absolute_block(
context, box, containing_block, fixed_boxes,
bottom_space, skip_stack)
elif isinstance(box, boxes.FlexContainerBox):
new_box = absolute_flex(
context, box, containing_block, fixed_boxes, cb)
new_box, resume_at = absolute_flex(
context, box, containing_block, fixed_boxes,
bottom_space, skip_stack, original_containing_block)
else:
assert isinstance(box, boxes.BlockReplacedBox)
new_box = absolute_replaced(context, box, containing_block)
resume_at = None
context.finish_block_formatting_context(new_box)
return new_box
return new_box, resume_at


def absolute_replaced(context, box, containing_block):
Expand Down
4 changes: 3 additions & 1 deletion weasyprint/layout/block.py
Expand Up @@ -716,7 +716,9 @@ def block_container_layout(context, box, bottom_space, skip_stack,
if new_box.style['position'] == 'relative':
# New containing block, resolve the layout of the absolute descendants
for absolute_box in absolute_boxes:
absolute_layout(context, absolute_box, new_box, fixed_boxes)
absolute_layout(
context, absolute_box, new_box, fixed_boxes, bottom_space,
skip_stack=None)

for child in new_box.children:
relative_positioning(child, (new_box.width, new_box.height))
Expand Down
4 changes: 3 additions & 1 deletion weasyprint/layout/column.py
Expand Up @@ -299,6 +299,8 @@ def create_column_box(children):
if box.style['position'] == 'relative':
# New containing block, resolve the layout of the absolute descendants
for absolute_box in absolute_boxes:
absolute_layout(context, absolute_box, box, fixed_boxes)
absolute_layout(
context, absolute_box, box, fixed_boxes, bottom_space,
skip_stack=None)

return box, skip_stack, next_page, [], False
4 changes: 3 additions & 1 deletion weasyprint/layout/inline.py
Expand Up @@ -864,7 +864,9 @@ def split_inline_box(context, box, position_x, max_x, bottom_space, skip_stack,

if new_box.style['position'] == 'relative':
for absolute_box in absolute_boxes:
absolute_layout(context, absolute_box, new_box, fixed_boxes)
absolute_layout(
context, absolute_box, new_box, fixed_boxes, bottom_space,
skip_stack=None)

if resume_at is not None:
index = tuple(resume_at)[0]
Expand Down

0 comments on commit f5d6d54

Please sign in to comment.