How to set the width of a cell when making a table in pyfpdf

221 Views Asked by At

This is a continuation of my previous question. You can check It here. So I got the tables figured out more but I cant find a way to get the width of the cells so all of the strings (dont count the header cell, I want it to be as wide as the longest value from the column) would fit in them just perfectly and set it for the whole column.

Also is there a way to start placing a table in a differen position based on the width that has been set for all the columns(I was thinking about a tuple to which you add a vaule on every iteration). I want to start the table where the arrow is. The table_data would be just one row and 4 columns with one column being empty:

table_data = ["razem", " ", "  10,03zl  ", "  74,51  "]

You can find the whole code and data in the previous question thanks to help of frankfalse.

I thought about something like getting the longest string from a column and setting the width based on that. For example in my table there's a value "bulka tarta" which is the longest string in the whole table_data beside "Nazwa Uslugi/Towaru" which is a header.enter image description here

But I couldn't work it out in code

1

There are 1 best solutions below

0
User051209 On BEST ANSWER

I suggest you a solution where you set the width of the column of the table for all the column of the table and, after that, you adjust the height of each row.
This is exactly the solution which I have proposed to you in your previous post for the header of the table.

In the code below I have added two example rows to your data list:

  • in the first row the second element value is bulka tarta XXXXX. This string can't be placed in the width column selected for all the column. So the height of the entire row is increased;
  • the last row added to data, contains the string smietana abcd 123456 abcdef which is contained in a multicell with 4 lines.
from fpdf import FPDF

data = [
    ['Lp.', 'Nazwa Uslugi/Towaru', 'Jm', 'Ilosc', 'Cena Netto', 'Wartosc Netto', 'VAT', 'Kwota VAT', 'Wartosc brutto'],
    ['1', 'maka', 'szt.', '10kg', '  11,32  zl ', '  11,32  zl ', '23%', '  2,60  zl ', '  13,92  zl '],
    ['2', 'ryz', 'szt.', '15kg', '  10,00  zl ', '  10,00  zl ', '8%', '  0,80  zl ', '  10,80  zl '],
    ['3', 'makaron', 'szt.', '5kg', '  3,60  zl ', '  3,60  zl ', '23%', '  0,83  zl ', '  4,43  zl '],
    ['4', 'kasza', 'szt.', '8kg', '  2,00  zl ', '  2,00  zl ', '8%', '  0,16  zl ', '  2,16  zl '],
    ['5', 'cukier', 'szt.', '20kg', '  20,00  zl ', '  20,00  zl ', '8%', '  1,60  zl ', '  21,60  zl '],
    ['6', 'soda', 'szt.', '3kg', '  1,00  zl ', '  1,00  zl ', '23%', '  0,23  zl ', '  1,23  zl '],
    ['7', 'smietana', 'szt.', '1l', '  15,00  zl ', '  15,00  zl ', '23%', '  3,45  zl ', '  18,45  zl '],
    ['8', 'bulka tarta', 'szt.', '2kg', '  1,56  zl ', '  1,56  zl ', '23%', '  0,36  zl ', '  1,92  zl'],
    ['9', 'bulka tarta XXXXX', 'szt.', '2kg', '  1,56  zl ', '  1,56  zl ', '23%', '  0,36  zl ', '  1,92  zl']]

CELL_WIDTH = 20

def get_num_of_lines_in_multicell(pdf, message):
    global CELL_WIDTH
    # divide the string in words
    words = message.split(" ")
    line = ""
    n = 1
    previous_line_width = 0
    for word in words:
        line += word + " "
        line_width = pdf.get_string_width(line)
        # In the next if it is necessary subtract 1 to the WIDTH
        if line_width > (CELL_WIDTH-1) * 2 - 1:
            # the multi_cell() insert a line break
            n += 2
            # reset of the string
            line = word + " "
        elif line_width > CELL_WIDTH-1:
            # the multi_cell() insert a line break
            n += 1
            # reset of the string
            line = word + " "
    return n

def get_num_lines_max(pdf, row):
    num_lines_max = 0
    for item in row:
        num_lines = get_num_of_lines_in_multicell(pdf, item)
        if num_lines > num_lines_max:
            num_lines_max = num_lines
    return num_lines_max

def simple_table(spacing=2):
    global CELL_WIDTH
    pdf = FPDF()

    #pdf.add_font('Segoe Ui', '', r'C:\Windows\Fonts\seguibl.ttf')
    pdf.set_font('Times')

    pdf.add_page()
    #pdf.set_font("Segoe Ui")
    counter = 0

    col_width = pdf.w / 10
    CELL_WIDTH = col_width
    row_height = pdf.font_size

    for row in data:
        counter += 1
        new_x = 10
        saveY = 0
        if counter == 1:
            for item in row:
                pdf.set_xy(new_x, 10)
                pdf.set_fill_color(128, 128, 128)
                num_lines = get_num_of_lines_in_multicell(pdf, item)
                if num_lines == 1:
                    pdf.multi_cell(col_width, row_height*3, txt=item, border=1, align="C", fill=True)
                elif num_lines == 2:
                    pdf.multi_cell(col_width, (row_height*3)/2, txt=item, border=1, align="C", fill=True)
                else:
                    pdf.multi_cell(col_width, row_height, txt=item, border=1, align="C", fill=True)
                if pdf.get_y() > saveY:
                    saveY = pdf.get_y()
                new_x += col_width
                pdf.set_xy(new_x, 10)
            pdf.set_xy(10, saveY)
        else:
            num_lines_max = get_num_lines_max(pdf, row)
            if num_lines_max == 1:
                for item in row:
                    pdf.cell(col_width, row_height * spacing, txt=item, border=1, align="C")
                pdf.ln(row_height * spacing)
            else:
                multicell_height = row_height * num_lines_max
                y_multicell_row = pdf.get_y()
                for item in row:
                    num_lines = get_num_of_lines_in_multicell(pdf, item)
                    pdf.multi_cell(col_width, multicell_height/num_lines, txt=item, border=1, align="C", fill=False)
                    if pdf.get_y() > saveY:
                        saveY = pdf.get_y()
                    new_x += col_width
                    pdf.set_xy(new_x, y_multicell_row)
                pdf.set_xy(10, saveY)

    pdf.output('testing.pdf')

simple_table()

Following picture show you the resulting table contained in the file testing.pdf: enter image description here

I advise you that this answer is not suited for all possible the strings; for example is not suited for the string bulka tartaXXXXX because the word tartaXXXXX is longer than the width of the column.
If your data list contains some words longer than the value:

col_width = pdf.w / 10

I suggest you to increase the value of col_width.