""" weasyprint.tests.layout.block ----------------------------- Tests for blocks layout. """ import pytest from weasyprint.formatting_structure import boxes from ..testing_utils import assert_no_logs, render_pages @assert_no_logs def test_block_widths(): page, = render_pages('''

''') html, = page.children assert html.element_tag == 'html' body, = html.children assert body.element_tag == 'body' assert body.width == 120 divs = body.children paragraphs = [] for div in divs: assert isinstance(div, boxes.BlockBox) assert div.element_tag == 'div' assert div.width == 100 for paragraph in div.children: assert isinstance(paragraph, boxes.BlockBox) assert paragraph.element_tag == 'p' assert paragraph.padding_left == 2 assert paragraph.padding_right == 2 assert paragraph.border_left_width == 1 assert paragraph.border_right_width == 1 paragraphs.append(paragraph) assert len(paragraphs) == 15 # width is 'auto' assert paragraphs[0].width == 94 assert paragraphs[0].margin_left == 0 assert paragraphs[0].margin_right == 0 # No 'auto', over-constrained equation with ltr, the initial # 'margin-right: 0' was ignored. assert paragraphs[1].width == 50 assert paragraphs[1].margin_left == 0 # No 'auto', over-constrained equation with rtl, the initial # 'margin-left: 0' was ignored. assert paragraphs[2].width == 50 assert paragraphs[2].margin_right == 0 # width is 'auto' assert paragraphs[3].width == 64 assert paragraphs[3].margin_left == 20 # margin-right is 'auto' assert paragraphs[4].width == 50 assert paragraphs[4].margin_left == 20 # margin-left is 'auto' assert paragraphs[5].width == 50 assert paragraphs[5].margin_left == 24 # Both margins are 'auto', remaining space is split in half assert paragraphs[6].width == 50 assert paragraphs[6].margin_left == 22 # width is 'auto', other 'auto' are set to 0 assert paragraphs[7].width == 74 assert paragraphs[7].margin_left == 20 # width is 'auto', other 'auto' are set to 0 assert paragraphs[8].width == 74 assert paragraphs[8].margin_left == 0 # width is 'auto', other 'auto' are set to 0 assert paragraphs[9].width == 94 assert paragraphs[9].margin_left == 0 # sum of non-auto initially is too wide, set auto values to 0 assert paragraphs[10].width == 200 assert paragraphs[10].margin_left == 0 # Constrained by min-width, same as above assert paragraphs[11].width == 200 assert paragraphs[11].margin_left == 0 # Constrained by max-width, same as paragraphs[6] assert paragraphs[12].width == 50 assert paragraphs[12].margin_left == 22 # NOT constrained by min-width assert paragraphs[13].width == 94 assert paragraphs[13].margin_left == 0 # 70% assert paragraphs[14].width == 70 assert paragraphs[14].margin_left == 0 @assert_no_logs def test_block_heights_p(): page, = render_pages('''

''') html, = page.children body, = html.children heights = [div.height for div in body.children] assert heights == [90, 90 * 3, 20, 120, 20, 120, 90, 90] @assert_no_logs def test_block_heights_img(): page, = render_pages('''
''') html, = page.children body, = html.children heights = [div.height for div in body.children] assert heights == [40, 20, 20, 20, 20, 20] @assert_no_logs def test_block_heights_img_no_body_height(): # Same but with no height on body: percentage *-height is ignored page, = render_pages('''
''') html, = page.children body, = html.children heights = [div.height for div in body.children] assert heights == [40, 40, 20, 40, 20, 0] @assert_no_logs def test_block_percentage_heights_no_html_height(): page, = render_pages(''' ''') html, = page.children assert html.element_tag == 'html' body, = html.children assert body.element_tag == 'body' # Since html’s height depend on body’s, body’s 50% means 'auto' assert body.height == 0 @assert_no_logs def test_block_percentage_heights(): page, = render_pages(''' ''') html, = page.children assert html.element_tag == 'html' body, = html.children assert body.element_tag == 'body' # This time the percentage makes sense assert body.height == 150 @assert_no_logs @pytest.mark.parametrize('size', ( ('width: 10%; height: 1000px',), ('max-width: 10%; max-height: 1000px; height: 2000px',), ('width: 5%; min-width: 10%; min-height: 1000px',), ('width: 10%; height: 1000px; min-width: auto; max-height: none',), )) def test_box_sizing(size): # http://www.w3.org/TR/css3-ui/#box-sizing page, = render_pages('''
''' % size) html, = page.children body, = html.children div_1, div_2, div_3, div_4 = body.children for div in div_1, div_2: assert div.style['box_sizing'] == 'content-box' assert div.width == 1000 assert div.height == 1000 assert div.padding_width() == 1020 assert div.padding_height() == 1020 assert div.border_width() == 1022 assert div.border_height() == 1022 assert div.margin_height() == 1222 # margin_width() is the width of the containing block # padding-box assert div_3.style['box_sizing'] == 'padding-box' assert div_3.width == 980 # 1000 - 20 assert div_3.height == 980 assert div_3.padding_width() == 1000 assert div_3.padding_height() == 1000 assert div_3.border_width() == 1002 assert div_3.border_height() == 1002 assert div_3.margin_height() == 1202 # border-box assert div_4.style['box_sizing'] == 'border-box' assert div_4.width == 978 # 1000 - 20 - 2 assert div_4.height == 978 assert div_4.padding_width() == 998 assert div_4.padding_height() == 998 assert div_4.border_width() == 1000 assert div_4.border_height() == 1000 assert div_4.margin_height() == 1200 @assert_no_logs @pytest.mark.parametrize('size', ( ('width: 0; height: 0'), ('max-width: 0; max-height: 0'), ('min-width: 0; min-height: 0; width: 0; height: 0'), )) def test_box_sizing_zero(size): # http://www.w3.org/TR/css3-ui/#box-sizing page, = render_pages('''
''' % size) html, = page.children body, = html.children for div in body.children: assert div.width == 0 assert div.height == 0 assert div.padding_width() == 20 assert div.padding_height() == 20 assert div.border_width() == 22 assert div.border_height() == 22 assert div.margin_height() == 222 # margin_width() is the width of the containing block COLLAPSING = ( ('10px', '15px', 15), # not 25 # "The maximum of the absolute values of the negative adjoining margins is # deducted from the maximum of the positive adjoining margins" ('-10px', '15px', 5), ('10px', '-15px', -5), ('-10px', '-15px', -15), ('10px', 'auto', 10), # 'auto' is 0 ) NOT_COLLAPSING = ( ('10px', '15px', 25), ('-10px', '15px', 5), ('10px', '-15px', -5), ('-10px', '-15px', -25), ('10px', 'auto', 10), # 'auto' is 0 ) @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_1(margin_1, margin_2, result): # Siblings page, = render_pages('''

Lorem ipsum

dolor sit amet ''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, p2 = body.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_2(margin_1, margin_2, result): # Not siblings, first is nested page, = render_pages('''

Lorem ipsum

dolor sit amet ''' % (margin_1, margin_2)) html, = page.children body, = html.children div, p2 = body.children p1, = div.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_3(margin_1, margin_2, result): # Not siblings, second is nested page, = render_pages('''

Lorem ipsum

dolor sit amet

''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, div = body.children p2, = div.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_4(margin_1, margin_2, result): # Not siblings, second is doubly nested page, = render_pages('''

Lorem ipsum

dolor sit amet

''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, div1 = body.children div2, = div1.children p2, = div2.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_5(margin_1, margin_2, result): # Collapsing with children page, = render_pages('''

Lorem ipsum

dolor sit amet

''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, div1 = body.children div2, = div1.children p2, = div2.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() # Parent and element edge are the same: assert div1.border_box_y() == p2.border_box_y() assert div2.border_box_y() == p2.border_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', NOT_COLLAPSING) def test_vertical_space_6(margin_1, margin_2, result): # Block formatting context: Not collapsing with children page, = render_pages('''

Lorem ipsum

dolor sit amet

''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, div1 = body.children div2, = div1.children p2, = div2.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_7(margin_1, margin_2, result): # Collapsing through an empty div page, = render_pages('''

Lorem ipsum

dolor sit amet ''' % (2 * (margin_1, margin_2))) html, = page.children body, = html.children p1, div, p2 = body.children p1_bottom = p1.content_box_y() + p1.height p2_top = p2.content_box_y() assert p2_top - p1_bottom == result @pytest.mark.parametrize('margin_1, margin_2, result', NOT_COLLAPSING) def test_vertical_space_8(margin_1, margin_2, result): # The root element does not collapse page, = render_pages('''

Lorem ipsum ''' % (margin_1, margin_2)) html, = page.children body, = html.children p1, = body.children p1_top = p1.content_box_y() # Vertical space from y=0 assert p1_top == result @pytest.mark.parametrize('margin_1, margin_2, result', COLLAPSING) def test_vertical_space_9(margin_1, margin_2, result): # DOES collapse page, = render_pages('''

Lorem ipsum ''' % (margin_1, margin_2)) html, = page.children body, = html.children div, = body.children p1, = div.children p1_top = p1.content_box_y() # Vertical space from y=0 assert p1_top == result @assert_no_logs def test_box_decoration_break_block_slice(): # http://www.w3.org/TR/css3-background/#the-box-decoration-break page_1, page_2 = render_pages('''

''') html, = page_1.children body, = html.children paragraph, = body.children img_1, img_2 = paragraph.children assert paragraph.position_y == 0 assert paragraph.margin_top == 5 assert paragraph.border_top_width == 3 assert paragraph.padding_top == 2 assert paragraph.content_box_y() == 10 assert img_1.position_y == 10 assert img_2.position_y == 50 assert paragraph.height == 90 assert paragraph.margin_bottom == 0 assert paragraph.border_bottom_width == 0 assert paragraph.padding_bottom == 0 assert paragraph.margin_height() == 100 html, = page_2.children body, = html.children paragraph, = body.children img_1, img_2 = paragraph.children assert paragraph.position_y == 0 assert paragraph.margin_top == 0 assert paragraph.border_top_width == 0 assert paragraph.padding_top == 0 assert paragraph.content_box_y() == 0 assert img_1.position_y == 0 assert img_2.position_y == 40 assert paragraph.height == 80 assert paragraph.padding_bottom == 2 assert paragraph.border_bottom_width == 3 assert paragraph.margin_bottom == 5 assert paragraph.margin_height() == 90 @assert_no_logs def test_box_decoration_break_block_clone(): # http://www.w3.org/TR/css3-background/#the-box-decoration-break page_1, page_2 = render_pages('''

''') html, = page_1.children body, = html.children paragraph, = body.children img_1, img_2 = paragraph.children assert paragraph.position_y == 0 assert paragraph.margin_top == 5 assert paragraph.border_top_width == 3 assert paragraph.padding_top == 2 assert paragraph.content_box_y() == 10 assert img_1.position_y == 10 assert img_2.position_y == 50 assert paragraph.height == 80 # TODO: bottom margin should be 0 # https://www.w3.org/TR/css-break-3/#valdef-box-decoration-break-clone # "Cloned margins are truncated on block-level boxes." # See https://github.com/Kozea/WeasyPrint/issues/115 assert paragraph.margin_bottom == 5 assert paragraph.border_bottom_width == 3 assert paragraph.padding_bottom == 2 assert paragraph.margin_height() == 100 html, = page_2.children body, = html.children paragraph, = body.children img_1, img_2 = paragraph.children assert paragraph.position_y == 0 assert paragraph.margin_top == 0 assert paragraph.border_top_width == 3 assert paragraph.padding_top == 2 assert paragraph.content_box_y() == 5 assert img_1.position_y == 5 assert img_2.position_y == 45 assert paragraph.height == 80 assert paragraph.padding_bottom == 2 assert paragraph.border_bottom_width == 3 assert paragraph.margin_bottom == 5 assert paragraph.margin_height() == 95 @assert_no_logs def test_box_decoration_break_clone_bottom_padding(): page_1, page_2 = render_pages('''

a
b
c
''') html, = page_1.children body, = html.children article, = body.children assert article.height == 80 - 2 * 12 div_1, div_2 = article.children assert div_1.position_y == 12 assert div_2.position_y == 12 + 20 html, = page_2.children body, = html.children article, = body.children assert article.height == 20 div, = article.children assert div.position_y == 12 @pytest.mark.xfail @assert_no_logs def test_box_decoration_break_slice_bottom_padding(): # pragma: no cover # Last div fits in first, but not article's padding. As it is impossible to # break between a parent and its last child, put last child on next page. # TODO: at the end of block_container_layout, we should check that the box # with its bottom border/padding doesn't cross the bottom line. If it does, # we should re-render the box with a max_position_y including the bottom # border/padding. page_1, page_2 = render_pages('''
a
b
c
''') html, = page_1.children body, = html.children article, = body.children assert article.height == 80 - 12 div_1, div_2 = article.children assert div_1.position_y == 12 assert div_2.position_y == 12 + 20 html, = page_2.children body, = html.children article, = body.children assert article.height == 20 div, = article.children assert div.position_y == 0 @assert_no_logs def test_overflow_auto(): page, = render_pages('''
bla bla bla
toto toto''') html, = page.children body, = html.children article, = body.children assert article.height == 50 + 10 + 10 @assert_no_logs def test_box_margin_top_repagination(): # Test regression: https://github.com/Kozea/WeasyPrint/issues/943 page_1, page_2 = render_pages('''

1
1
2
2

title

''') html, = page_1.children body, = html.children p, div = body.children assert div.margin_top == 20 assert div.padding_box_y() == 10 + 20 html, = page_2.children body, = html.children div, h1 = body.children assert div.margin_top == 0 assert div.padding_box_y() == 0 @assert_no_logs def test_continue_discard(): page_1, = render_pages('''
a
b
c
d
e
f
''') html, = page_1.children body, = html.children article, = body.children assert article.height == 3 * 25 div_1, div_2, div_3 = article.children assert div_1.position_y == 1 assert div_2.position_y == 1 + 25 assert div_3.position_y == 1 + 25 * 2 assert article.border_bottom_width == 1 @assert_no_logs def test_continue_discard_children(): page_1, = render_pages('''
a
b
c
d
e
f
''') html, = page_1.children body, = html.children article, = body.children assert article.height == 2 + 3 * 25 section, = article.children assert section.height == 3 * 25 div_1, div_2, div_3 = section.children assert div_1.position_y == 2 assert div_2.position_y == 2 + 25 assert div_3.position_y == 2 + 25 * 2 assert article.border_bottom_width == 1