跳到内容
  • 福昕首页
  • 开发中心
  • SDK文档资料
  • 插件商店
  • 福昕首页
  • 开发中心
  • SDK文档资料
  • 插件商店
申请试用
  • 企业自动化
    • Compressor
  • 福昕CloudAPI
  • 福昕PDF SDK 软件开发工具包
    • 福昕PDF SDK(ActiveX)
    • 福昕PDF SDK(桌面/服务器)
    • 福昕PDF SDK(Plug-in)
    • 福昕 PDF SDK(安卓)
    • 福昕PDF SDK(iOS)
    • 福昕PDF SDK(Web)
  • 福昕管理控制台
    • 公有云
    • 私有云
    • 通用情况
  • 福昕阅读器
    • RMS插件
  • 福昕高级编辑器
    • AI助手
    • Mac版本
      • 常规问题
    • Windows版本
      • 表单
      • ECM集成
      • 互联PDF
      • 企业管理指南
      • 保护
      • 内容编辑
      • 创建PDF
      • 压缩
      • 图章
      • 安装与卸载
      • 常见问题
      • 打印
      • 注释/评论
      • 福昕插件
      • 翻译助手
      • 翻译助手教程
      • 试用与激活
      • 转换
      • 页面管理
    • 教育用户
      • 论文查重
      • 论文畅
      • 操作指南
    • 网页版
      • Foxit eSign
      • 电子签章
    • 订阅
    • 资源
  • 福昕高级编辑器Linux版本
  • 福船图纸管理系统
  • 福昕PDF SDK 软件开发工具包 > 福昕PDF SDK(Web)
  • 标签:
  • PDF编辑,UIExtension,数字签名,文档权限,编辑权限

WebSDK 判断 PDF 是否被数字签名限制编辑,并转为可在线编辑的 PDF(基于 UIExtension)

  • 福昕知识库
  • 2026-09-20
项目 内容
适用产品 福昕 PDF SDK for Web(WebSDK)11.1,UIExtension 全功能查看器
场景 B/S 业务系统中打开”已签名的工程图纸/合同”,内核禁止修改正文;需要 ① 判断是否受限、② 在浏览器端转成可在线编辑的普通 PDF 后入库/加工
结论 两步均可行且已闭环实测。路径只有一条:逐个清空全部已签署签名域 → 另存 → 重新打开校验

一、判断:这份 PDF 是否被签名限制了编辑

1.1 先分清两种互不相干的成因

来源 特征 关键 API
加密权限(owner password) 文件带 /Encrypt,权限位由加密字典 /P 控制 pdfDoc.hasOwnerPassword()、getPermissions()
数字签名(本文重点) 文件带签名,内核据此把”修改文档”权限置 0 pdfDoc.getPDFForm()、docRender.getUserPermission()

数字签名本身又分两种,业务表现一致(都不能改正文),只是严格程度不同:

  • 认证签名(DocMDP):签名字典带 /Reference/TransformMethod /DocMDP + /TransformParams /P 1|2|3,硬性限定允许做哪些改动;
  • 普通签名(approval):无 DocMDP,但内核同样禁止修改正文。

实务提示:客户常把两者统称为”签名限制了编辑权限”。先按下面判档,再决定处理路径。

1.2 判档:读权限位

// ① 枚举已签署的签名域
const form = pdfDoc.getPDFForm();
const fields = form ? await form.getFields() : [];
const signed = [];
for (const f of fields) {
  const ext = f.getExtType && f.getExtType();
  if (!ext || ext.type !== 7) continue;     // ★ 签名域是对象 {type:7},不是数字 7
  if (!(await f.isSigned())) continue;
  signed.push({ name: f.getName(), signer: await f.getSigner(),
                subFilter: await f.getSubfilter(), signTime: (await f.getSignInfo()).signTime });
}

// ② 读受限档位(权威依据)
const up = docRender.getUserPermission();
const checks = {
  cannotModifyAny: up.checkCannotModifyAny(),
  modify:   up.checkModify(),      // 0 / 8
  fillForm: up.checkFillForm(),    // 0 / 256
  annotForm: up.checkAnnotForm(),  // 0 / 32
  assemble: up.checkAssemble()     // 0 / 1024
};

let level;
if (checks.cannotModifyAny || (checks.modify === 0 && checks.fillForm === 0)) level = 'P1';
else if (checks.modify === 0 && checks.fillForm > 0 && checks.annotForm === 0) level = 'P2';
else if (checks.modify === 0 && checks.fillForm > 0 && checks.annotForm > 0) level = 'P3_OR_ORDINARY';
else if (checks.modify === 0) level = 'ORDINARY';
else level = 'NONE';

判档表(WebSDK 11.1 实测值):

文档类型 checkCannotModifyAny checkModify checkFillForm checkAnnotForm getUserPermissions()
无签名 false 8 256 32 4294967295
普通签名 false 0 256 32 4294966263
DocMDP P=1 true 0 0 0 4294965975
DocMDP P=2 false 0 256 0 4294966231
DocMDP P=3 false 0 256 32 4294966263

1.3 两条必须知道的边界(实测)

  • getSignInfo() 给不出 DocMDP 档位:getSignInfo().mdpAction.type 恒为 0、getMDPAction() 恒为 {type:'none'} —— 它是签名域锁定动作(FieldMDP),与 DocMDP 的 /TransformParams/P 无关。档位只能由权限位反推。
  • P=3 与普通签名在公开接口下无法区分(权限位逐位相同、getSignInfo() 也相同),要精确区分需解析 PDF 的 /Perms 与签名字典 /Reference。但两者业务表现与处理路径完全一致,不影响落地。

二、转换:把它变成可在线编辑的普通 PDF

2.1 五条路,只有一条推荐

路径 做法 结果
A 直接另存 getFile({flags:0}) ❌ 无效:产物仍带签名、仍受限
B removeSignature(name) ❌ 先抛 Permission error
C 逐个 clearSignedData() → getFile() ✅ 推荐(唯一验证可行的路径)
D createNewDoc + insertPages 页面级重建 ⚠️ 可编辑,但丢失全部 OCG 图层(无法恢复)——分层图纸不要用
E extractPages ⚠️ 可编辑,但返回值是分块的、须拼接,且同样丢图层

2.2 推荐实现(可直接替换)

async function convertToEditablePDF(pdfui) {
  const docRender = await pdfui.getPDFDocRender();
  const pdfDoc = await docRender.getPDFDoc();

  // ① 逐个清除"已签署"的签名域 —— 必须全部清,只清一个无效
  const form = await pdfDoc.getPDFForm();
  for (const f of (form ? await form.getFields() : [])) {
    const ext = f.getExtType && f.getExtType();
    if (!ext || ext.type !== 7) continue;
    if (!(await f.isSigned())) continue;
    await f.clearSignedData();          // 低层 API,DocMDP P=1 上同样可用
  }

  // ② 另存
  const file = await pdfDoc.getFile({ flags: 0 });
  const blob = new Blob([await file.arrayBuffer()], { type: 'application/pdf' });

  // ③ 闭环校验(务必做)
  await pdfui.openPDFByFile(new File([blob], 'converted.pdf', { type: 'application/pdf' }));
  const after = await pdfui.getPDFDocRender().then(r => r.getUserPermission());
  if (after.checkModify() === 0) throw new Error('转换后仍受限,请检查是否遗漏签名域');
  return blob;
}

2.3 落地时的 5 个注意点(均为实测)

  1. 必须清除”全部”已签署签名域:多签名文档只清一个时,文档仍受限(另一个签名还在)。→ 循环清除每一个 + 逐个回读确认。
  2. “另存”不等于”解锁”:getFile({flags:0}) 在受限文档上会成功,但产物仍受限——它只解决”能不能写出文件”,不解决”能不能编辑”。
  3. 签名域识别用 getExtType().type === 7:它返回的是对象 {type:7},不是数字 7;按 === 7 过滤会漏掉全部签名域。
  4. 该接口未列入公开 .d.ts(Web 层):clearSignedData() 在原生 SDK 已公开、可正常使用,但 Web 层没有类型声明 → 请锁定 SDK 版本,升级时把 §三 的闭环校验当回归用例跑一遍。
  5. SDK 未提供现成的”清除签名”UI 入口:需自行加工具栏按钮或自制右键菜单项,内部调用 clearSignedData() 即可。

P=1(checkFillForm == 0)同样走第 2.2 节这条路径 —— 低层 clearSignedData() 不经过 UI 服务层的 checkPermission(fillForm) 校验,实测可用。

三、闭环校验清单(转换后必须逐项确认)

  1. 已签署签名域数量为 0;
  2. getUserPermission().checkModify() > 0;
  3. 真正执行一次编辑不再抛 Permission error(例如 pdfDoc.setMetadataValue(k, v, false));
  4. 图纸类文档:页数、MediaBox、/Rotate、OCG 图层数与名称、注释数逐项与原件一致。

提示:清签名后,内存中的文档仍受限(checkModify 依旧为 0),必须”另存 + 重新打开”才解锁——这是内核在打开文档时判定”该文档带签名”的行为,不是接口异常。

四、合规提醒(务必向业务方说明)

清除签名后,原签名的密码学有效性即失效,文档不再可验签。是否允许这样做属于客户业务与合规决策,SDK 只提供能力。建议:转换前让业务方确认审批链,转换后留痕(谁、何时、为何转换、原件留存)。

五、签名外观:通常不必保留

清除签名会同时清空签名域的外观流(实测:外观流从绘制签章图的内容变成空的 q Q),即转换后签章外观会消失。

实际项目中这通常可接受(例如本文所依据的客户案例,最终用户已确认”外观不重要、必要时可不显示,重点是能编辑图纸内容”)。

若确有客户要求保留外观,两条实测结论供参考:

  1. 必须先取图、再清签名:sigField.getImage() 在受限文档上即可读取(无需修改权限),清签名后再取就没了;
  2. 不要在受限文档上直接 flatten(PDFPage.flatten / PDFDoc.flatten / PDFPage.flattenAnnot 全部抛 Permission error,且同一会话内即使已清签名仍被拦)→ 必须另存并重新打开产物后才能 flatten。

需要时的链路:getImage() → 清签名 → 另存 → 重新打开 → addAnnot({type:'stamp', rect}) + stamp.setImage(url) 贴回 →(可选)flatten(0) 固化 → 再另存。

附:验证环境

WebSDK 11.1 全功能版(UIExtension)+ 真实 Chrome;素材包括自造的 DocMDP P1/P2/P3 认证签名 PDF(openssl 生成自签证书并逐份 openssl cms -verify 校验有效)与普通签名 PDF,以及客户真实已签工程图纸(2 个普通签名、25 个 OCG 图层、MediaBox 2384×3370、/Rotate 270)。

关键实测数据:推荐路径对客户真实图纸的转换结果 —— 已签署签名域 2 → 0、checkModify 0 → 8、真实编辑调用成功、页数/尺寸/旋转/25 个 OCG 图层/注释数全部保全,产物约 560 KB、耗时约 0.6 s;同一路径连续压测 41/41 次通过(含 DocMDP P=1 素材与客户真实图纸)。

相关内容

如何启用/禁用多个福昕高级PDF 编辑器的显示窗口?

WebSDK 创建书签并设置 GoToR / Launch 动作(打开另一PDF指定页 / 打开非PDF文件)

如何在Windows上使用福昕小工具(FoxitInfoCollectTool.exe)收集福昕高级PDF编辑器的日志?

如何在Microsoft Edge浏览器中启用 IE 模式?

如何解决福昕高级PDF编辑器外接显示器打印对话框窗口显示问题

如何用命令行或GPO部署安装福昕并同步激活程序?

如果需要比对同类型文档,或是一份文档的新旧稿件,有什么好方法?

奇数页和偶数页的文档如何合并成一个文档?

通过账号登录授权提示err8001怎么办?

如何给多个文档添加连续且唯一性的贝茨编号?

推荐内容

WebSDK 判断 PDF 是否被数字签名限制编辑,并转为可在线编辑的 PDF(基于 UIExtension)

如何启用/禁用多个福昕高级PDF 编辑器的显示窗口?

WebSDK 创建书签并设置 GoToR / Launch 动作(打开另一PDF指定页 / 打开非PDF文件)

如何在Windows上使用福昕小工具(FoxitInfoCollectTool.exe)收集福昕高级PDF编辑器的日志?

如何在Microsoft Edge浏览器中启用 IE 模式?

如何解决福昕高级PDF编辑器外接显示器打印对话框窗口显示问题

如何用命令行或GPO部署安装福昕并同步激活程序?

如果需要比对同类型文档,或是一份文档的新旧稿件,有什么好方法?

奇数页和偶数页的文档如何合并成一个文档?

通过账号登录授权提示err8001怎么办?

产品
  • 应用行业
  • 白皮书
开发支持
  • 开发中心
  • SDK文档资料

销售咨询:010-50951668

客服电话:0591-38509808

销售咨询
微信公众号

©2026 福建福昕软件开发股份有限公司 版权所有

隐私策略