[Python] 纯文本查看 复制代码
import os
import re
import tkinter as tk
from tkinter import filedialog, ttk, scrolledtext
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF
import threading
class SVGToPDFConverter:
def __init__(self, root):
self.root = root
self.root.title("SVG到PDF批量转换器")
self.root.geometry("700x500")
self.root.resizable(True, True)
# 设置中文字体支持
self.style = ttk.Style()
self.style.configure("TLabel", font=("SimHei", 10))
self.style.configure("TButton", font=("SimHei", 10))
self.style.configure("TEntry", font=("SimHei", 10))
# 源文件夹路径
self.source_dir = tk.StringVar()
# 输出文件夹路径
self.output_dir = tk.StringVar()
self.create_widgets()
def create_widgets(self):
# 创建主框架
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 源文件夹选择
ttk.Label(main_frame, text="SVG源文件夹:").grid(row=0, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.source_dir, width=50).grid(row=0, column=1, pady=5)
ttk.Button(main_frame, text="浏览...", command=self.select_source_dir).grid(row=0, column=2, padx=5, pady=5)
# 输出文件夹选择
ttk.Label(main_frame, text="PDF输出文件夹:").grid(row=1, column=0, sticky=tk.W, pady=5)
ttk.Entry(main_frame, textvariable=self.output_dir, width=50).grid(row=1, column=1, pady=5)
ttk.Button(main_frame, text="浏览...", command=self.select_output_dir).grid(row=1, column=2, padx=5, pady=5)
# 转换按钮
ttk.Button(main_frame, text="开始转换", command=self.start_conversion).grid(row=2, column=0, columnspan=3,
pady=10)
# 进度条
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
self.progress_bar.grid(row=3, column=0, columnspan=3, sticky=tk.EW, pady=5)
# 状态文本框
ttk.Label(main_frame, text="转换状态:").grid(row=4, column=0, sticky=tk.NW, pady=5)
self.status_text = scrolledtext.ScrolledText(main_frame, width=60, height=15, wrap=tk.WORD)
self.status_text.grid(row=4, column=1, columnspan=2, pady=5, sticky=tk.NSEW)
# 配置网格权重,使控件可以随窗口大小调整
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(4, weight=1)
def select_source_dir(self):
dir_path = filedialog.askdirectory(title="选择SVG源文件夹")
if dir_path:
self.source_dir.set(dir_path)
def select_output_dir(self):
dir_path = filedialog.askdirectory(title="选择PDF输出文件夹")
if dir_path:
self.output_dir.set(dir_path)
def log(self, message):
"""在状态文本框中添加日志信息"""
self.status_text.insert(tk.END, message + "\n")
self.status_text.see(tk.END) # 滚动到最后一行
def preprocess_svg(self, svg_path, temp_path, default_color="#000000"):
"""预处理SVG文件,替换url()颜色引用"""
try:
with open(svg_path, 'r', encoding='utf-8') as f:
svg_content = f.read()
# 替换颜色引用为默认颜色
svg_content = re.sub(r'url\(#.*?\)', default_color, svg_content)
with open(temp_path, 'w', encoding='utf-8') as f:
f.write(svg_content)
return True
except Exception as e:
self.log(f"SVG预处理失败: {str(e)}")
return False
def svg_to_pdf(self, svg_file_path, pdf_file_path, use_preprocessing=True):
"""将单个SVG文件转换为PDF文件,可选择预处理"""
temp_svg = None
try:
# 如果需要预处理且包含url颜色引用
if use_preprocessing:
with open(svg_file_path, 'r', encoding='utf-8') as f:
content = f.read()
if 'url(#)' in content:
temp_svg = f"temp_{os.path.basename(svg_file_path)}"
if not self.preprocess_svg(svg_file_path, temp_svg):
return False
svg_path = temp_svg
else:
svg_path = svg_file_path
else:
svg_path = svg_file_path
drawing = svg2rlg(svg_path)
renderPDF.drawToFile(drawing, pdf_file_path)
return True
except Exception as e:
self.log(f"转换错误: {str(e)}")
return False
finally:
# 清理临时文件
if temp_svg and os.path.exists(temp_svg):
try:
os.remove(temp_svg)
except:
pass
def batch_convert(self):
"""批量转换函数,在单独线程中运行"""
svg_dir = self.source_dir.get()
pdf_dir = self.output_dir.get()
# 验证文件夹路径
if not svg_dir or not os.path.isdir(svg_dir):
self.log("请选择有效的SVG源文件夹")
self.progress_var.set(0)
return
if not pdf_dir or not os.path.isdir(pdf_dir):
self.log("请选择有效的PDF输出文件夹")
self.progress_var.set(0)
return
# 获取所有SVG文件
svg_files = []
for filename in os.listdir(svg_dir):
if filename.lower().endswith('.svg') and os.path.isfile(os.path.join(svg_dir, filename)):
svg_files.append(filename)
if not svg_files:
self.log("在源文件夹中未找到任何SVG文件")
self.progress_var.set(0)
return
total_files = len(svg_files)
self.log(f"找到 {total_files} 个SVG文件,开始转换...")
# 逐个转换文件
success_count = 0
for i, filename in enumerate(svg_files, 1):
svg_path = os.path.join(svg_dir, filename)
pdf_filename = os.path.splitext(filename)[0] + '.pdf'
pdf_path = os.path.join(pdf_dir, pdf_filename)
try:
# 先尝试直接转换
success = self.svg_to_pdf(svg_path, pdf_path, use_preprocessing=False)
# 如果失败,尝试使用预处理
if not success:
self.log(f"尝试使用预处理转换 {filename}...")
success = self.svg_to_pdf(svg_path, pdf_path, use_preprocessing=True)
if success:
success_count += 1
self.log(f"成功转换: {filename} -> {pdf_filename}")
else:
self.log(f"无法转换: {filename}")
except Exception as e:
self.log(f"处理 {filename} 时出错: {str(e)}")
# 更新进度条
progress = (i / total_files) * 100
self.progress_var.set(progress)
self.root.update_idletasks()
self.log(f"转换完成!成功转换 {success_count}/{total_files} 个文件")
self.progress_var.set(100)
def start_conversion(self):
"""开始转换,在新线程中运行以避免界面冻结"""
# 清空之前的日志
self.status_text.delete(1.0, tk.END)
self.progress_var.set(0)
# 在新线程中运行转换,防止界面冻结
conversion_thread = threading.Thread(target=self.batch_convert)
conversion_thread.daemon = True # 当主线程退出时,子线程也退出
conversion_thread.start()
if __name__ == "__main__":
root = tk.Tk()
app = SVGToPDFConverter(root)
root.mainloop()