这是一款基于 Python 开发的图形化工具,可以批量将文件夹中的图片自动转换成 PDF 文件。支持多层文件夹递归查找图片,自动按图片尺寸合并生成长图,并按需分页生成多页 PDF。即使面对大尺寸图片也能轻松处理,适合漫画、文档扫描等批量整理使用。
主要特点:

wechat_2025-05-20_093350_468.png (17.21 KB, 下载次数: 0)
下载附件
2025-5-20 09:46 上传

wechat_2025-05-20_093410_189.png (21.08 KB, 下载次数: 0)
下载附件
2025-5-20 09:46 上传

wechat_2025-05-20_095628_222.png (61.53 KB, 下载次数: 0)
下载附件
2025-5-20 09:56 上传
附上代码
[Python] 纯文本查看 复制代码import os
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
from PIL import Image
from pathlib import Path
import threading
import queue
MAX_HEIGHT = 65000 # PIL的最大高度限制
class ProgressWindow:
def __init__(self):
self.window = tk.Tk()
self.window.title("转换进度")
self.window.geometry("500x300")
# 窗口居中
screen_width = self.window.winfo_screenwidth()
screen_height = self.window.winfo_screenheight()
x = (screen_width - 500) // 2
y = (screen_height - 300) // 2
self.window.geometry(f"500x300+{x}+{y}")
# 进度显示文本框
self.text = tk.Text(self.window, wrap=tk.WORD, width=50, height=15)
self.text.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
# 进度条
self.progress = ttk.Progressbar(self.window, mode='indeterminate')
self.progress.pack(padx=10, pady=5, fill=tk.X)
# 取消按钮
self.cancel_button = ttk.Button(self.window, text="完成", command=self.window.destroy)
self.cancel_button.pack(pady=5)
self.cancel_button.state(['disabled'])
# 消息队列
self.queue = queue.Queue()
self.window.after(100, self.check_queue)
def check_queue(self):
try:
while True:
msg = self.queue.get_nowait()
if msg == "DONE":
self.progress.stop()
self.cancel_button.state(['!disabled'])
self.text.insert(tk.END, "\n转换完成!\n")
else:
self.text.insert(tk.END, msg + "\n")
self.text.see(tk.END)
except queue.Empty:
self.window.after(100, self.check_queue)
def create_long_image(image_files, start_idx, end_idx):
# 打开指定范围的图片
images = [Image.open(f) for f in image_files[start_idx:end_idx]]
# 计算总高度和最大宽度
total_height = sum(img.height for img in images)
max_width = max(img.width for img in images)
# 创建新的长图
long_image = Image.new('RGB', (max_width, total_height), 'white')
# 垂直拼接图片
y_offset = 0
for img in images:
# 如果图片宽度小于最大宽度,居中放置
x_offset = (max_width - img.width) // 2
long_image.paste(img, (x_offset, y_offset))
y_offset += img.height
img.close()
return long_image
def get_pdf_name(input_folder, current_folder):
"""根据目录结构决定PDF文件名"""
relative_path = os.path.relpath(current_folder, input_folder)
# 如果当前文件夹就是输入文件夹,使用当前文件夹名
if relative_path == '.':
return os.path.basename(current_folder)
# 检查是否有子文件夹
has_subfolder_with_images = False
for root, dirs, files in os.walk(current_folder):
if root != current_folder: # 如果不是当前目录
# 检查这个子目录是否包含图片
for file in files:
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
has_subfolder_with_images = True
break
if has_subfolder_with_images:
break
# 如果没有包含图片的子文件夹,使用当前文件夹名
if not has_subfolder_with_images:
return os.path.basename(current_folder)
# 否则使用相对路径
return relative_path
def convert_folder_to_pdf(input_folder, output_folder, progress_queue):
try:
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 遍历输入文件夹中的所有子文件夹
for root, dirs, files in os.walk(input_folder):
# 跳过输出文件夹本身
if os.path.abspath(root) == os.path.abspath(output_folder):
continue
# 记录当前遍历的文件夹
with open("log.txt", "a", encoding="utf-8") as logf:
logf.write(f"遍历文件夹: {root}\n")
# 获取当前文件夹中的所有图片文件
image_files = []
for file in sorted(files):
if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".bmp")):
image_files.append(os.path.join(root, file))
if image_files:
try:
progress_queue.put(f"正在处理文件夹: {root}")
with open("log.txt", "a", encoding="utf-8") as logf:
logf.write(f"处理图片: {image_files}\n")
# 获取PDF文件名
pdf_name = get_pdf_name(input_folder, root)
pdf_filename = os.path.join(output_folder, f"{pdf_name}.pdf")
with open("log.txt", "a", encoding="utf-8") as logf:
logf.write(f"输出PDF路径: {pdf_filename}\n")
# 确保输出目录存在
os.makedirs(os.path.dirname(pdf_filename), exist_ok=True)
# 计算图片高度并分组
current_height = 0
start_idx = 0
image_groups = []
# 打开所有图片以获取高度
images_heights = [Image.open(f).height for f in image_files]
# 根据高度限制分组
for i, height in enumerate(images_heights):
if current_height + height > MAX_HEIGHT:
# 当前组已满,添加到groups
image_groups.append((start_idx, i))
start_idx = i
current_height = height
else:
current_height += height
# 添加最后一组
if start_idx
成品:https://www.123865.com/s/tMQnTd-DBzsH
最后,请各位大佬动动小手,送送免费的评分,赚点评分消违规,万分感谢!