PDF图纸批量添加指定二维码
由于工作需要,需在几百张的PDF图纸上添加对应的二维码,所以就自行用PYTHON编写了一个小工具。分享给有需要的人。
功能是预先在EXCEL里指定对应需求的数据,根据选择列来生成二维码并放置在PDF图纸的左下角。
源码如下:
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageDraw, ImageFont
import pandas as pd
import fitz# PyMuPDF
import os
import io
import qrcode
#基本数据获取代码段
def browse_folder(entry_var):
#浏览文件夹对话框,选择后更新entry变量
folder_path = filedialog.askdirectory()
entry_var.set(folder_path)
def browse_file(entry_var, filetypes):
#浏览文件对话框,选择后更新entry变量,支持特定文件类型
file_path = filedialog.askopenfilename(filetypes=filetypes)
entry_var.set(file_path)
if file_path:# 如果选择了文件,则尝试读取并更新列名下拉菜单
global df
df = pd.read_excel(file_path)
update_column_options()
def update_column_options():
#更新列名下拉菜单的选项
global file_name_dropdown, date_dropdown, qty_dropdown, order_dropdown, column_options
column_options = df.columns.tolist()
file_name_dropdown.set(column_options[0])# 设置默认选中第一个列为文件名列
date_dropdown.set(column_options[0])# 设置默认选中第一个列为
qty_dropdown.set(column_options[0])# 设置默认选中第一个列为
order_dropdown.set(column_options[0])# 设置默认选中第一个列为
file_name_dropdown_menu['menu'].delete(0, 'end')# 清除现有选项
date_dropdown_menu['menu'].delete(0, 'end')# 清除现有选项
qty_dropdown_menu['menu'].delete(0, 'end')# 清除现有选项
order_dropdown_menu['menu'].delete(0, 'end')# 清除现有选项
for option in column_options:
file_name_dropdown_menu['menu'].add_command(label=option, command=tk._setit(file_name_dropdown, option))
date_dropdown_menu['menu'].add_command(label=option, command=tk._setit(date_dropdown, option))
qty_dropdown_menu['menu'].add_command(label=option, command=tk._setit(qty_dropdown, option))
order_dropdown_menu['menu'].add_command(label=option, command=tk._setit(order_dropdown, option))
root.update_idletasks()# 更新界面
#功能函数代码段
def add_image_to_pdf(pdf_path, output_path, qr_value, customer_input, batch_input, date_value, qty_value):
# 打开PDF文件
doc = fitz.open(pdf_path)
page = doc.load_page(0)
page_rect = page.rect
width = page_rect.width
height = page_rect.height
rotation = page.rotation
# 合并文本
text = [f"客户:{customer_input}", f"交期:{date_value}", f"批次:{batch_input}", f"数量:{qty_value}件"]
# 创建并保存图片
image_size = 200
line_width = 1
font_size = 20
font_path = "C:/Windows/Fonts/msyhbd.ttc"
img = Image.new("RGBA", (image_size, image_size), (255, 255, 255, 0))
draw = ImageDraw.Draw(img)
for i in range(line_width):
draw.rectangle([(i, i), (image_size - i - 1, image_size - i - 1)], outline=(0, 0, 0))
font = ImageFont.truetype(font_path, font_size)
text_height = font.getbbox('A')[3]
row_spacing = (image_size - 2*line_width) / 5
for idx, line in enumerate(text):
text_position = (line_width + 10, line_width + row_spacing*(idx+1) - text_height/2)
draw.text(text_position, line, fill=(0, 0, 0), font=font)
# 计算图片最大可缩放尺寸,保持原始宽高比
scaled_img_width = int(page_rect.width * 0.14)
scaled_img_height = int(page_rect.height * 0.14)
scaled_img_size = min(scaled_img_width, scaled_img_height)
# 重新调整图片尺寸
img_resized = img.resize((scaled_img_size, scaled_img_size), resample=Image.LANCZOS)
# 将PIL图像转换为BytesIO对象,以便插入PDF
img_byte_arr = io.BytesIO()
img_resized.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=0,
)
qr.add_data(qr_value)
qr.make(fit=True)
qr_img = qr.make_image(fill_color="black", back_color="transparent")
# 计算二维码尺寸为页面大小的20%
#qr_size = qr.modules_count * qr.box_size
qr_width_percent = int(page_rect.width * 0.12)
qr_height_percent = int(page_rect.height * 0.12)
scaled_qr_size = min(qr_width_percent, qr_height_percent)
qr_img = qr_img.resize((scaled_qr_size, scaled_qr_size), resample=Image.LANCZOS)
# 将二维码图片转换为BytesIO对象
qr_img_byte_arr = io.BytesIO()
qr_img.save(qr_img_byte_arr, format='PNG')
qr_img_byte_arr = qr_img_byte_arr.getvalue()
# 根据页面方向调整二维码位置
if width > height:# 页面横向
if rotation == 0 or rotation == 180:
#水印位置
rect = fitz.Rect(width - scaled_img_size, 0, width, scaled_img_size)# 右上角ok
page.insert_image(rect, stream=img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
#二维码位置
img_rect = fitz.Rect(0, height - scaled_qr_size, scaled_qr_size, height)# 左下角ok
page.insert_image(img_rect, stream=qr_img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
elif rotation == 90 or rotation == 270:
#水印位置
rect = fitz.Rect(height - scaled_img_size, 0, height, scaled_img_size)# 右上角ok
page.insert_image(rect, stream=img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
#二维码位置
img_rect = fitz.Rect(0, 0, scaled_qr_size, scaled_qr_size)# 左上角ok
page.insert_image(img_rect, stream=qr_img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
if width < height: # 页面纵向
if rotation == 0 or rotation == 180:
#水印位置
rect = fitz.Rect(width - scaled_img_size, 0, width, scaled_img_size)# 右上角
page.insert_image(rect, stream=img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
#二维码位置
img_rect = fitz.Rect(0, 0, scaled_qr_size, scaled_qr_size)# 左上角ok
page.insert_image(img_rect, stream=qr_img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
elif rotation == 90 or rotation == 270:
#水印位置
rect = fitz.Rect(height - scaled_img_size, 0, height, scaled_img_size)# 右上角ok
page.insert_image(rect, stream=img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
#二维码位置
img_rect = fitz.Rect(0, width - scaled_qr_size, scaled_qr_size, width)# 左上角ok
page.insert_image(img_rect, stream=qr_img_byte_arr, overlay=True, keep_proportion=True, rotate=0)
# 保存PDF
doc.save(output_path)
doc.close()
print(f"水印和二维码已成功添加到PDF: {output_path}")
# 执行处理函数段
def start_processing():
#开始处理按钮的回调函数
try:
# 获取用户输入的路径和文件名
pdf_folder = pdf_folder_entry.get()
output_folder = output_folder_entry.get()
excel_path = excel_file_entry.get()
customer_input = customer_entry.get()
batch_input = batch_entry.get()
# 确保路径有效
if not os.path.isdir(pdf_folder) or not os.path.isdir(output_folder) or not os.path.isfile(excel_path):
messagebox.showerror("Error", "Please ensure both PDF and Output folders are valid.")
return
# 读取Excel文件
df = pd.read_excel(excel_path)
file_names = file_name_dropdown.get()
date_values = date_dropdown.get()
qty_values= qty_dropdown.get()
qr_columns = order_dropdown.get()
if qr_columns not in df.columns or file_names not in df.columns or date_values not in df.columns or qty_values not in df.columns:
messagebox.showerror("Error", "Selected columns do not exist in the Excel file.")
return
for _, row in df.iterrows():
# 构建完整的文件路径
file_name = str(row[file_names])
pdf_path = os.path.join(pdf_folder, f"{file_name}.pdf")
output_path = os.path.join(output_folder, f"{file_name}.pdf")
qr_value = str(row[qr_columns])
qty_value= str(row[qty_values])
date_value= str(row[date_values])
customer_input= str(customer_input)
batch_input= str(batch_input)
# 调用处理函数
add_image_to_pdf(pdf_path, output_path, qr_value, customer_input, batch_input, date_value, qty_value)
messagebox.showinfo("Success", "Processing completed successfully.")
except Exception as e:
messagebox.showerror("Error", f"An error occurred during processing: {e}")
# 初始化GUI
root = tk.Tk()
root.title("Excel批量添加PDF水印和二维码")
# 创建标签和输入框
tk.Label(root, text="原始PDF文件夹:").grid(row=0, column=0, pady=5)
pdf_folder_entry = tk.StringVar()
tk.Entry(root, textvariable=pdf_folder_entry, width=30).grid(row=0, column=1, pady=5)
tk.Button(root, text="浏览", command=lambda: browse_folder(pdf_folder_entry)).grid(row=0, column=2, pady=5)
tk.Label(root, text="输出PDF文件夹:").grid(row=1, column=0, pady=5)
output_folder_entry = tk.StringVar()
tk.Entry(root, textvariable=output_folder_entry, width=30).grid(row=1, column=1, pady=5)
tk.Button(root, text="浏览", command=lambda: browse_folder(output_folder_entry)).grid(row=1, column=2, pady=5)
tk.Label(root, text="Excel文件路径:").grid(row=2, column=0, pady=5)
excel_file_entry = tk.StringVar()
tk.Entry(root, textvariable=excel_file_entry, width=30).grid(row=2, column=1, pady=5)
tk.Button(root, text="浏览", command=lambda: browse_file(excel_file_entry, [('Excel Files', '*.xlsx;*.xls')])).grid(row=2, column=2, pady=5)
# 新增客户名称和批次号输入框
tk.Label(root, text="客户名称:").grid(row=3, column=0, pady=5)
customer_entry = tk.StringVar()
tk.Entry(root, textvariable=customer_entry).grid(row=3, column=1, pady=5)
tk.Label(root, text="批次号:").grid(row=3, column=2, pady=5)
batch_entry = tk.StringVar()
tk.Entry(root, textvariable=batch_entry).grid(row=3, column=3, pady=5)
# 创建列名下拉选择的框架
column_frame = tk.Frame(root)
column_frame.grid(row=4, column=0, columnspan=3, pady=5)
tk.Label(column_frame, text="指定文件名列:").grid(row=5, column=0, pady=5)
file_name_dropdown = tk.StringVar()
column_options = []# 列名选项将在读取Excel后填充
file_name_dropdown_menu = tk.OptionMenu(column_frame, file_name_dropdown, "")
file_name_dropdown_menu.grid(row=5, column=1, columnspan=2, pady=5)
tk.Label(column_frame, text="指定交期列:").grid(row=6, column=0, pady=5)
date_dropdown = tk.StringVar()
column_options = []# 列名选项将在读取Excel后填充
date_dropdown_menu = tk.OptionMenu(column_frame, date_dropdown, "")
date_dropdown_menu.grid(row=6, column=1, columnspan=2, pady=5)
tk.Label(column_frame, text="指定数量列:").grid(row=7, column=0, pady=5)
qty_dropdown = tk.StringVar()
column_options = []# 列名选项将在读取Excel后填充
qty_dropdown_menu = tk.OptionMenu(column_frame, qty_dropdown, "")
qty_dropdown_menu.grid(row=7, column=1, columnspan=2, pady=5)
tk.Label(column_frame, text="指定工单号列:").grid(row=8, column=0, pady=5)
order_dropdown = tk.StringVar()
column_options = []# 列名选项将在读取Excel后填充
order_dropdown_menu = tk.OptionMenu(column_frame, order_dropdown, "")
order_dropdown_menu.grid(row=8, column=1, columnspan=2, pady=5)
# 创建开始处理按钮
tk.Button(root, text="开始处理", command=start_processing).grid(row=9, column=0,columnspan=4, pady=5)
# 主事件循环
root.mainloop()
感谢无私的分享! 挺好玩的 试试去谢谢分享
页:
[1]