将png格式的印章改到pdf发票上。python代码在本地跑是可以实现需求,但是通过影刀调用模块没报错却盖章失败
回答
收藏

将png格式的印章改到pdf发票上。python代码在本地跑是可以实现需求,但是通过影刀调用模块没报错却盖章失败

d
derekjohntr
2025-09-04 16:49·浏览量:260
d
derekjohntr
影刀见习开发者
发布于 2025-09-04 16:49260浏览


# 使用提醒:

# 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是没问题的可以实现需求。但是放在影刀里跑就是盖不上。

用影刀执行后,比对盖章前后文件的大小,文件大小是有变化的,但是就是看不见章

收藏
全部回答1
最新
发布回答
回答