使用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
相关产品推荐
相关产品推荐

