使用Python PIL生成PDF时内存错误的修复方案咨询
解决大量图片生成PDF时的Memory Error问题
看起来你遇到的核心问题是内存耗尽:你的代码把所有生成的标签图片都存在imlist列表里,当处理大量图片时,内存被这些PIL Image对象占满,引发MemoryError。而报错里的AttributeError只是内存不足导致图片加载失败的次生问题,根源还是内存占用过高。
下面是针对性的解决方案,修改后的代码可以支持处理任意数量的图片,同时保持内存占用在合理水平:
核心思路
不再一次性把所有图片存在内存里,而是逐个生成标签并保存为单个临时PDF文件,最后用PDF合并工具把这些临时文件合并成一个最终PDF。这样内存里每次只需要处理一张图片,不会随着图片数量线性增长。
修改后的代码
首先需要安装PyPDF2库(用于合并PDF):
pip install PyPDF2
替换你的代码为以下版本:
from PIL import Image, ImageDraw, ImageFont import textwrap import pandas import datetime import os from tkinter import filedialog from tqdm import tqdm_gui from PyPDF2 import PdfMerger import tempfile datenow = datetime.datetime.now() pdate = datenow.strftime("%d-%m-%y") y = 0 excel = filedialog.askopenfilename(initialdir="/", title="Select A File", filetypes=(('Excel File', '*.xlsx'),('all files', '*.*'))) save_dir = filedialog.askdirectory() # 创建自动清理的临时目录,存储单个页面PDF with tempfile.TemporaryDirectory(dir=save_dir) as temp_dir: df = pandas.read_excel(excel) merger = PdfMerger() # 初始化PDF合并器 # 遍历Excel数据,生成标签 for data in tqdm_gui(df.values, desc="Generating labels..."): name, no = data x = 0 while x < no: x += 1 y += 1 LH = len(name) # 根据名称长度调整字体大小和换行宽度 if LH < 32: font_size = 70 wrap_width = 16 else: font_size = 53 wrap_width = 22 name_upper = name.upper() wrapped_text = textwrap.wrap(name_upper, width=wrap_width) MAX_W, MAX_H = 800, 592 # 加载模板图片并绘制文本 im = Image.open('TFC.jpg') draw = ImageDraw.Draw(im) font = ImageFont.truetype('OpenSans-ExtraBold.ttf', font_size) current_h, line_pad = 155, -5 for line in wrapped_text: w, h = draw.textsize(line, font=font) draw.text(((MAX_W - w) / 2, current_h), line, font=font) current_h += h + line_pad # 保存当前标签为单个PDF,然后关闭图片释放内存 temp_pdf = os.path.join(temp_dir, f"temp_label_{y}.pdf") im.save(temp_pdf, save_all=True) im.close() # 将临时PDF添加到合并队列 merger.append(temp_pdf) # 生成最终的合并PDF final_pdf_path = os.path.join(save_dir, f"{pdate} TF {y}.pdf") merger.write(final_pdf_path) merger.close() os.startfile(save_dir)
关键优化点
- 内存控制:每个标签生成后立即保存为PDF并关闭图片对象,释放内存,避免累积所有图片
- 临时文件自动清理:使用
tempfile.TemporaryDirectory,程序结束后会自动删除所有临时PDF文件,无需手动清理 - 代码健壮性:修复了原代码中
name = name.upper()修改原始数据的问题,改用独立变量存储大写名称 - 进度提示优化:给
tqdm_gui添加了描述文本,更直观了解生成进度
额外优化建议
如果想进一步提升性能,可以提前加载固定资源(模板图片和字体),避免重复IO操作:
# 在循环开始前提前加载模板和字体 template_image = Image.open('TFC.jpg') font_cache = {} # 循环内替换图片加载和字体加载部分 im = template_image.copy() # 复制模板,避免修改原始图片 if font_size not in font_cache: font_cache[font_size] = ImageFont.truetype('OpenSans-ExtraBold.ttf', font_size) font = font_cache[font_size]
这样可以减少重复打开图片和字体文件的开销,提升处理大量图片时的速度。
内容的提问来源于stack exchange,提问作者Raja Monsingh Charles
相关产品推荐
相关产品推荐

