# 使用提醒:
# 1. xbot包提供软件自动化、数据表格、Excel、日志、AI等功能
# 2. package包提供访问当前应用数据的功能,如获取元素、访问全局变量、获取资源文件等功能
# 3. 当此模块作为流程独立运行时执行main函数
# 4. 可视化流程中可以通过"调用模块"的指令使用此模块
import xbot
from xbot import print, sleep
from .import package
from .package import variables as glv
import fitz # PyMuPDF
from PIL import Image
import io
import os
from typing import Tuple
# def main(args):
# print("hhhhhh")
# pass
class PDFStampAdder:
def __init__(self, pdf_path: str, stamp_path: str, output_path: str):
"""
初始化PDF盖章器
Args:
pdf_path: PDF文件路径
stamp_path: 公章图片路径
output_path: 输出文件路径
"""
self.pdf_path = pdf_path
self.stamp_path = stamp_path
self.output_path = output_path
self.stamp_image = None
self.stamp_size = None
print(self)
def load_stamp(self, target_width: int = 60):
"""
加载公章图片并调整大小(保持高质量)
Args:
target_width: 目标宽度(像素)
"""
try:
# 加载公章图片
stamp = Image.open(self.stamp_path)
# 转换为RGBA模式以支持透明度
# if stamp.mode != 'RGBA':
# stamp = stamp.convert('RGBA')
# 计算新的尺寸,保持高分辨率
aspect_ratio = stamp.height / stamp.width
new_width = target_width
new_height = int(target_width * aspect_ratio)
# 使用高质量重采样
# self.stamp_image = stamp.resize(
# (new_width, new_height),
# Image.Resampling.LANCZOS # 高质量重采样
# )
self.stamp_size = (new_width, new_height)
print(f"公章加载成功,尺寸: {new_width}x{new_height}")
except Exception as e:
raise Exception(f"加载公章图片失败: {str(e)}")
def is_area_clear(self, page, rect: fitz.Rect, threshold: float = 0.1) -> bool:
"""
检查指定区域是否足够清晰(文字较少)
Args:
page: PDF页面对象
rect: 检查区域
threshold: 文字密度阈值
Returns:
bool: 区域是否清晰
"""
try:
# 获取该区域内的文字
text_dict = page.get_text("dict")
chars_in_rect = 0
# 遍历所有文字块
for block in text_dict.get("blocks", []):
if "lines" in block:
for line in block["lines"]:
for span in line.get("spans", []):
# 检查字符是否在指定区域内
for char in span.get("chars", []):
char_rect = fitz.Rect(
char["bbox"][0], char["bbox"][1],
char["bbox"][2], char["bbox"][3]
)
if rect.intersects(char_rect):
chars_in_rect += 1
# 简单估算:如果字符密度较低,则认为区域较清晰
area = rect.width * rect.height
char_density = chars_in_rect / max(area, 1)
return char_density < threshold
except Exception as e:
print(f"检查区域清晰度时出错: {str(e)}")
return True # 出错时默认认为区域可用
def find_optimal_stamp_position(self, page) -> Tuple[float, float]:
"""
寻找最佳盖章位置(底部居中并避开文字)
Args:
page: PDF页面对象
Returns:
Tuple[float, float]: 盖章位置的x, y坐标
"""
# 获取页面尺寸
page_rect = page.rect
page_width = page_rect.width
page_height = page_rect.height
if not self.stamp_size:
raise Exception("请先加载公章图片")
stamp_width, stamp_height = self.stamp_size
# 初始位置:底部居中
x = (page_width - stamp_width) / 2
y = page_height - stamp_height - 6 # 距离底部30像素
# 创建检查区域
check_rect = fitz.Rect(
max(0, x - 20), max(0, y - 20),
min(page_width, x + stamp_width + 20),
min(page_height, y + stamp_height + 50)
)
# 如果初始位置文字较多,尝试向上移动
max_attempts = 10
step_size = 15
for attempt in range(max_attempts):
if self.is_area_clear(page, check_rect):
break
# 向上移动
y -= step_size
check_rect = fitz.Rect(
max(0, x - 20), max(0, y - 20),
min(page_width, x + stamp_width + 20),
min(page_height, y + stamp_height + 50)
)
# 如果移动到页面上部仍无法找到清晰区域,则使用初始位置
if y < page_height * 0.3:
y = page_height - stamp_height - 30
break
return x, y
def add_stamp_to_page(self, page, x: float, y: float):
"""
在指定页面添加公章(高清晰度版本)
Args:
page: PDF页面对象
x: 盖章位置x坐标
y: 盖章位置y坐标
"""
try:
# 方法1:直接使用原始图片数据(推荐)
if self.stamp_path.lower().endswith(('.png', '.jpg', '.jpeg')):
# 直接插入原始图片文件,保持最高质量
img_rect = fitz.Rect(x, y, x + self.stamp_size[0], y + self.stamp_size[1])
print(f"章图片:{self.stamp_path}")
page.insert_image(img_rect, filename=self.stamp_path)
else:
# 方法2:使用优化的PNG格式
img_buffer = io.BytesIO()
# 保存为高质量PNG
self.stamp_image.save(
img_buffer,
format='PNG',
optimize=True,
compress_level=1 # 最低压缩,最高质量
)
img_buffer.seek(0)
# 在页面上插入图片
img_rect = fitz.Rect(x, y, x + self.stamp_size[0], y + self.stamp_size[1])
page.insert_image(img_rect, stream=img_buffer.getvalue())
except Exception as e:
raise Exception(f"添加公章到页面失败: {str(e)}")
def process_pdf(self, stamp_width: int = 60, quality_factor: float = 1.5):
"""
处理整个PDF文件(优化清晰度)
Args:
stamp_width: 公章宽度(像素)- 增大以提高清晰度
quality_factor: 质量因子,用于调整图片大小
"""
try:
# 打开PDF文件
doc = fitz.open(self.pdf_path)
print(f"PDF文件加载成功,共{len(doc)}页")
# 加载公章(使用更大的尺寸以提高清晰度)
adjusted_width = int(stamp_width * quality_factor)
self.load_stamp(adjusted_width)
# 处理每一页
for page_num in range(len(doc)):
page = doc[page_num]
# print(f"正在处理第{page_num + 1}页...")
# 寻找最佳盖章位置
x, y = self.find_optimal_stamp_position(page)
print(f"坐标:{x},{y}")
# 添加公章
self.add_stamp_to_page(page, x, y)
print(f"第{page_num + 1}页处理完成")
# 保存结果(优化PDF质量)
doc.save(
self.output_path,
garbage=4, # 最大垃圾回收
deflate=True, # 压缩
clean=True # 清理
)
doc.close()
print(f"PDF处理完成,已保存到: {self.output_path}")
except Exception as e:
raise Exception(f"处理PDF文件时出错: {str(e)}")
def add_stamp_to_pdf_advanced(pdf_path: str, stamp_path: str, output_path: str,
stamp_width: int = 80,
position: str = "bottom_center",
margin: int = 30,
quality_factor: float = 2.0):
"""
高级版函数:为PDF添加公章(更多自定义选项)
Args:
pdf_path: PDF文件路径
stamp_path: 公章图片路径
output_path: 输出文件路径
stamp_width: 公章宽度(像素)
position: 位置(目前只支持bottom_center)
margin: 边距
quality_factor: 质量因子(越大越清晰)
"""
try:
# 创建PDF盖章器实例
stamp_adder = PDFStampAdder(pdf_path, stamp_path, output_path)
# 打开PDF文件
doc = fitz.open(pdf_path)
# 加载高质量公章
adjusted_width = int(stamp_width * quality_factor)
stamp_adder.load_stamp(adjusted_width)
# 处理每一页
for page_num in range(len(doc)):
page = doc[page_num]
# 获取页面尺寸
page_rect = page.rect
page_width = page_rect.width
page_height = page_rect.height
if not stamp_adder.stamp_size:
raise Exception("公章未加载")
stamp_width_actual, stamp_height_actual = stamp_adder.stamp_size
# 计算盖章位置(底部居中)
x = (page_width - stamp_width_actual) / 2
y = page_height - stamp_height_actual - margin
# 添加公章
stamp_adder.add_stamp_to_page(page, x, y)
# 保存结果
doc.save(output_path, garbage=4, deflate=True, clean=True)
doc.close()
print(f"高清盖章完成,已保存到: {output_path}")
return True
except Exception as e:
print(f"盖章失败: {str(e)}")
return False
# if __name__ == "__main__":
# # 运行示例
# # main()
# # 或者使用简化函数
# result = add_stamp_to_pdf(r"D:\workspace\personalSpace\rpa\file\80125.pdf", r"file\图片.png",
# "output4.pdf")
# if result:
# print("盖章成功!")
def add_stamp_to_pdf(pdf_path: str, stamp_path: str, output_path: str,
stamp_width: int = 60, quality_factor: float = 1.5):
"""
简化版函数:为PDF添加公章(高清版本)
Args:
pdf_path: PDF文件路径
stamp_path: 公章图片路径
output_path: 输出文件路径
stamp_width: 公章宽度(像素)
quality_factor: 质量因子
"""
try:
stamp_adder = PDFStampAdder(pdf_path, stamp_path, output_path)
stamp_adder.process_pdf(stamp_width, quality_factor)
return True
except Exception as e:
print(f"盖章失败: {str(e)}")
return False
相同源文件pdf、章图片png、相同python代码、相同文件路径。本地运行python是没问题的可以实现需求。但是放在影刀里跑就是盖不上。
用影刀执行后,比对盖章前后文件的大小,文件大小是有变化的,但是就是看不见章