commit d2e79cbe8e0143ba4d3e01cc479f2a29f43d8a45 Author: xingzi Date: Fri Oct 13 17:39:54 2023 +0800 all diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c8f697 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +build +dist +*.spec \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/images/girl.jpeg b/images/girl.jpeg new file mode 100644 index 0000000..f621e74 Binary files /dev/null and b/images/girl.jpeg differ diff --git a/index.py b/index.py new file mode 100644 index 0000000..813189a --- /dev/null +++ b/index.py @@ -0,0 +1,129 @@ +import os +import fitz + +from PIL import Image +from tkinter import filedialog +from tkinter import Tk, Button, Label, messagebox, Toplevel +from tkinter.filedialog import askopenfilename + + +def add_image_watermark(input_image_path, watermark_image_path, output_image_path): + water_width = 100 + water_height = 100 + water_right = 50 + water_bottom = 100 + # 打开原始图片和要添加的水印图片 + original_img = Image.open(input_image_path).convert("RGBA") + watermark_img = Image.open(watermark_image_path).convert("RGBA") + + # 获取原始图片和水印图片的尺寸 + original_width, original_height = original_img.size + + # 将水印图片缩放到与原始图片相同的大小 + watermark_img = watermark_img.resize((water_width, water_height)) + + # 合并原始图片和水印图片,并将结果转换为RGB模式 + # watermarked_img = Image.alpha_composite(original_img, watermark_img).convert("RGB") + original_img.paste(watermark_img, (original_width - (water_width+water_right), + original_height-(water_height+water_bottom)), watermark_img) + + # 保存水印后的图片为JPEG格式 + original_img.save(output_image_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__": + # items = os.listdir(pdfs) + # for f in items: + # name, suffix = os.path.splitext(f) + # if suffix == '.pdf': + # out = outPath + '/' + name + '_watered' + suffix + # input_file = os.path.join(pdfs, f) + # # img2pdf(water_path_png) + # # add_watermark(input_file,water_path_pdf,out) + # add_watermask_fitz(input_file, water_path_pdf, out) + + # 获取当前目录 + current_dir = os.getcwd() + # 获取脚本自身的路径 + script_path = os.path.dirname(os.path.abspath(__file__)) + + water_path_png = os.path.join(current_dir, 'waters/logo.png') + water_path_pdf = os.path.join(current_dir, 'waters/logo.pdf') + + outPath = os.path.join(current_dir, 'output') + pdfs = os.path.join(current_dir, 'pdfs') + + def on_closing(): + root.destroy() + root.quit() + exit() + + def select_file(label, extensions=['pdf', 'png', 'jpeg', 'jpg']): + Tk().withdraw() + file_path = filedialog.askopenfilename() + if file_path: + file_extension = file_path.split('.')[-1].lower() + if file_extension in extensions: + label.config(text=file_path) + else: + CustomMessageBox(root, "无效的文件类型。请选择PDF、PNG、JPEG或JPG文件。") + select_file() + + def add_watermask(): + file_path = lable_file.cget("text") + water_path = lable_stamp.cget("text") + print(file_path, water_path) + baseaname = os.path.basename(file_path) + name, suffix = os.path.splitext(baseaname) + out_path = os.path.join(os.path.dirname( + file_path), name + '_watered' + suffix) + pdf_file = fitz.open(file_path) + page = pdf_file[0] + page.insert_image(page.bound(), filename=water_path) + pdf_file.save(out_path) + lable_water.config(text=out_path) + + root = Tk() + root.protocol("WM_DELETE_WINDOW", on_closing) + root.geometry("600x400") + + # 选择文件 + lable_file = Label(root, text="请选择PDF或图片") + lable_file.grid(row=0, column=1) + button_file = Button(root, text="选择PDF", + command=lambda: select_file(lable_file)) + button_file.grid(row=0, column=0) + + # 选择公章 + lable_stamp = Label(root, text="请选择选择公章") + lable_stamp.grid(row=1, column=1) + button_stamp = Button(root, text="选择公章", command=lambda: select_file(lable_stamp, + extensions=['png', 'jpeg', 'jpg'])) + button_stamp.grid(row=1, column=0) + + # 添加水印 + button_water = Button(root, text="添加水印", command=add_watermask) + button_water.grid(row=2, column=0) + lable_water = Label(root, text="") + lable_water.grid(row=2, column=1) + + root.mainloop() diff --git a/pdfs/girl.pdf b/pdfs/girl.pdf new file mode 100644 index 0000000..33b1c07 Binary files /dev/null and b/pdfs/girl.pdf differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0e20c22 Binary files /dev/null and b/requirements.txt differ diff --git a/stamptool.py b/stamptool.py new file mode 100644 index 0000000..ffe0d1b --- /dev/null +++ b/stamptool.py @@ -0,0 +1,294 @@ +import os +import sys +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 + + +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 = '' # 最后一个添加印章后的文件 + + 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) + os.startfile(dir_name) + + def open_file(): + global last_stamped + os.startfile(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() diff --git a/test.py b/test.py new file mode 100644 index 0000000..18acccf --- /dev/null +++ b/test.py @@ -0,0 +1,13 @@ +from PIL import Image + + +img = Image.open('首营资料.jpg').convert("RGBA") + +pdi = img.info.get('dpi')[0] +img_width, img_heigth = img.size + +a4_width = 210 +stamp_width = 44 + +x = stamp_width/a4_width * img_width +print(x) diff --git a/waters/logo.png b/waters/logo.png new file mode 100644 index 0000000..1825bb6 Binary files /dev/null and b/waters/logo.png differ