作者 by Doubt-Fact /
如题,简易的PDF与图片互相转换的工具。
安装包:
PDF2img setup.zip
绿色版:
pdf2img green.7z
代码(使用deepseek生成注释):
# 导入必要的库
import tkinter as tk # 用于创建GUI界面
from tkinter import filedialog, messagebox, ttk # filedialog用于文件选择,messagebox用于弹出消息框,ttk是tkinter的扩展
from PIL import Image # Python Imaging Library,用于处理图片
import os # 用于处理文件和目录路径
import fitz # PyMuPDF库,用于处理PDF文件
import glob # 用于查找符合特定模式的文件路径
import re # 正则表达式模块,用于字符串匹配和处理
# 定义主应用程序类
class FileConverterApp:
def __init__(self, root):
# 初始化应用程序
self.root = root # 主窗口
self.root.title("PDF-图片互转") # 设置窗口标题
self.root.geometry("900x400") # 设置窗口大小
# 尝试设置窗口图标
try:
self.root.iconbitmap("favicon.ico")
except:
pass # 如果图标文件不存在,忽略错误
# 创建主框架,用于放置其他控件
self.main_frame = ttk.Frame(root, padding="20")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 转换类型选择,使用StringVar来存储用户选择的转换类型
self.convert_type = tk.StringVar(value="pdf_to_image")
self.create_type_selector() # 创建转换类型选择器
# 文件选择区域,用于选择输入文件和输出路径
self.file_frame = ttk.Frame(self.main_frame)
self.file_frame.pack(fill=tk.X, pady=10)
self.input_path = tk.StringVar() # 存储输入文件路径
self.output_path = tk.StringVar() # 存储输出路径
# 按钮区域,用于放置转换按钮
self.btn_frame = ttk.Frame(self.main_frame)
self.btn_frame.pack(fill=tk.X, pady=10)
# 状态栏,用于显示当前状态
self.status = ttk.Label(root, text="准备就绪", anchor=tk.W)
self.status.pack(side=tk.BOTTOM, fill=tk.X)
self.create_widgets() # 创建其他控件
def create_type_selector(self):
# 创建转换类型选择器
types = [
("PDF 转 图片", "pdf_to_image"),
("图片 转 PDF", "image_to_pdf")
]
frame = ttk.LabelFrame(self.main_frame, text="选择转换类型")
frame.pack(fill=tk.X, pady=10)
# 创建单选按钮,让用户选择转换类型
for text, value in types:
ttk.Radiobutton(frame, text=text, variable=self.convert_type,
value=value, command=self.update_ui).pack(side=tk.LEFT, padx=10)
def create_widgets(self):
# 创建输入文件选择控件
ttk.Label(self.file_frame, text="输入文件:").pack(side=tk.LEFT)
self.input_entry = ttk.Entry(self.file_frame, textvariable=self.input_path, width=40)
self.input_entry.pack(side=tk.LEFT, padx=5)
self.input_button = ttk.Button(self.file_frame, text="浏览", command=self.select_input)
self.input_button.pack(side=tk.LEFT)
# 创建输出路径选择控件
ttk.Label(self.file_frame, text="输出路径:").pack(side=tk.LEFT, padx=(10, 0))
self.output_entry = ttk.Entry(self.file_frame, textvariable=self.output_path, width=40)
self.output_entry.pack(side=tk.LEFT, padx=5)
self.output_button = ttk.Button(self.file_frame, text="选择", command=self.select_output)
self.output_button.pack(side=tk.LEFT)
# 创建转换按钮
self.convert_btn = ttk.Button(self.btn_frame, text="开始转换", command=self.start_conversion)
self.convert_btn.pack(pady=10)
def update_ui(self):
# 根据用户选择的转换类型更新UI
conversion_type = self.convert_type.get()
if conversion_type == "image_to_pdf":
self.input_path.set("")
self.output_path.set("")
self.input_entry.config(state='disabled') # 禁用输入文件选择框
self.input_button.config(text="选择目录") # 修改按钮文本
else:
self.input_entry.config(state='normal') # 启用输入文件选择框
self.input_button.config(text="浏览") # 恢复按钮文本
def select_input(self):
# 选择输入文件或目录
conversion_type = self.convert_type.get()
if conversion_type == "image_to_pdf":
path = filedialog.askdirectory() # 选择目录
if path:
self.input_path.set(path)
else:
filetypes = [("PDF文件", "*.pdf")]
path = filedialog.askopenfilename(filetypes=filetypes) # 选择PDF文件
if path:
self.input_path.set(path)
if not self.output_path.get():
# 设置默认输出路径为输入文件所在目录
default_output = os.path.splitext(path)[0] + self.get_output_ext()
self.output_path.set(default_output)
def select_output(self):
# 选择输出路径
conversion_type = self.convert_type.get()
default_ext = self.get_output_ext()
if conversion_type == "image_to_pdf":
path = filedialog.asksaveasfilename(defaultextension=default_ext,
filetypes=[("PDF文件", "*.pdf")])
else:
path = filedialog.asksaveasfilename(defaultextension=default_ext,
filetypes=[(f"{default_ext[1:]}文件", f"*{default_ext}")])
if path:
self.output_path.set(path)
def get_output_ext(self):
# 获取输出文件的默认扩展名
conversion_type = self.convert_type.get()
if conversion_type == "pdf_to_image":
return "_images"
elif conversion_type == "image_to_pdf":
return ".pdf"
return ""
def start_conversion(self):
# 开始转换
conversion_type = self.convert_type.get()
input_path = self.input_path.get()
output_path = self.output_path.get()
if not input_path or not output_path:
messagebox.showwarning("错误", "请选择输入文件和输出路径")
return
try:
self.status.config(text="正在转换,请稍候...")
if conversion_type == "pdf_to_image":
self.pdf_to_image(input_path, output_path) # 调用PDF转图片函数
elif conversion_type == "image_to_pdf":
self.image_to_pdf(input_path, output_path) # 调用图片转PDF函数
self.status.config(text="转换完成!")
messagebox.showinfo("成功", "转换完成!")
except Exception as e:
self.status.config(text="转换失败")
messagebox.showerror("错误", f"转换失败:{str(e)}")
def pdf_to_image(self, pdf_path, output_folder):
# 将PDF文件转换为图片
try:
doc = fitz.open(pdf_path) # 打开PDF文件
if not os.path.exists(output_folder):
os.makedirs(output_folder) # 如果输出目录不存在,创建它
for page_num in range(len(doc)):
page = doc.load_page(page_num) # 加载PDF的每一页
pix = page.get_pixmap(dpi=300) # 将页面转换为图片,设置DPI为300
output_path = os.path.join(output_folder, f"page_{page_num + 1}.jpg") # 设置输出图片路径
pix.save(output_path) # 保存图片
finally:
doc.close() # 关闭PDF文件
def image_to_pdf(self, image_folder, pdf_path):
# 将图片转换为PDF文件
# 获取所有jpg和png文件并自然排序
image_files = glob.glob(os.path.join(image_folder, "*.jpg")) + glob.glob(os.path.join(image_folder, "*.png"))
# 自然排序逻辑,确保文件按数字顺序排列
image_files.sort(key=lambda x: [
int(text) if text.isdigit() else text.lower()
for text in re.split(r'(\d+)', os.path.basename(x))
])
images = []
for img_file in image_files:
try:
im = Image.open(img_file) # 打开图片
if im.mode == 'RGBA':
im = im.convert('RGB') # 如果图片是RGBA模式,转换为RGB模式
images.append(im) # 将图片添加到列表中
except Exception as e:
print(f"跳过文件 {img_file}: {str(e)}") # 如果图片无法打开,跳过并打印错误信息
if images:
# 将所有图片保存为一个PDF文件
images[0].save(pdf_path, save_all=True, append_images=images[1:])
# 程序入口
if __name__ == "__main__":
root = tk.Tk() # 创建主窗口
app = FileConverterApp(root) # 创建应用程序实例
root.mainloop() # 进入主事件循环
评论已关闭