首页 > 编程问答 >使用 Python 生成多页 PDF 报告,其中包含表格、页眉、页脚和可调整单元格数据(使用 reportlab)

使用 Python 生成多页 PDF 报告,其中包含表格、页眉、页脚和可调整单元格数据(使用 reportlab)

时间:2024-07-26 07:21:53浏览次数:14  
标签:python python-3.x reportlab

我想在Python中生成带有表格、页眉、页脚和可调整单元格数据的多页PDF报告(使用reportlab)

但是,由于奇怪的原因,字体大小并没有随着表格内容而改变...... 我尝试在这里改变: ('FONTSIZE', (0, 1), (-1, -1), 24), # 内容的字体大小

但什么也没发生

import time
from reportlab.lib.pagesizes import landscape, letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, PageTemplate, Frame
from reportlab.lib.styles import getSampleStyleSheet

# Function to add header and footer
def add_header_footer(canvas, doc):
    canvas.saveState()
    # Header
    header_text = "Report Header"
    canvas.setFont('Helvetica-Bold', 14)
    text_width = canvas.stringWidth(header_text, 'Helvetica-Bold', 14)
    canvas.drawCentredString(doc.pagesize[0] / 2, doc.pagesize[1] - 80, header_text)  # Adjusted position
    
    # Footer
    footer_text = f"Page {doc.page}"
    canvas.setFont('Helvetica', 10)
    text_width = canvas.stringWidth(footer_text, 'Helvetica', 10)
    canvas.drawString((doc.pagesize[0] - text_width) / 2, 40, footer_text)
    canvas.restoreState()

# Function to generate PDF with ordered data in a table format
def generate_pdf(data, pdf_filename):
    # Create the document
    doc = SimpleDocTemplate(pdf_filename, pagesize=landscape(letter), topMargin=1.5*inch)  # Adjust topMargin here
    
    # Define the table data
    table_data = [['Parameter Name', 'Expected Value', 'Your INI Value']]
    
    # Get the sample style sheet
    styles = getSampleStyleSheet()
    styleN = styles['Normal']
    
    # Add "Results for the Ini file:" text as a paragraph before the table
    results_text = "Results for the Ini file:"
    results_paragraph = Paragraph(results_text, styles['Heading2'])
    
    # Calculate the height of the results paragraph
    results_paragraph_height = results_paragraph.wrap(doc.width, doc.topMargin)[1]
    
    # Populate the table with data
    for i in range(len(data['Parameter_Name'])):
        # Ensure each cell in the row is a Paragraph object
        row = [
            Paragraph(data['Parameter_Name'][i], styleN),
            Paragraph(data['Expected_Value'][i], styleN),
            Paragraph(data['Your_INI_Value'][i], styleN)
        ]
        
        table_data.append(row)
    
    # Create the table
    table = Table(table_data, colWidths=[2.5 * inch, 3.0 * inch, 3.0 * inch], repeatRows=1)  # use repeatRows=1 if you want the header of the table to be repetad in teh table in the next pages
    
    # Add style to the table
    style = TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 14), # For header in table
        ('BOTTOMPADDING', (0, 0), (-1, 0), 8),
        ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
        ('GRID', (0, 0), (-1, -1), 1, colors.black),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 1), (-1, -1), 24),  # Font size for content
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),  # Ensure text starts from the top of the cell
    ])
    table.setStyle(style)

    # Apply conditional styling for rows where values differ
    for i in range(1, len(table_data)):  # Start from 1 to skip header row
        expected_value = data['Expected_Value'][i-1]
        your_ini_value = data['Your_INI_Value'][i-1]
        if expected_value != your_ini_value:
            # Apply red background and white text for differing rows
            table.setStyle(TableStyle([
                ('BACKGROUND', (0, i), (-1, i), colors.red),
                ('TEXTCOLOR', (0, i), (-1, i), colors.white)
            ]))

    # Define the frame for the page template
    frame = Frame(doc.leftMargin, doc.bottomMargin + 5 + results_paragraph_height, doc.width, doc.height - 5 - results_paragraph_height, id='normal')
    # frame = Frame(doc.leftMargin, doc.bottomMargin + 100 + results_paragraph_height, doc.width, doc.height - 100 - results_paragraph_height, id='normal')
    
    # Add the PageTemplate with header and footer
    template = PageTemplate(id='test', frames=frame, onPage=add_header_footer)
    doc.addPageTemplates([template])
    
    # # Calculate row heights for each row in the table
    # row_heights = []
    # for row in table_data:
    #     # Ensure all elements in row are Paragraph objects
    #     if all(isinstance(cell, Paragraph) for cell in row):
    #         row_heights.append(max([p.wrap(doc.width, doc.height)[1] for p in row]))
    
    # # Remove the header row height
    # if row_heights:
    #     header_row_height = row_heights.pop(0)
    # else:
    #     header_row_height = 0
    
    # Build the PDF
    elements = [results_paragraph, table]
    doc.build(elements, onFirstPage=add_header_footer, onLaterPages=add_header_footer)

if __name__ == '__main__':
    # Example data (20 parameters with fictitious names and values)
    data = {
        'Parameter_Name': [
            'Parameter 1', 'Parameter 2', 'Parameter 3', 'Parameter 4', 'Parameter 5',
            'Parameter 6', 'Parameter 7', 'Parameter 8', 'Parameter 9', 'Parameter 10',
            'Parameter 11', 'Parameter 12', 'Parameter 13', 'Parameter 14', 'Parameter 15',
            'Parameter 16', 'Parameter 17', 'Parameter 18', 'Parameter 19', 'Parameter 20'
        ],
        'Expected_Value': [
            'Value 1', 'Expected Value 2', 'Expected Value 3', 'Expected Value 4', 'Expected Value 5',
            'Expected Value 6', 'Value 7', 'Expected Value 8', 'Expected Value 9', 'Expected Value 10',
            'Expected Value 11', 'Expected Value 12', 'Expected Value 13', 'Expected Value 14', 'Expected Value 15',
            'Expected Value 16', 'Value 17', 'Expected Value 18', 'Expected Value 19', 'Expected Value 20'
        ],
        'Your_INI_Value': [
            'Value 1', 'Your INI Value 2', 'Your INI Value 3', 'Your INI Value 4', 'Your INI Value 5',
            'Your INI Value 6', 'Value 7', 'Your INI Value 8', 'Example of large data for a cell, and autoadjust for Your INI Value 9', 'Your INI Value 10',
            'Your INI Value 11', 'Your INI Value 12', 'Your INI Value 13', 'Your INI Value 14', 'Your INI Value 15',
            'Your INI Value 16', 'Value 17', 'Your INI Value 18', 'Your INI Value 19', 'Your INI Value 20'
        ]
    }
    # PDF filename with timestamp
    # pdf_filename = f"ordered_data_{time.strftime('%Y%m%d%H%M%S')}.pdf"
    pdf_filename = f"ordered_data.pdf"
    
    # Generate the PDF with ordered data
    generate_pdf(data, pdf_filename)
    print(pdf_filename)

任何解决方案??

enter image description here

我注意到,因为每个单元格该行是一个 Paragraph 对象,它需要设置该对象的 Fontsize,所以我基本上修改了这个变量“styleN”,所以现在它可以工作:

# Define a custom style for table cells
styleN = ParagraphStyle(
    name='CellStyle',
    parent=styles['Normal'],
    fontName='Times-Roman',
    fontSize=12,  # Set the desired font size here for the cells
    leading=12
)

说的对,问题出在将每个单元格内容包装在 Paragraph 对象中,但没有为这些段落设置字体大小。找到了一种通过修改 styleN 来解决此问题的有效方法。

以下是改进后的代码,并附带解释:

import time
from reportlab.lib.pagesizes import landscape, letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, PageTemplate, Frame
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle

# Function to add header and footer
def add_header_footer(canvas, doc):
    canvas.saveState()
    # Header
    header_text = "Report Header"
    canvas.setFont('Helvetica-Bold', 14)
    text_width = canvas.stringWidth(header_text, 'Helvetica-Bold', 14)
    canvas.drawCentredString(doc.pagesize[0] / 2, doc.pagesize[1] - 80, header_text)

    # Footer
    footer_text = f"Page {doc.page}"
    canvas.setFont('Helvetica', 10)
    text_width = canvas.stringWidth(footer_text, 'Helvetica', 10)
    canvas.drawString((doc.pagesize[0] - text_width) / 2, 40, footer_text)
    canvas.restoreState()

# Function to generate PDF with ordered data in a table format
def generate_pdf(data, pdf_filename):
    # Create the document
    doc = SimpleDocTemplate(pdf_filename, pagesize=landscape(letter), topMargin=1.5*inch)

    # Define the table data
    table_data = [['Parameter Name', 'Expected Value', 'Your INI Value']]

    # Get the sample style sheet
    styles = getSampleStyleSheet()

    # Define a custom style for table cells with desired font size
    styleN = ParagraphStyle(
        name='CellStyle',
        parent=styles['Normal'],
        fontName='Times-Roman',
        fontSize=12,  # Set the desired font size here
        leading=14 # Add leading for better readability
    )

    # Add "Results for the Ini file:" text as a paragraph before the table
    results_text = "Results for the Ini file:"
    results_paragraph = Paragraph(results_text, styles['Heading2'])

    # Calculate the height of the results paragraph
    results_paragraph_height = results_paragraph.wrap(doc.width, doc.topMargin)[1]

    # Populate the table with data, using the defined style for cells
    for i in range(len(data['Parameter_Name'])):
        row = [
            Paragraph(data['Parameter_Name'][i], styleN),
            Paragraph(data['Expected_Value'][i], styleN),
            Paragraph(data['Your_INI_Value'][i], styleN)
        ]
        table_data.append(row)

    # Create the table
    table = Table(table_data, colWidths=[2.5 * inch, 3.0 * inch, 3.0 * inch], repeatRows=1)

    # Add style to the table
    style = TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 14),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 8),
        ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
        ('GRID', (0, 0), (-1, -1), 1, colors.black),
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),
    ])
    table.setStyle(style)

    # Apply conditional styling for rows where values differ
    for i in range(1, len(table_data)): 
        expected_value = data['Expected_Value'][i-1]
        your_ini_value = data['Your_INI_Value'][i-1]
        if expected_value != your_ini_value:
            table.setStyle(TableStyle([
                ('BACKGROUND', (0, i), (-1, i), colors.red),
                ('TEXTCOLOR', (0, i), (-1, i), colors.white)
            ]))

    # Define the frame for the page template
    frame = Frame(doc.leftMargin, doc.bottomMargin + 5 + results_paragraph_height, 
                  doc.width, doc.height - 5 - results_paragraph_height, id='normal')

    # Add the PageTemplate with header and footer
    template = PageTemplate(id='test', frames=frame, onPage=add_header_footer)
    doc.addPageTemplates([template])

    # Build the PDF
    elements = [results_paragraph, table]
    doc.build(elements, onFirstPage=add_header_footer, onLaterPages=add_header_footer)

if __name__ == '__main__':
    # ... (rest of your code remains the same)

解释:

  1. ParagraphStyle for Cells: 我们创建了一个名为 styleN 的自定义 ParagraphStyle ,它继承自 styles['Normal'] 并设置了所需的 fontSize
  2. Applying the Style: 在创建 Paragraph 对象来填充表格单元格时,我们使用了这个 styleN 来确保每个单元格都具有指定的字体大小。
  3. Removing Redundant Styles: TableStyle 中删除了 'FONTNAME' 'FONTSIZE' ,因为我们现在在 ParagraphStyle 中处理字体样式。

通过这种方式,的表格单元格现在将使用在 styleN 中定义的字体大小,并且可以根据需要轻松调整。

标签:python,python-3.x,reportlab
From: 78795145

相关文章

  • Python 和 ReportLab。如何更改 Table 对象中的字体和字体大小?
    我需要更改表对象(ReportLab)中特定单元格的文本样式。据我了解,它可以通过TableStayle类完成,但我找不到示例。my_table=Table(data,colWidths=column_widths,rowHeights=row_heghts)my_colour_black=[0,0,0]my_colour_ligt_gray=[0.8,0.8,0.8]my_colour_gra......
  • 如何在Python中查找字符串中所有出现的子字符串,同时忽略某些字符?
    我想找到所有出现的子字符串,同时忽略某些字符。我怎样才能在Python中做到这一点?示例:long_string='thisisat`es"t.Doesthetestwork?'small_string="test"chars_to_ignore=['"','`']print(find_occurrences(long_string,small_string......
  • 如何让 python 为我提供正确的函数解决方案并根据符号更改零的格式?
    我正在尝试制作一个二次公式计算器。一切看起来都很好,但是,当我输入一些函数(例如x^2-2x+1和x^2+2x+1)时,当它们有不同的解决方案时,它会给我相同的零。我遇到的另一个问题是,在代码的末尾,format_solution(root),我希望它能够根据符号改变格式。例如,如果零是(x-1),我不希望它输入(......
  • PYTHON 代码执行错误 - 冻结 importlib._bootstrap>(1165)_find_and_load()?
    在MACOS10.15(CATALINA)上执行此PYTHON代码时出现以下错误。我正在使用IDLEShell编写PYTHON3.11。Python3.11.4(v3.11.4:d2340ef257,Jun 62023,19:15:51)[Clang13.0.0(clang-1300.0.29.30)]ondarwinType"help","copyright","credits"o......
  • 如何修复我的 python flet 音频可视化工具在终端中不出现错误的情况下无法工作
    标题:音频播放,但频谱可视化未在FletAudioVisualizer中显示我正在使用Flet框架开发音频可视化器。声音播放正确,但频谱可视化未显示。这是我的代码:importfletasftimportnumpyasnpfrompydubimportAudioSegmentfrompydub.utilsimportmake_chunksimportm......
  • 使用隐式 __init__ 方法向 python 数据类添加显式 dunder 方法
    我在python中创建了一个数据类,选择使用隐式定义的__init__方法。然后,我尝试显式重写比较dunder方法之一(__eq__)。因为没有明确的__init__方法,所以'this'参数不存在,所以python抛出了一个NameError。为了做到这一点,即使自动生成的很好,我是否也必......
  • 84 我正在使用 Python 开发 selenium 自动化项目。我收到错误 .NoSuchElementExceptio
    场景是这样的,我将打开一个网页,在使用selenium单击该网页后,它会要求位置访问权限,屏幕上会出现一堆按钮,我正在尝试定位其中一个按钮,但即使正确给出了Xpath地址,我得到.NoSuchElementException:错误能够单击目标按钮你正在使用Selenium在Python中开发自动化项目,并遇到......
  • 在Python 3中删除两个指定字符串之间的字符串
    我正在从事一个NLP项目,该项目要求我从一段文本中删除计算机代码。代码包含在标签<pre><code>和</code></pre>之间。现在我可以做一个简单的正则表达式匹配,但我想概括这个函数,以便它可以删除任何两个指定字符串之间的文本,即使它们是嵌套的。例如,如果我有一个......
  • Azure Open AI - Python 和 Java API 之间 gpt4o 的结果截然不同
    我使用Java和PythonAPI对AzureOpenAI进行相同的调用,但收到截然不同的结果:相同的系统提示相同的用户提示适用于Java和Python的azureai包的相同(最新)版本尽管输入的用户和系统提示完全相同,但响应却非常不同-python提示是“正确的”并......
  • leetcode 输出错误? (Python)
    我的VSCode/本地终端给出了[1,4,1,5,1,6]的正确输出,但不知何故leetcode给了我完全不同的输出。我在这里错过了什么吗?这怎么可能?顺便说一下,这是wigglesort2将我的本地代码复制粘贴到leetcode中给出了不同的输出数组很难在没有看到你的代码的情况下......