fpdf2 多单元格长字符串问题,单元格无法调整大小

发布于 2025-01-16 06:50:38 字数 928 浏览 0 评论 0原文

我对 fpdf2 lib 有问题, 我有一长串文本想要放入单元格中,但单元格无法调整大小。

多单元处理错误的结果

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')

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')

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一指流沙 2025-01-23 06:50:38

据我发现,库中没有解决方案,但我写了这个小解决方法。

输入稍微更改为 pandas DataFrame。

该解决方案根据行中字符串的最大长度和提供的列宽度来估计每行需要多少行。

不幸的是,字母没有固定的宽度。我用字体大小 * 0.6 估计宽度。您可能想根据您使用的字体来尝试它。

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')

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')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文