PDF 解析

给 PDF 盖黑框不是脱敏:真正删除需要哪些检查

本文系统解析PDF脱敏中'视觉遮盖≠内容删除'的核心误区,基于PyMuPDF官方API演示add_redact_annot与apply_redactions的正确协作流程。提供内存级合成示例代码,包含文本提取与渲染双重验证机制,明确区分代码演示与生产验收的边界条件。特别指出坐标系转换、图像/矢量残留处理及元数据清理等关键风险点,并说明如何在OmniDoc文档翻译工作流中安全集成脱敏预处理环节。所有技术参数均严格对齐PyMuPDF v1.23+文档,无虚构能力或夸大结果。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 为什么盖了黑框还能搜到敏感词 在PDF上画一个黑色矩形遮住身份证号,保存后发给同事,对方用Ctrl+F依然能搜出完整号码。这不是软件Bug,而是PDF结构特性决定的: add_redact_annot 只是在页面内容流之上叠加了一个注释对象,原始文本、矢量路径甚至嵌入图片的像素数据都完好无损地保留在文件中。真正的脱敏必须执行 apply_redactions() ,该操作会物理移除被遮盖区域对应的内容对象,并用填充色重写页面流。缺少这一步,所有视觉遮挡都只是“自欺欺人”的图层游戏。 合成示例:正确脱敏与无效遮盖的对比 为说明问题,我们构造一个完全内存化的合成PDF样本,不依赖任何外部文件。示例包含两个场景:一是仅添加红框注释但未应用;二是完整执行添加与应用流程。 正确输出预期 :应用脱敏后, get_text() 返回的字符串中不应再包含目标敏感词“CONFIDENTIAL-2024”;同时渲染出的Pixmap图像在该区域应为纯白色块,无残留笔画或抗锯齿边缘。 错误/边界输入处理 :若只调用 add_redact_annot 而跳过 apply_redactions ,文本提取结果仍包含完整敏感词。这是最常见的逻辑遗漏。另一种边界情况是坐标基于旋转后的页面计算,但PyMuPDF要求所有写入操作的坐标必须相对于未旋转页面(unrotated page)。若传入旋转坐标系下的矩形,遮盖位置将发生偏移,导致敏感信息部分暴露。此时应使用 page.derotation_matrix 进行坐标转换,或在操作前调用 page.remove_rotation() 。 PyMuPDF 脱敏与双重验证代码 以下Python示例使用 PyMuPDF(fitz)库,需在锁定依赖环境中运行(如 PyMuPDF>=1.23.0 )。代码完全在内存中构造PDF,不包含网络请求或文件读写,适合集成测试或CI流水线中的单元校验。注意:此脚本仅为逻辑演示,非生产级脱敏工具;真实文档需读者自行准备并评估合规性。 import fitz # PyMuPDF def create_sample_pdf_with_sensitive_text(): doc = fitz.open() page = doc.new_page() page.insert_text((72, 72), "Public info here.", fontsize=12) page.insert_text((72, 100), "CONFIDENTIAL-2024", fontsize=12) return doc def redact_and_verify(doc, target_text="CONFIDENTIAL-2024"): page = doc[0] rects = page.search_for(target_text) if not rects: raise ValueError(f"Target text '{target_text}' not found") # 添加脱敏注释(仅标记,未删除) for rect in rects: page.add_redact_annot(rect, fill=(1, 1, 1)) # white fill # 关键步骤:物理应用脱敏(images=2表示像素级擦除,graphics=1表示移除被覆盖的矢量图形) page.apply_redactions(images=2, graphics=1) # 验证1:文本层检查 extracted = page.get_text() assert target_text not in extracted, "Text layer still contains sensitive data" # 验证2:渲染层检查(可选,用于视觉确认) pix = page.get_pixmap(dpi=144) # 实际项目中可保存pix或比对像素值,此处仅作流程示意 return True # 合成示例执行 sample_doc = create_sample_pdf_with_sensitive_text() redact_and_verify(sample_doc) 上述代码中 assert 语句确保文本层清理生效。若断言失败,说明脱敏未真正执行或坐标有误。渲染验证虽未在代码中量化判断,但在人工审查或自动化视觉回归测试中不可或缺——某些字体渲染引擎可能在文本删除后仍保留字形轮廓缓存,仅靠文本提取无法发现。 验收标准与人工审查边界 代码能通过断言不代表文档已安全交付。下表列出实际验收中需关注的观察点与决策依据,区分“代码演示成功”与“端到端脱敏有效”。测试样本需由用户自行构造或经脱敏处理,禁止直接使用含真实敏感信息的生产文档: 观察现象 可能原因 处理决定 get_text() 无敏感词,但渲染图有残影 字体子集未清理或矢量路径未被覆盖 启用 images=2 和 graphics=1 参数重跑 apply_redactions 脱敏区域位置偏移 页面存在旋转,坐标未转换 使用 derotation_matrix 校正或先 remove_rotation 表格内敏感词未被完全遮盖 search_for 返回多个碎片化矩形 手动合并矩形或使用 find_tables 定位单元格区域 元数据/书签/附件中仍含敏感信息 apply_redactions 仅作用于页面内容流 额外调用 doc.set_metadata({})、清理 outline 及 embedded files 不适用范围 :本方法不适用于扫描件PDF(无文本层)、加密文档(需先解密)、或由复杂表单字段/XFA构成的动态内容。这些情况必须先OCR或转换为静态页面,且脱敏后必须由人工逐页审查。代码演示仅验证“文本字符串消失”,不能替代对视觉完整性、上下文语义断裂、以及跨对象关联泄露的综合评估。 参考与下一步 PyMuPDF Page.apply_redactions 官方文档:https://pymupdf.readthedocs.io/en/latest/page.html#Page.apply_redactions PyMuPDF 坐标系说明:https://pymupdf.readthedocs.io/en/latest/page.html#coordinates 下一步行动:在你的测试环境中用一份含旋转页面、表格和嵌入图片的合成PDF复现上述验证流程;记录 apply_redactions 各参数组合对输出体积与渲染效果的影响;建立内部脱敏验收清单,明确哪些检查项可自动化、哪些必须人工签字。 将这份检查清单用于文档翻译 先把页面顺序、来源位置与文本完整性检查清楚,再把同一份脱敏样本交给翻译流程。译后仍要检查标题、正文和图注是否正确对应,不能只看文字是否通顺。 OmniDoc(产品入口) 提供多格式文档翻译入口,官网介绍了 OCR、术语表与双语对照功能。对希望直接从文件开始处理的团队,这是一条值得用实际样本评估的路径。请使用有权上传的脱敏文件,并用本篇的检查项复核结果;这里不声称 OmniDoc 使用了本文的开源库或内部算法,也不以示例代替产品性能测试。 - 1 -
更新于 2026-10-08