Skip to content

Commit

Permalink
FPDF.table() now raises an error when a single row is too high to be …
Browse files Browse the repository at this point in the history
…rendered on a single page
  • Loading branch information
Lucas-C committed Apr 9, 2024
1 parent 148a1b6 commit 0981d5a
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
* fixed type hint of member `level` in class [`OutlineSection`](https://py-pdf.github.io/fpdf2/fpdf/outline.html#fpdf.outline.OutlineSection) from `str` to `int`.
### Changed
* improved the performance of `FPDF.start_section()` - _cf._ [issue #1092](https://github.com/py-pdf/fpdf2/issues/1092)
* [`FPDF.table()`](https://py-pdf.github.io/fpdf2/Tables.html) now raises an error when a single row is too high to be rendered on a single page
### Deprecated
* The `dd_tag_indent` & `li_tag_indent` parameters of `FPDF.write_html()` are replaced by the new `tag_indents` generic parameter.
* The `heading_sizes` & `pre_code_font` parameters of `FPDF.write_html()` are replaced by the new `tag_styles` generic parameter.
Expand Down
2 changes: 1 addition & 1 deletion fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3402,7 +3402,7 @@ def will_page_break(self, height):
"""
return (
# ensure that there is already some content on the page:
self.y > self.t_margin
self.y >= self.t_margin
and self.y + height > self.page_break_trigger
and not self.in_footer
and self.accept_page_break
Expand Down
32 changes: 22 additions & 10 deletions fpdf/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def render(self):
)

# Defining table global horizontal position:
prev_l_margin = self._fpdf.l_margin
prev_x, prev_y, prev_l_margin = self._fpdf.x, self._fpdf.y, self._fpdf.l_margin
if table_align == Align.C:
self._fpdf.l_margin = (self._fpdf.w - self._width) / 2
self._fpdf.x = self._fpdf.l_margin
Expand Down Expand Up @@ -213,10 +213,21 @@ def render(self):
# actually render the cells
self._fpdf.y += self._outer_border_margin[1]
for i, row in enumerate(self.rows):
# pylint: disable=protected-access
page_break = self._fpdf._perform_page_break_if_need_be(
row_info[i].pagebreak_height
)
pagebreak_height = row_info[i].pagebreak_height
page_break = self._fpdf.will_page_break(pagebreak_height)
if page_break:
if self._fpdf.y + pagebreak_height > self._fpdf.page_break_trigger:
# Restoring original position on page:
self._fpdf.x, self._fpdf.y, self._fpdf.l_margin = (
prev_x,
prev_y,
prev_l_margin,
)
raise ValueError(
f"The row with index {i} is too high and cannot be rendered on a single page"
)
# pylint: disable=protected-access
self._fpdf._perform_page_break_if_need_be(pagebreak_height)
if page_break and i >= self._num_heading_rows:
# repeat headings on top:
self._fpdf.y += self._outer_border_margin[1]
Expand Down Expand Up @@ -365,9 +376,8 @@ def _render_table_cell(

# place cursor (required for images after images)

if (
height_query_only
): # not rendering, cell_x_positions is not relevant (and probably not provided)
# not rendering, cell_x_positions is not relevant (and probably not provided):
if height_query_only:
cell_x = 0
else:
cell_x = cell_x_positions[j]
Expand Down Expand Up @@ -476,7 +486,7 @@ def _render_table_cell(
dy = 0

if cell_height is not None:
actual_text_height = cell_height_info.rendered_height[j]
actual_text_height = cell_height_info.rendered_heights[j]

if v_align == VAlign.M:
dy = (cell_height - actual_text_height) / 2
Expand Down Expand Up @@ -841,8 +851,10 @@ def write(self, text, align=None):
@dataclass(frozen=True)
class RowLayoutInfo:
height: float
# accumulated rowspans to take in account when considering page breaks:
pagebreak_height: float
rendered_height: dict
# heights of every cell in the row:
rendered_heights: dict
merged_heights: list


Expand Down
Binary file modified test/table/table_with_rowspan_and_pgbreak.pdf
Binary file not shown.
27 changes: 27 additions & 0 deletions test/table/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,3 +775,30 @@ def test_table_cell_fill_mode(tmp_path):
pass
pdf.ln()
assert_pdf_equal(pdf, HERE / "table_cell_fill_mode.pdf", tmp_path)


def test_table_with_very_long_text():
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica")
with pytest.raises(ValueError) as error:
with pdf.table() as table:
row = table.row()
row.cell(LOREM_IPSUM)
# Adding columns to have the content of the 1st cell to overflow:
row.cell("")
row.cell("")
assert (
str(error.value)
== "The row with index 0 is too high and cannot be rendered on a single page"
)
with pytest.raises(ValueError) as error:
with pdf.table() as table:
row = table.row()
row.cell("")
row.cell("")
row.cell(LOREM_IPSUM)
assert (
str(error.value)
== "The row with index 0 is too high and cannot be rendered on a single page"
)
5 changes: 2 additions & 3 deletions test/table/test_table_rowspan.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@ def test_table_with_rowspan_and_pgbreak(tmp_path):
]

pdf.add_page()
y0 = pdf.h - pdf.b_margin
with pdf.local_context(**line_opts):
pdf.line(0, y0, pdf.w, y0)

# simple table
# with pdf.table(TABLE_DATA, **table_opts):
Expand All @@ -181,7 +178,9 @@ def test_table_with_rowspan_and_pgbreak(tmp_path):
# -- verify break occurs before the offending rowspan
# -- verify header reproduction
pdf.set_y(pdf.h - 85)
y0 = pdf.h - pdf.b_margin
with pdf.local_context(**line_opts):
pdf.line(0, y0, pdf.w, y0)
pdf.line(0, pdf.y, pdf.w, pdf.y)
with pdf.table(TABLE_DATA, **table_opts):
pass
Expand Down

0 comments on commit 0981d5a

Please sign in to comment.