1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 00:21:15 +03:00

Handle margin-break

Some corner-cases are not handled yet, but that's much better than the current
situation.

Fix #115.
This commit is contained in:
Guillaume Ayoub 2019-03-18 18:13:43 +01:00
parent 5fc905baa1
commit 75d8d983ae
6 changed files with 94 additions and 7 deletions

View File

@ -124,6 +124,7 @@ INITIAL_VALUES = {
'break_after': 'auto',
'break_before': 'auto',
'break_inside': 'auto',
'margin_break': 'auto',
'orphans': 2,
'widows': 2,

View File

@ -301,6 +301,13 @@ def break_inside(keyword):
return keyword in ('auto', 'avoid', 'avoid-page', 'avoid-column')
@property()
@single_keyword
def margin_break(keyword):
"""``margin-break`` property validation."""
return keyword in ('auto', 'keep', 'discard')
@property(unstable=True)
@single_token
def page(token):

View File

@ -204,6 +204,7 @@ class LayoutContext(object):
self.excluded_shapes = None # Not initialized yet
self.string_set = defaultdict(lambda: defaultdict(lambda: list()))
self.current_page = None
self.forced_break = False
# Cache
self.strut_layouts = {}

View File

@ -41,6 +41,15 @@ def block_level_layout(context, box, max_position_y, skip_stack,
if box.margin_bottom == 'auto':
box.margin_bottom = 0
if (context.current_page > 1 and page_is_empty):
# TODO: we should take care of cases when this box doesn't have
# collaping borders with the first child of the page, see
# test_margin_break_clearance.
if box.style['margin_break'] == 'discard':
box.margin_top = 0
elif box.style['margin_break'] == 'auto' and context.forced_break:
box.margin_top = 0
collapsed_margin = collapse_margin(
adjoining_margins + [box.margin_top])
box.clearance = get_clearance(context, box, collapsed_margin)
@ -428,11 +437,6 @@ def block_container_layout(context, box, max_position_y, skip_stack,
page_name = block_level_page_name(last_in_flow_child, child)
if page_name or page_break in (
'page', 'left', 'right', 'recto', 'verso'):
if page_break == 'page':
page_break = 'any'
elif page_break not in ('left', 'right', 'recto', 'verso'):
assert page_name
page_break = 'any'
page_name = child.page_values()[0]
next_page = {'break': page_break, 'page': page_name}
resume_at = (index, None)

View File

@ -545,6 +545,7 @@ def make_page(context, root_box, page_type, resume_at, page_number,
# See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
assert isinstance(root_box, (boxes.BlockBox, boxes.FlexContainerBox))
context.create_block_formatting_context()
context.current_page = page_number
page_is_empty = True
adjoining_margins = []
positioned_boxes = [] # Mixed absolute and fixed
@ -722,16 +723,20 @@ def remake_page(index, context, root_box, html, cascaded_styles,
page_state = copy.deepcopy(initial_page_state)
next_page_name = initial_next_page['page']
first = index == 0
# TODO: handle recto/verso and add test
blank = ((initial_next_page['break'] == 'left' and right_page) or
(initial_next_page['break'] == 'right' and not right_page))
if blank:
next_page_name = None
side = 'right' if right_page else 'left'
page_type = PageType(
side, blank, first, name=(next_page_name or None))
page_type = PageType(side, blank, first, name=(next_page_name or None))
set_page_type_computed_styles(
page_type, cascaded_styles, computed_styles, html)
context.forced_break = (
initial_next_page['break'] != 'any' or initial_next_page['page'])
context.margin_clearance = False
# make_page wants a page_number of index + 1
page_number = index + 1
page, resume_at, next_page = make_page(

View File

@ -427,6 +427,75 @@ def test_page_breaks_complex_8():
assert div_4.height == 20
@assert_no_logs
@pytest.mark.parametrize('break_after, margin_break, margin_top', (
('page', 'auto', 0),
('auto', 'auto', 5),
('page', 'keep', 5),
('auto', 'keep', 5),
('page', 'discard', 0),
('auto', 'discard', 0),
))
def test_margin_break(break_after, margin_break, margin_top):
page_1, page_2 = render_pages('''
<style>
@page { size: 70px; margin: 0 }
div { height: 63px; margin: 5px 0 8px;
break-after: %s; margin-break: %s }
</style>
<section>
<div></div>
</section>
<section>
<div></div>
</section>
''' % (break_after, margin_break))
html, = page_1.children
body, = html.children
section, = body.children
div, = section.children
assert div.margin_top == 5
html, = page_2.children
body, = html.children
section, = body.children
div, = section.children
assert div.margin_top == margin_top
@pytest.mark.xfail
@assert_no_logs
def test_margin_break_clearance():
page_1, page_2 = render_pages('''
<style>
@page { size: 70px; margin: 0 }
div { height: 63px; margin: 5px 0 8px; break-after: page }
</style>
<section>
<div></div>
</section>
<section>
<div style="border-top: 1px solid black">
<div></div>
</div>
</section>
''')
html, = page_1.children
body, = html.children
section, = body.children
div, = section.children
assert div.margin_top == 5
html, = page_2.children
body, = html.children
section, = body.children
div_1, = section.children
assert div_1.margin_top == 0
div_2, = div_1.children
assert div_2.margin_top == 5
assert div_2.content_box_y() == 5
@assert_no_logs
def test_page_names_1():
pages = render_pages('''