使用FPDF插入JpegImageFile到PDF遇TypeError问题求助
问题
尝试使用FPDF库在PDF中展示多张图片,这些图片以JpegImageFile对象的形式存储在嵌套列表all_pics中。调用self.image方法时出现错误:TypeError: unhashable type: 'JpegImageFile',尝试遍历列表也提示JPEGFileType不可迭代,请问如何将这些图片成功插入PDF?
使用的代码:
from fpdf import FPDF class FPDF(FPDF): def print_images (self, num, all_pics): for img in all_pics[num]: self.image(img, x = None, y = None, w = 0, h = 0, type = '', link = '') def main_pdf(num, all_pics, bearing_data): if num != 'Select number': export = st.button("Export Report") if export: pdf=FPDF() for i in range(num): pdf.print_images(i, all_pics)
报错回溯信息:
TypeError: unhashable type: 'JpegImageFile' Traceback: File "C:\Users\dksojfab\Anaconda3\envs\Julias_works\lib\site-packages\streamlit\scriptrunner\script_runner.py", line 554, in _run_script exec(code, module.__dict__) File "form_main.py", line 16, in <module> conv.main_pdf(num, all_pics, bearing_info) File "C:\Users\dksojfab\OneDrive - Alfa Laval\Documents\Python Scripts\convert_to_pdf.py", line 106, in main_pdf pdf.print_images(i, all_pics) File "C:\Users\dksojfab\OneDrive - Alfa Laval\Documents\Python Scripts\convert_to_pdf.py", line 94, in print_images self.image(img, x = None, y = None, w = 0, h = 0, type = '', link = '') File "C:\Users\dksojfab\Anaconda3\envs\Julias_works\lib\site-packages\fpdf\fpdf.py", line 150, in wrapper return fn(self, *args, **kwargs) File "C:\Users\dksojfab\Anaconda3\envs\Julias_works\lib\site-packages\fpdf\fpdf.py", line 960, in image if not name in self.images:
解决方案
错误原因
FPDF的image()方法不直接支持PIL的JpegImageFile对象:
- 该方法默认接受文件路径字符串或二进制文件对象,而非PIL图片实例
- 报错中的
if not name in self.images是因为FPDF会用第一个参数作为键缓存图片,而JpegImageFile是不可哈希类型,无法作为字典键使用
解决方法
以下两种方法可直接实现需求:
方法1:将JpegImageFile转为BytesIO二进制流(推荐,无需写入磁盘)
引入io模块,把PIL图片对象转为二进制流后传入image(),同时指定图片类型为JPG:
from fpdf import FPDF import io from PIL import Image # 确保导入PIL class FPDF(FPDF): def print_images(self, num, all_pics): # 先确认all_pics[num]是可迭代的图片列表 if not isinstance(all_pics[num], (list, tuple)): raise ValueError(f"all_pics[{num}]不是可迭代的图片列表") for img in all_pics[num]: # 将JpegImageFile转为BytesIO img_bytes = io.BytesIO() img.save(img_bytes, format='JPEG') img_bytes.seek(0) # 重置文件指针到开头 # 传入二进制流,指定类型为JPG self.image(img_bytes, x=None, y=None, w=0, h=0, type='JPG', link='') def main_pdf(num, all_pics, bearing_data): if num != 'Select number': export = st.button("Export Report") if export: pdf=FPDF() for i in range(num): pdf.add_page() # 注意:需要先添加页面才能插入图片 pdf.print_images(i, all_pics) pdf.output("report.pdf") # 别忘了输出PDF文件
方法2:将JpegImageFile保存为临时文件(适合需要持久化图片的场景)
使用tempfile模块创建临时文件,写入图片后传入文件路径:
from fpdf import FPDF import tempfile from PIL import Image class FPDF(FPDF): def print_images(self, num, all_pics): if not isinstance(all_pics[num], (list, tuple)): raise ValueError(f"all_pics[{num}]不是可迭代的图片列表") for img in all_pics[num]: with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file: img.save(temp_file, format='JPEG') temp_path = temp_file.name # 传入临时文件路径 self.image(temp_path, x=None, y=None, w=0, h=0, type='JPG', link='') # 可选:删除临时文件 import os os.unlink(temp_path) def main_pdf(num, all_pics, bearing_data): if num != 'Select number': export = st.button("Export Report") if export: pdf=FPDF() for i in range(num): pdf.add_page() pdf.print_images(i, all_pics) pdf.output("report.pdf")
额外注意点
- 原代码缺少
add_page()调用:FPDF默认没有页面,必须先添加页面才能插入内容 - 确认
all_pics的结构:确保all_pics[i]是可迭代的图片列表,避免"JPEGFileType不可迭代"的错误
内容的提问来源于stack exchange,提问作者jufi98
相关产品推荐
相关产品推荐

