fpdf2 multicell long string problem, cell not resize

Viewed 375

I Have problem with fpdf2 lib, I have a long string of text that I want to fit into a cell, but cell not going resize.

result of bad multicell processing

from fpdf import FPDF

data = (
    ("First name", "Last name", "Age", "Cities"),
    ("Jules", "Smith", "34", "Warsaw, New York, Sydney, Berlin, Moscow, Washington, Hamburg, Monachium, Lipsk, Essen, Dresno, Bonn, Hannover, Stuttgart, Rome, San Diego, Los Angeles"),
    ("Mary", "Ramos", "45", "Orlando"),
    ("Carlson", "Banks", "19", "Los Angeles"),
    ("Lucas", "Cimon", "31", "Saint-Mahturin-sur-Loire"),
)

pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=10)
line_height = pdf.font_size * 2.5
col_width = pdf.epw / 4  # distribute content evenly
for row in data:
    for datum in row:
        pdf.multi_cell(col_width, line_height, datum, border=1, ln=3, max_line_height=pdf.font_size)
    pdf.ln(line_height)
pdf.output('table_with_cells.pdf')
1 Answers

There is no solution in the library as far as I found, but I wrote this little workaround.

The input is slightly changed to a pandas DataFrame.

The solution estimates for each row how many lines it is going to need based on the maximum length of the strings in the row and a provided column width.

Unfortunately, letters don't have a fixed width. I estimate the width with font size * 0.6. You might want to play around with it based on what font you use.

from fpdf import FPDF
import pandas as pd
import math

class PDF(FPDF):

     def estimate_lines_needed(self, iter, col_width: float) -> int:
        """_summary_

        Args:
            iter (iterable): a row in your table
            col_width (float): cell width

        Returns:
            _type_: _description_
        """
        font_width_in_mm = (
            self.font_size_pt * 0.35 * 0.6
        )  # assumption: a letter is half his height in width, the 0.5 is the value you want to play with
        max_cell_text_len_header = max([len(str(col)) for col in iter])  # how long is the longest string?
        return math.ceil(max_cell_text_len_header * font_width_in_mm / col_width)

    def table(self, table: pd.DataFrame):
        """Add table to pdf

        Args:
            table (pd.DataFrame): a table to be added to the document
        """

        # one pt is ~0.35mm
        # font size is in pt

        index_width = 80
        col_width = (self.epw - index_width) / (table.shape[1])  # distribute content evenly across pdf

        lines_needed = self.estimate_lines_needed(table.columns, col_width)

        # empty cell to start
        self.multi_cell(
            w=index_width,
            h=self.font_size * lines_needed,
            txt="",
            border=0,
            ln=3,
            max_line_height=self.font_size,
        )

        # header
        for col in table.columns:
            self.multi_cell(
                col_width,
                self.font_size * lines_needed,
                col,
                border="BL",
                ln=1
                if col == table.columns[-1]
                else 3,   # if it is the last col, go to beginning of next line, otherwise continue
                max_line_height=self.font_size,
            )
        # table
        for index, row in table.iterrows():

            lines_needed = self.estimate_lines_needed(iter=row.to_list(), col_width=col_width)
            self.multi_cell(
                index_width, self.font_size * lines_needed, str(index), border="TBR", ln=3, max_line_height=self.font_size
            )
            for col in table.columns:
                self.multi_cell(
                    col_width,
                    self.font_size * lines_needed,
                    str(row[col]),
                    border="TBL",
                    ln=1 if col == table.columns[-1] else 3,
                    max_line_height=self.font_size,
                )
        self.ln(5)  # add a small gap after the table




#test data
data = pd.DataFrame({
    "First name": ["Jules", "Mary", "Carlson", "Lucas"],
    "Last name":["Smith", "Ramos", "Banks", "Cimon"],
    "Age (number of years that you lived)":[34, 45, 19, 31],
    "Cities":["Warsaw, New York, Sydney, Berlin, Moscow, Washington, Hamburg, Monachium, Lipsk, Essen, Dresno, Bonn, Hannover, Stuttgart, Rome, San Diego, Los Angeles", "Los Angeles","Orlando", "Saint-Mahturin-sur-Loire"],
    }
)


# generate pdf
pdf = PDF()
pdf.add_page()
pdf.set_font("Times", size=10)
pdf.table(data)
pdf.output('table_with_cells.pdf')
Related