好友
阅读权限 10
听众
最后登录 1970-1-1
由于工作需要,需在几百张的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 ()