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.