【DuodooBMS】给PDF附件加“受控”水印的完整Python实现
给PDF附件加“受控”水印的完整Python实现
功能需求
在实际工作中,许多文件需要添加水印以标识其状态,例如“受控”“机密”等。对于PDF文件,添加水印不仅可以增强文件的可识别性,还可以防止未经授权的使用。本代码的功能需求是:
-
修复PDF文件:在添加水印之前,确保PDF文件是完整且可读的,避免因文件损坏导致操作失败。
-
添加水印:在PDF的每一页上添加指定的水印图像或文字,水印可以设置位置、角度和透明度。
-
保存输出:将添加水印后的PDF文件保存到指定路径,并返回其二进制数据以便后续处理。

实现过程
-
修复PDF文件
-
使用
PyMuPDF库打开PDF文件,并尝试修复。如果文件损坏,PyMuPDF可以尝试修复并保存为一个新的二进制流。 -
如果修复失败,则直接返回原始的PDF二进制数据。
Python复制
def repair_pdf(self, input_pdf_binary):try:# 使用 PyMuPDF 打开并修复 PDFdoc = fitz.open(stream=input_pdf_binary, filetype="pdf")repaired_pdf_binary = BytesIO()doc.save(repaired_pdf_binary)doc.close()repaired_pdf_binary.seek(0)return repaired_pdf_binary.read()except Exception as e:print(f"Error repairing PDF: {e}")return input_pdf_binary -
-
添加水印
-
使用
reportlab库创建一个临时的PDF文件作为水印。水印可以是图像或文字,支持设置位置、角度和透明度。 -
使用
PyPDF2库将水印PDF与原始PDF合并。通过merge_page方法,将水印添加到每一页。
Python复制
def add_watermark(self, input_pdf_binary, output_pdf, watermark_image, x_position=30, y_position=50, opacity=1):# 尝试修复 PDFrepaired_pdf_binary = self.repair_pdf(input_pdf_binary)input_pdf_obj = PdfReader(BytesIO(repaired_pdf_binary)) # 从二进制数据中读取 PDFoutput_pdf_obj = PdfWriter()output_buffer = BytesIO()# 创建一个临时的 PDF 作为水印page_width, page_height = A4[1], A4[0]c = canvas.Canvas(output_buffer, pagesize=(page_width, page_height))try:c.setFillColor(colors.white) # 将背景设置为白色c.setFillColor(colors.red) # 设置字体颜色为红色c.setFont("Helvetica", 12) # 设置字体和字体大小c.setFillAlpha(opacity) # 设置透明度c.setStrokeColor(colors.transparent) # 设置笔触颜色为透明img = ImageReader(watermark_image)if x_position is not None and y_position is not None:c.saveState()c.translate(x_position, y_position)c.rotate(20)c.drawImage(img, 0, 0, width=60, height=25)c.restoreState()else:x = (page_width - 60) / 2y = (page_height - 25) / 2c.saveState()c.translate(x, y)c.rotate(20)c.drawImage(img, 0, 0, width=60, height=25)c.restoreState()except Exception as e:raise ValueError(f"Error drawing image: {e}")c.showPage()c.save()# 将水印 PDF 与原始 PDF 合并watermark_pdf = PdfReader(output_buffer)for page in input_pdf_obj.pages:page.merge_page(watermark_pdf.pages[0])output_pdf_obj.add_page(page)# 保存输出 PDFfinal_output_buffer = BytesIO()output_pdf_obj.write(final_output_buffer)binary_data = final_output_buffer.getvalue()with open(output_pdf, 'wb') as f:output_pdf_obj.write(f)return binary_data -
-
调用示例
-
准备一个PDF文件和一个水印图像文件。
-
调用
add_watermark方法,指定输入PDF、输出路径、水印图像路径等参数。
Python复制
if __name__ == "__main__":from io import BytesIOfrom PyPDF2 import PdfReader, PdfWriterfrom reportlab.pdfgen import canvasfrom reportlab.lib.pagesizes import A4from reportlab.lib import colorsfrom reportlab.lib.utils import ImageReaderimport fitzclass WatermarkPDF:def repair_pdf(self, input_pdf_binary):try:doc = fitz.open(stream=input_pdf_binary, filetype="pdf")repaired_pdf_binary = BytesIO()doc.save(repaired_pdf_binary)doc.close()repaired_pdf_binary.seek(0)return repaired_pdf_binary.read()except Exception as e:print(f"Error repairing PDF: {e}")return input_pdf_binarydef add_watermark(self, input_pdf_binary, output_pdf, watermark_image, x_position=30, y_position=50, opacity=1):repaired_pdf_binary = self.repair_pdf(input_pdf_binary)input_pdf_obj = PdfReader(BytesIO(repaired_pdf_binary))output_pdf_obj = PdfWriter()output_buffer = BytesIO()page_width, page_height = A4[1], A4[0]c = canvas.Canvas(output_buffer, pagesize=(page_width, page_height))try:c.setFillColor(colors.white)c.setFillColor(colors.red)c.setFont("Helvetica", 12)c.setFillAlpha(opacity)c.setStrokeColor(colors.transparent)img = ImageReader(watermark_image)if x_position is not None and y_position is not None:c.saveState()c.translate(x_position, y_position)c.rotate(20)c.drawImage(img, 0, 0, width=60, height=25)c.restoreState()else:x = (page_width - 60) / 2y = (page_height - 25) / 2c.saveState()c.translate(x, y)c.rotate(20)c.drawImage(img, 0, 0, width=60, height=25)c.restoreState()except Exception as e:raise ValueError(f"Error drawing image: {e}")c.showPage()c.save()watermark_pdf = PdfReader(output_buffer)for page in input_pdf_obj.pages:page.merge_page(watermark_pdf.pages[0])output_pdf_obj.add_page(page)final_output_buffer = BytesIO()output_pdf_obj.write(final_output_buffer)binary_data = final_output_buffer.getvalue()with open(output_pdf, 'wb') as f:output_pdf_obj.write(f)return binary_data# 示例调用watermark_pdf = WatermarkPDF()with open("example.pdf", "rb") as f:input_pdf_binary = f.read()watermark_image = "watermark.png"output_pdf = "output_with_watermark.pdf"watermark_pdf.add_watermark(input_pdf_binary, output_pdf, watermark_image) -
实现总结
本代码通过PyMuPDF修复PDF文件,使用reportlab创建水印PDF,并通过PyPDF2将水印合并到原始PDF中。整个过程支持自定义水印的位置、角度和透明度,能够灵活地满足不同场景的需求。代码结构清晰,易于扩展和维护,适合在实际项目中使用。
让转型不迷航——邹工转型手札
相关文章:
【DuodooBMS】给PDF附件加“受控”水印的完整Python实现
给PDF附件加“受控”水印的完整Python实现 功能需求 在实际工作中,许多文件需要添加水印以标识其状态,例如“受控”“机密”等。对于PDF文件,添加水印不仅可以增强文件的可识别性,还可以防止未经授权的使用。本代码的功能需求是…...
【虚幻引擎UE】UE4.23到UE5.5的核心功能变化
简单总结从UE4.23到UE5.5,虚幻引擎的重大变化: 1. WebGL/HTML5 平台支持和像素流 UE4.23-UE4.25:移除官方HTML5支持,改为社区插件维护。 但通过第三方插件(如WebAssemblyWebGPU)可在浏览器运行部分项目。U…...
阿里云《AI 剧本生成与动画创作》解决方案技术评测
引言 随着人工智能技术的发展,越来越多的工具和服务被应用于内容创作领域。阿里云推出的《AI 剧本生成与动画创作》解决方案,利用函数计算 FC 构建 Web 服务,结合百炼模型服务和 ComfyUI 工具,实现了从故事剧本撰写、插图设计、声…...
commons-io 包 IOUtils、FileUtils、FilenameUtils
1. IOUtils void IOUtils.closeQuietly(Closeable... closeables) 无条件关闭流。int IOUtils.copy(InputStream inputStream, OutputStream outputStream) 将字节从InputStream复制到OutputStream,返回复制的长度,流最大不能超过2G,默认缓冲…...
JavaScript 加密技术全面指南
一、加密技术概述 在现代 Web 开发中,加密技术在保护用户数据和确保信息安全方面发挥着至关重要的作用。本文将带您了解 JavaScript 加密技术的基本概念、分类及其在实际应用中的场景。 加密的基本概念 加密是一种将明文数据转换为密文的技术,以保护数…...
【笔记】deep-seek wechat项目
1、安装ollama ollama官网 2、ollama上部署deepseek ollama官网下载deepseek模型(我下了1.5B) 3、配置python 国内镜像源 pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ 安装依赖包 pip install wxauto pip instal…...
FloodFill算法——搜索算法
一、什么是FloodFill算法 FloodFill算法字面意思就是洪水灌溉法,比如我们有这么一块地: 0表示平原,正数表示高地,负数表示凹地,那么当洪水来临时这些凹地会被优先灌满。而我们要找的正是这些联通块,如&…...
H5接入支付宝手机网站支付并实现
小程序文档 - 支付宝文档中心 1.登录 支付宝开放平台 创建 网页/移动应用 2.填写创建应用信息 3.配置开发设置 4.网页/移动应用:需要手动上线。提交审核后,预计 1 个工作日的审核时间。详细步骤可点击查看 上线应用 。应用上线后,还需要完成…...
基于SpringBoot+uniapp的在线办公小程序+LW示例参考
1.项目介绍 系统角色:管理员、普通用户功能模块:员工管理、部门信息管理、职位信息管理、会议记录、待办事项、工资信息、留言板等技术选型:SpringBoot,Vue(后端管理web),uniapp等测试环境&…...
文章精读篇——OMG-LLaVA
题目:OMG-LLaVA: Bridging Image-level, Object-level, Pixel-level Reasoning and Understanding 会议:Conference on Neural Information Processing Systems 2024 论文:http://arxiv.org/abs/2406.19389 主页:https://lxtgh…...
两个同一对象targetList和 sourceList 去重
我现在需要解决的问题是从一个Java的源列表`sourceList`中移除所有在目标列表`targetList`中存在的数据,并且还要去除`targetList`中的重复数据。让我先理清楚这两个问题的思路。 首先,如何快速从`sourceList`中移除含有`targetList`的数据。这里的“含有”应该是指两个列表中…...
软件开发 | GitHub企业版常见问题解读
什么是GitHub企业版? GitHub企业版是一个企业级软件开发平台,专为现代化开发的复杂工作流程而设计。 作为可扩展的平台解决方案,GitHub企业版使组织能够无缝集成其他工具和功能,并根据特定需求定制开发环境,提高整体…...
Docker 网络的配置与管理
目录 查看所有网络 查看网络详细信息 创建新的网络 删除网络 清理未使用的网络 将容器连接到网络 将容器从网络中断开 将容器端口映射到宿主机 绑定到特定 IP 地址 为容器设置自定义 DNS 查看所有网络 docker network ls 功能:列出所有 Docker 网络。 工…...
新手自学:如何用gromacs对简单分子复合物进行伞形采样
1、建立体系: 1、将蛋白的pdb文件转化为gmx: gmx pdb2gmx -f 2BEG_model1_capped.pdb -ignh -ter -o complex.gro 这个网页可以实现将多肽序列转化为pdb: ProBuilder On-line 这个教程的蛋白2BFG包含两条链(chain A和B) 在生成的topol文件中,增加如下的内容,效果就…...
力扣第一题 哈希解法 O(n)时间复杂度
题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那俩个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返…...
elementui: el-dialog的header设置样式不生效
问: el-dialog的header设置样式不生效 回答: 场景: <el-dialogv-model"dialogVisible"width"800px":before-close"beforeClose"append-to-body:close-on-click-modal"false"title"增加文…...
libpcap 的使用
1.libpcap的模式 有线环境: 使用混杂模式promisous,完成监听无线环境: 使用监听模式monitor,完成监听 2.交叉编译libpcap 设置好交叉编译工具链后下载libpcap源码使用configure进行构建:–disable-shared 构建静态库,–host 、…...
ArcGISPro AA表O_Name字段 内容 复制到BB表BB字段里
import arcpy# 设置工作空间和要处理的表路径 resource_shape_table r"AA表.shp" # 源表路径 resource_assets_table r"BB表.shp" # 目标表路径# 使用 SearchCursor 读取源表中的 O_Name 字段 with arcpy.da.SearchCursor(resource_shape_table, [O_Na…...
2.5 使用注解进行单元测试详解
Mockito 使用注解进行单元测试详解 Mockito 提供了一系列注解来简化测试代码的编写,减少手动创建和管理 Mock 对象的样板代码。结合 JUnit 5,可以更高效地构建清晰、易维护的单元测试。 1. 核心注解概览 注解作用Mock创建并注入一个 Mock 对象…...
当没有OpenGL时,Skia如何绘制?
Skia 是可以在没有 OpenGL 的情况下进行图形绘制的,但是具体能否成功绘制图形,取决于 Skia 是如何配置的,以及平台上是否提供了其他的底层图形 API。 Skia 的底层依赖 Skia 的目标是提供一种跨平台的 2D 图形绘制接口。为了加速图形渲染&…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
