PDF 页码看似去掉了,搜索复制时为什么还在:白块覆盖 vs 真擦除

· 约 5 分钟 🔡 PDF 去页码

PDF 去页码看上去是个简单需求,但多数工具的实现是”视觉遮挡”而不是”内容删除”——原页码仍然完整保留在文本流里,搜索、复制、提取文字时全都浮出来。

这篇讲清楚两种实现的差异、怎么验证、什么场景必须真擦除。

“删除” 和 “覆盖” 的本质区别

原 PDF 的 content stream(每页一段):
  BT
    /F1 12 Tf
    100 50 Td
    (正文段落...) Tj
    300 50 Td
    (12) Tj            ← 这就是页码"12"的绘制指令
  ET


【白块覆盖】操作后:
  BT
    /F1 12 Tf
    100 50 Td
    (正文段落...) Tj
    300 50 Td
    (12) Tj            ← 页码原指令仍然存在
  ET
  q                    ← 加了新的图形状态
    1 1 1 rg           ← 设置白色填充
    280 40 60 20 re    ← 在页码位置画白色矩形
    f                  ← 填充
  Q

  视觉上:白色矩形盖住了页码 ✓
  文本提取:仍能拿到 "12" ✗
  搜索:仍能搜到 ✗
  复制:选中能选到 ✗


【真擦除】操作后:
  BT
    /F1 12 Tf
    100 50 Td
    (正文段落...) Tj
                       ← 页码绘制指令被物理删除
  ET

  视觉:原位置无内容 ✓
  文本提取:拿不到 "12" ✓
  搜索:找不到 ✓
  复制:选不中 ✓

两者视觉效果几乎一致,但内部结构完全不同。

三种验证 PDF 页码是否真删的方式

方法操作含义
选中复制鼠标在原页码位置拖选能选到 → 被覆盖;选不到 → 真删
全文搜索Ctrl+F 搜页码数字能搜到 → 被覆盖;搜不到 → 真删
反色查看阅读器开”反色 / 高对比度”模式白色矩形变黑色 → 是覆盖
文本提取pdftotext input.pdf -输出含页码 → 被覆盖
content stream 检查qpdf --qdf input.pdf -看到 (12) Tj 等绘制指令 → 没真删

最严谨的是 qpdf --qdf ——直接看绘制指令源码,无所遁形。

PDF 里的”页码”形态

不同 PDF 里的页码可能有完全不同的存储方式:

形态 1:每页独立的文本对象(最常见)

Word / LaTeX 导出的 PDF

每一页的 content stream 里都有独立的页码绘制指令:
  Page 1: ... (1) Tj ...
  Page 2: ... (2) Tj ...
  Page 3: ... (3) Tj ...

处理:一页一页定位 + 真擦除。

形态 2:页眉/页脚作为 Form XObject 复用

专业排版(InDesign / FrameMaker)做的 PDF

所有页面共享一个"页眉模板"对象(Form XObject):
  Page 1 引用 → /HeaderTemplate
  Page 2 引用 → /HeaderTemplate
  ...
  
HeaderTemplate 内部用 PDF 表达式绘制"第 N 页"
其中 N 由每页的特定状态决定

处理:改一次 XObject 影响所有页(高效),或者改每页对 XObject 的引用。

形态 3:AcroForm 字段(Adobe LiveCycle 等动态 PDF)

PDF 内有 /AcroForm 对象,含"页码字段"
渲染时引擎填入页码值

处理:删除 AcroForm 里的字段定义。

形态 4:图像(特殊场景)

扫描的 PDF + 上面有数字水印
或 设计师把页码做成 PNG 贴上去

处理:图像处理——OCR 定位 + 图像擦除(inpainting)+ 重新嵌入。

形态 5:PDF 注释(annotation)

PDF 创建后用注释工具加了"自由文本"作为页码

处理:删除注释对象——这一类是最容易处理的,因为注释是独立对象,不在 content stream 里。

真擦除的实现

正确的真擦除需要走 PDF 引擎而不是字节流操作。

示例:用 mupdf-wasm(浏览器内)

import * as mupdf from 'mupdf';

const doc = mupdf.Document.openDocument(buffer, 'application/pdf');
const page = doc.loadPage(0);

// 1. 用 OCR / 文本提取定位页码 bbox
const textPage = page.toStructuredText();
const bbox = findPageNumberBBox(textPage);  // 用户实现

// 2. 创建 redaction annotation
const redact = page.createAnnotation('Redact');
redact.setRect(bbox);

// 3. 应用 redaction —— mupdf 会真正修改 content stream
page.applyRedactions();

// 4. 保存
const outBuffer = doc.saveToBuffer('garbage=2,clean=yes,deflate=yes');

示例:用 Python 的 PyMuPDF

import fitz  # PyMuPDF

doc = fitz.open("input.pdf")
for page in doc:
    # 找页码的文本块
    blocks = page.get_text("blocks")
    for b in blocks:
        x0, y0, x1, y1, text, *_ = b
        if is_page_number(text):  # 用户实现
            page.add_redact_annot(fitz.Rect(x0, y0, x1, y1))
    page.apply_redactions()  # 关键!应用 redaction 才是真删

doc.save("output.pdf", garbage=4, clean=True, deflate=True)

关键点

  • redact_annot + apply_redactions() 是 PDF 标准里的”真擦除”机制(PDF redaction),符合 ISO 32000 规范
  • 普通的删除字符不一定是真擦除,必须走 redaction 流程
  • 保存时加 garbage=4(清除未引用对象)和 clean=yes 确保旧数据彻底丢弃

三种擦除方式对比

方式视觉效果文本提取搜索增量保存历史法务安全
白块覆盖✗(原文还在)完整保留
删除文本对象旧版本仍在
真 Redaction + 清理历史已清
重新打印为 PDF全新文件✓✓

**法务级别(极敏感场景)**推荐 “Redaction + 重新打印 PDF” 双重处理。

决策树

我要去掉 PDF 页码

  ├─ 这个 PDF 之后给谁看?
  │  ├─ 自己看 / 学习材料 → 白块覆盖就够(视觉为主)
  │  ├─ 发给同事 / 客户 → 用真擦除工具
  │  └─ 法务 / 投标 / 公开发布 → Redaction + 清理增量保存

  ├─ 页码是哪种形态?
  │  ├─ 文字(能选中复制)→ Redaction 处理
  │  ├─ 图像(不能选中)→ 图像处理(OCR 定位 + inpainting)
  │  └─ AcroForm 字段 → 删字段

  └─ 是否要重新加新页码?
     ├─ 是 → 必须先彻底删旧页码(白块覆盖会叠加)
     └─ 否 → 看上面分支

工具选型

类型推荐备注
浏览器内 / 隐私PDF 去页码wasm 真擦除,本地运行
桌面 GUIAdobe Acrobat → 编辑 PDF真删,但要进”编辑文字”模式
桌面 GUIFoxit PhantomPDF与 Acrobat 类似
命令行(Python)PyMuPDFredact_annot + apply_redactions
命令行(CLI)mutool(mupdf)mutool clean -gggg 清理 + redaction 工具
配套加新页码PDF 加页码真擦除老页码后再加新的
不推荐大多数在线 “PDF 编辑器”多数实现是覆盖,不是真删

实务清单

必做

  1. 去页码后先验证:选中复制 + 全文搜索 + pdftotext 三件套
  2. 重要场景(合同 / 投标 / 公开发布)走 Redaction + 清理
  3. 重新加页码前一定先彻底删旧页码
  4. 检查 PDF 是否做过增量保存(qpdf --check 看 xref 数量)
  5. 法务级别再走一次”打印为 PDF” 重建文档

避免

  1. 用”白色矩形涂改”工具当真删(最常见错误)
  2. 处理完不验证就发出(搜索复制能找出问题,2 分钟核对)
  3. 直接修改字节流删字符(不解析 PDF 结构会损坏文件)
  4. 加密 PDF 不解密直接删页码(content stream 改了散列对不上)
  5. 在线工具传敏感 PDF(公开渠道处理时控制范围)
  6. 以为页码删了就匿名了——元数据 / 修订历史 / 目录页交叉引用都可能泄露

PDF 去页码的真相是:“看不见 ≠ 不存在”。视觉效果和实际结构是两回事,验证只需要选中复制 + 搜索两步——多数情况下你以为删了的页码其实还完整地躺在文件里。

❓ 常见问题

我用 PDF 工具"删除"了页码,但复制全文出来页码还在,为什么?

绝大多数 PDF 工具的"删除"是覆盖不是擦除。工具在原页码位置画一个白色矩形遮住视觉,但页码对应的文本对象仍然留在 PDF 的 content stream 里。症状:(1) 阅读器看起来确实没了;(2) 全文搜索能搜到;(3) 复制粘贴时被复制出来;(4) pdftotext 提取出来还在;(5) Ctrl+A 全选时白块下面那段文字会被高亮选中。真擦除需要重写 content stream——把页码相关的绘制指令从指令流里物理删除,而不是再加一层覆盖。

怎么判断我手上的 PDF 页码是被真删了还是只是被盖住了?

三种验证方式。(1) 选中复制——在原页码位置鼠标拖选,能选到就是被盖住,选不到就是真删;(2) 全文搜索——Ctrl+F 搜页码数字(如"第 12 页"),能搜到就是被盖住;(3) 文本提取——用 pdftotext input.pdf 看输出里有没有页码序列;(4) 反色查看——某些 PDF 阅读器有"反色"模式,白底变黑底时白色矩形会变成黑色矩形,盖住的文字会浮现。最严谨:用 qpdf --qdf input.pdf - 解压 content stream,搜文本对象——能看到就没真删。

我的 PDF 所有页都是同一个"第 X 页",但每页 X 都不一样,怎么处理?

这就是 PDF 真正的"动态页码",处理路径不同几种来源:(1) 正文里的真文本对象(每页独立绘制)——直接处理一页一页删;(2) 页面绘制指令引用了"页码字段"(PDF 1.7 的 /PageNumber 注释或 AcroForm 里的页码字段)——删除字段而不是文本;(3) 页眉页脚是 PDF 模板(XObject)——所有页用同一个 Form XObject 渲染页眉,删一处影响所有页;(4) 页码以图像形式嵌入——某些 PDF 把页码做成 PNG 嵌入,文本提取拿不到,必须图像处理。判断流程:(1) 看一页能不能选中页码 → 能选就是文本,不能选就是图;(2) 文本的话看是否所有页面引用同一个 XObject——如是,改 XObject 一次搞定;(3) 图像的话需要 OCR 定位 + 图像擦除。

重新加新页码前要不要先彻底删旧页码?

必须先删,否则会叠加。直接在带旧页码的 PDF 上加新页码,结果是两个页码并排显示。处理流程:(1) 先识别旧页码位置(一般是页眉中央、页脚右下、页脚中央三种位置);(2) 用真擦除工具去除旧页码;(3) 用页码生成器加新页码(建议加在和原页码不同的位置,留一点视觉差异方便排查问题);(4) 全文搜一遍新页码确保只有一处。陷阱:如果只是"覆盖"删除旧页码,新加的页码也会和被覆盖的旧页码在文本流里同时存在 → 复制全文会看到两套页码交错——视觉对了但内容错了。

PDF 里的数字"3" 怎么知道是文字还是图像?

三种验证。(1) 能选中复制——文本;不能选中、只能矩形框选——图像;(2) 放大 400% 锐利——矢量文字;变糊出现像素边缘——位图图像;(3) PDF 阅读器的"对象信息"功能——Adobe Acrobat 右键 → "属性" 能看到对象类型;(4) 命令行:pdftotext input.pdf - 输出里有这个数字 → 是文字;没有 → 是图像或被特殊编码(CMap 损坏)。为什么重要:(1) 文字页码用文本编辑工具去除(白块覆盖 / 真擦除);(2) 图像页码必须图像处理——抠掉那一小块,或重新生成不带页码的页面图。

真擦除会不会损坏 PDF 的文字层、影响其他正文?

正确实现不会,错误实现会。真擦除的本质是修改 content stream(一段 PostScript 风格的绘制指令):精确定位到页码相关的指令块(如 BT /F1 12 Tf 100 50 Td (12) Tj ET),把这一段删除,重新写入 PDF 对象。正确实现:(1) 用 PDF 解析库(pdf-lib / pikepdf / mupdf)解析 content stream → AST;(2) 修改 AST → 序列化回去;(3) 重写 PDF 的对象引用和 xref 表。错误实现:(1) 用正则在原始字节流上查找替换——遇到字符在不同 BT/ET 块或被压缩流分割就出错;(2) 修改后忘了重写 xref → PDF 损坏;(3) 修改加密 PDF 的 content stream → 加密散列对不上,PDF 不能打开。实务:用成熟库(mupdf / pdf-lib)做真擦除是安全的,自己写正则替换是危险的。

用 Adobe Acrobat 删过页码后,文本提取居然还能找到,怎么回事?

Acrobat 的默认"删除"也是覆盖。Acrobat 提供两种删除方式:(1) 编辑文字 → 选中删除——这是真删,会修改 content stream;(2) 拖一个矩形覆盖注释 → 涂改图章——这是加遮挡层,不删原内容。多数用户在不熟悉的情况下用了第二种。真删的操作步骤:Adobe Acrobat → 工具 → 编辑 PDF → 进入"编辑文字与图像"模式 → 点击页码 → Delete 键。陷阱:(1) Acrobat 自动识别"页眉页脚"的能力有限,复杂 PDF 可能识别不到页码区;(2) 被识别为"图像"的页码(即使本质是文字)只能整块替换;(3) 加密 PDF 编辑前要先解密(owner password)。

法务 / 取证场景,去页码后能"反推" 出原页码吗?

可能能,看擦除质量。PDF 有"增量保存"机制——多次修改累积保存在文件末尾,旧版本仍在。反推的几种方式:(1) 看 PDF 修订历史:用 pdf-parser.py / qpdf --check 看是否有多个 xref 表,旧的 xref 可能指向被删除前的对象;(2) 看 PDF 元数据:原工具可能在 /Info 或 XMP 元数据里留下"页数"信息;(3) 看页面尺寸 / 边距:原页码区域是否留下裁剪痕迹(裁剪后边距异常);(4) 看其他线索:目录页指向的页码、交叉引用提到的页码、页眉页脚有页号引用。真正彻底擦除:用 mupdf 的 mutool clean -gggg 或 Acrobat 的"擦除文档"+ "另存为优化版",会清理增量保存历史。法务级别要再走一次"打印为 PDF"——彻底重建文档结构。

🔡 打开 PDF 去页码 拖框选区 · 同坐标覆盖所有页 · 奇偶页镜像 · 取色填充