Skip to content

Commit

Permalink
Change the resume_at and skip_stack structure
Browse files Browse the repository at this point in the history
Many operations, including page breaks, require a pointer to a specific
position of the box tree. For example, we used to have this structure to point
to the beginning of the first child of the second child:

(1, (2, None))

We now use:

{1: {2: None}}

This change is the first step to handle parallel flows (see #36). It doesn’t
change anything to the layout for now, but it allows us to store multiple
pointers in the same structure.

The next step is to handle multiple pointers in skip_stack during boxes layout.
It means that most of the *_layout() function need an extra for-loop to manage
multiple skip stacks.

We’ll then need to split new types of boxes: table cells, floats, absolutes…
  • Loading branch information
liZe committed Aug 22, 2021
1 parent 89b6fe3 commit 3aca9f1
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 136 deletions.
26 changes: 13 additions & 13 deletions tests/test_text.py
Expand Up @@ -28,10 +28,10 @@ def test_line_content():
for width, remaining in [(100, 'text for test'),
(45, 'is a text for test')]:
text = 'This is a text for test'
_, length, resume_at, _, _, _ = make_text(
_, length, resume_index, _, _, _ = make_text(
text, width, font_family=SANS_FONTS.split(','), font_size=19)
assert text[resume_at:] == remaining
assert length + 1 == resume_at # +1 is for the removed trailing space
assert text[resume_index:] == remaining
assert length + 1 == resume_index # +1 for the removed trailing space


@assert_no_logs
Expand All @@ -46,16 +46,16 @@ def test_line_breaking():
string = 'Thïs is a text for test'

# These two tests do not really rely on installed fonts
_, _, resume_at, _, _, _ = make_text(string, 90, font_size=1)
assert resume_at is None
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=1)
assert resume_index is None

_, _, resume_at, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_at:].decode('utf-8') == (
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'is a text for test')

_, _, resume_at, _, _, _ = make_text(
_, _, resume_index, _, _, _ = make_text(
string, 100, font_family=SANS_FONTS.split(','), font_size=19)
assert string.encode('utf-8')[resume_at:].decode('utf-8') == (
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'text for test')


Expand All @@ -64,11 +64,11 @@ def test_line_breaking_rtl():
string = 'لوريم ايبسوم دولا'

# These two tests do not really rely on installed fonts
_, _, resume_at, _, _, _ = make_text(string, 90, font_size=1)
assert resume_at is None
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=1)
assert resume_index is None

_, _, resume_at, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_at:].decode('utf-8') == (
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'ايبسوم دولا')


Expand Down
8 changes: 4 additions & 4 deletions weasyprint/formatting_structure/build.py
Expand Up @@ -1489,12 +1489,12 @@ def _inner_block_in_inline(box, skip_stack=None):
if is_start:
skip = 0
else:
skip, skip_stack = skip_stack
(skip, skip_stack), = skip_stack.items()

for i, child in enumerate(box.children[skip:]):
index = i + skip
if isinstance(child, boxes.BlockLevelBox) and \
child.is_in_normal_flow():
if (isinstance(child, boxes.BlockLevelBox) and
child.is_in_normal_flow()):
assert skip_stack is None # Should not skip here
block_level_box = child
index += 1 # Resume *after* the block
Expand All @@ -1511,7 +1511,7 @@ def _inner_block_in_inline(box, skip_stack=None):
changed = True
new_children.append(new_child)
if block_level_box is not None:
resume_at = (index, resume_at)
resume_at = {index: resume_at}
box = box.copy_with_children(new_children)
break
else:
Expand Down
3 changes: 1 addition & 2 deletions weasyprint/layout/__init__.py
Expand Up @@ -152,8 +152,7 @@ def layout_document(html, root_box, context, max_loops=8):
watch_elements_after = []
for i, page in enumerate(pages):
# We need the updated page_counter_values
resume_at, next_page, right_page, page_state, remake_state = (
context.page_maker[i + 1])
_, _, _, page_state, _ = context.page_maker[i + 1]
page_counter_values = page_state[1]

for child in page.descendants():
Expand Down
35 changes: 16 additions & 19 deletions weasyprint/layout/blocks.py
Expand Up @@ -101,7 +101,6 @@ def block_box_layout(context, box, max_position_y, skip_stack,
result = columns_layout(
context, box, max_position_y, skip_stack, containing_block,
page_is_empty, absolute_boxes, fixed_boxes, adjoining_margins)

resume_at = result[1]
# TODO: this condition and the whole relayout are probably wrong
if resume_at is None:
Expand All @@ -115,25 +114,24 @@ def block_box_layout(context, box, max_position_y, skip_stack,
context, box, max_position_y, skip_stack,
containing_block, page_is_empty, absolute_boxes,
fixed_boxes, adjoining_margins)

return result
elif box.is_table_wrapper:
table_wrapper_width(
context, box, (containing_block.width, containing_block.height))
block_level_width(box, containing_block)

new_box, resume_at, next_page, adjoining_margins, collapsing_through = \
block_container_layout(
context, box, max_position_y, skip_stack, page_is_empty,
absolute_boxes, fixed_boxes, adjoining_margins, discard)
result = block_container_layout(
context, box, max_position_y, skip_stack, page_is_empty,
absolute_boxes, fixed_boxes, adjoining_margins, discard)
new_box = result[0]
if new_box and new_box.is_table_wrapper:
# Don't collide with floats
# http://www.w3.org/TR/CSS21/visuren.html#floats
position_x, position_y, _ = avoid_collisions(
context, new_box, containing_block, outer=False)
new_box.translate(
position_x - new_box.position_x, position_y - new_box.position_y)
return new_box, resume_at, next_page, adjoining_margins, collapsing_through
return result


@handle_min_max_width
Expand Down Expand Up @@ -316,10 +314,9 @@ def block_container_layout(context, box, max_position_y, skip_stack,
skip = 0
first_letter_style = getattr(box, 'first_letter_style', None)
else:
skip, skip_stack = skip_stack
(skip, skip_stack), = skip_stack.items()
first_letter_style = None
for i, child in enumerate(box.children[skip:]):
index = i + skip
for index, child in enumerate(box.children[skip:], start=(skip or 0)):
child.position_x = position_x
# XXX does not count margins in adjoining_margins:
child.position_y = position_y
Expand Down Expand Up @@ -356,7 +353,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
if result:
new_children, resume_at = result
break
resume_at = (index, None)
resume_at = {index: None}
break
elif child.is_running():
running_name = child.style['position'][1]
Expand Down Expand Up @@ -419,7 +416,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
# Remove lines to keep them for the next page
del new_children[-needed:]
# Page break here, resume before this line
resume_at = (index, skip_stack)
resume_at = {index: skip_stack}
is_page_break = True
break
# TODO: this is incomplete.
Expand All @@ -444,7 +441,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
break

if new_children:
resume_at = (index, new_children[-1].resume_at)
resume_at = {index: new_children[-1].resume_at}
if is_page_break:
break
else:
Expand All @@ -462,7 +459,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
'page', 'left', 'right', 'recto', 'verso'):
page_name = child.page_values()[0]
next_page = {'break': page_break, 'page': page_name}
resume_at = (index, None)
resume_at = {index: None}
break
else:
page_break = 'auto'
Expand Down Expand Up @@ -589,7 +586,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
new_children = []

if new_children:
resume_at = (index, None)
resume_at = {index: None}
break
else:
# This was the first child of this box, cancel the box
Expand All @@ -602,7 +599,7 @@ def block_container_layout(context, box, max_position_y, skip_stack,
# TODO: back-track somehow when all lines fit but not borders
new_children.append(new_child)
if resume_at is not None:
resume_at = (index, resume_at)
resume_at = {index: resume_at}
break
else:
resume_at = None
Expand Down Expand Up @@ -817,7 +814,7 @@ def find_earlier_page_break(children, absolute_boxes, fixed_boxes):
if index < orphans:
return None
new_children = children[:index]
resume_at = (0, new_children[-1].resume_at)
resume_at = {0: new_children[-1].resume_at}
remove_placeholders(children[index:], absolute_boxes, fixed_boxes)
return new_children, resume_at

Expand All @@ -838,7 +835,7 @@ def find_earlier_page_break(children, absolute_boxes, fixed_boxes):
index += 1 # break after child
new_children = children[:index]
# Get the index in the original parent
resume_at = (children[index].index, None)
resume_at = {children[index].index: None}
break
previous_in_flow = child

Expand All @@ -862,7 +859,7 @@ def find_earlier_page_break(children, absolute_boxes, fixed_boxes):
new_children.append(next_child)

# Index in the original parent
resume_at = (new_child.index, resume_at)
resume_at = {new_child.index: resume_at}
index += 1 # Remove placeholders after child
break
else:
Expand Down
12 changes: 7 additions & 5 deletions weasyprint/layout/flex.py
Expand Up @@ -261,7 +261,7 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
child.padding_top + child.padding_bottom)
if getattr(box, axis) == 'auto' and (
child_height + box.height > available_main_space):
resume_at = (i, None)
resume_at = {i: None}
children = children[:i + 1]
break
box.height += child_height
Expand Down Expand Up @@ -844,8 +844,10 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
page_is_empty, absolute_boxes, fixed_boxes,
adjoining_margins=[], discard=False)[:2]
if new_child is None:
if resume_at and resume_at[0]:
resume_at = (resume_at[0] + i - 1, None)
if resume_at:
index, = resume_at
if index:
resume_at = (index + i - 1, None)
else:
box.children.append(new_child)
if child_resume_at is not None:
Expand All @@ -854,8 +856,8 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
else:
first_level_skip = 0
if resume_at:
first_level_skip += resume_at[0]
resume_at = (first_level_skip + i, child_resume_at)
first_level_skip += tuple(resume_at)[0]
resume_at = {first_level_skip + i: child_resume_at}
if resume_at:
break

Expand Down

0 comments on commit 3aca9f1

Please sign in to comment.