import os
import tkinter as tk
from tkinter import filedialog, messagebox
import fitz # Библиотека PyMuPDF
def mm_to_points(mm):
"""Конвертирует миллиметры в пункты PDF (1 дюйм = 25.4 мм = 72 пункта)."""
return (mm / 25.4) * 72
def process_pdf():
# 1. Выбор исходного PDF-файла
pdf_path = filedialog.askopenfilename(
title="Выберите исходный PDF-документ",
filetypes=[("PDF файлы", "*.pdf")],
)
if not pdf_path:
return
# 2. Выбор изображения
img_path = filedialog.askopenfilename(
title="Выберите изображение для вставки",
filetypes=[
("Изображения", "*.png *.jpg *.jpeg *.bmp"),
("Все файлы", "*.*"),
],
)
if not img_path:
return
# 3. Считывание координат и размеров из полей ввода
try:
x_mm = float(entry_x.get())
y_mm = float(entry_y.get())
w_mm = float(entry_w.get())
h_mm = float(entry_h.get())
except ValueError:
messagebox.showerror(
"Ошибка", "Пожалуйста, введите корректные числа в поля координат."
)
return
# Перевод мм в пункты PDF
x1 = mm_to_points(x_mm)
y1 = mm_to_points(y_mm)
x2 = x1 + mm_to_points(w_mm)
y2 = y1 + mm_to_points(h_mm)
rect = fitz.Rect(x1, y1, x2, y2)
# 4. Обработка PDF
try:
doc = fitz.open(pdf_path)
for page in doc:
# 1. Сначала вставляем картинку стандартным методом страницы
page.insert_image(rect, filename=img_path)
# 2. Создаем невидимую аннотацию-подложку, которая принудительно включает
# режим Multiply для всех новых графических объектов на этой странице
annot = page.add_rect_annot(rect)
# Настраиваем аннотацию (делаем её прозрачной, но задаем режим смешивания)
annot.set_colors(stroke=None, fill=None) # Без цвета и рамок
annot.set_opacity(1.0)
# Прописываем режим Multiply напрямую в словарь аннотации
# Это включает поддержку композитинга на странице в Acrobat и других вьюверах
annot_xref = annot.xref
if annot_xref > 0:
doc.xref_set_key(annot_xref, "BM", "/Multiply")
annot.update()
# Формируем имя для сохранения нового файла
dir_name, file_name = os.path.split(pdf_path)
output_path = os.path.join(dir_name, "ready_" + file_name)
# Сохраняем с оптимизацией структуры
doc.save(output_path, garbage=3, deflate=True)
doc.close()
messagebox.showinfo(
"Успех", f"Файл успешно сохранен как:\n{output_path}"
)
except Exception as e:
messagebox.showerror(
"Ошибка обработки", f"Произошла ошибка при изменении PDF:\n{str(e)}"
)
# --- Создание графического интерфейса ---
root = tk.Tk()
root.title("Вставка изображения в PDF (Multiply)")
root.geometry("380x280")
root.resizable(False, False)
frame = tk.LabelFrame(root, text=" Настройки положения (в мм) ")
frame.pack(padx=15, pady=15, fill="x", ipady=5)
# Поля ввода
tk.Label(frame, text="Отступ слева (X):").grid(
row=0, column=0, sticky="w", pady=5, padx=10
)
entry_x = tk.Entry(frame, width=10)
entry_x.insert(0, "20") # Значение по умолчанию
entry_x.grid(row=0, column=1, pady=5)
tk.Label(frame, text="Отступ сверху (Y):").grid(
row=1, column=0, sticky="w", pady=5, padx=10
)
entry_y = tk.Entry(frame, width=10)
entry_y.insert(0, "20")
entry_y.grid(row=1, column=1, pady=5)
tk.Label(frame, text="Ширина картинки:").grid(
row=2, column=0, sticky="w", pady=5, padx=10
)
entry_w = tk.Entry(frame, width=10)
entry_w.insert(0, "50")
entry_w.grid(row=2, column=1, pady=5)
tk.Label(frame, text="Высота картинки:").grid(
row=3, column=0, sticky="w", pady=5, padx=10
)
entry_h = tk.Entry(frame, width=10)
entry_h.insert(0, "50")
entry_h.grid(row=3, column=1, pady=5)
# Кнопка запуска процесса
btn_start = tk.Button(
root,
text="Выбрать файлы и запустить",
command=process_pdf,
bg="#bfbfbf",
fg="black",
font=("Segoe UI", 10),
)
btn_start.pack(fill="x", padx=15, pady=5, ipady=5)
root.mainloop()