Skip to content

Commit

Permalink
WIP: fix for #283
Browse files Browse the repository at this point in the history
Make Flowable.flow() additionally return whether the flowable was too
tall for the page.
  • Loading branch information
brechtm committed Jun 10, 2022
1 parent 05447c8 commit 4722b40
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 39 deletions.
47 changes: 25 additions & 22 deletions src/rinoh/flowable.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from .draw import ShapeStyle, Rectangle, Line, LineStyle, Stroke
from .layout import (InlineDownExpandingContainer, VirtualContainer,
MaybeContainer, ContainerOverflow, EndOfContainer,
PageBreakException, ReflowRequired)
PageBreakException, ReflowRequired, DownExpandingContainer)
from .strings import UserStrings
from .style import Styled, Style
from .text import StyledText
Expand Down Expand Up @@ -236,7 +236,7 @@ def flow(self, container, last_descender, state=None, space_below=0,
left=margin_left,
right=right)
initial_before = state.initial
state, width, inner_top_to_baseline, descender = \
state, width, inner_top_to_baseline, descender, tall = \
self.flow_inner(margin_container, last_descender, state=state,
space_below=space_below + self_space_below,
**kwargs)
Expand All @@ -258,7 +258,7 @@ def flow(self, container, last_descender, state=None, space_below=0,

container.advance2(self_space_below, ignore_overflow=True)
container.document.progress(self, container)
return margin_left + width + margin_right, top_to_baseline, descender
return margin_left + width + margin_right, top_to_baseline, descender, tall

def flow_inner(self, container, last_descender, state=None, space_below=0,
**kwargs):
Expand Down Expand Up @@ -287,16 +287,17 @@ def border_width(attribute):
last_descender = None if border_top else last_descender
if draw_top:
if not container.advance2(border_top + padding_top):
return state, 0, 0, last_descender
return state, 0, 0, last_descender, False
pad_cntnr = InlineDownExpandingContainer('PADDING', container,
left=left, right=right)
try:
content_width, first_line_ascender, descender = \
content_width, first_line_ascender, descender, tall = \
self.render(pad_cntnr, last_descender, state=state,
space_below=total_space_below, **kwargs)
state = None
assert container.advance2(padding_border_bottom)
except EndOfContainer as eoc:
tall = False
state = eoc.flowable_state
first_line_ascender = 0
descender = last_descender
Expand All @@ -316,7 +317,7 @@ def border_width(attribute):
top=draw_top, bottom=state is None)
top_to_baseline = border_top + padding_top + first_line_ascender
descender = None if border_bottom else descender
return state, bordered_width, top_to_baseline, descender
return state, bordered_width, top_to_baseline, descender, tall

def render_frame(self, container, width, height, top=True, bottom=True):
width, height = float(width), - float(height)
Expand Down Expand Up @@ -348,7 +349,7 @@ def render_border(start, end, stroke, corr_x, corr_y):
if bottom:
render_border((0, height), (width, height), border_bottom, 0, 1)

def render(self, container, descender, state, space_below=0, **kwargs):
def render(self, container, descender, state, space_below=0, tall=False, **kwargs):
"""Renders the flowable's content to `container`, with the flowable's
top edge lining up with the container's cursor. `descender` is the
descender height of the preceding line or `None`."""
Expand All @@ -374,7 +375,7 @@ def get_style(self, attribute, flowable_target):
hide=False)[attribute]

def flow(self, container, last_descender, state=None, **kwargs):
return 0, 0, last_descender
return 0, 0, last_descender, False


class AnchorFlowable(DummyFlowable):
Expand Down Expand Up @@ -529,6 +530,7 @@ def render(self, container, descender, state, first_line_only=False,
empty_page = container.page._empty
initial_state = copy(state)
saved_state = copy(state)
any_tall = False
try:
while True:
width, top_to_baseline, descender = \
Expand All @@ -554,10 +556,10 @@ def render(self, container, descender, state, first_line_only=False,
state.prepend(exc.flowable_state)
exc.flowable_state = state
raise exc
return max_flowable_width, first_top_to_baseline or 0, descender
return max_flowable_width, first_top_to_baseline or 0, descender, False

def _flow_with_next(self, state, container, descender, space_below=0,
**kwargs):
allow_overflow=False, **kwargs):
try:
flowable, flowable_state = state.next_flowable()
while flowable.is_hidden(container):
Expand All @@ -567,8 +569,8 @@ def _flow_with_next(self, state, container, descender, space_below=0,
except StopIteration:
raise LastFlowableException(descender)
flowable.parent = self
with MaybeContainer(container) as maybe_container:
max_flowable_width, top_to_baseline, descender = \
with MaybeContainer(container, max_height=float('+inf') if allow_overflow else None) as maybe_container:
max_flowable_width, top_to_baseline, descender, tall = \
flowable.flow(maybe_container, descender, state=flowable_state,
space_below=space_below if state.at_end else 0,
**kwargs)
Expand All @@ -581,6 +583,7 @@ def _flow_with_next(self, state, container, descender, space_below=0,
width, _, descender = self._flow_with_next(state, container,
descender,
space_below=space_below,
allow_overflow=tall,
**kwargs)
except EndOfContainer as eoc:
if eoc.flowable_state.initial:
Expand Down Expand Up @@ -701,7 +704,7 @@ def label_width(self, container):
label_min_width = self.get_style('label_min_width', container)
label_max_width = self.get_style('label_max_width', container)
virtual_container = VirtualContainer(container)
label_width, _, _ = self.label.flow(virtual_container, 0)
label_width, _, _, _ = self.label.flow(virtual_container, 0)
spillover = (label_width > label_max_width.to_points(container.width)
if label_max_width else True)
return max(label_width, label_min_width), spillover
Expand All @@ -711,7 +714,7 @@ def initial_state(self, container):
return LabeledFlowableState(self, initial_content_state)

def render(self, container, last_descender, state, label_column_width=None,
space_below=0, **kwargs):
space_below=0, tall=False, **kwargs):
def style(name):
return self.get_style(name, container)

Expand All @@ -731,7 +734,7 @@ def style(name):
elif free_label_width > label_width:
if style('wrap_label'):
vcontainer = VirtualContainer(container, width=label_max_width)
wrapped_width, _, _ = self.label.flow(vcontainer, 0)
wrapped_width, _, _, _ = self.label.flow(vcontainer, 0)
if wrapped_width < label_max_width:
label_width = wrapped_width
else:
Expand Down Expand Up @@ -784,15 +787,15 @@ def style(name):
else:
container.advance(label_height - offset_content)
descender = label_descender
return left + width, label_baseline, descender
return left + width, label_baseline, descender, False

def _flow_label(self, container, last_descender, max_width, y_offset,
space_below):
label_container = \
InlineDownExpandingContainer('LABEL', container, width=max_width,
advance_parent=False)
label_container.advance(y_offset)
_, _, label_descender = \
_, _, label_descender, _ = \
self.label.flow(label_container, last_descender, None, space_below)
return label_container.height, label_descender

Expand All @@ -801,7 +804,7 @@ def _flow_content(self, container, last_descender, state, left,
content_container = \
InlineDownExpandingContainer('CONTENT', container, left=left,
advance_parent=False)
width, _, content_descender \
width, _, content_descender, _ \
= self.flowable.flow(content_container, last_descender,
state=state.content_flowable_state,
space_below=space_below)
Expand All @@ -812,7 +815,7 @@ def find_baseline(flowable, container, last_descender, state=None, **kwargs):
virtual_container = VirtualContainer(container)
inline_ctnr = InlineDownExpandingContainer('DUMMY', virtual_container,
advance_parent=False)
_, baseline, _ = flowable.flow(inline_ctnr, last_descender,
_, baseline, _, _ = flowable.flow(inline_ctnr, last_descender,
state=state, first_line_only=True)
return baseline

Expand Down Expand Up @@ -881,13 +884,13 @@ def flow(self, container, last_descender, state=None, **kwargs):
document.add_sideways_float(self)
state = CompletedFlowableState()
self.page_break(container, state)
return 0, 0, last_descender
return 0, 0, last_descender, False
elif (float == FloatLocation.HERE and id not in document.floats):
super().flow(container.float_space, None)
document.floats.add(id)
if not container.page.check_overflow():
raise ReflowRequired
return 0, 0, last_descender
return 0, 0, last_descender, False
return super().flow(container, last_descender, state=state, **kwargs)


Expand All @@ -897,7 +900,7 @@ def __init__(self, page_break=Break.ANY):
super().__init__(style=style)

def render(self, container, last_descender, state=None, **kwargs):
return 0, 0, last_descender
return 0, 0, last_descender, False


from .paragraph import Paragraph
9 changes: 7 additions & 2 deletions src/rinoh/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,17 @@ def render(self, container, last_descender, state, **kwargs):
scale_width * dpi_scale_x,
scale_height * dpi_scale_y,
self.rotate)
ignore_overflow = (self.scale == Scale.FIT) or container.page._empty
if container.page._empty:
self.warn(f"Image {self.filename} does not fit within the page "
f"margins", container)
ignore_overflow = True
else:
ignore_overflow = self.scale == Scale.FIT
try:
container.advance(h, ignore_overflow)
except ContainerOverflow:
raise EndOfContainer(state)
return w, h, 0
return w, h, 0, container.page._empty


class InlineImageArgs(ImageArgsBase):
Expand Down
2 changes: 1 addition & 1 deletion src/rinoh/inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def spans(self, document):
def flow_inline(self, container, last_descender, state=None):
baseline = self.baseline or self.get_style('baseline', container)
virtual_container = VirtualContainer(container)
width, _, _ = self.flow(virtual_container, last_descender, state=state)
width, _, _, _ = self.flow(virtual_container, last_descender, state=state)
return InlineFlowableSpan(width, baseline, virtual_container)


Expand Down
16 changes: 9 additions & 7 deletions src/rinoh/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,10 +441,11 @@ class InlineDownExpandingContainer(ConditionalDownExpandingContainerBase):
"""

def __init__(self, name, parent, left=None, width=None, right=None,
advance_parent=True, place=True):
max_height=None, advance_parent=True, place=True):
super().__init__(name, None, parent, left=left, top=parent.cursor,
width=width, right=right,
max_height=parent.remaining_height, place=place)
max_height=max_height or parent.remaining_height,
place=place)
if advance_parent:
parent._cursor.addends.append(self._cursor)

Expand Down Expand Up @@ -490,14 +491,15 @@ def __init__(self, name, type, parent, left=None, middle=None, width=None,


class _MaybeContainer(InlineDownExpandingContainer):
def __init__(self, parent, left=None, width=None, right=None):
def __init__(self, parent, left=None, width=None, right=None, max_height=None):
super().__init__('MAYBE', parent, left=left, width=width, right=right,
place=False)
max_height=max_height, place=False)


class MaybeContainer(ContextManager):
def __init__(self, parent, left=None, width=None, right=None):
self._container = _MaybeContainer(parent, left, width, right)
def __init__(self, parent, left=None, width=None, right=None, max_height=None):
self._container = _MaybeContainer(parent, left, width, right,
max_height=max_height)

def __enter__(self):
return self._container
Expand Down Expand Up @@ -581,7 +583,7 @@ def flow_footnotes(self, preallocate=False):
footnote_id = footnote.get_id(self.document)
if footnote_id not in (self._placed_footnotes
| self.document.placed_footnotes):
_, _, descender = footnote.flow(maybe_container,
_, _, descender, _ = footnote.flow(maybe_container,
self._descenders[-1],
footnote=True)
self._descenders.append(descender)
Expand Down
2 changes: 1 addition & 1 deletion src/rinoh/paragraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ def typeset_line(line, last_line=False):
if text_align == TextAlign.RIGHT:
container.left -= float(container.width - max_line_width)

return max_line_width, first_line.advance, descender
return max_line_width, first_line.advance, descender, False


class StaticParagraph(ParagraphBase):
Expand Down
2 changes: 1 addition & 1 deletion src/rinoh/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def flow(self, container, last_descender, state=None, footnote=False,
**kwargs):
location = self.get_style('location', container)
if not footnote and location == NoteLocation.FOOTER:
return 0, 0, last_descender
return 0, 0, last_descender, False
return super().flow(container, last_descender, state, **kwargs)


Expand Down
4 changes: 2 additions & 2 deletions src/rinoh/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def prepare(self, container):
def flow(self, container, last_descender, state=None, **kwargs):
if self.level == 1 and container.page.chapter_title:
container.page.create_chapter_title(self)
result = 0, 0, None
result = 0, 0, None, False
else:
result = super().flow(container, last_descender, state, **kwargs)
return result
Expand Down Expand Up @@ -430,7 +430,7 @@ def render(self, container, descender, state, **kwargs):
width = float(container.width)
line = Line((0, 0), (width, 0), parent=self)
line.render(container)
return width, 0, 0
return width, 0, 0, False


class OutOfLineFlowables(GroupedFlowables):
Expand Down
4 changes: 2 additions & 2 deletions src/rinoh/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def render_rows(section, next_row_index=0):
if min(next_row_index, rows_left) >= split_minimum_rows:
state.body_row_index = next_row_index
raise EndOfContainer(state)
return sum(state.column_widths), 0, 0
return sum(state.column_widths), 0, 0, False

def _size_columns(self, container):
"""Calculate the table's column sizes constrained by:
Expand Down Expand Up @@ -299,7 +299,7 @@ def _widths_from_content(self, fixed, max_cell_width, container):
def cell_content_width(cell):
buffer = VirtualContainer(container, width=max_cell_width,
never_placed=True)
width, _, _ = cell.flow(buffer, None)
width, _, _, _ = cell.flow(buffer, None)
return float(width)

widths = [width if width else 0 for width in fixed]
Expand Down
2 changes: 1 addition & 1 deletion src/rinoh/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def create_chapter_title(self, heading):
descender = None
for flowable in self.get_config_value('chapter_title_flowables',
self.document):
_, _, descender = flowable.flow(self.chapter_title, descender)
_, _, descender, _ = flowable.flow(self.chapter_title, descender)


class SidewaysBodyPage(BodyPageBase):
Expand Down
Binary file added tests_regression/images/tall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions tests_regression/rst/figure_tall.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

.. figure:: ../images/tall.png
:width: 50%

A tall figure.
5 changes: 5 additions & 0 deletions tests_regression/rst/image_tall.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

Some text preceding a tall image.

.. image:: ../images/tall.png
:width: 50%

0 comments on commit 4722b40

Please sign in to comment.