Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 265 additions & 28 deletions fpdf/table.py

Large diffs are not rendered by default.

Binary file added overly_long_tablespan_29.pdf
Binary file not shown.
Binary file added overly_long_tablespan_30.pdf
Binary file not shown.
19 changes: 19 additions & 0 deletions repro_tablespan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from fpdf import FPDF
from fpdf.enums import TableSpan

pdf = FPDF()

pdf.auto_page_break = True
pdf.set_font("Arial", size=12)
pdf.add_page()

data = [
["Header 1", "Header 2", "Header 3"],
["Data 1", "Data 2", "Data 3"],
] + [[TableSpan.ROW, "Data 5", "Data 6"]] * 30 #this seems to be the threshold -> 32 rows of this size

with pdf.table(data) as table:
pass

pdf.output("overly_long_tablespan.pdf")
print("Wrote overly_long_tablespan.pdf")
Binary file not shown.
Binary file added test/table/table_rowspan_issue_1460_multipage.pdf
Binary file not shown.
Binary file not shown.
75 changes: 73 additions & 2 deletions test/table/test_table_rowspan.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from pathlib import Path

from fpdf import FPDF, FontFace
from fpdf.enums import TableSpan
from fpdf.enums import TableHeadingsDisplay, TableSpan

from test.conftest import assert_pdf_equal
from test.conftest import LOREM_IPSUM, assert_pdf_equal

HERE = Path(__file__).resolve().parent
IMG_DIR = HERE.parent / "image"
Expand Down Expand Up @@ -223,6 +223,77 @@ def test_table_with_rowspan_and_pgbreak(tmp_path):
assert_pdf_equal(pdf, HERE / "table_with_rowspan_and_pgbreak.pdf", tmp_path)


#helper function for issue #1460 data
def _issue_1460_table_data(span_extra_rows: int = 30):
"""Minimal data from issue #1460 (rowspan via TableSpan.ROW)."""
return [
["Header 1", "Header 2", "Header 3"],
["Data 1", "Data 2", "Data 3"],
] + [[TableSpan.ROW, "Data 5", "Data 6"] for _ in range(span_extra_rows)]


def test_table_rowspan_issue_1460_multipage(tmp_path):
# Issue #1460: rowspan taller than one page must render across multiple pages.
pdf = FPDF()
pdf.auto_page_break = True
pdf.set_font("Helvetica", size=12)
pdf.add_page()
with pdf.table(_issue_1460_table_data(30)):
pass
assert_pdf_equal(pdf, HERE / "table_rowspan_issue_1460_multipage.pdf", tmp_path)


def test_table_rowspan_issue_1460_repeat_headings(tmp_path):
# Same as multipage case, but headings must repeat after each page break.
pdf = FPDF()
pdf.auto_page_break = True
pdf.set_font("Helvetica", size=12)
pdf.add_page()
with pdf.table(
_issue_1460_table_data(30),
repeat_headings=TableHeadingsDisplay.ON_TOP_OF_EVERY_PAGE,
):
pass
assert_pdf_equal(
pdf, HERE / "table_rowspan_issue_1460_repeat_headings.pdf", tmp_path
)
Comment on lines +246 to +259
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test and test_table_rowspan_issue_1460_multipage() seem to produce identical files - you could even reference the same PDF. Maybe the idea was to test TableHeadingsDisplay.NONE to cover both paths?



def test_table_rowspan_across_pages_merged_cell_long_text(tmp_path):
# Rowspan anchor cell contains enough text to overflow; span crosses page breaks.
long_text = (LOREM_IPSUM + "\n\n") * 2
data = [
["Header 1", "Header 2", "Header 3"],
[long_text, "Col B", "Col C"],
] + [[TableSpan.ROW, f"R{i}", f"R{i}"] for i in range(35)]
pdf = FPDF()
pdf.auto_page_break = True
pdf.set_font("Helvetica", size=12)
pdf.add_page()
with pdf.table(data):
pass
assert_pdf_equal(
pdf, HERE / "table_rowspan_across_pages_merged_cell_long_text.pdf", tmp_path
)

def test_table_rowspan_issue_1460_merged_cell_long_text_no_header(tmp_path):
# Same as multipage case, but no header
long_text = (LOREM_IPSUM + "\n\n") * 2
data = [
["Text", "Header 2", "Header 3"],
[long_text, "Col B", "Col C"],
] + [[TableSpan.ROW, f"R{i}", f"R{i}"] for i in range(35)]
pdf = FPDF()
pdf.auto_page_break = True
pdf.set_font("Helvetica", size=12)
pdf.add_page()
with pdf.table(data, first_row_as_headings=False):
pass
assert_pdf_equal(
pdf, HERE / "table_rowspan_issue_1460_merged_cell_long_text_no_header.pdf", tmp_path
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actions pipeline is failing because this file has not been commited.

)


def test_table_with_rowspan_images(tmp_path):
# Verify that the rowspans interact correctly with pagebreaks
pdf = FPDF()
Expand Down
Loading