【DuodooBMS】给PDF附件加“受控”水印的完整Python实现

news2025/5/14 10:08:00

给PDF附件加“受控”水印的完整Python实现

功能需求

在实际工作中,许多文件需要添加水印以标识其状态,例如“受控”“机密”等。对于PDF文件,添加水印不仅可以增强文件的可识别性,还可以防止未经授权的使用。本代码的功能需求是:

  1. 修复PDF文件:在添加水印之前,确保PDF文件是完整且可读的,避免因文件损坏导致操作失败。

  2. 添加水印:在PDF的每一页上添加指定的水印图像或文字,水印可以设置位置、角度和透明度。

  3. 保存输出:将添加水印后的PDF文件保存到指定路径,并返回其二进制数据以便后续处理。

实现过程

  1. 修复PDF文件

    • 使用PyMuPDF库打开PDF文件,并尝试修复。如果文件损坏,PyMuPDF可以尝试修复并保存为一个新的二进制流。

    • 如果修复失败,则直接返回原始的PDF二进制数据。

    Python复制

    def repair_pdf(self, input_pdf_binary):
        try:
            # 使用 PyMuPDF 打开并修复 PDF
            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_binary
  2. 添加水印

    • 使用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):
        # 尝试修复 PDF
        repaired_pdf_binary = self.repair_pdf(input_pdf_binary)
        input_pdf_obj = PdfReader(BytesIO(repaired_pdf_binary))  # 从二进制数据中读取 PDF
        output_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) / 2
                y = (page_height - 25) / 2
                c.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)
    
        # 保存输出 PDF
        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
  3. 调用示例

    • 准备一个PDF文件和一个水印图像文件。

    • 调用add_watermark方法,指定输入PDF、输出路径、水印图像路径等参数。

    Python复制

    if __name__ == "__main__":
        from io import BytesIO
        from PyPDF2 import PdfReader, PdfWriter
        from reportlab.pdfgen import canvas
        from reportlab.lib.pagesizes import A4
        from reportlab.lib import colors
        from reportlab.lib.utils import ImageReader
        import fitz
    
        class 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_binary
    
            def 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) / 2
                        y = (page_height - 25) / 2
                        c.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中。整个过程支持自定义水印的位置、角度和透明度,能够灵活地满足不同场景的需求。代码结构清晰,易于扩展和维护,适合在实际项目中使用。

 

让转型不迷航——邹工转型手札

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GitCode 助力 Dora SSR:开启游戏开发新征程

项目仓库(点击阅读原文链接可直达) https://gitcode.com/ippclub/Dora-SSR 跨越技术藩篱,构建游戏开发乐园 Dora SSR 是一款致力于打破游戏开发技术壁垒的开源游戏引擎。其诞生源于开发者对简化跨平台游戏开发环境搭建的强烈渴望&#xff0…

Mediamtx+Python读取webrtc流

一、功能思路: 1、我采用ffmpeg -re -stream_loop -1 -i xcc.mp4 -c:v libx264 -profile:v baseline -x264opts "bframes0:repeat_headers1" -b:v 1500k -preset fast -f flv rtmp://127.0.0.1:1835/stream/111推流到mediamtx的rtmp上 2、通过mediamtx自…

每日一题——矩阵最长递增路径

矩阵最长递增路径问题 题目描述数据范围:进阶要求:示例示例 1示例 2 题解思路算法步骤:代码实现代码解释复杂度分析总结 题目描述 给定一个 n 行 m 列的矩阵 matrix,矩阵内所有数均为非负整数。你需要在矩阵中找到一条最长路径&a…

【CLIP系列】4:目标检测(ViLD、GLIP)

目录 1 ViLD2 GLIP2.1 前言2.2 损失计算2.3 模型框架 1 ViLD OPEN-VOCABULARY OBJECT DETECTION VIA VISION AND LANGUAGE KNOWLEDGE DISTILLATION 从标题就能看出来,作者是把CLIP模型当成一个Teacher,去蒸馏他自己的网络,从而能Zero Shot去…

Cesium for Unity Linux版本

Cesium for Unity 直装不支持Linux 参照官方开发流程一些操作命令issues 宝藏最后运行图 参照官方开发流程 https://github.com/CesiumGS/cesium-unity/blob/main/Documentation~/developer-setup.md 系统已经安装过dotnet和cmake xuefeixuefei:~$ dotnet --version 9.0.102 …

关于 IoT DC3 中驱动(Driver)的理解

在开源IoT DC3物联网系统中,驱动(Driver)扮演着至关重要的角色,它充当了软件系统与物理设备之间的桥梁。驱动的主要功能是依据特定的通信协议连接到设备,并根据设备模板中配置的位号信息进行数据采集和指令控制。不同的…

EasyRTC嵌入式WebRTC视频通话SDK支持Web浏览器、Linux、ARM、Android、iOS

随着互联网技术的飞速发展,实时通信(RTC)已经成为现代应用中不可或缺的一部分。无论是视频会议、在线教育、远程医疗,还是社交娱乐,实时通信技术都在其中扮演着重要角色。 然而,WebRTC技术在PC和移动端的支…

数据库脚本MySQL8转MySQL5

由于生产服务器版本上部署的是MySQL5,而开发手里的脚本代码是MySQL8。所以只能降版本了… 升级版本与降级版本脚本转换逻辑一样 MySQL5与MySQL8版本SQL脚本区别 大多数无需调整、主要是字符集与排序规则 MySQL5与MySQL8版本SQL字符集与排序规则 主要操作&…

【PGCCC】commit_delay 对性能的提升:PostgreSQL 基准测试

通过禁用参数可以来调整事务工作负载synchronous_commit。该措施有惊人效果。但在操作系统崩溃期间丢失已提交事务的可能性使其成为许多应用程序无法启动的因素。因此我决定写下来。 WAL 刷新是事务数据库工作负载的瓶颈 为了确保已提交的事务不会丢失,PostgreSQL…

AI大模型随机初始化权重并打印网络结构方法(以Deepseekv3为例,单机可跑)

背景 当前大模型的权重加载和调用,主要是通过在HuggingFace官网下载并使用transformer的库来加以实现;其中大模型的权重文件较大(部分>100GB),若只是快速研究网络结构和数据流变化,则无需下载权重。本文…

Ollama+WebUI+DeepSeek部署自己的本地大模型

前言 使用AI几乎成为互联网工作者必备技能了,DeepSeek的出现把AI再次推向高潮,在本文中,我们将带领大家借助 Ollama、WebUI 和 deepseek 这三个工具,成功搭建属于自己的本地大模型环境。Ollama 作为一款轻量级的大模型运行工具&a…

(篇六)基于PyDracula搭建一个深度学习的软件之新版本ultralytics-8.3.28调试

ultralytics-8.3.28版本debug记录 1传入文件 代码太多不粘贴在这里了,完整代码写在了篇三 def open_src_file(self):config_file config/fold.jsonconfig json.load(open(config_file, r, encodingutf-8))open_fold config[open_fold]if not os.path.exists(op…

NLP Word Embeddings

Word representation One-hot形式 在上一周介绍RNN类模型时,使用了One-hot向量来表示单词的方式。它的缺点是将每个单词视为独立的,算法很难学习到单词之间的关系。 比如下面的例子,即使语言模型已经知道orange juice是常用组合词&#xf…

使用HX搭建UNI-APP云开发项目(适合新手小白与想学云开发的宝子)

什么是uni-app云开发 uni-app云开发是uni-app提供的一套后端服务,它可以帮助开发者快速搭建起一个完整的后端服务,包括数据库、云函数、存储等。开发者只需要关注前端页面的开发,后端服务由uni-app云开发提供。 uni-app云开发的优势: 快速搭建后端服务:uni-app云开发提供了…

sql:时间盲注和boolen盲注

关于时间盲注&#xff0c;boolen盲注的后面几个获取表、列、具体数据的函数补全 时间盲注方法 import time import requests# 获取数据库名 def inject_database(url):dataname for i in range(1, 20):low 32high 128mid (low high) // 2while low < high:payload &q…

【STM32】ADC|多通道ADC采集

本次实现的是ADC实现数字信号与模拟信号的转化&#xff0c;数字信号时不连续的&#xff0c;模拟信号是连续的。 1.ADC转化的原理 模拟-数字转换技术使用的是逐次逼近法&#xff0c;使用二分比较的方法来确定电压值 当单片机对应的参考电压为3.3v时&#xff0c;0~ 3.3v(模拟信…

arcgis for js实现层叠立体效果

在 Web 开发中&#xff0c;利用 ArcGIS for JS 实现一些炫酷的地图效果能够极大地提升用户体验。本文将详细介绍如何使用 ArcGIS for JS 实现层叠立体效果&#xff0c;并展示最终的效果图。 效果图 实现思路 要实现层叠立体效果&#xff0c;关键在于获取边界图形的坐标&#xf…

多模态本地部署和ollama部署Llama-Vision实现视觉问答

文章目录 一、模型介绍二、预期用途1. 视觉问答(VQA)与视觉推理2. 文档视觉问答(DocVQA)3. 图像字幕4. 图像-文本检索5. 视觉接地 三、本地部署1. 下载模型2. 模型大小3. 运行代码 四、ollama部署1. 安装ollama2. 安装 Llama 3.2 Vision 模型3. 运行 Llama 3.2-Vision 五、效果…

【DeepSeek】deepseek可视化部署

目录 1 -> 前文 2 -> 部署可视化界面 1 -> 前文 【DeepSeek】DeepSeek概述 | 本地部署deepseek 通过前文可以将deepseek部署到本地使用&#xff0c;可是每次都需要winR输入cmd调出命令行进入到命令模式&#xff0c;输入命令ollama run deepseek-r1:latest。体验很…

【Git版本控制器】:第一弹——Git初识,Git安装,创建本地仓库,初始化本地仓库,配置config用户名,邮箱信息

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ 相关笔记&#xff1a; https://blog.csdn.net/dj…