You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
import sys
import platform
from fpdf import FPDF
from PIL import Image
from tkinter import filedialog
from tkinter import Tk, Button, Label, Toplevel
from tkinter.filedialog import askdirectory, askopenfiles
from pypdf import PdfWriter, PdfReader, Transformation
import math
import random
import subprocess
def get_os():
return platform.system()
def check_output(path):
dir_name = os.path.dirname(path)
if False == os.path.exists(dir_name):
pass
# 创建文件夹
os.mkdir(dir_name)
def img_to_pdf(img_path, out_path="", x=0, y=0, w=100, h=100):
"""图片转PDF"""
if out_path == "":
dir_name = os.path.dirname(img_path)
basename = os.path.basename(img_path)
name, _ = os.path.splitext(basename)
out_path = os.path.join(dir_name, name + ".pdf")
pdf = FPDF()
pdf.add_page()
pdf.image(img_path, x=x, y=y, w=w, h=h)
pdf.output(out_path, "F")
return out_path
def add_image_watermark(input_path, watermark_path, output_path):
"""图片添加印章"""
# 打开原始图片和要添加的水印图片
original_img = Image.open(input_path).convert("RGBA")
watermark_img = Image.open(watermark_path).convert("RGBA")
# 获取原始图片
original_width, original_height = original_img.size
water_width = water_height = math.ceil(38 / 210 * original_width)
# 将水印图片缩放到与原始图片相同的大小
watermark_img = watermark_img.resize((water_width, water_height))
watermark_img = watermark_img.rotate(random.uniform(-180, 180))
# 合并原始图片和水印图片并将结果转换为RGB模式
original_img.paste(
watermark_img,
(
math.ceil(original_width - original_width / 2.6),
math.ceil(original_height - original_height / 2.6),
),
watermark_img,
)
# 保存水印后的图片
original_img.save(output_path)
def add_pdf_watermark(intput_file, watermark_path, output_path):
"""PDF添加印章"""
stamp = PdfReader(watermark_path).pages[0]
writer = PdfWriter(clone_from=intput_file)
for page in writer.pages:
page.merge_transformed_page(
stamp, Transformation().translate(980, 60).scale(0.37)
)
writer.write(output_path)
class CustomMessageBox(Toplevel):
def __init__(self, master, text, width=400, height=100):
super().__init__(master)
self.transient(master)
self.title("Error")
self.label = Label(self, text=text)
self.label.pack(padx=20, pady=20)
# 设置了宽和高
self.width = width
self.height = height
self.geometry(f"{width}x{height}+{master.winfo_x()}+{master.winfo_y()}")
self.grab_set() # 使得对话框不会在点击窗口之外的地方时关闭
self.wait_window(self) # 等待对话框关闭
if __name__ == "__main__":
multi_files = [] # 多选的文件
dir_files = [] # 文件夹中的文件
water_path = "" # 印章路径
tmp_water = "" # 当为图片时删除临时生成的PDF印章
last_stamped = "" # 最后一个添加印章后的文件
print(print(get_os()))
def reset():
global multi_files, dir_files, water_path, tmp_water
multi_files = []
dir_files = []
water_path = ""
tmp_water = ""
lable_dir.config(text="")
lable_files.config(text="")
lable_stamp.config(text="")
label_create.config(text="")
button_create.config(state="disabled")
button_opendir.place_forget()
button_openfile.place_forget()
def on_closing():
root.destroy()
root.quit()
exit()
def check_status():
# 如果文件和水印都选择便启用水印生成按钮,否则禁用
if (
lable_files.cget("text") != "" or lable_dir.cget("text") != ""
) and lable_stamp.cget("text") != "":
button_create.config(state="normal")
else:
button_create.config(state="disabled")
def select_files(extensions=["pdf", "png", "jpeg", "jpg"]):
Tk().withdraw()
files = askopenfiles()
# 检测文件类型
for item in files:
file_extension = item.name.split(".")[-1].lower()
if file_extension not in extensions:
CustomMessageBox(root, "无效的文件类型。")
select_files()
return
paths = [(item.name) for item in files]
global multi_files
multi_files = paths
str = ",".join(paths)
if len(str) >= 50:
str = str[:50] + "..."
lable_files.config(text=str)
check_status()
def select_dir():
root = Tk()
root.withdraw()
folder_path = askdirectory()
items = recursive_walk(folder_path)
global dir_files
dir_files = items
if len(folder_path) >= 50:
folder_path = str[:50] + "..."
lable_dir.config(text=folder_path)
check_status()
def select_file(label, extensions=["png", "jpeg", "jpg"]):
Tk().withdraw()
file_path = filedialog.askopenfilename()
if file_path:
file_extension = file_path.split(".")[-1].lower()
if file_extension in extensions:
# 隐藏操作按钮
button_opendir.place_forget()
button_openfile.place_forget()
if len(file_path) >= 50:
file_path = str[:50] + "..."
label.config(text=file_path)
# 检查水印生成按键可用状态
check_status()
else:
CustomMessageBox(root, "无效的文件类型。")
select_file()
def recursive_walk(dir_path):
file_list = []
# 遍历指定文件夹
for root, dirs, files in os.walk(dir_path):
for file in files:
# 检查文件是否是PDF或图片
if (
file.endswith(".pdf")
or file.endswith(".png")
or file.endswith(".jpg")
or file.endswith(".jpeg")
):
# 如果是,则将其完整路径添加到列表中
file_list.append(os.path.join(root, file))
return file_list
def add_watermask(file_path, water_path):
baseaname = os.path.basename(file_path)
name, suffix = os.path.splitext(baseaname)
dir_path = os.path.dirname(file_path)
# # 判断目标文件是图片还是PDF
if suffix.lower() == ".pdf":
global tmp_water
# 如果水印是图片先转成PDF
if tmp_water == "":
if water_path.split(".")[-1].lower() != "pdf":
tmp_water = img_to_pdf(water_path)
out_path = os.path.join(dir_path, "output", name + "_watered" + suffix)
check_output(out_path)
add_pdf_watermark(file_path, tmp_water, out_path)
else:
out_path = os.path.join(dir_path, "output", name + "_watered" + ".png")
check_output(out_path)
add_image_watermark(file_path, water_path, out_path)
# 显示操作按钮
global last_stamped
last_stamped = out_path
label_create.config(text="生成成功:")
label_create.place(x=120, y=4 * padding_y, width=80, height=lable_height)
button_openfile.place(
x=200, y=4 * padding_y, width=button_width, height=button_height
)
button_opendir.place(
x=280, y=4 * padding_y, width=button_width, height=button_height
)
def multi_watermask():
global multi_files, dir_files, tmp_water
water_path = lable_stamp.cget("text")
selected_files = list(set(multi_files + dir_files))
for item in selected_files:
add_watermask(item, water_path)
# 删除临时PDF印章
if tmp_water != "":
os.remove(tmp_water)
tmp_water = ""
def open_dirname():
global last_stamped
dir_name = os.path.dirname(last_stamped)
if get_os == "Windows":
os.startfile(dir_name)
else:
subprocess.run(["open", dir_name])
def open_file():
global last_stamped
if get_os == "Windows":
os.startfile(last_stamped)
else:
subprocess.run(["open", last_stamped])
def get_path(relative_path):
try:
base_path = sys._MEIPASS
except AttributeError:
base_path = os.path.abspath(".")
return os.path.normpath(os.path.join(base_path, relative_path))
# 主窗口
button_width = 70
button_height = 26
label_width = 300
lable_height = 26
button_x = 50
label_x = 150
button_y = 100
padding_y = 40
root = Tk()
root.title("印章助手")
root.resizable(False, False)
root.protocol("WM_DELETE_WINDOW", on_closing)
# 获取屏幕宽度和高度
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# 计算窗口的理想位置
x_pos = int((screen_width - 500) / 2)
y_pos = int((screen_height - 300) / 2)
# 设置窗口位置
root.geometry("{}x{}+{}+{}".format(500, 300, x_pos, y_pos))
# 选择文件-多选
button_files = Button(root, text="选择文件", command=select_files)
button_files.place(
x=button_x, y=padding_y, width=button_width, height=button_height
)
lable_files = Label(root, text="")
lable_files.place(x=label_x, y=padding_y, width=label_width, height=lable_height)
# 选择目录
button_dir = Button(root, text="选择目录", command=select_dir)
button_dir.place(
x=button_x, y=2 * padding_y, width=button_width, height=button_height
)
lable_dir = Label(root, text="")
lable_dir.place(x=label_x, y=2 * padding_y, width=label_width, height=lable_height)
# 选择公章
button_stamp = Button(
root,
text="选择公章",
command=lambda: select_file(lable_stamp, extensions=["png", "jpeg", "jpg"]),
)
button_stamp.place(
x=button_x, y=3 * padding_y, width=button_width, height=button_height
)
lable_stamp = Label(root, text=get_path("waters/stamp.png"))
lable_stamp.place(
x=label_x, y=3 * padding_y, width=label_width, height=lable_height
)
# 预览
label_create = Label(root, text="")
button_opendir = Button(root, text="目录", command=open_dirname)
button_openfile = Button(root, text="预览", command=open_file)
# 添加水印
button_create = Button(root, text="生成", command=multi_watermask, state="disabled")
button_create.place(
x=120, y=6 * padding_y, width=button_width, height=button_height
)
# 重置
button_reset = Button(root, text="重置", command=reset)
button_reset.place(x=320, y=6 * padding_y, width=button_width, height=button_height)
root.mainloop()