FPDF2 Python:如何计算出多格内长串后的高度?

0g0grzrc  于 2023-03-21  发布在  Python
关注(0)|答案(1)|浏览(112)

我在FPDF中计算多单元格的正确高度时遇到问题。当使用pdf.ln()时,由于单元格中的内容溢出,我遇到了重叠问题。对于任何长度的字符串,我可以做些什么来动态解决这个问题?

itemname = "This is a thing this is a thing this is a thing"
lineheight = pdf.font_size * 2.5
pdf.multi_cell(col_width * 9, line_height, textname, border=0, align="L",ln=3, max_line_height=7)
pdf.ln()
pdf.multi_cell(col_width * 9, line_height, "Shipping Charges", border=0, align="L",ln=3, max_line_height=7)

这是给结果如图所示,如果我添加更多的内容,以项目名称的运费,礼品 Package 和项目名称,并放置在它的顶部

nqwrtyyt

nqwrtyyt1#

我采用了一种不同的方法。这有点复杂,但你可以将每个单元格添加到一行中,并让它占用所需的空间。找到单元格的底部并跟踪它。当你将单元格添加到一行时,找出哪个是最低的,然后从该位置开始下一行。这样,下一行将足够远,以容纳前一行中最高的单元格。
修改Bvalgard的代码(主要是,我用add_cell函数替换了multi_cell,并跟踪最低点):

def add_cell(pdf, width, line_height, content, border, align, lowest_point, highest_point):
    pdf.multi_cell(width, line_height, content, border=border, align=align, ln=0, max_line_height=pdf.font_size*1.25)
    current_x = pdf.get_x()
    current_y = pdf.get_y()
    if current_y > lowest_point:
        lowest_point = current_y
        print("new lowest point:", lowest_point)
    pdf.set_xy(current_x, highest_point)
    print("row x", pdf.get_x(), "row y", pdf.get_y())
    return lowest_point

def create_table(pdf, table_data, title='', data_size = 10, title_size=12, align_data='L', align_header='L', cell_width='even', x_start='x_default',emphasize_data=[], emphasize_style=None, emphasize_color=(0,0,0)):
    """
    table_data: 
                list of lists with first element being list of headers
    title: 
                (Optional) title of table (optional)
    data_size: 
                the font size of table data
    title_size: 
                the font size fo the title of the table
    align_data: 
                align table data
                L = left align
                C = center align
                R = right align
    align_header: 
                align table data
                L = left align
                C = center align
                R = right align
    cell_width: 
                even: evenly distribute cell/column width
                uneven: base cell size on lenght of cell/column items
                int: int value for width of each cell/column
                list of ints: list equal to number of columns with the widht of each cell / column
    x_start: 
                where the left edge of table should start
    emphasize_data:  
                which data elements are to be emphasized - pass as list 
                emphasize_style: the font style you want emphaized data to take
                emphasize_color: emphasize color (if other than black) 
    
    """
    default_style = pdf.font_style
    if emphasize_style == None:
        emphasize_style = default_style
    # default_font = pdf.font_family
    # default_size = pdf.font_size_pt
    # default_style = pdf.font_style
    # default_color = pdf.color # This does not work

    # Get Width of Columns
    def get_col_widths():
        col_width = cell_width
        if col_width == 'even':
            col_width = pdf.epw / len(data[0]) - 1  # distribute content evenly   # epw = effective page width (width of page not including margins)
        elif col_width == 'uneven':
            col_widths = []

            # searching through columns for largest sized cell (not rows but cols)
            for col in range(len(table_data[0])): # for every row
                longest = 0 
                for row in range(len(table_data)):
                    cell_value = str(table_data[row][col])
                    value_length = pdf.get_string_width(cell_value)
                    if value_length > longest:
                        longest = value_length
                col_widths.append(longest + 4) # add 4 for padding
            col_width = col_widths


                    ### compare columns 

        elif isinstance(cell_width, list):
            col_width = cell_width  # TODO: convert all items in list to int        
        else:
            # TODO: Add try catch
            col_width = int(col_width)
        return col_width

    # Convert dict to lol
    # Why? because i built it with lol first and added dict func after
    # Is there performance differences?
    if isinstance(table_data, dict):
        header = [key for key in table_data]
        data = []
        for key in table_data:
            value = table_data[key]
            data.append(value)
        # need to zip so data is in correct format (first, second, third --> not first, first, first)
        data = [list(a) for a in zip(*data)]

    else:
        header = table_data[0]
        data = table_data[1:]

    line_height = pdf.font_size * 2.5
    # line_height = pdf.font_size * 10

    col_width = get_col_widths()
    pdf.set_font(size=title_size)

    # Get starting position of x
    # Determin width of table to get x starting point for centred table
    if x_start == 'C':
        table_width = 0
        if isinstance(col_width, list):
            for width in col_width:
                table_width += width
        else: # need to multiply cell width by number of cells to get table width 
            table_width = col_width * len(table_data[0])
        # Get x start by subtracting table width from pdf width and divide by 2 (margins)
        margin_width = pdf.w - table_width
        # TODO: Check if table_width is larger than pdf width

        center_table = margin_width / 2 # only want width of left margin not both
        x_start = center_table
        pdf.set_x(x_start)
    elif isinstance(x_start, int):
        pdf.set_x(x_start)
    elif x_start == 'x_default':
        x_start = pdf.set_x(pdf.l_margin)

    # TABLE CREATION #

    # add title
    if title != '':
        pdf.multi_cell(0, line_height, title, border=0, align='j', ln=3, max_line_height=pdf.font_size)
        pdf.ln(line_height) # move cursor back to the left margin

    pdf.set_font(size=data_size)
    # add header
    y1 = pdf.get_y()
    if x_start:
        x_left = x_start
    else:
        x_left = pdf.get_x()
    x_right = pdf.epw + x_left

    lowest_point = pdf.get_y()
    highest_point = pdf.get_y()
    print("header x", pdf.get_x(), "header y", pdf.get_y())

    if  not isinstance(col_width, list):
        if x_start:
            pdf.set_x(x_start)
        for datum in header:
            # pdf.multi_cell(col_width, line_height, datum, border=0, align=align_header, ln=3, max_line_height=pdf.font_size)
            lowest_point = add_cell(pdf, col_width, line_height, datum, 0, align_header, lowest_point, highest_point)
            x_right = pdf.get_x()
        # pdf.ln(line_height) # move cursor back to the left margin
        # y2 = pdf.get_y()
        # pdf.line(x_left,y1,x_right,y1)
        # pdf.line(x_left,y2,x_right,y2)
        # lowest_point += line_height/2.0

        pdf.line(x_left,highest_point,x_right,highest_point)
        pdf.line(x_left,lowest_point,x_right,lowest_point)
        for row in data:
            # if x_start: # not sure if I need this
            pdf.set_xy(x_start, lowest_point)
            highest_point = lowest_point
            for datum in row:
                if datum in emphasize_data:
                    pdf.set_text_color(*emphasize_color)
                    pdf.set_font(style=emphasize_style)
                    # pdf.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size)
                    lowest_point = add_cell(pdf, col_width, line_height, datum, 0, align_header, lowest_point, highest_point)
                    pdf.set_text_color(0,0,0)
                    pdf.set_font(style=default_style)
                else:
                    # pdf.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named pdf
                    lowest_point = add_cell(pdf, col_width, line_height, datum, 0, align_header, lowest_point, highest_point)
            # pdf.ln(line_height) # move cursor back to the left margin
            # pdf.ln(line_height)
            lowest_point += line_height/2.0
            pdf.line(x_left,lowest_point,x_right,lowest_point)
            print("making line at", lowest_point)
        pdf.set_xy(x_left, lowest_point)
    else:
        if x_start:
            pdf.set_x(x_start)
        for i in range(len(header)):
            datum = header[i]
            # pdf.multi_cell(col_width[i], line_height, datum, border=0, align=align_header, ln=3, max_line_height=pdf.font_size)
            lowest_point = add_cell(pdf, col_width[i], line_height, datum, 0, align_header, lowest_point, highest_point)
            x_right = pdf.get_x()
        # pdf.ln(line_height) # move cursor back to the left margin
        # y2 = pdf.get_y()
        # pdf.line(x_left,y1,x_right,y1)
        # pdf.line(x_left,y2,x_right,y2)
        # lowest_point += line_height/2.0

        pdf.line(x_left,highest_point,x_right,highest_point)
        pdf.line(x_left,lowest_point,x_right,lowest_point)
        for i in range(len(data)):
            # if x_start:
            #     pdf.set_x(x_start)
            pdf.set_xy(x_start, lowest_point)
            highest_point = lowest_point
            row = data[i]
            for i in range(len(row)):
                datum = row[i]
                if not isinstance(datum, str):
                    datum = str(datum)
                adjusted_col_width = col_width[i]
                if datum in emphasize_data:
                    pdf.set_text_color(*emphasize_color)
                    pdf.set_font(style=emphasize_style)
                    # pdf.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size)
                    lowest_point = add_cell(pdf, adjusted_col_width, line_height, datum, 0, align_header, lowest_point, highest_point)
                    pdf.set_text_color(0,0,0)
                    pdf.set_font(style=default_style)
                else:
                    # pdf.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named pdf
                    lowest_point = add_cell(pdf, adjusted_col_width, line_height, datum, 0, align_header, lowest_point, highest_point)
            lowest_point += line_height/2.0
            pdf.line(x_left,lowest_point,x_right,lowest_point)
        pdf.set_xy(x_left, lowest_point)
    y3 = pdf.get_y()
    pdf.line(x_left,y3,x_right,y3)
    pdf.ln()

pdf = FPDF(orientation='L', unit='mm', format='A4')
pdf.add_page()

cell_widths = "even"

create_table(pdf, table_data = my_table_data, data_size=10, align_header='L', align_data='L', cell_width=cell_widths, x_start='C')

相关问题