Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multipage table using fpdf #1196

Open
tekkadan95 opened this issue Jun 5, 2024 · 2 comments
Open

multipage table using fpdf #1196

tekkadan95 opened this issue Jun 5, 2024 · 2 comments

Comments

@tekkadan95
Copy link

I am struggling to handle pagebreaks with multicell tables.
example pdf

As you can see the rest of the row gets printed on the y position of the next page, not the first one.

I am using user input from a pyqt application. This is my code snippet from the application:

the second part is a minimal reproducible example to highlight the problem.
I don't know if I have a logic error, or fpdf is not viable for this approach. This is something for an invoice generator.

def add_table_row(pdf, product, widths, alignments):
        pdf.set_font('Helvetica', '', 9)
        x_start = 25
        y_before = pdf.get_y()
        max_y = y_before

        for width, field, align in zip(widths, product, alignments):
            # Set x position to 25 for the first column
            pdf.set_xy(x_start, y_before)
            pdf.multi_cell(width, 5, field, border=1, align=align)
            x_start += width  # Move x position for the next column
            max_y = max(max_y, pdf.get_y())

        pdf.set_y(max_y)

   # Add table rows
    for idx, product in enumerate(product_lines, start=1):
        current_y = pdf.get_y()
        if current_y + 10 > pdf.h - 25:
            pdf.add_page()
            current_y = 25  # Set Y position to top margin for new page

        pdf.set_xy(25, current_y)  # Reset X position for the new row
        formatted_menge = f"{locale.format_string('%.2f', product['menge'], grouping=True)} {product['einheit']}"
        formatted_preis_netto = f"{locale.format_string('%.2f', product['preis_netto'], grouping=True)} EUR"
        formatted_betrag_netto = f"{product['betrag_netto']} EUR"
        product_data = [str(idx) + '.', product['produktname'], formatted_menge,
                        formatted_preis_netto, product['ust'] + ' %', formatted_betrag_netto]

        add_table_row(pdf, product_data, widths, alignments)

from fpdf import FPDF
 
 
class PDF(FPDF):
    def header(self):
        self.set_font('Arial', 'B', 12)
        self.cell(0, 10, 'Multi-page Table Example', 0, 1, 'C')
 
    def footer(self):
        self.set_y(-15)
        self.set_font('Arial', 'I', 8)
        self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
 
 
def add_table_row(pdf, product, widths, alignments):
    x_start = 25
    y_before = pdf.get_y()
    max_y = y_before
 
    for width, field, align in zip(widths, product, alignments):
        pdf.set_xy(x_start, y_before)
        pdf.multi_cell(width, 10, field, border=1, align=align)
        x_start += width
        max_y = max(max_y, pdf.get_y())
 
    pdf.set_y(max_y)
 
 
# Create instance of FPDF class & add a page
pdf = PDF()
pdf.add_page()
 
# Set initial position for the first row
pdf.set_xy(25, 50)
 
# Sample data
products = [
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
        "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
     "150", "7.50", "20 %", "900.00"],
 
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product B", "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
    ["1", "Product A", "100", "5.00", "20 %", "600.00"],
    ["2", "Product Bahsdhasdhkjashd ahsdhjk ashdjk ahjkdhjka hjkdlahjskld hjklasdjhkl ahjklads hjkadhjkasdhjk ahjkhajkl",
        "150", "7.50", "20 %", "900.00"],
    ["3", "Product C", "200", "10.00", "20 %", "1200.00"],
    ["4", "Product D", "250", "12.50", "20 %", "1500.00"],
    ["5", "Product E", "300", "15.00", "20 %", "1800.00"],
]
 
# Column widths and alignments
widths = [10, 70, 20, 30, 20, 30]
alignments = ['C', 'L', 'R', 'R', 'C', 'R']
 
# Add rows to the table
for product in products:
    current_y = pdf.get_y()
    if current_y + 10 > pdf.h - 25:
        pdf.add_page()
        pdf.set_xy(25, 50)  # Reset position at the top of the new page
    add_table_row(pdf, product, widths, alignments)
 
# Save the pdf with name .pdf
pdf_file_path = 'multi_page_table.pdf'
pdf.output(pdf_file_path)
print(f"PDF generated: {pdf_file_path}")

thanks in advance for any tips/help

@andersonhc
Copy link
Collaborator

andersonhc commented Jun 6, 2024

Let me introduce you to our table feature, it will definitely make your life easier.

You can replace most of your code by something like this:

with pdf.table(
    width=180,
    col_widths=(10, 70, 20, 30, 20, 30),
    text_align=("C", "L", "R", "R", "C", "R"),
) as table:
    for product in products:
        row = table.row()
        for field in product:
            row.cell(field)

@andersonhc
Copy link
Collaborator

@tekkadan95 please let us know if that solution works for you and we can close the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants