wxn153 发表于 2024-11-22 10:32

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()

tche 发表于 2024-11-22 20:54


感谢无私的分享!

PaulYangss 发表于 2024-11-23 11:57

挺好玩的

15479 发表于 2024-11-23 17:33

试试去谢谢分享
页: [1]
查看完整版本: PDF图纸批量添加指定二维码