You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ReportLab生成PDF多行表单字段换行显示异常求助

ReportLab生成PDF多行表单字段无法自动换行的解决方法

问题现象

使用ReportLab创建带表单字段的PDF时,其他功能正常,但多行表单字段的文本无法自动换行显示——只有点击字段时才能看到全部内容;修改字段内容后,文本又能正常换行。

无效尝试

曾编写脚本在PDF生成后打开并修改字段(添加句号),但未解决问题。

解决方法

通过pdfrw库设置PDF的NeedAppearances标志,强制PDF阅读器渲染字段的外观,让多行文本正确显示。


1. 文本自动换行处理函数

手动为文本添加换行符,配合表单字段实现换行效果:

def insert_line_breaks(text, max_chars):
    words = text.split()
    processed_text = ""
    current_line = ""

    for word in words:
        if len(current_line) + len(word) <= max_chars:
            current_line += word + " "
        else:
            if current_line:
                # 将当前行添加到处理后的文本,使用正确的换行符
                processed_text += current_line.rstrip() + "\n"
            current_line = word + " "  # 用当前单词开始新行

    # 添加最后一行(如果不为空)
    if current_line:
        processed_text += current_line

    return processed_text

2. 生成带表单字段的PDF主函数

使用ReportLab创建PDF并配置多行表单字段:

def create_pdf_with_form_fields_from_json(json_data_path):
    try:
        # 读取并解析JSON数据
        with open(json_data_path, 'r') as file:
            form_fields = json.load(file)

        # 在页面上绘制图片的函数
        def draw_images(image_paths_list, start_height, page_width):
            print("Drawing images...")
            margin = 0.5 * inch
            lower_pictures_by = 1.2 * inch
            reduce_box_height_by = 0.48 * inch
            box_size = (page_width - 3 * margin) / 2
            box_height = box_size - reduce_box_height_by
            x_start = margin
            y_start = start_height - lower_pictures_by - box_height
            gap_between_boxes = 0.25 * inch

            for i, path in enumerate(image_paths_list):
                if i >= 4:  # 限制最多4张图片
                    break

                x_position = x_start + (i % 2) * (box_size + gap_between_boxes)
                y_position = y_start - (i // 2) * (box_height + gap_between_boxes)
                c.setStrokeColor(colors.lightgrey)
                c.rect(x_position, y_position, box_size, box_height)
                c.drawImage(path, x_position, y_position, width=box_size, height=box_height, preserveAspectRatio=True, mask='auto')
            print("Images drawn.")

        # 清理kunde_entry,移除特殊字符
        kunde_name = re.sub(r'[^A-Za-z0-9 ]+', '', kunde_entry.get())

        # 提取客户编号和送货地址
        kunde_info = kunde_entry.get().split(',')
        if len(kunde_info) >= 2:
            kundenummer = kunde_info[0].strip()  # 客户编号
            kundenavn = kunde_info[1].strip()    # 客户名称
        else:
            messagebox.showerror("Error", "Invalid Kundenummer and name format.")
            return
        print("Starting PDF generation...")

        leveringsadresse = leveringsadresse_entry.get().strip()

        # 构造PDF文件名
        base_filename = f"{kundenummer}_{leveringsadresse}"
        # 截取文件名确保不超过30个字符
        sliced_filename = base_filename[:30] if len(base_filename) > 30 else base_filename
        pdf_filename = f"F://1. NYT FÆLLES SCANPIPE//ScanPipe_Logistik//Leveringsbeskrivelser//{sliced_filename}.pdf"

        # 设置画布
        c = canvas.Canvas(pdf_filename, pagesize=letter)
        page_width, page_height = letter

       # 处理文本,自动添加换行符
        max_line_length = 80  # 根据PDF布局调整此数值
        leveringsbeskrivelse = insert_line_breaks(leveringsbeskrivelse_entry.get("1.0", "end-1c"), max_line_length)
        srv_leveringsbeskrivelse = insert_line_breaks(srv_leveringsbeskrivelse_entry.get("1.0", "end-1c"), max_line_length)
        alarm = insert_line_breaks(alarm_entry.get("1.0", "end-1c"), max_line_length)

        print("Generating PDF for:", kundenummer, kundenavn)

        # 初始化当前页面
        current_page = 1

        formfieldsheigth = 600
        draw_images(image_paths, formfieldsheigth, page_width)

        # 根据JSON数据添加表单字段和文本框
        for field in form_fields:
            field_name = field.get('title', 'Untitled')  # 如果没有title,默认使用'Untitled'
            field_content = field.get('content', '')  # 如果没有content,默认空字符串
            x, y, width, height = field['x0'], field['y0'], field['width'], field['height']
            subtype = field.get('Subtype', 'Formfield')  # 如果没有Subtype,默认'Formfield'
            field_page = field.get('page')

            if field_name in ["Kundenavn og nummer", "Kundenavn og nummer_SVR"]:
                field_content = kunde_name
            elif field_name in ["By", "By_SVR"]:
                field_content = by_entry.get()
            elif field_name in ["Leveringsadresse", "Leveringsadresse_SVR"]:
                field_content = leveringsadresse
            elif field_name in ["Postnummer", "Postnummer_SVR"]:
                field_content = postnr_entry.get()
            elif field_name in ["Kontaktperson", "Kontaktperson_SVR"]:
                field_content = kontaktperson_entry.get()
            elif field_name in ["Tlf", "Tlf_SVR"]:
                field_content = telefon_entry.get()
            elif field_name in ["Alarm", "Alarm_SVR"]:
                field_content = alarm
            elif field_name == "Leveringsbeskrivelse":
                field_content = leveringsbeskrivelse
            elif field_name == "Leveringsbeskrivelse_SVR":
                field_content = srv_leveringsbeskrivelse

            # 检查字段所在页面是否与当前页面不同
            if field_page != current_page:
                # 添加新页面并更新current_page
                c.showPage()
                current_page = field['page']

            if subtype == 'Formfield':
                # 创建多行表单字段
                c.acroForm.textfield(name=field_name, tooltip=field_name,
                                     x=x, y=y, width=width, height=height, 
                                     borderColor=colors.black, fillColor=colors.white,
                                     textColor=colors.black, forceBorder=True, maxlen=500,
                                     fieldFlags='multiline', value=field_content)
                
            elif subtype == 'Textbox':
                # 设置填充色为矢车菊蓝,边框色为黑色
                c.setFillColor(colors.cornflower)
                c.setStrokeColor(colors.black)

                # 绘制带黑色边框、填充矢车菊蓝的矩形
                c.rect(x, y, width, height, stroke=1, fill=1)

                # 设置文本颜色为黑色
                c.setFillColorRGB(0, 0, 0)

                # 在框内绘制文本
                c.drawString(x + 2, y + height / 2, field_content)  # 调整x坐标以适应边框


        draw_images(srv_image_paths, formfieldsheigth, page_width)

        # 完成PDF生成
        c.save()
        print("PDF generated successfully:", pdf_filename)
    except Exception as e:
        print("An error occurred while generating the PDF:", e)

3. 设置PDF的NeedAppearances标志函数

使用pdfrw修改已生成的PDF,强制阅读器渲染字段外观:

def update_pdf_need_appearances_flag(pdf_path):
    try:
        pdf = pdfrw.PdfReader(pdf_path)
        if "/AcroForm" in pdf.Root:
            pdf.Root.AcroForm.update(pdfrw.PdfDict(NeedAppearances=pdfrw.PdfObject('true')))
        else:
            print("No AcroForm found in PDF. This PDF might not contain form fields.")

        pdfrw.PdfWriter().write(pdf_path, pdf)
        print("Updated NeedAppearances flag in PDF:", pdf_path)
    except Exception as e:
        print("An error occurred while updating the PDF:", e)

内容的提问来源于stack exchange,提问作者Jeppe Silas Nygaard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 01:09:51