API 参考
npm 包 rust-ofd 的完整 API 面:核心模块的 OfdDoc(解析/渲染/检索/验签)与转换、工具、注释、OFD/A、签名等自由函数。核心 OfdDoc 的方法同步执行(WASM 单线程);转换/工具类自由函数均为 async(首次调用自动懒加载对应模块,可用 initConvert/initTool 显式预载)。bytes 入参均为 Uint8Array。
初始化与全局函数
| 函数 | 签名 | 说明 |
|---|---|---|
init |
(module_or_path?) → Promise |
加载核心 WASM;Node 下必须显式传字节(见快速开始) |
initConvert / initTool |
(opts?) → Promise |
显式预载转换 / 工具模块(可选;不预载则相关函数首次调用时自动加载) |
setFallbackFont |
(bytes) → void |
全局回退字体(TTF/OTF/TTC 字节);须在 await init() 完成后调用(wasm-bindgen 导出,初始化前调用会抛错) |
addFallbackFont |
(bytes) → void |
追加回退字体:主回退未覆盖的语种按注入顺序补位(如泰文/泰米尔) |
setStyledFallbackFont |
(style, bytes) → void |
按风格注入转换模块回退字体(0.12.0):style ∈ "song"|"serif"|"hei"|"sans"|"kai";非嵌入字体转换时按 PDF 字体名选风格匹配者做替代子集(详见 convert 字体保真) |
initLicense |
(token) → void |
闭环构建激活授权(默认无授权校验构建为 no-op) |
licenseStatus |
() → LicenseStatus |
授权状态明细(enforced/active/env/typ/lic/hosts/scope/module/deploymentId/didBaked/did/expiresAt/reason);scope 为功能档位(0.14.0 起 preview/convert/full)、module 为本 wasm 模块标识(core/convert/tool) |
resetLicense |
() → void |
本地失活(联网复核确认服务端吊销/到期后调用) |
wasmVersion |
() → string |
内核版本号(与发布版本一致,可用于核对部署产物) |
授权的时间边界(集成前必读):过期判定取「客户端时钟」与「单调流逝」两者的较大值—— 单调流逝可挡住系统级时钟回拨(把系统时间改回去不会让已过期授权"复活"), 但页面脚本仍可重定义
Date.now/performance.now,所以 客户端时钟不是权威。 在线授权请在 Web 集成层定时向授权服务端复核(licenseStatus().expiresAt只是本地判断), 服务端确认吊销/到期后调用resetLicense()立即失活;离线授权的有效期以签发时的ttl为准,需要长期可用时用较短的ttl配合在线复核。
功能档位(0.14.0 起):token 携带
scope档位(preview仅基础预览 /convert预览+转换 /full全功能,对应价格页三档),内核按模块身份强制——档位不足时initLicense与对应模块的功能调用抛错(原因码scope_denied,licenseStatus().reason可查)。 预览版部署上调用转换/工具函数即得到明确中文报错;packages/ofd的聚合licenseStatus()对scope_denied不拉低整体active(未购模块≠授权失效)。功能级档位(0.16.0 起):注释与附件读写、签名与签章校验(
verifySignatures/signOfd/annotations/attachments/attachmentBytes,均在本页 core 模块章节) 划入全功能版——预览版 / 转换版授权调用即抛错(原因码同scope_denied),各接口小节 已用提示块标注。
OfdDoc(核心模块)
构造与属性
| 成员 | 签名 | 说明 |
|---|---|---|
| 构造 | new OfdDoc(bytes: Uint8Array) |
从 OFD 文件字节解析;失败抛 Error(中文错误信息) |
pageCount |
number(getter) |
总页数 |
docType |
string(getter) |
"OFD" 或 "OFD-A" |
signatureCount |
number(getter) |
签名数量(全部 DocBody 合计) |
free() |
void |
释放 wasm 侧内存(见文末错误处理与内存管理) |
docInfo(): DocInfo
interface DocInfo {
docId?: string | null; title?: string | null; author?: string | null; subject?: string | null;
abstractText?: string | null; creationDate?: string | null; modDate?: string | null;
keywords?: string | null; creator?: string | null; producer?: string | null;
/** [键, 值] 数组,保持包内声明顺序 */
customDatas: [string, string][];
/** 文档用途(DocInfo/DocUsage,如 "E-government";null = 未声明;v0.18.0) */
docUsage?: string | null;
/** 封面图像路径(DocInfo/Cover,包根相对);null = 未声明;v0.18.0 */
cover?: string | null;
/** 文档权限声明(Document.xml/Permissions;null = 未声明;v0.11.0) */
permissions?: { print?: boolean; export?: boolean; edit?: boolean; annot?: boolean;
signature?: boolean; watermark?: boolean; fullScreen?: boolean; copy?: boolean } | null;
/** 视图首选项(Document.xml/VPreferences;null = 未声明;v0.11.0) */
vPreferences?: { hideToolbar?: boolean; hideMenubar?: boolean;
hideWindowUI?: boolean; pageMode?: string } | null;
}
键名为 camelCase(v0.22.0 起文档与运行时口径对齐——此前本页与 .d.ts 手写为 snake_case 系文档陈旧,运行时序列化一直是 camelCase,消费方不受影响)。日期字段格式 YYYY-MM-DDThh:mm:ss;customDatas 可承载 Dublin Core / DA/T 46 档案等扩展元数据。permissions 为文档制作方声明的权限(本库仅导出、不强制拦截,阅读器可据此提示或限制)。编辑元数据用工具模块的 editOfdMeta。
outline(): OutlineElem[](v0.18.0 多文档聚合)
大纲/书签树(无大纲时为空数组;多 DocBody 归档包聚合全部文档,条目带 docIndex):
interface OutlineElem { title: string; dest: string; docIndex: number; children: OutlineElem[] }
配套 docPageCounts(): number[] 返回逐文档页数(单文档等价 [pageCount]),
供把 search 命中的 docIndex+pageIndex(文档内)换算成全局页号。
pageSize(page): Float64Array
页面物理尺寸 [宽mm, 高mm](页级 Area 优先 → 文档级 CommonData → A4 兜底)。用于按真实纸张呈现:A4 在 96DPI 下即 210/25.4*96 ≈ 794px 宽。
pageImages(page): string[]
页面引用的全部图像,按出现顺序返回 dataURL(按魔数判 PNG/JPEG/BMP/TIFF),可直接作为 <img src>。闭环构建下逐次复验授权:失效时抛出授权错误(而非静默返回空数组)。
图层、多媒体与语义标引(v0.11.0 新增)
pageLayers(page): LayerEntry[] 与 setLayerVisible(id, visible) / resetLayerVisibility()
图层面板三件套:枚举页面图层(含声明隐藏的层)、运行时切换显隐、恢复文档声明状态。
interface LayerEntry { id: number | null; type: "body" | "background" | "fore"; userId?: string | null; visible: boolean }
const layers = doc.pageLayers(0);
const target = layers.find((l) => l.id != null);
doc.setLayerVisible(target.id, false); // 隐藏;true 可强制显示声明隐藏的层
const png = doc.renderPng(0, 3.78); // 之后的全部渲染出口都按覆盖值绘制
doc.resetLayerVisibility(); // 回到文档声明状态
无显式层的旧式文档 pageLayers 返回 [];id 为 null 的层无法运行时覆盖。
medias(): MediaEntry[] 与 mediaBytes(id): Uint8Array | null
多媒体资源清单(图像/视频/音频;同 ID 时 DocumentRes 优先)与按资源 ID 取文件字节(任意类型通用,视频封面是独立的 Image 条目):
interface MediaEntry { id: number | null; type: "Image" | "Video" | "Audio";
format?: string | null; duration?: number | null; autoStart?: boolean | null; hasFile: boolean }
const video = doc.medias().find((m) => m.type === "Video" && m.hasFile);
const bytes = doc.mediaBytes(video.id); // → Blob → <video src>
pageVideos(page): VideoRegion[]
页内视频对象区域(矩形为左上原点 mm)。静态渲染呈现 PosterID 封面图(无封面画占位框);播放由前端在区域上放播放按钮、经 mediaBytes 取字节自建播放器实现(astro-site 已内置该形态)。
customTags(): CustomTagEntry[]
文档语义标引条目(GB/T 33190 第 16 章,如数科发票字段标引)。file 指向厂商自定义 XML,其格式由调用方解析——本接口提供的是「标引存在与位置」的数据入口。0.17.0 起支持国标 TypeID 属性与 <FileLoc> 子元素写法,Doc_0/Tags/CustomTags.xml 约定路径(Document.xml 未引用)自动发现。
customTagBytes(index): Uint8Array | null
取第 index 个标引条目的内容文件字节(0.17.0)。外部 URL 引用或下标越界返回 null;路径基准按「包内实际存在」择优(CustomTags.xml 所在目录 / 文档根 / 包根)。
渲染
renderPng(page, scale): Uint8Array
渲染为 PNG 字节。scale 为像素/毫米:96DPI 传 96/25.4 ≈ 3.78;2.0 即 2 倍 A4 精度。
renderCanvas(page, scale): CanvasScript
渲染为 Canvas 指令流(指令集见下一页),浏览器配合 playCanvasScript 回放。images 按普通对象(Record<id, dataURL>)序列化,便于 Object.entries 遍历。
exportImage(page, format?, dpi?, quality?): Uint8Array(v0.7.0 新增)
导出单页为图片字节(归档出图/缩略图场景)。format:"png"(默认)| "jpeg";dpi:输出分辨率(默认 96,上限 2540,与渲染守卫同口径);quality:JPEG 质量 1–100(默认 90,PNG 忽略)。JPEG 单边上限 65535 像素,超限报错提示降低 DPI。
const jpg = doc.exportImage(0, "jpeg", 300, 90); // 300dpi 首页
const blob = new Blob([jpg], { type: "image/jpeg" });
exportLongImage(pages?, format?, dpi?, quality?): Uint8Array(v0.7.0 新增)
多页竖向拼接为一张长图(白底、水平居中;档案"单图交付"场景)。pages 为 0 起页索引数组,缺省 = 全部页。总画布沿用渲染守卫(边长 ≤16384px、总像素 ≤64M),超限报错。
const longPng = doc.exportLongImage([0, 1, 2], "png", 150); // 前 3 页长图
文本与检索
pageText(page): string
提取整页文本(按阅读顺序)。无文本页返回空串。闭环构建下逐次复验授权:失效时抛出授权错误(而非静默返回空串)。
pageTextLayout(page, includeHidden?): TextSpan[]
页面文本布局(span 包围盒,页面 mm 坐标、左上原点,与 Canvas 坐标一致;前端自行乘 scale)。旋转/斜切文本以近似盒包含(45° 水印等斜排内容也有 span,包围盒为外接矩形)。includeHidden 为 true 时一并返回 Visible="false" 的隐藏文本层——扫描件/检测报告把可检索文字放在隐藏层,阅读器据此实现"文字不可见但可拖选复制"(PDF.js textLayer 同款形态)。前端拖选/检索高亮/文本选择均以此实现,参考 astro-site。
extractFields(page?): InvoiceFields(v0.7.0 新增)
电子发票字段提取(基于文本层布局的模板化提取:行聚类 + 标签列对齐 + 正则兜底)。发票字体普遍未嵌入,调用前须先 addFallbackFont 注入回退字体,否则文本层无法定位、结果恒为空。非发票文档不报错:各字段为 null 且 matched=false,调用方据此走全文降级。
interface InvoiceFields {
invoiceType?: string; // 票种标题(如「北京增值税电子普通发票」)
invoiceNumber?: string; // 发票号码(旧版 8 位 / 全电票 20 位)
invoiceCode?: string; // 发票代码(旧版 10–12 位;全电票无)
issueDate?: string; // 开票日期(原文格式)
checkCode?: string; // 校验码
machineNumber?: string; // 机器编号
buyerName?: string; // 购买方名称(版式第一个「名称」标签块)
buyerTaxId?: string; // 购买方纳税人识别号
sellerName?: string;
sellerTaxId?: string;
amountExcludingTax?: string; // 合计金额(不含税)
taxAmount?: string; // 合计税额
totalAmount?: string; // 价税合计(小写)
totalAmountCn?: string; // 价税合计(大写中文)
matched: boolean; // 关键字段(号码/日期/购销方)命中 ≥2
}
百望增值税电子发票实测:号码/代码/日期/校验码/购销方名称与税号/价税合计(大小写)全部提取正确(见 ofd-render/src/invoice.rs 测试与 tests/data/invoice-baiwang-vat*.ofd 样本)。
formFields(page?, includeHidden?): FormField[](v0.8.0 新增)
表单字段(数科 AreaHolder 命名填写区 ∩ 文本层)。OFD 国标没有表单域——生态事实标准是数科 AreaHolderBlocks.xml 命名填写区(区域名+边界),内核按约定发现式加载;字段值 = 区域内命中的文本层 span 拼接(pdf2ofd 生成的表单值文本即此形态)。与 extractFields 同因:值文本未嵌入时须先 addFallbackFont。无填写区的文档返回 []。
interface FormField { name: string; value: string; boundary: [number, number, number, number] }
search(query, caseInsensitive?): SearchHit[]
全文检索(同步)。匹配忽略空白——页文本在文本对象之间插换行(逐字成对象的文档即"字字换行"),多字词组仍能跨行命中;start/end 指向原始 pageText 的字符偏移(区间含其间换行):
interface SearchHit { docIndex: number; pageIndex: number; pageId: number | null; start: number; end: number; snippet: string }
docIndex > 0 表示命中来自文档内引用的附属文档(如 OFD/A 附件),主视图不可直接跳转。
交互区域与附件
pageIds(): (number | null)[]
全部页面对象 ID(下标即页序号;未分配 ID 的页为 null),供解析 Goto Dest。
pageLinks(page): LinkRegion[]
页面可交互区域(矩形为左上原点 mm;kind: "goto" | "uri"):
interface LinkRegion { x: number; y: number; w: number; h: number; event: string; kind: "goto" | "uri"; target: string }
attachments(): AttachmentEntry[] 与 attachmentBytes(index, docIndex?)
授权档位
全功能版能力(v0.16.0 起):预览版 / 转换版授权调用时抛错(v0.18.0 起
attachmentBytes与attachments同口径抛错,此前静默返回null)。
附件列表与附件字节(多 DocBody 聚合、条目带 docIndex;index 为所属文档内序号,
取字节用 attachmentBytes(index, docIndex);非内嵌/越界返回 null):
interface AttachmentEntry { index: number; docIndex: number; id: number | null; name: string | null; format: string | null; size: number | null; embedded: boolean; loc: string | null }
annotations(): AnnotInfo[]
授权档位
全功能版能力(v0.16.0 起):预览版 / 转换版授权调用时抛错。
注释概要列表(type 为注释文件中的原始类型标签):
interface AnnotInfo { id: number | null; docIndex: number; type: string; pageIndex: number | null; pageRef: number | null; author: string | null; creatTime: string | null; remark: string | null }
(v0.18.0 起多 DocBody 聚合全部文档,条目带 docIndex,pageIndex 为所属文档内序号。)
增删注释请用下方注释操作自由函数。
验签
verifySignatures(trustedKeyHex?, anchors?, crls?): SigReport[]
授权档位
全功能版能力(v0.16.0 起):预览版 / 转换版授权调用时抛错。
校验文档中的数字签名(同步),报告字段见签名验证报告。文档没有签名时返回 []。
trustedKeyHex:信任公钥(SM2 点04||X||Y的 hex)——纯数字签名(SignedValue 为裸 DER(r,s)、无内嵌证书)时必需。v0.18.0 语义:显式传入时签名值只按它判定(usedEmbeddedCert=false);签名者内嵌证书自验通过但与传入公钥不一致时在报告warnings中告警——以「签名值有效」作可信判据的调用方由此可识别自签证书冒充(未传时维持内嵌证书优先)anchors:信任锚——PEM 文本(可含多张证书)或 base64 编码的证书 DER;给出后逐个签名做证书链校验crls(v0.18.0):CRL(证书吊销列表)——-----BEGIN X509 CRL-----PEM 块(可多块)或 base64(DER)。给出后对签名者证书做离线吊销查询:CRL 须由签发者证书(信任锚/签名包证书集内)验签后才采信;命中吊销 →chainStatus="revoked"。CLI 侧为verify --crl <文件>
v0.18.0 起 CMS/PKCS#7 SignedData 真验签(此前该形态按「格式不支持」区分,无法给出结论):SM3withSM2 / SM2-1(GB/T 35275 国密变体)与 SHA 族 RSA(sha256WithRSA / sha1WithRSA)——真实政务签章(topsec、云南 CA 等)直接给出 signatureValid 结论;CMS unsignedAttrs 携带 RFC 3161 时间戳时验证 messageImprint 与 TSA 签名,报告见 timestamp 字段(present/verified/genTime)。
转换(自由函数 · async)
| 函数 | 签名 | 说明 |
|---|---|---|
pdfPageCount |
(pdf) → Promise<number> |
探测 PDF 页数 |
convertPdfToOfd |
(pdf, textMode?, pages?, password?) → Promise<Uint8Array> |
返回 OFD 包字节 |
convertPdfDetailed |
同参 → Promise<{ ofd, report: ConvertReport }> |
附转换报告(详见转换页) |
convertOfdToPdf |
(ofd, textMode?, pages?) → Promise<Uint8Array> |
OFD → PDF 矢量转换 |
convertOfdToPdfDetailed |
同参 → Promise<{ pdf, report: OfdToPdfReport }> |
附 OFD→PDF 报告 |
textMode(PDF→OFD):"auto" | "embed" | "outline";(OFD→PDF):"real" | "outline"(real 为真实可检索文本,默认);pages: "1-3,5";password: 加密 PDF 口令。
文档工具(自由函数 · async,工具模块)
| 函数 | 签名 | 说明 |
|---|---|---|
extractPages |
(ofd, pages) → Promise<Uint8Array> |
抽取页面("1,3-5",1 起) |
deletePages |
(ofd, pages) → Promise<Uint8Array> |
删除页面 |
rotatePages |
(ofd, pages, deg) → Promise<Uint8Array> |
旋转页面(pages 空=全部;deg 90|180|270 顺时针) |
mergeOfd |
(files, mergeOutline?) → Promise<Uint8Array> |
合并多个 OFD(首个为目标文档) |
splitOfd |
(ofd, each) → Promise<Uint8Array[]> |
拆分文档(每 each 页一份) |
addTextWatermark |
(ofd, text, size?, alpha?, tile?, rotation?) → Promise<Uint8Array> |
文字水印(垫底层不遮挡内容) |
addImageWatermark |
(ofd, image, format?, alpha?, widthMm?, heightMm?, tile?, rotation?) → Promise<Uint8Array> |
图像水印(tile=false 居中) |
createOfd |
(spec, fonts?, images?) → Promise<Uint8Array> |
结构化生成 OFD(spec JSON:texts/rects/lines/images,详见 index.d.ts 的 CreateOfdSpec) |
editOfdMeta |
(ofd, patch) → Promise<EditOfdMetaResult> |
编辑文档元数据 DocInfo(v0.22.0,见下) |
editOfdMeta(ofd, patch)(v0.22.0 新增)
编辑首个文档的元数据(GB/T 33190 DocInfo 全字段),返回 { ofd, report }。全功能版能力(工具模块即全功能档)。加密件拒绝。
interface DocInfoPatch {
// 每个标准字段三态:缺 key 不动 / null 清除 / 字符串设值(trim 后空串视为清除)
title?: string | null; author?: string | null; subject?: string | null;
abstract?: string | null; keywords?: string | null; creator?: string | null;
producer?: string | null; docUsage?: string | null; cover?: string | null;
creationDate?: string | null; modDate?: string | null; // YYYY-MM-DDThh:mm:ss
customDatas?: [string, string][]; // 整体替换([] = 清空)
autoModDate?: boolean; // 缺省 true:自动刷 ModDate
}
interface MetaEditReport {
docCount: number; // >1 = 多文档包,仅编辑了首个
changed: string[]; // 变更的国标字段名(Title/Author/…/ModDate/CustomDatas)
customAdded: number; customRemoved: number;
createdDocInfo: boolean; // 原 DocInfo 缺失、本次新建
hadSignature: boolean; // 含签名:改写使既有签名摘要失配,应提示重新签名
}
const res = await editOfdMeta(ofdBytes, {
title: "归档著录标题", // 设值
author: null, // 清除
customDatas: [["档号", "A-2026-001"]], // 整体替换自定义键值
});
if (res.report.hadSignature) { /* 提示重新签名 */ }
日期字段格式非法时整体报错且产物不变;DocID 只读(文档身份标识,OFD-A 合规依赖,缺失由 ofdaRepair 补全)。参考实现:astro-site 阅读器「信息」面板与 /meta 独立工作台。
注释操作(自由函数 · async,工具模块)
| 函数 | 签名 | 说明 |
|---|---|---|
listAnnotations |
(ofd) → Promise<AnnotationInfo[]> |
列出文档注释 |
addAnnotation |
(ofd, spec, image?) → Promise<Uint8Array> |
添加注释,返回新 OFD 字节 |
removeAnnotation |
(ofd, annotId) → Promise<Uint8Array> |
删除指定 ID 注释 |
fillFormFields |
(ofd, fills) → Promise<Uint8Array> |
表单填写(v0.8.0,见下) |
AnnotationSpec 按 type 取相关字段(完整定义见 index.d.ts):
type:"highlite" | "path" | "ink" | "link" | "watermark" | "stamp"(兼容"highlight");坐标 mm、页 0 起- 通用:
pageIndex、boundary: [x, y, w, h](w/h 为 0 时由内容推导)、author、remark、color: "r,g,b" highlite:rects(矩形组);path:points(折线顶点)+lineWidth;link:uri或gotoPagewatermark:text、fontSize、angle、alpha;stamp:印章image(PNG/JPEG 字节)+ 显示尺寸imageW/imageHink(手写签批,v0.8.0):strokes: [{ points: [[x, y], …], width }]多笔一次落成一条标准Path注释(GB/T 33190 第 15 章原生表达,数科/ofdrw 均可渲染)——每笔独立 PathObject 与线宽(压感由前端烘进width),lineWidth为缺省笔宽;单点笔自动扩为微线段。阅读器示例「签批」按钮即此形态(触屏/手写板压感采集)
表单填写(自由函数 · async,工具模块,v0.8.0)
fillFormFields(ofd, fills): Promise<Uint8Array>
把值文本写入数科 AreaHolder 命名填写区,返回新 OFD 字节。fills: [{ name, value }, …];未命中字段名抛错并列出文档全部填写区名单。值以 TextObject 写入所属页正文层(数科语义,跨阅读器可见、可检索);填写后用 formFields 可取回值(区域∩文本层闭环)。注意:填写会修改页内容文件——已签名文档的既有签名摘要将失配(等同修改正文),应在填写后重新签名。
加密与解密(自由函数 · async,工具模块,v0.11.0)
SM4-CBC 内容加密 + SM2 数字信封(每接收者一条),与 CLI encrypt/decrypt 子命令同底座、字节级一致:
import { isEncryptedOfd, encryptOfd, decryptOfd } from "rust-ofd";
const locked = await encryptOfd(ofdBytes, [recipientPublicKeyHex]); // 04||X||Y hex
const needKey = await isEncryptedOfd(locked); // true(普通文档 false)
const plain = await decryptOfd(locked, recipientPrivateKeyHex); // 32 字节大端 hex
isEncryptedOfd 不消耗授权(阅读器可用它把「打开失败」分流为密钥解锁流程);私钥与任何信封不匹配时报「私钥与任何信封不匹配」。
安全语义(选型前必读):
- 本形态提供机密性(SM4-CBC 内容加密 + SM2 数字信封),不提供完整性 / 抗篡改;
- 完整性校验依赖包内
Encryption.xml的Checksum,而该元数据与密文同处一个包、不受密钥保护——能改写包的攻击者可以同时改写两者。因此**「解密成功」只说明拿到了明文,不等于内容可信**;需要抗篡改请叠加数字签名(本页下文的signOfd/verifySignatures两节)并校验签名结论; - 旧版包(Entry 无独立 IV)回退使用顶层单一 IV,同一 CEK 下多文件复用 IV——为互操作性仍支持解密此类包,但不要用它加密新数据。
OFD/A 归档合规(自由函数 · async,工具模块)
GB/T 42133-2022 检测与修复(v0.21.0 起 14 个检查项:格式规则 + 四性文件级要求——引用完整性、DocID 元数据、扩展信息残留、签名有效性),详见 changelog 与 astro-site 阅读器「归档」面板。原生端等价命令为 ofd-cli ofda-check / ofda-repair / ofda-batch(用法见仓库 docs/site/bindings/cli.md,该目录不随线上文档站发布):
| 函数 | 签名 | 说明 |
|---|---|---|
ofdaCheck |
(ofd) → Promise<OfdaCheckReport> |
14 项逐项检查;missingFonts 给出未嵌入字型清单,signatures 给出逐签名验签状态(签名值+文件摘要,不判证书链信任) |
ofdaRepair |
(ofd, fonts?, options?) → Promise<OfdaRepairResult> |
自动修复另存(子集化嵌入字体/图像白名单/去技术化/悬空引用清理/大纲治理/附件治理/DocID 补全/修复留痕),报告含逐条动作、修复前后复检与 warnings;options 见下方 |
ofdaConvert |
(ofd) → Promise<Uint8Array> |
仅转存 OFD/A 基础形态(去技术化,不含字体嵌入/图像修复);加密/多文档/已签件拒绝;需要完整合规请用 ofdaRepair |
collectSystemFonts |
(names) → Promise<{ found, missing }> |
Chromium queryLocalFonts(需 https/localhost 与用户授权)读取系统字体;按名称归一化匹配(子集前缀/空白/样式后缀/中文-英文别名),未授权时 found 为空 |
已签名件处置(v0.21.0,GB/T 42133 6.13):修复重写受保护文件会使验签必败,因此 ofdaRepair 对含签名件默认拒绝(错误消息说明处置方式);验签合规的签章件可免技术化带签名归档(检测报告 signatures 全部有效即属此类);确需修复时显式传 { stripSignature: true } 剥离——SES 内嵌印章图片转为页面图像保留视觉(矢量/外置印章记 stamp-visual-lost 警告),剥离动作与印章去向全部记入报告。
浏览器端合规修复流程(工具模块需全功能档):
import { ofdaCheck, ofdaRepair, collectSystemFonts } from "rust-ofd";
const before = await ofdaCheck(bytes);
const names = before.missingFonts.map((f) => f.name);
const { found, missing } = await collectSystemFonts(names);
// missing 非空时:提示用户安装字体,或用业务允许的替代字体补入 found
const { ofd, report } = await ofdaRepair(bytes, found);
const after = await ofdaCheck(ofd); // 复检
// 已签件:ofdaRepair(bytes, found) 报错时按需显式剥离
// const { ofd, report } = await ofdaRepair(bytes, found, { stripSignature: true });
- 修复报告
report.warnings说明被替代/未嵌入的字体;after.missingFonts仍不空时不得宣称合规。 - 修复留痕(DA/T 70 处理过程记入元数据):默认写入
DocInfo/CustomDatas三键OFDA-Repair-Time/Tool/Summary,重复修复覆盖同键;传{ noTrace: true }关闭。 - 原生批量修复推荐
ofd-cli ofda-repair/ofda-batch:可扫描系统字体目录、显式--fallback-font替代、--strip-signature剥离签名并输出 JSON/CSV 报告。 - OFD/A
single-doc规则要求单文档:ofdaRepair对多 DocBody 输入拒绝(属不可无损修复),需先归档拆分为单文档。
本地签名(自由函数 · 同步)
signOfd(ofd, opts?): Uint8Array
授权档位
全功能版能力(v0.16.0 起):预览版 / 转换版授权调用时抛错。
对文档本地签名:即时生成 SM2 密钥对与自签证书(CN=signer),SES v4 + 印章,返回签名后的新 OFD 字节。
import { signOfd } from "rust-ofd";
const signed = signOfd(ofdBytes, {
signer: "张三", // 签署人显示名(自签证书 CN)
seal: sealPngBytes, // 印章图片 PNG/JPG(缺省内置占位章)
page: 0, // 盖章页(0 起;null 不盖章做纯数字签名)
x: 140, y: 240, w: 40, h: 40, // 印章 Boundary(mm,页面左上原点)
basis: { reason: "同意", location: "北京", provider: "rust-ofd" },
});
注意:签名针对当前内容——签名后若再做旋转/合并等修改,摘要会失效(验签显示"文档已被修改")。选项完整定义见 SignOfdOptions(index.d.ts)。
错误处理与内存管理
所有可失败方法抛 Error,message 为中文(如 "未知文本模式: x(可选 auto|embed|outline)")。
内存管理:wasm 对象不走浏览器 GC。换文档/卸载页面前应显式调用 doc.free() 释放(之后实例不可再用);长会话连续打开多份文档时不释放会持续累积。类实例亦支持 using(Symbol.dispose)语法自动释放。