[Python] Wrapping Table Headers in PDF using Python - fpdf

Discussão em 'Python' iniciado por Stack, Abril 19, 2025.

  1. Stack

    Stack Membro Participativo

    I'm building trying to build a pdf report in Python using fpdf. I want to take some data from my analysis stored in a DataFrame and create tables and graphs. Just like what you would do in Excel. My issues is I can't see to get the headers to wrap text like you would get if you used the "Wrap Text" function in Excel if you were creating a table. I've tried multiple iterations and the best I've come up with is the below:

    data1 = {
    'Category': ['A', 'B', 'C', 'D'],
    'Values': [10.1234, 20.5678, 15.9101, 25.1121]
    }

    data2 = {
    'Category': ['E', 'F', 'G', 'H'],
    'I expect this to wrap': [30.2345, 40.6789, 35.3456, 45.7890]
    }




    df1 = pd.DataFrame(data1)
    df2 = pd.DataFrame(data2)

    # Create a PDF class
    class PDF(FPDF):
    def header(self):
    self.set_font('Arial', 'B', 12)
    self.cell(0, 10, 'PDF Report with Charts and Tables', 0, 1, 'C')
    self.ln(10)

    def chapter_title(self, title):
    self.set_font('Arial', 'B', 12)
    self.cell(0, 10, title, 0, 1, 'L')
    self.ln(5)

    def table(self, df):
    self.set_font('Helvetica', '', 10)

    # Calculate column widths
    index_col_width = max(self.get_string_width(str(index)) for index in df.index) + 10
    col_widths = {col: self.get_string_width(col) + 10 for col in df.columns}
    max_col_width = max(col_widths.values())

    # Row height
    row_height = self.font_size + 5

    # Header
    self.set_fill_color(255, 255, 255)
    self.set_draw_color(0, 0, 0)

    # Wrap headers
    x_start = self.get_x()
    y_start = self.get_y()

    self.multi_cell(index_col_width, row_height, 'Member', border=1, align='C', fill=True)
    self.set_xy(x_start + index_col_width, y_start)

    for col_name in df.columns:
    x_start = self.get_x()
    y_start = self.get_y()
    self.multi_cell(max_col_width, row_height, col_name, border=1, align='C', fill=True)
    self.set_xy(x_start + max_col_width, y_start)

    self.ln(row_height)

    # Data
    fill = False
    for index, row in df.iterrows():
    fill = not fill
    self.set_fill_color(240, 240, 240) if fill else self.set_fill_color(255, 255, 255)
    self.cell(index_col_width, row_height, str(index), border=1, fill=True, align='L')
    for cell in row:
    formatted_cell = f"{cell:.2f}" if isinstance(cell, float) else str(cell)
    self.cell(max_col_width, row_height, formatted_cell, border=1, fill=True, align='L')
    self.ln(row_height)

    # Create a PDF document
    pdf = PDF()
    pdf.set_auto_page_break(auto=True, margin=15)

    # Add first table
    pdf.add_page(orientation='L')
    pdf.chapter_title('Table 1: Category vs Values')
    pdf.table(df1)

    # Add second table
    pdf.add_page(orientation='L')
    pdf.chapter_title('Table 2: Category vs Values')
    pdf.table(df2)


    # Create chart for demonstration
    plt.figure(figsize=(10, 5.63)) # Adjust figure size to fit within page
    plt.bar(df1['Category'], df1['Values'], color='skyblue')
    plt.title('Sample Bar Chart')
    plt.xlabel('Category')
    plt.ylabel('Values')
    plt.savefig('bar_chart.png') # Save the figure
    plt.close()



    # Add chart
    pdf.add_page(orientation='L')
    pdf.chapter_title('Sample Bar Chart')
    pdf.image('bar_chart.png', x=10, y=30, w=250) # Adjust the positioning and size as needed

    # Output the PDF
    pdf.output('report_test_2_check.pdf')


    This gives me the below:

    [​IMG]

    Below is an excel example of what I expect for the second table:

    [​IMG]

    Continue reading...

Compartilhe esta Página