Python编写的小工具-PDF拼接高清图片
来源:https://www.52pojie.cn/thread-2108169-1-1.html本人从事制药行业法规事务工作,业余时间运营着一个公众号,经常需要把国家药品监督管理局发布的PDF版法规转成图片通过公众号进行分享。直接使用Windows的截图工具,或者Adobe Acrobat Pro(专业版)虽然都可以成功转换,但是清晰度都不尽如人意,也无法自行指定导出的清晰度,也无法实现自行定义…
来源:https://www.52pojie.cn/thread-2108169-1-1.html
本人从事制药行业法规事务工作,业余时间运营着一个公众号,经常需要把国家药品监督管理局发布的PDF版法规转成图片通过公众号进行分享。直接使用Windows的截图工具,或者Adobe Acrobat Pro(专业版)虽然都可以成功转换,但是清晰度都不尽如人意,也无法自行指定导出的清晰度,也无法实现自行定义的拼接效果。今天用Python编写了一个小工具,可以指定清晰度(300dpi即可导出高清图片),图片接拼方式(如3行2列),欢迎大家下载试用:通过百度网盘分享的文件:202605 PDF拼接高清图片
链接: https://pan.baidu.com/s/1SQxZJVahqP3sOaiGt0i-9A 提取码: 52pj
同时把源代码分享如下,请吾爱的大神们多多指点:
[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import fitz
from PIL import Image
import os
import threading
import math
def convert_pdf_thread():
threading.Thread(target=convert_pdf, daemon=True).start()
def convert_pdf():
pdf_path = pdf_entry.get()
if not pdf_path:
messagebox.showwarning("提示", "请选择 PDF 文件")
return
try:
rows = int(row_var.get())
cols = int(col_var.get())
dpi = int(dpi_var.get())
except ValueError:
messagebox.showerror("错误", "请输入有效数字")
return
if rows <= 0 or cols <= 0:
messagebox.showerror("错误", "行数和列数必须大于 0")
return
output_dir = filedialog.askdirectory(title="选择图片保存目录")
if not output_dir:
return
doc = fitz.open(pdf_path)
total_pages = len(doc)
# 进度窗口
progress_win = tk.Toplevel(root)
progress_win.title("正在处理")
progress_win.geometry("360x120")
progress_win.resizable(False, False)
tk.Label(progress_win, text="正在转换 PDF 页面...").pack(pady=10)
progress_bar = ttk.Progressbar(
progress_win, length=300, mode="determinate", maximum=total_pages
)
progress_bar.pack(pady=5)
progress_label = tk.Label(progress_win, text="0 / {}".format(total_pages))
progress_label.pack()
zoom = dpi / 72
mat = fitz.Matrix(zoom, zoom)
images = []
for i in range(total_pages):
page = doc.load_page(i)
pix = page.get_pixmap(matrix=mat)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
images.append(img)
progress_bar["value"] = i + 1
progress_label.config(text=f"{i + 1} / {total_pages}")
progress_win.update_idletasks()
doc.close()
# 网格拼接
pages_per_image = rows * cols
output_images = []
for i in range(0, len(images), pages_per_image):
chunk = images[i:i + pages_per_image]
while len(chunk) < pages_per_image:
chunk.append(Image.new("RGB", chunk[0].size, (255, 255, 255)))
widths, heights = zip(*(img.size for img in chunk))
cell_width = max(widths)
cell_height = max(heights)
big_width = cell_width * cols
big_height = cell_height * rows
new_img = Image.new("RGB", (big_width, big_height), (255, 255, 255))
for idx, img in enumerate(chunk):
r = idx // cols
c = idx % cols
new_img.paste(img, (c * cell_width, r * cell_height))
output_images.append(new_img)
# 保存
base_name = os.path.splitext(os.path.basename(pdf_path))[0]
for idx, img in enumerate(output_images, start=1):
out_path = os.path.join(output_dir, f"{base_name}_{idx}.png")
img.save(out_path, "PNG")
progress_win.destroy()
messagebox.showinfo("完成", f"成功导出 {len(output_images)} 张图片")
# ===== GUI =====
root = tk.Tk()
root.title("PDF 转拼接图片工具 by xhlbudd@52pojie")
root.geometry("440x320")
# PDF
tk.Label(root, text="PDF 文件:").pack(anchor="w", padx=10, pady=(10, 0))
pdf_entry = tk.Entry(root, width=52)
pdf_entry.pack(padx=10)
tk.Button(
root,
text="浏览",
command=lambda: pdf_entry.insert(
0, filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])
),
).pack(pady=5)
# 拼接布局
frame_layout = tk.Frame(root)
frame_layout.pack(pady=10)
tk.Label(frame_layout, text="拼接布局(行 × 列):").grid(row=0, column=0, padx=5)
row_var = tk.StringVar(value="3")
col_var = tk.StringVar(value="1")
tk.Entry(frame_layout, textvariable=row_var, width=5).grid(row=0, column=1)
tk.Label(frame_layout, text="×").grid(row=0, column=2)
tk.Entry(frame_layout, textvariable=col_var, width=5).grid(row=0, column=3)
# DPI
tk.Label(root, text="导出清晰度(DPI):").pack(anchor="w", padx=10)
dpi_var = tk.StringVar(value="300")
tk.Entry(root, textvariable=dpi_var, width=10).pack(anchor="w", padx=10)
# 开始按钮
tk.Button(
root,
text="开始转换",
bg="#4CAF50",
fg="white",
height=2,
command=convert_pdf_thread,
).pack(pady=15)
root.mainloop()