最直接方案是用Pillow的save()批量合成PDF,但要求所有图片先convert("RGB");含透明通道或需压缩时选fitz;路径须用pathlib处理,DPI与尺寸不匹配会导致模糊。

用 Pillow 合成 PDF 最直接,但只支持 RGB 模式图片
直接用 Pillow 的 save() 方法批量保存为 PDF 是最轻量的方案,不需要额外 PDF 库。但它有个硬性限制:所有图片必须是 RGB 模式(比如 JPG、PNG),RGBA(带透明通道)或 LA 会报错 ValueError: image mode RGBA not supported。
实操建议:
- 先用
img.convert("RGB")统一转模式,RGBA图片会丢弃 alpha 通道(白底替代),如需保留透明效果,得换reportlab或fitz - 文件路径列表必须按目标顺序排序,
os.listdir()不保证顺序,改用sorted(glob.glob("*.jpg"))或sorted(Path(".").glob("*.png")) - 第一张图作为
pdf_path传给save(),其余传入save_all=True, append_images=[...]
from PIL import Image
import glob
<p>imgs = [Image.open(p).convert("RGB") for p in sorted(glob.glob("page_*.png"))]
if imgs:
imgs[0].save("output.pdf", save_all=True, append_images=imgs[1:])处理含透明通道或需要压缩的场景,选 fitz(PyMuPDF)
fitz 能原生支持 PNG 透明背景、指定页面尺寸、甚至嵌入压缩参数,适合对输出质量有要求的批量任务。但它不是纯 Python 实现,安装时可能触发编译(Windows 用户建议用 pip install PyMuPDF 官方 wheel)。
常见坑点:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
-
fitz.Page默认尺寸是 A4(595×842),若图片远大于此,会自动缩放并留白;用fitz.Rect手动设page.set_cropbox()可精准控制显示区域 - 直接
page.insert_image()插入 PNG 时,透明通道默认保留,但导出 PDF 后某些阅读器可能渲染为黑底——加参数keep_proportion=False, overlay=False并确保图片已转RGB更稳妥 - 大图合成时内存增长快,建议逐页写入而非全加载到内存:
doc.save("out.pdf", garbage=4, deflate=True)开启压缩
批量处理时路径和编码容易出错,优先用 pathlib + 显式编码声明
中文路径、空格、特殊符号在 open() 或 Image.open() 中极易引发 FileNotFoundError 或乱码,尤其在 Windows 上默认编码不一致。
正确做法:
- 统一用
Path对象操作路径:list(Path("扫描件/").glob("*.jpg")),避免字符串拼接 - 打开图片前检查存在性:
if img_path.exists() and img_path.suffix.lower() in {".jpg", ".jpeg", ".png"} - 如果原始文件名含中文且
PIL报OSError: cannot identify image file,尝试先用pathlib.Path(img_path).resolve().as_posix()转为正斜杠路径再传入
合成后 PDF 打不开或文字模糊?重点查 DPI 和图像尺寸
PDF 本身不存“分辨率”,但嵌入的图像像素尺寸和页面大小共同决定清晰度。如果合成后文字发虚、线条锯齿,大概率是图片物理尺寸太小,被拉伸填充 A4 页面所致。
验证与修复方法:
- 用
img.size查原始宽高(如(800, 1130)),再对比目标页面尺寸(A4 是595×842pt ≈595×842像素 @72dpi);若图片宽度仅 600px,放大到 A4 就必然模糊 - 合成前用
img.resize((1200, int(1200 * img.height / img.width)), Image.LANCZOS)提前升采样(仅限文字/线条图,照片慎用) -
fitz中可设zoom = 2提高插入精度:mat = fitz.Matrix(zoom, zoom),再传给page.insert_image(..., matrix=mat)
多图合成 PDF 看似简单,真正卡住的往往不是代码行数,而是模式转换、路径编码、DPI 匹配这三处细节——尤其当来源是手机拍照或扫描仪输出时,原始图像状态差异极大,别跳过检查步骤。


















