PDF 中的一段文字被黑色矩形盖住后,画面上可能看不见,文件里的文字却未必被删除。判断处理是否生效,要检查导出后的文件。
下面用 ComPDF Web SDK 对同一份教学 PDF 做三种处理:普通矩形遮挡、仅创建密文标记、应用密文。随后重新读取输出,核对目标文字和需要保留的内容。

先看输出结果
教学文件含有两个标记:TEST-SECRET-ALPHA 是本次处理目标,KEEP-PUBLIC-BETA 应保持原样。它们是虚构教学文字,不是账号、密钥或客户信息。
| 处理方式 | 目标文字能否提取 | 保留标记是否还在 |
|---|---|---|
| 普通黑色矩形 | 能 | 在 |
| 创建密文标记,未应用 | 能 | 在 |
| 应用密文后导出 | 本次未提取到 | 在 |

图:三个真实输出 PDF 的独立渲染与文字提取核对。仅创建密文标记不会删除正文;导出文件在其他阅读器中的标记外观可能不同,不能靠是否出现黑块判断已经应用。
这组对照使用 Web SDK 2.9.4。关键 PDF 处理由 ComPDF 完成,后面的 Python 读取仅用于独立核验输出。
1. 准备最小项目
本例在 Node.js 24.18.0、Vite 7.1.9 下运行。创建目录并安装依赖:
sh
mkdir compdf-redaction-demo
cd compdf-redaction-demo
npm init -y
npm install --save-exact @compdfkit_pdf_sdk/webviewer@2.9.4 vite@7.1.9
mkdir -p public/webviewer public/samples
cp -a node_modules/@compdfkit_pdf_sdk/webviewer/dist/. public/webviewer
从SDK 试用许可取得 Web 许可,在项目根目录创建 .env.local:
dotenv
VITE_COMPDF_LICENSE=YOUR_WEB_SDK_LICENSE
这是 Web SDK 许可,不是 Cloud API 的 x-api-key。许可配置见添加 Web 许可密钥。公开示例只保留占位符,实际许可单独配置。
把待检查的 PDF 放到 public/samples/redaction-source.pdf。如果需要快速制作与下文坐标对应的最小教学输入,可以运行:
sh
python3 -m pip install reportlab
python
# make_sample.py
from reportlab.pdfgen import canvas
c = canvas.Canvas('public/samples/redaction-source.pdf', pagesize=(595, 842))
c.setFont('Helvetica', 18)
c.drawString(60, 500, 'TEST-SECRET-ALPHA')
c.drawString(60, 410, 'KEEP-PUBLIC-BETA')
c.save()
sh
python3 make_sample.py
配图中的输入另加了品牌和中文教学说明,两个标记的位置与上面的最小输入相同。实际文件需要按自己的页面坐标选区域,不能照搬矩形。
2. 添加普通遮挡,再创建密文标记
在根目录保存下面的 index.html:
html
<!doctype html>
<html lang="zh-CN">
<meta charset="UTF-8">
<title>PDF 密文验证</title>
<style>
body { font-family: sans-serif; margin: 16px; }
button { margin: 0 6px 10px 0; padding: 8px; }
#viewer { height: 80vh; }
</style>
<button id="reset" disabled>重新打开原件</button>
<button id="cover" disabled>添加普通遮挡</button>
<button id="mark" disabled>创建密文标记</button>
<button id="apply" disabled>应用密文</button>
<button id="save" disabled>下载当前 PDF</button>
<p id="status">正在加载</p>
<div id="viewer"></div>
<script type="module">
import WebViewer from '@compdfkit_pdf_sdk/webviewer';
const status = document.querySelector('#status');
const core = await WebViewer.init({
path: '/',
pdfUrl: '/samples/redaction-source.pdf',
license: import.meta.env.VITE_COMPDF_LICENSE
}, document.querySelector('#viewer'));
const doc = core.docViewer;
let stage = 'source';
doc.addEvent('documentloaded', () => {
document.querySelectorAll('button').forEach(b => b.disabled = false);
status.textContent = '原件已打开';
});
// Web SDK 的页面坐标以左上角为原点,pageIndex 从 0 开始。
const rect = { left: 55, top: 320, right: 310, bottom: 353 };
document.querySelector('#reset').onclick = async () => {
await core.UI.loadDocument('/samples/redaction-source.pdf');
stage = 'source';
};
document.querySelector('#cover').onclick = async () => {
await doc.addAnnotations({
type: 'square', pageIndex: 0, rect,
borderWidth: 0, opacity: 1, fillTransparency: 1,
fillColor: '#000000', color: '#000000',
borderColor: '#000000', borderStyle: 'solid', dashes: []
});
stage = 'covered';
status.textContent = '已添加普通矩形';
};
document.querySelector('#mark').onclick = async () => {
await doc.addAnnotations({
type: 'redact', pageIndex: 0, rect,
fillColor: '#000000', erasure: false
});
stage = 'marked';
status.textContent = '已标记,尚未应用';
};
document.querySelector('#apply').onclick = async () => {
await doc.applyRedactions('all');
stage = 'redacted';
status.textContent = '已应用;请导出后核查';
};
document.querySelector('#save').onclick = async () => {
const data = await doc.exportPDF(true);
const url = URL.createObjectURL(new Blob([data], { type: 'application/pdf' }));
const a = document.createElement('a');
a.href = url;
a.download = stage + '.pdf';
a.click();
setTimeout(() => URL.revokeObjectURL(url), 10000);
};
</script>
</html>
启动:
sh
npx vite --host 127.0.0.1
打开终端显示的本地地址。这里将 SDK 静态资源复制到 public/webviewer,初始化的 path 使用 /,使阅读器入口落到 /webviewer/index.html;部署到子目录时,应一起调整资源与 PDF 路径。
按下面顺序操作,确保三份输出来自同一原件:
- 添加普通遮挡,下载
covered.pdf。 - 重新打开原件,创建密文标记,先下载
marked.pdf。 - 对当前标记应用密文,再下载
redacted.pdf。
ComPDF 的密文处理分为标记和应用两个步骤。addAnnotations({ type: 'redact', ... }) 只是标记区域;本例随后调用 applyRedactions('all') 才处理标记内容。保留原件,在副本上完成验证。

图:本地教学项目嵌入 ComPDF Web SDK 的实际操作界面,目标区域已应用密文。下文仍检查导出文件。
3. 独立核对导出文件
把三份文件放在同一目录,安装读取工具:
sh
python3 -m pip install pypdf
python
# verify_outputs.py
from pathlib import Path
from pypdf import PdfReader
for name in ['covered', 'marked', 'redacted']:
text = '\n'.join(page.extract_text() or ''
for page in PdfReader(Path(name + '.pdf')).pages)
print(name, {
'target_found': 'TEST-SECRET-ALPHA' in text,
'retained_found': 'KEEP-PUBLIC-BETA' in text,
})
sh
python3 verify_outputs.py
本次结果:
text
covered target_found=True retained_found=True
marked target_found=True retained_found=True
redacted target_found=False retained_found=True
再用另一阅读器打开输出,检查目标区域和周围内容。文字提取结果用于核验这组教学标记,不证明图片、附件、表单、元数据或历史内容都已经清理。
实际文件还需要检查什么
这里处理的是电子 PDF 中已知位置的文字区域。扫描图片、隐藏 OCR 层、多页重复信息以及页面外的附件等,要作为独立检查项处理。也不要把删除一个关键词当作完整脱敏:检查全部目标出现位置、非目标内容是否保留,以及接收方拿到的最终文件。
先用自己的教学副本跑通"标记 → 应用 → 导出 → 重开核查",确认结果符合任务,再接入业务流程。