mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 08:27:22 +03:00
Use index stacks instead of popleft for line and page breaks.
This commit is contained in:
parent
3729c7e5d3
commit
cc85f95c5b
@ -406,13 +406,16 @@ def _inner_block_in_inline(box, skip_stack=None):
|
||||
|
||||
for index, child in box.enumerate_skip(skip):
|
||||
if isinstance(child, boxes.BlockLevelBox):
|
||||
assert skip_stack is None # Should no skip here
|
||||
block_level_box = child
|
||||
index += 1 # Resume *after* the block
|
||||
else:
|
||||
if isinstance(child, boxes.InlineBox):
|
||||
recursion = _inner_block_in_inline(child, skip_stack)
|
||||
skip_stack = None
|
||||
new_child, block_level_box, resume_at = recursion
|
||||
else:
|
||||
assert skip_stack is None # Should no skip here
|
||||
if isinstance(child, boxes.ParentBox):
|
||||
# inline-block or inline-table.
|
||||
new_child = block_in_inline(child)
|
||||
|
@ -30,7 +30,7 @@ from ..css.values import get_pixel_value
|
||||
from ..formatting_structure import boxes
|
||||
|
||||
|
||||
def make_page(document, page_number):
|
||||
def make_page(document, page_number, resume_at):
|
||||
"""Take just enough content from the beginning to fill one page.
|
||||
|
||||
Return ``page, finished``. ``page`` is a laid out Page object, ``finished``
|
||||
@ -58,9 +58,10 @@ def make_page(document, page_number):
|
||||
# TODO: handle cases where the root element is something else.
|
||||
# See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
||||
assert isinstance(root_box, boxes.BlockBox)
|
||||
page.root_box, finished = block_box_layout(root_box, page_content_bottom)
|
||||
page.root_box, resume_at = block_box_layout(
|
||||
root_box, page_content_bottom, resume_at)
|
||||
|
||||
return page, finished
|
||||
return page, resume_at
|
||||
|
||||
|
||||
def layout(document):
|
||||
@ -75,9 +76,10 @@ def layout(document):
|
||||
"""
|
||||
pages = []
|
||||
page_number = 1
|
||||
resume_at = None
|
||||
while True:
|
||||
page, finished = make_page(document, page_number)
|
||||
page, resume_at = make_page(document, page_number, resume_at)
|
||||
pages.append(page)
|
||||
if finished:
|
||||
if resume_at is None:
|
||||
return pages
|
||||
page_number += 1
|
||||
|
@ -30,7 +30,7 @@ from ..css.values import get_single_keyword
|
||||
from ..formatting_structure import boxes
|
||||
|
||||
|
||||
def block_level_layout(box, max_position_y):
|
||||
def block_level_layout(box, max_position_y, skip_stack):
|
||||
"""Lay out the block-level ``box``.
|
||||
|
||||
:param max_position_y: the absolute vertical position (as in
|
||||
@ -39,19 +39,19 @@ def block_level_layout(box, max_position_y):
|
||||
|
||||
"""
|
||||
if isinstance(box, boxes.BlockBox):
|
||||
return block_box_layout(box, max_position_y)
|
||||
return block_box_layout(box, max_position_y, skip_stack)
|
||||
elif isinstance(box, boxes.BlockLevelReplacedBox):
|
||||
return block_replaced_box_layout(box), True
|
||||
return block_replaced_box_layout(box), None
|
||||
else:
|
||||
raise TypeError('Layout for %s not handled yet' % type(box).__name__)
|
||||
|
||||
|
||||
def block_box_layout(box, max_position_y):
|
||||
def block_box_layout(box, max_position_y, skip_stack):
|
||||
"""Lay out the block ``box``."""
|
||||
resolve_percentages(box)
|
||||
block_level_width(box)
|
||||
list_marker_layout(box)
|
||||
return block_level_height(box, max_position_y)
|
||||
return block_level_height(box, max_position_y, skip_stack)
|
||||
|
||||
|
||||
def block_replaced_box_layout(box):
|
||||
@ -133,7 +133,7 @@ def block_level_width(box):
|
||||
box.margin_right = margin_sum - margin_l
|
||||
|
||||
|
||||
def block_level_height(box, max_position_y):
|
||||
def block_level_height(box, max_position_y, skip_stack):
|
||||
"""Set the ``box`` height."""
|
||||
assert isinstance(box, boxes.BlockBox)
|
||||
|
||||
@ -151,8 +151,13 @@ def block_level_height(box, max_position_y):
|
||||
|
||||
new_box = box.copy()
|
||||
new_box.empty()
|
||||
while box.children:
|
||||
child = box.children.popleft()
|
||||
|
||||
if skip_stack is None:
|
||||
skip = 0
|
||||
else:
|
||||
skip, skip_stack = skip_stack
|
||||
|
||||
for index, child in box.enumerate_skip(skip):
|
||||
if not child.is_in_normal_flow():
|
||||
continue
|
||||
# TODO: collapse margins
|
||||
@ -160,24 +165,31 @@ def block_level_height(box, max_position_y):
|
||||
child.position_x = position_x
|
||||
child.position_y = position_y
|
||||
if isinstance(child, boxes.LineBox):
|
||||
for line in get_new_lineboxes(child, max_position_y):
|
||||
lines, resume_at = get_new_lineboxes(
|
||||
child, max_position_y, skip_stack)
|
||||
skip_stack = None
|
||||
for line in lines:
|
||||
new_box.add_child(line)
|
||||
position_y += line.height
|
||||
if child.children:
|
||||
box.children.appendleft(child)
|
||||
if resume_at is not None:
|
||||
resume_at = (index, resume_at)
|
||||
break
|
||||
else:
|
||||
new_child, finished = block_level_layout(child, max_position_y)
|
||||
new_child, resume_at = block_level_layout(
|
||||
child, max_position_y, skip_stack)
|
||||
skip_stack = None
|
||||
new_position_y = position_y + new_child.margin_height()
|
||||
# TODO: find a way to break between blocks
|
||||
# if new_position_y <= max_position_y:
|
||||
new_box.add_child(new_child)
|
||||
position_y = new_position_y
|
||||
# else:
|
||||
# finished = False
|
||||
if not finished:
|
||||
box.children.appendleft(child)
|
||||
# resume_at = (index, None) # or something... XXX
|
||||
if resume_at is not None:
|
||||
resume_at = (index, resume_at)
|
||||
break
|
||||
else:
|
||||
resume_at = None
|
||||
|
||||
if new_box.height == 'auto':
|
||||
new_box.height = position_y - initial_position_y
|
||||
@ -185,5 +197,4 @@ def block_level_height(box, max_position_y):
|
||||
# If there was a list marker, we kept it on `new_box`. Do not repeat on
|
||||
# `box` on the next page.
|
||||
box.outside_list_marker = None
|
||||
finished = not box.children
|
||||
return new_box, finished
|
||||
return new_box, resume_at
|
||||
|
@ -31,74 +31,38 @@ from ..formatting_structure import boxes
|
||||
from ..css.values import get_single_keyword, get_single_pixel_value
|
||||
|
||||
|
||||
class InlineContext(object):
|
||||
"""Context manager for inline boxes."""
|
||||
def __init__(self, linebox, page_bottom):
|
||||
self.linebox = linebox
|
||||
self.page_bottom = page_bottom
|
||||
self.position_y = linebox.position_y
|
||||
self.position_x = linebox.position_x
|
||||
self.containing_block_width = linebox.containing_block_size()[0]
|
||||
self.lines = []
|
||||
self.execute_formatting()
|
||||
|
||||
def deep_copy(self, box):
|
||||
"""Copy a ``box`` and its children recursively."""
|
||||
copy_box = box.copy()
|
||||
if isinstance(box, boxes.ParentBox):
|
||||
copy_box.empty()
|
||||
for child in box.children:
|
||||
if isinstance(box, boxes.ParentBox):
|
||||
copy_child = self.deep_copy(child)
|
||||
else:
|
||||
copy_child = child.copy()
|
||||
copy_box.add_child(copy_child)
|
||||
return copy_box
|
||||
else:
|
||||
return copy_box
|
||||
|
||||
def save(self, line):
|
||||
"""Save the line and the position_y."""
|
||||
self.copy_line = self.deep_copy(line)
|
||||
self._position_y = self.position_y
|
||||
|
||||
def restore(self):
|
||||
"""Restore the linebox children."""
|
||||
for child in self.copy_line.children:
|
||||
self.linebox.children.appendleft(child)
|
||||
child.parent = self.linebox
|
||||
self.position_y = self._position_y
|
||||
|
||||
def execute_formatting(self):
|
||||
"""Break the lines until the bottom of the page is reached."""
|
||||
def get_new_lineboxes(linebox, page_bottom, skip_stack):
|
||||
"""Get the ``linebox`` lines until ``page_bottom`` is reached."""
|
||||
first = True
|
||||
position_y = linebox.position_y
|
||||
position_x = linebox.position_x
|
||||
containing_block_width = linebox.containing_block_size()[0]
|
||||
lines = []
|
||||
while 1:
|
||||
line = layout_next_linebox(
|
||||
self.linebox, self.containing_block_width)
|
||||
if line is None:
|
||||
break
|
||||
self.save(line)
|
||||
line, resume_at = layout_next_linebox(
|
||||
linebox, containing_block_width, skip_stack)
|
||||
from ..tests.test_boxes import serialize, prettify
|
||||
white_space_processing(line)
|
||||
compute_linebox_dimensions(line)
|
||||
compute_linebox_positions(line, self.position_x, self.position_y)
|
||||
compute_linebox_positions(line, position_x, position_y)
|
||||
vertical_align_processing(line)
|
||||
compute_linebox_dimensions(line)
|
||||
compute_linebox_dimensions(line) # XXX twice?
|
||||
if not is_empty_line(line):
|
||||
self.position_y += line.height
|
||||
position_y += line.height
|
||||
# Yield at least one line to avoid infinite loop.
|
||||
# TODO: Find another way ...
|
||||
if self.page_bottom >= self.position_y or first:
|
||||
self.lines.append(line)
|
||||
if page_bottom >= position_y or first:
|
||||
lines.append(line)
|
||||
first = False
|
||||
else:
|
||||
self.restore()
|
||||
# Resume before this line
|
||||
resume_at = skip_stack
|
||||
break
|
||||
|
||||
|
||||
def get_new_lineboxes(linebox, page_bottom):
|
||||
"""Get the ``linebox`` lines until ``page_bottom`` is reached."""
|
||||
inline_context = InlineContext(linebox, page_bottom)
|
||||
return inline_context.lines
|
||||
if resume_at is None:
|
||||
break
|
||||
else:
|
||||
skip_stack = resume_at
|
||||
return lines, resume_at
|
||||
|
||||
|
||||
def inline_replaced_box_layout(box):
|
||||
@ -305,122 +269,73 @@ def get_new_empty_line(linebox):
|
||||
return new_line
|
||||
|
||||
|
||||
def layout_next_linebox(linebox, allocate_width):
|
||||
"""Cut the ``linebox`` to fit in ``alocate_width``.
|
||||
|
||||
Eg.::
|
||||
|
||||
LineBox[
|
||||
InlineBox[
|
||||
TextBox('Hello.'),
|
||||
],
|
||||
InlineBox[
|
||||
InlineBox[TextBox('Word :D')],
|
||||
TextBox('This is a long long long text'),
|
||||
]
|
||||
]
|
||||
|
||||
is turned into::
|
||||
|
||||
[
|
||||
LineBox[
|
||||
InlineBox[
|
||||
TextBox('Hello.'),
|
||||
],
|
||||
InlineBox[
|
||||
InlineBox[TextBox('Word :D')],
|
||||
TextBox('This is a long'),
|
||||
]
|
||||
], LineBox[
|
||||
InlineBox[
|
||||
TextBox(' long long text'),
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
"""
|
||||
def layout_next_linebox(linebox, remaining_width, skip_stack):
|
||||
"""Same as split_inline_level."""
|
||||
assert isinstance(linebox, boxes.LineBox)
|
||||
new_line = get_new_empty_line(linebox)
|
||||
remaining_width = allocate_width
|
||||
while linebox.children:
|
||||
child = linebox.children.popleft()
|
||||
|
||||
part1, part2 = split_inline_level(child, remaining_width)
|
||||
assert part1 is not None
|
||||
|
||||
if part1.margin_width() > remaining_width and new_line.children:
|
||||
# part1 is too wide, and the line is non-empty:
|
||||
# put child entirely on the next line.
|
||||
if part2 is not None:
|
||||
linebox.children.appendleft(part2)
|
||||
part2 = part1
|
||||
if skip_stack is None:
|
||||
skip = 0
|
||||
else:
|
||||
remaining_width -= part1.margin_width()
|
||||
new_line.add_child(part1)
|
||||
skip, skip_stack = skip_stack
|
||||
|
||||
if part2 is not None:
|
||||
linebox.children.appendleft(part2)
|
||||
# This line is done, create a new one and reset
|
||||
# the available width.
|
||||
return new_line
|
||||
for index, child in linebox.enumerate_skip(skip):
|
||||
new_child, resume_at = split_inline_level(
|
||||
child, remaining_width, skip_stack)
|
||||
skip_stack = None
|
||||
|
||||
if new_line.children:
|
||||
return new_line
|
||||
margin_width = new_child.margin_width()
|
||||
if margin_width > remaining_width and new_line.children:
|
||||
# too wide, and the inline is non-empty:
|
||||
# put child entirely on the next line.
|
||||
resume_at = (index, None)
|
||||
break
|
||||
else:
|
||||
remaining_width -= margin_width
|
||||
new_line.add_child(new_child)
|
||||
|
||||
if resume_at is not None:
|
||||
resume_at = (index, resume_at)
|
||||
break
|
||||
else:
|
||||
resume_at = None
|
||||
|
||||
return new_line, resume_at
|
||||
|
||||
|
||||
def split_inline_level(box, available_width):
|
||||
"""Split an inline-level box and return ``(part1, part2)``.
|
||||
def split_inline_level(box, available_width, skip_stack):
|
||||
"""Fit as much content as possible from an inline-level box in a width.
|
||||
|
||||
* The first part is non-empty (unless the box is empty)
|
||||
* Have the first part as big as possible while being narrower than
|
||||
``available_width``, if possible (may overflow is no split is possible.)
|
||||
* ``part2`` may be None.
|
||||
Return ``(new_box, resume_at)``. ``resume_at`` is ``None`` if all of the
|
||||
content fits. Otherwise it can be passed as a ``skip_stack`` parameter
|
||||
to resume where we left off.
|
||||
|
||||
``new_box`` is non-empty (unless the box is empty) and as big as possible
|
||||
while being narrower than ``available_width``, if possible (may overflow
|
||||
is no split is possible.)
|
||||
|
||||
"""
|
||||
if isinstance(box, boxes.TextBox):
|
||||
part1, part2 = split_text_box(box, available_width)
|
||||
compute_textbox_dimensions(part1)
|
||||
new_box, resume_at = split_text_box(box, available_width, skip_stack)
|
||||
compute_textbox_dimensions(new_box)
|
||||
elif isinstance(box, boxes.InlineBox):
|
||||
resolve_percentages(box)
|
||||
if box.margin_left == 'auto':
|
||||
box.margin_left = 0
|
||||
if box.margin_right == 'auto':
|
||||
box.margin_right = 0
|
||||
part1, part2 = split_inline_box(box, available_width)
|
||||
compute_inlinebox_dimensions(part1)
|
||||
new_box, resume_at = split_inline_box(box, available_width, skip_stack)
|
||||
compute_inlinebox_dimensions(new_box)
|
||||
elif isinstance(box, boxes.AtomicInlineLevelBox):
|
||||
compute_atomicbox_dimensions(box)
|
||||
part1 = box
|
||||
part2 = None
|
||||
else:
|
||||
assert False, box
|
||||
return part1, part2
|
||||
new_box = box
|
||||
resume_at = None
|
||||
#else: unexpected box type here
|
||||
return new_box, resume_at
|
||||
|
||||
|
||||
def split_inline_box(inlinebox, remaining_width):
|
||||
"""Split an inline box and return ``(part1, part2)``.
|
||||
|
||||
The same rules as split_inline_box() apply.
|
||||
|
||||
Eg.::
|
||||
|
||||
InlineBox[
|
||||
InlineBox[TextBox('Word :D')],
|
||||
TextBox('This is a long long long text'),
|
||||
]
|
||||
|
||||
is turned into::
|
||||
|
||||
(
|
||||
InlineBox[
|
||||
InlineBox[TextBox('Word :D')],
|
||||
TextBox('This is a long'),
|
||||
], InlineBox[
|
||||
TextBox(' long long text'),
|
||||
]
|
||||
)
|
||||
|
||||
"""
|
||||
def split_inline_box(inlinebox, remaining_width, skip_stack):
|
||||
"""Same behavior as split_inline_level."""
|
||||
assert isinstance(inlinebox, boxes.InlineBox)
|
||||
resolve_percentages(inlinebox)
|
||||
left_spacing = (inlinebox.padding_left + inlinebox.margin_left +
|
||||
@ -432,86 +347,75 @@ def split_inline_box(inlinebox, remaining_width):
|
||||
new_inlinebox = inlinebox.copy()
|
||||
new_inlinebox.empty()
|
||||
|
||||
while inlinebox.children:
|
||||
child = inlinebox.children.popleft()
|
||||
if skip_stack is None:
|
||||
skip = 0
|
||||
else:
|
||||
skip, skip_stack = skip_stack
|
||||
|
||||
part1, part2 = split_inline_level(child, remaining_width)
|
||||
assert part1 is not None
|
||||
for index, child in inlinebox.enumerate_skip(skip):
|
||||
|
||||
new_child, resume_at = split_inline_level(
|
||||
child, remaining_width, skip_stack)
|
||||
skip_stack = None
|
||||
|
||||
# TODO: this is non-optimal when last_child is True and
|
||||
# width <= remaining_width < width + right_spacing
|
||||
# with
|
||||
# width = part1.margin_width()
|
||||
|
||||
last_child = not inlinebox.children # ie. the list is now empty
|
||||
if last_child:
|
||||
pass
|
||||
# TODO: take care of right_spacing
|
||||
# TODO: on the last child, take care of right_spacing
|
||||
|
||||
if part1.margin_width() > remaining_width and new_inlinebox.children:
|
||||
# part1 is too wide, and the inline is non-empty:
|
||||
margin_width = new_child.margin_width()
|
||||
|
||||
if (margin_width > remaining_width and new_inlinebox.children):
|
||||
# too wide, and the inline is non-empty:
|
||||
# put child entirely on the next line.
|
||||
if part2 is not None:
|
||||
inlinebox.children.appendleft(part2)
|
||||
part2 = part1
|
||||
resume_at = (index, None)
|
||||
break
|
||||
else:
|
||||
remaining_width -= part1.margin_width()
|
||||
new_inlinebox.add_child(part1)
|
||||
remaining_width -= margin_width
|
||||
new_inlinebox.add_child(new_child)
|
||||
|
||||
if part2 is not None:
|
||||
inlinebox.children.appendleft(part2)
|
||||
if resume_at is not None:
|
||||
inlinebox.reset_spacing('left')
|
||||
new_inlinebox.reset_spacing('right')
|
||||
return new_inlinebox, inlinebox
|
||||
resume_at = (index, resume_at)
|
||||
break
|
||||
else:
|
||||
resume_at = None
|
||||
|
||||
return new_inlinebox, None
|
||||
return new_inlinebox, resume_at
|
||||
|
||||
|
||||
def split_text_box(textbox, allocate_width):
|
||||
"""Split a text box and return ``(part1, part2)``.
|
||||
def split_text_box(textbox, available_width, skip):
|
||||
"""Keep as much text as possible from a TextBox in a limitied width.
|
||||
Try not to overflow but always have some text in ``new_textbox``
|
||||
|
||||
The same rules as split_inline_box() apply, but the text will also be
|
||||
split at preserved newline characters.
|
||||
Return ``(new_textbox, skip)``. ``skip`` is the number of UTF-8 bytes
|
||||
to skip form the start of the TextBox for the next line, or ``None``
|
||||
if all of the text fits.
|
||||
|
||||
Eg.::
|
||||
|
||||
TextBox('This is a long long long long text')
|
||||
|
||||
is turned into::
|
||||
|
||||
(
|
||||
TextBox('This is a long long'),
|
||||
TextBox(' long long text')
|
||||
)
|
||||
|
||||
But::
|
||||
|
||||
TextBox('Thisisalonglonglonglongtext')
|
||||
|
||||
is turned into::
|
||||
|
||||
(
|
||||
TextBox('Thisisalonglonglonglongtext'),
|
||||
None
|
||||
)
|
||||
Also break an preserved whitespace.
|
||||
|
||||
"""
|
||||
assert isinstance(textbox, boxes.TextBox)
|
||||
font_size = get_single_pixel_value(textbox.style.font_size)
|
||||
if font_size == 0:
|
||||
return textbox, None
|
||||
fragment = TextFragment(textbox.utf8_text, textbox.style,
|
||||
width=allocate_width,
|
||||
context=cairo.Context(textbox.document.surface))
|
||||
skip = skip or 0
|
||||
utf8_text = textbox.utf8_text[skip:]
|
||||
fragment = TextFragment(utf8_text, textbox.style,
|
||||
cairo.Context(textbox.document.surface), available_width)
|
||||
split = fragment.split_first_line()
|
||||
if split is None:
|
||||
if skip:
|
||||
textbox = textbox.copy()
|
||||
textbox.utf8_text = utf8_text # with skip
|
||||
return textbox, None
|
||||
first_end, second_start = split
|
||||
first_tb = textbox.copy()
|
||||
first_tb.utf8_text = textbox.utf8_text[:first_end]
|
||||
second_tb = textbox.copy()
|
||||
second_tb.utf8_text = textbox.utf8_text[second_start:]
|
||||
return first_tb, second_tb
|
||||
new_textbox = textbox.copy()
|
||||
new_textbox.utf8_text = utf8_text[:first_end]
|
||||
return new_textbox, skip + second_start
|
||||
|
||||
|
||||
def white_space_processing(linebox):
|
||||
|
@ -463,7 +463,7 @@ def test_linebox_text():
|
||||
</style>
|
||||
<p><em>Lorem Ipsum</em>is very <strong>coool</strong></p>'''
|
||||
|
||||
page, = parse(page % {'fonts': FONTS, 'width': 200})
|
||||
page, = parse(page % {'fonts': FONTS, 'width': 250})
|
||||
paragraph, = body_children(page)
|
||||
return paragraph
|
||||
|
||||
@ -488,7 +488,7 @@ def test_linebox_positions():
|
||||
p { width:%(width)spx; font-family:%(fonts)s;}
|
||||
</style>
|
||||
<p>this is test for <strong>Weasyprint</strong></p>'''
|
||||
page, = parse(page % {'fonts': FONTS, 'width': 200})
|
||||
page, = parse(page % {'fonts': FONTS, 'width': 250})
|
||||
paragraph, = body_children(page)
|
||||
return paragraph
|
||||
|
||||
@ -579,9 +579,12 @@ def test_inlinebox_spliting():
|
||||
|
||||
def get_parts(inlinebox, width):
|
||||
"""Yield the parts of the splitted ``inlinebox`` of given ``width``."""
|
||||
copy_inlinebox = inlinebox.copy()
|
||||
while copy_inlinebox.children:
|
||||
yield split_inline_box(copy_inlinebox, width)[0]
|
||||
skip = None
|
||||
while 1:
|
||||
box, skip = split_inline_box(inlinebox, width, skip)
|
||||
yield box
|
||||
if skip is None:
|
||||
break
|
||||
|
||||
def get_joined_text(parts):
|
||||
"""Get the joined text from ``parts``."""
|
||||
@ -616,7 +619,7 @@ def test_inlinebox_spliting():
|
||||
|
||||
# test with width = 100
|
||||
parts = list(get_parts(inlinebox, 100))
|
||||
assert len(parts) != 1
|
||||
assert len(parts) > 1
|
||||
assert original_text == get_joined_text(parts)
|
||||
|
||||
inlinebox = get_inlinebox(content)
|
||||
@ -625,7 +628,7 @@ def test_inlinebox_spliting():
|
||||
|
||||
# test with width = 10
|
||||
parts = list(get_parts(inlinebox, 10))
|
||||
assert len(parts) != 1
|
||||
assert len(parts) > 1
|
||||
assert original_text == get_joined_text(parts)
|
||||
|
||||
# with margin-border-padding
|
||||
@ -678,8 +681,12 @@ def test_inlinebox_text_after_spliting():
|
||||
|
||||
def get_parts(inlinebox, width):
|
||||
"""Yield the parts of the splitted ``inlinebox`` of given ``width``."""
|
||||
while inlinebox.children:
|
||||
yield split_inline_box(inlinebox, width)[0]
|
||||
skip = None
|
||||
while 1:
|
||||
box, skip = split_inline_box(inlinebox, width, skip)
|
||||
yield box
|
||||
if skip is None:
|
||||
break
|
||||
|
||||
def get_full_text(inlinebox):
|
||||
"""Get the full text in ``inlinebox``."""
|
||||
@ -743,7 +750,7 @@ def test_page_and_linebox_breaking():
|
||||
texts.extend(get_full_text(lines))
|
||||
return u' '.join(texts)
|
||||
|
||||
content = '1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15'
|
||||
content = u'1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15'
|
||||
|
||||
pages = get_pages(content)
|
||||
assert len(pages) == 2
|
||||
|
Loading…
Reference in New Issue
Block a user