jorisschellekens / borb

borb is a library for reading, creating and manipulating PDF files in python.
https://borbpdf.com/
Other
3.4k stars 147 forks source link

Another bug with SingleColumnLayoutWithOverflow #200

Closed siancu closed 6 months ago

siancu commented 8 months ago

I am trying to add a long table with an image in a cell in each row and I'm getting the following error:

Traceback (most recent call last):
  File "/Users/si/Developer/github.com/siancu/borb-table-example/main.py", line 57, in <module>
    main()
  File "/Users/si/Developer/github.com/siancu/borb-table-example/main.py", line 50, in main
    layout.add(table)
  File "/Users/si/Developer/github.com/siancu/borb-table-example/.venv/lib/python3.11/site-packages/borb/pdf/canvas/layout/page_layout/single_column_layout_with_overflow.py", line 207, in add
    super(SingleColumnLayoutWithOverflow, self).add(t)
  File "/Users/si/Developer/github.com/siancu/borb-table-example/.venv/lib/python3.11/site-packages/borb/pdf/canvas/layout/page_layout/multi_column_layout.py", line 205, in add
    assert False, f"{layout_element.__class__.__name__} is too tall to fit inside column / page. Needed {round(layout_box.get_height())} pts, only {round(available_box.get_height())} pts available."
AssertionError: FixedColumnWidthTable is too tall to fit inside column / page. Needed 521 pts, only 476 pts available.

If I remove the image and put a Paragraph with a text in it, then it works. I made a table with approx. 200 rows without problems.

However, once I add the image, it breaks even with 3 rows (it works with 2). It breaks when it should go to the next page.

Here is the script that reproduces the issue. The image is a png file with the size 250x156.

from decimal import Decimal
from pathlib import Path

from borb.pdf import Document
from borb.pdf import FixedColumnWidthTable
from borb.pdf import PDF
from borb.pdf import Page
from borb.pdf import Paragraph
from borb.pdf import SingleColumnLayoutWithOverflow
from borb.pdf import Image
from borb.pdf.page.page_size import PageSize

doc = Document()

page = Page(PageSize.A4_LANDSCAPE.value[0], PageSize.A4_LANDSCAPE.value[1])  # A4 Landscape
doc.add_page(page)

# with 2 it works, with 3 it doesn't
# number_of_samples = 2
number_of_samples = 3
layout = SingleColumnLayoutWithOverflow(page)
table = FixedColumnWidthTable(
    number_of_columns=7,
    number_of_rows=number_of_samples + 1,
    column_widths=[Decimal(2), Decimal(6), Decimal(1), Decimal(2), Decimal(1), Decimal(2), Decimal(1)],
)
# add the table header
table.add(Paragraph("Sample ID", font="Helvetica-Bold"))
table.add(Paragraph("Image", font="Helvetica-Bold"))
table.add(Paragraph("Valid", font="Helvetica-Bold"))
table.add(Paragraph("Data Mode", font="Helvetica-Bold"))
table.add(Paragraph("Other Mode", font="Helvetica-Bold"))
table.add(Paragraph("Some float", font="Helvetica-Bold"))
table.add(Paragraph("Done", font="Helvetica-Bold"))

for i in range(number_of_samples):
    table.add(Paragraph(f"Sample_{i}"))
    table.add(Image(Path("./sample.png"), width=Decimal(250), height=Decimal(156)))
    table.add(Paragraph("yes"))
    table.add(Paragraph("increasing"))
    table.add(Paragraph("="))
    table.add(Paragraph("4.342E+00"))
    table.add(Paragraph("no"))

table.set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2))
layout.add(table)

with open("/Users/si/Desktop/output.pdf", "wb") as out_file_handle:
    PDF.dumps(out_file_handle, doc)

Here is the image I used:

sample

jorisschellekens commented 6 months ago

This works in the latest release of borb (possibly previous releases as well, considering I don't think I changed SingleColumnLayoutWithOverflow).

I changed your test to the following code (you had multiple issues with text not fitting inside the columns you'd defined:

    def test_singlecolumnlayoutwithoverflow_with_images(self):

        pdf: Document = Document()
        page: Page = Page()
        pdf.add_page(page)

        layout: SingleColumnLayoutWithOverflow = SingleColumnLayoutWithOverflow(page)

        number_of_samples: int = 5
        table = FixedColumnWidthTable(
            number_of_columns=7,
            number_of_rows=number_of_samples + 1,
            column_widths=[Decimal(2), Decimal(6), Decimal(1), Decimal(2), Decimal(1), Decimal(2), Decimal(1)],
        )

        # add the table header
        table.add(Paragraph("S", font="Helvetica-Bold"))
        table.add(Paragraph("I", font="Helvetica-Bold"))
        table.add(Paragraph("V", font="Helvetica-Bold"))
        table.add(Paragraph("D", font="Helvetica-Bold"))
        table.add(Paragraph("O", font="Helvetica-Bold"))
        table.add(Paragraph("S", font="Helvetica-Bold"))
        table.add(Paragraph("D", font="Helvetica-Bold"))

        image_url: str = "https://images.unsplash.com/photo-1568322445389-f64ac2515020"

        for i in range(number_of_samples):
            table.add(Paragraph(f"Sample_{i}"))
            table.add(Image(image_url, width=Decimal(180), height=Decimal(156)))
            table.add(Paragraph("y"))
            table.add(Paragraph("i"))
            table.add(Paragraph("="))
            table.add(Paragraph("4"))
            table.add(Paragraph("n"))

        table.set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2))
        layout.add(table)

        with open(self.get_second_output_file(), "wb") as out_file_handle:
            PDF.dumps(out_file_handle, pdf)

when number_of_samples is set to 3, everything fits on a single Page. So I set it to 5, and it splits, as expected.

image

jorisschellekens commented 6 months ago

Just a heads-up, I will change the test to make the content a bit more generic. So you won't find an exact match (this test code) in the repo. But the name should be the same.