PYFPDF: Adjust cell height with a multicell

1.1k Views Asked by At

i have a problem for adjust the height of cell depending on height of multicell, this multicell changes its height by the number of words and width.My code that I am testing is the following.


datos = ["hola","Esto es el texto que determina el tamaño"]
pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 8)
line_height = pdf.font_size * 2.5
t = 0
lh_list = [] #list with proper line_height for each row #flag
a = []


#create lh_list of line_heights which size is equal to num rows of data
for row in datos:
    x = row.split()
    f = len(x)
    a.append(f)
    if f>1:
        alto = line_height
        lh_list.append(alto)
    else:
        lh_list.append(line_height)
print(lh_list)
print(a)
for j,row in enumerate(datos):
    if a[j]>1:
        pdf.multi_cell(40,lh_list[j],row,1,'J')
    else:
        pdf.cell(40,lh_list[j]*2,row,1,0,'J')

pdf.output('table_with_cells.pdf')

The result is: Result

and the desired result is: expected result

1

There are 1 best solutions below

0
On

You use the split_only argument of multi_cell to calculate the needed lines per cell for the text. With this information you can properly calculate the exact position of each cell. Be aware that the max_line_height argument has to be understand as line height of each line of a cell. Meaning that a value of 3 will result in a cell with 3, 6 or 9 mm heigth if the content need 1-3 lines within the cell to be printed.

from fpdf import FPDF


def pt2mm(points: int) -> float:
    """calculates size from points (eg. font size) to mm

    Parameters
    ----------
    pt : int
        size in points

    Returns
    -------
    float
        size in mm
    """
    return 0.3528 * points

pdf = FPDF()
pdf.add_page()
default_font_size = 8
pdf.set_font('helvetica', 'B', default_font_size)
line_height = default_font_size * 1.1
line_oversize = line_height - default_font_size
print(f'{default_font_size} -> {line_height} -> {line_oversize}')
table_data = [
    ("hola", "Esto es el texto que determina el tamaño"),
    ("hola hola hola hola hola", "Esto es el texto que determina el tamaño"),
    ("hola hola hola hola hola ", "Esto es el texto"),
    ("hola hola ", "Esto es el texto"),
]
col_widths = (30, 50)
col_align = ('L', 'L')

x_val_first_col = pdf.get_x()  # we have to draw the cell border by ourself, need start point coordination
for row in table_data:
    # check if any cell in this row has line breaks, saves maximum lines and calculate height and somem positions
    max_n = 1
    for col_width, row_data in zip(col_widths, row):
        max_n = max(max_n, len(pdf.multi_cell(w=col_width, txt=row_data, split_only=True)))
    row_height = (max_n + line_oversize) * pt2mm(default_font_size)
    row_y_top = pdf.get_y() + line_oversize / 2 * pt2mm(default_font_size)
    row_y_bottom = pdf.get_y() + row_height
    # draw each cell by itself
    for col_width, cell_data, cell_align in zip(col_widths, row, col_align):
        n_cell = len(pdf.multi_cell(w=col_width, txt=cell_data, split_only=True))
        if n_cell < max_n:
            y_cell_shift = (max_n - n_cell) * pt2mm(default_font_size) / 2
        else:
            y_cell_shift = 0
        cell_y_top = row_y_top + y_cell_shift
        pdf.set_xy(pdf.get_x(), cell_y_top)
        pdf.multi_cell(w=col_width, txt=cell_data, border=0, align=cell_align, new_x='RIGHT')
    # draw the horizontal line and return curser
    pdf.line(x1=x_val_first_col, y1=row_y_bottom, x2=x_val_first_col+sum(col_widths), y2=row_y_bottom)
    pdf.set_xy(x=x_val_first_col, y=row_y_bottom)
pdf.output('example.pdf')

The borders of the cell needed to be drawn separately. In my example I only draw the horizontal lines. To understand why you have to do this, just change border=0 to border=1 in my example.