Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Kozea/WeasyPrint
Browse files Browse the repository at this point in the history
  • Loading branch information
liZe committed Sep 13, 2019
2 parents c43a218 + 9581407 commit 396ec71
Show file tree
Hide file tree
Showing 5 changed files with 524 additions and 44 deletions.
14 changes: 9 additions & 5 deletions weasyprint/layout/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -723,16 +723,20 @@ def block_level_page_break(sibling_before, sibling_after):
"""
values = []
# https://drafts.csswg.org/css-break-3/#possible-breaks
block_parallel_box_types = (
boxes.BlockLevelBox, boxes.TableRowGroupBox, boxes.TableRowBox)

box = sibling_before
while isinstance(box, boxes.BlockLevelBox):
while isinstance(box, block_parallel_box_types):
values.append(box.style['break_after'])
if not (isinstance(box, boxes.ParentBox) and box.children):
break
box = box.children[-1]
values.reverse() # Have them in tree order

box = sibling_after
while isinstance(box, boxes.BlockLevelBox):
while isinstance(box, block_parallel_box_types):
values.append(box.style['break_before'])
if not (isinstance(box, boxes.ParentBox) and box.children):
break
Expand Down Expand Up @@ -796,7 +800,9 @@ def find_earlier_page_break(children, absolute_boxes, fixed_boxes):
previous_in_flow = child
if child.is_in_normal_flow() and (
child.style['break_inside'] not in ('avoid', 'avoid-page')):
if isinstance(child, boxes.BlockBox):
breakable_box_types = (
boxes.BlockBox, boxes.TableBox, boxes.TableRowGroupBox)
if isinstance(child, breakable_box_types):
result = find_earlier_page_break(
child.children, absolute_boxes, fixed_boxes)
if result:
Expand All @@ -807,8 +813,6 @@ def find_earlier_page_break(children, absolute_boxes, fixed_boxes):
resume_at = (new_child.index, resume_at)
index += 1 # Remove placeholders after child
break
elif isinstance(child, boxes.TableBox):
pass # TODO: find an earlier break between table rows.
else:
return None

Expand Down
15 changes: 10 additions & 5 deletions weasyprint/layout/inlines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1294,12 +1294,17 @@ def can_break_inside(box):
return False


def same_broken_child(skip_stack_1, skip_stack_2):
def same_broken_child(original_skip_stack, relative_skip_stack):
"""Check that the skip stacks design the same text box."""
while isinstance(skip_stack_1, tuple) and isinstance(skip_stack_2, tuple):
if skip_stack_1[1] is None and skip_stack_2[1] is None:
while (isinstance(original_skip_stack, tuple) and
isinstance(relative_skip_stack, tuple)):
if original_skip_stack[1] is None and relative_skip_stack[1] is None:
# The last levels of the two skip_stack are the same
return True
if skip_stack_1[0] != skip_stack_2[0]:
if relative_skip_stack[0] != 0:
# If at the current level the skip_stack is not 0, it means that
# it is not the first child that has been cut
return False
skip_stack_1, skip_stack_2 = skip_stack_1[1], skip_stack_2[1]
original_skip_stack = original_skip_stack[1]
relative_skip_stack = relative_skip_stack[1]
return False
134 changes: 100 additions & 34 deletions weasyprint/layout/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ def table_layout(context, table, max_position_y, skip_stack, containing_block,
page_is_empty, absolute_boxes, fixed_boxes):
"""Layout for a table box."""
# Avoid a circular import
from .blocks import block_container_layout
from .blocks import (
block_container_layout, block_level_page_break,
find_earlier_page_break)

column_widths = table.column_widths

Expand Down Expand Up @@ -61,6 +63,7 @@ def table_layout(context, table, max_position_y, skip_stack, containing_block,
def group_layout(group, position_y, max_position_y,
page_is_empty, skip_stack):
resume_at = None
next_page = {'break': 'any', 'page': None}
original_page_is_empty = page_is_empty
resolve_percentages(group, containing_block=table)
group.position_x = rows_x
Expand All @@ -78,6 +81,16 @@ def group_layout(group, position_y, max_position_y,
assert not skip_stack # No breaks inside rows for now
for i, row in enumerate(group.children[skip:]):
index_row = i + skip
row.index = index_row

if new_group_children:
page_break = block_level_page_break(
new_group_children[-1], row)
if page_break in ('page', 'recto', 'verso', 'left', 'right'):
next_page['break'] = page_break
resume_at = (index_row, None)
break

resolve_percentages(row, containing_block=table)
row.position_x = rows_x
row.position_y = position_y
Expand Down Expand Up @@ -207,7 +220,22 @@ def group_layout(group, position_y, max_position_y,
# Break if this row overflows the page, unless there is no
# other content on the page.
if next_position_y > max_position_y and not page_is_empty:
resume_at = (index_row, None)
if new_group_children:
previous_row = new_group_children[-1]
page_break = block_level_page_break(previous_row, row)
if page_break == 'avoid':
earlier_page_break = find_earlier_page_break(
new_group_children, absolute_boxes, fixed_boxes)
if earlier_page_break:
new_group_children, resume_at = earlier_page_break
break
else:
resume_at = (index_row, None)
break
if original_page_is_empty:
resume_at = (index_row, None)
else:
return None, None, next_page
break

position_y = next_position_y
Expand All @@ -219,7 +247,7 @@ def group_layout(group, position_y, max_position_y,
if resume_at and not original_page_is_empty and (
group.style['break_inside'] in ('avoid', 'avoid-page') or
not new_group_children):
return None, None
return None, None, next_page

group = group.copy_with_children(
new_group_children,
Expand All @@ -240,7 +268,7 @@ def group_layout(group, position_y, max_position_y,
# The last border spacing is outside of the group.
group.height -= border_spacing_y

return group, resume_at
return group, resume_at, next_page

def body_groups_layout(skip_stack, position_y, max_position_y,
page_is_empty):
Expand All @@ -250,16 +278,40 @@ def body_groups_layout(skip_stack, position_y, max_position_y,
skip, skip_stack = skip_stack
new_table_children = []
resume_at = None
next_page = {'break': 'any', 'page': None}

for i, group in enumerate(table.children[skip:]):
index_group = i + skip
group.index = index_group

if group.is_header or group.is_footer:
continue
new_group, resume_at = group_layout(

if new_table_children:
page_break = block_level_page_break(
new_table_children[-1], group)
if page_break in ('page', 'recto', 'verso', 'left', 'right'):
next_page['break'] = page_break
resume_at = (index_group, None)
break

new_group, resume_at, next_page = group_layout(
group, position_y, max_position_y, page_is_empty, skip_stack)
skip_stack = None

if new_group is None:
resume_at = (index_group, None)
if new_table_children:
previous_group = new_table_children[-1]
page_break = block_level_page_break(previous_group, group)
if page_break == 'avoid':
earlier_page_break = find_earlier_page_break(
new_table_children, absolute_boxes, fixed_boxes)
if earlier_page_break is not None:
new_table_children, resume_at = earlier_page_break
break
resume_at = (index_group, None)
else:
return None, None, next_page, position_y
break

new_table_children.append(new_group)
Expand All @@ -269,7 +321,8 @@ def body_groups_layout(skip_stack, position_y, max_position_y,
if resume_at:
resume_at = (index_group, resume_at)
break
return new_table_children, resume_at, position_y

return new_table_children, resume_at, next_page, position_y

# Layout for row groups, rows and cells
position_y = table.content_box_y() + border_spacing_y
Expand All @@ -278,7 +331,7 @@ def body_groups_layout(skip_stack, position_y, max_position_y,
def all_groups_layout():
if table.children and table.children[0].is_header:
header = table.children[0]
header, resume_at = group_layout(
header, resume_at, next_page = group_layout(
header, position_y, max_position_y,
skip_stack=None, page_is_empty=False)
if header and not resume_at:
Expand All @@ -290,7 +343,7 @@ def all_groups_layout():

if table.children and table.children[-1].is_footer:
footer = table.children[-1]
footer, resume_at = group_layout(
footer, resume_at, next_page = group_layout(
footer, position_y, max_position_y,
skip_stack=None, page_is_empty=False)
if footer and not resume_at:
Expand All @@ -311,54 +364,60 @@ def all_groups_layout():

if header and footer:
# Try with both the header and footer
new_table_children, resume_at, end_position_y = body_groups_layout(
skip_stack,
position_y=position_y + header_height,
max_position_y=max_position_y - footer_height,
page_is_empty=avoid_breaks)
new_table_children, resume_at, next_page, end_position_y = (
body_groups_layout(
skip_stack,
position_y=position_y + header_height,
max_position_y=max_position_y - footer_height,
page_is_empty=avoid_breaks))
if new_table_children or not page_is_empty:
footer.translate(dy=end_position_y - footer.position_y)
end_position_y += footer_height
return (header, new_table_children, footer,
end_position_y, resume_at)
end_position_y, resume_at, next_page)
else:
# We could not fit any content, drop the footer
footer = None

if header and not footer:
# Try with just the header
new_table_children, resume_at, end_position_y = body_groups_layout(
skip_stack,
position_y=position_y + header_height,
max_position_y=max_position_y,
page_is_empty=avoid_breaks)
new_table_children, resume_at, next_page, end_position_y = (
body_groups_layout(
skip_stack,
position_y=position_y + header_height,
max_position_y=max_position_y,
page_is_empty=avoid_breaks))
if new_table_children or not page_is_empty:
return (header, new_table_children, footer,
end_position_y, resume_at)
end_position_y, resume_at, next_page)
else:
# We could not fit any content, drop the header
header = None

if footer and not header:
# Try with just the footer
new_table_children, resume_at, end_position_y = body_groups_layout(
skip_stack,
position_y=position_y,
max_position_y=max_position_y - footer_height,
page_is_empty=avoid_breaks)
new_table_children, resume_at, next_page, end_position_y = (
body_groups_layout(
skip_stack,
position_y=position_y,
max_position_y=max_position_y - footer_height,
page_is_empty=avoid_breaks))
if new_table_children or not page_is_empty:
footer.translate(dy=end_position_y - footer.position_y)
end_position_y += footer_height
return (header, new_table_children, footer,
end_position_y, resume_at)
end_position_y, resume_at, next_page)
else:
# We could not fit any content, drop the footer
footer = None

assert not (header or footer)
new_table_children, resume_at, end_position_y = body_groups_layout(
skip_stack, position_y, max_position_y, page_is_empty)
return header, new_table_children, footer, end_position_y, resume_at
new_table_children, resume_at, next_page, end_position_y = (
body_groups_layout(
skip_stack, position_y, max_position_y, page_is_empty))
return (
header, new_table_children, footer, end_position_y, resume_at,
next_page)

def get_column_cells(table, column):
"""Closure getting the column cells."""
Expand All @@ -369,8 +428,17 @@ def get_column_cells(table, column):
for cell in row.children
if cell.grid_x == column.grid_x]

header, new_table_children, footer, position_y, resume_at = \
header, new_table_children, footer, position_y, resume_at, next_page = \
all_groups_layout()

if new_table_children is None:
assert resume_at is None
table = None
adjoining_margins = []
collapsing_through = False
return (
table, resume_at, next_page, adjoining_margins, collapsing_through)

table = table.copy_with_children(
([header] if header is not None else []) +
new_table_children +
Expand Down Expand Up @@ -413,10 +481,8 @@ def get_column_cells(table, column):
group.width = last.position_x + last.width - first.position_x
group.height = columns_height

next_page = {'break': 'any', 'page': table.style['page']}
if resume_at and not page_is_empty and (
table.style['break_inside'] in ('avoid', 'avoid-page') or
not new_table_children):
table.style['break_inside'] in ('avoid', 'avoid-page')):
table = None
resume_at = None
adjoining_margins = []
Expand Down
21 changes: 21 additions & 0 deletions weasyprint/tests/test_layout/test_inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,27 @@ def test_breaking_linebox_regression_9():
assert line2.children[1].text == 'ddd'


@assert_no_logs
def test_breaking_linebox_regression_10():
# Regression test for https://github.com/Kozea/WeasyPrint/issues/923
page, = parse(
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
'<p style="width:195px; font-family: ahem">'
' <span>'
' <span>xxxxxx YYY yyyyyy yyy</span>'
' ZZZZZZ zzzzz'
' </span> )x '
'</p>')
html, = page.children
body, = html.children
p, = body.children
line1, line2, line3, line4 = p.children
assert line1.children[0].children[0].children[0].text == 'xxxxxx YYY'
assert line2.children[0].children[0].children[0].text == 'yyyyyy yyy'
assert line3.children[0].children[0].text == 'ZZZZZZ zzzzz'
assert line4.children[0].text == ')x'


@assert_no_logs
def test_linebox_text():
page, = parse('''
Expand Down
Loading

0 comments on commit 396ec71

Please sign in to comment.