主题
快速开始(WASM / JavaScript)
npm 包名 rust-ofd(packages/ofd),由 wasm-pack --target web 产物加一层手写包装构成:入口 + Canvas 播放器 + 完整 TypeScript 类型(index.d.ts)。
在线演示
下面就是本页内嵌的真实内核(WASM,非截图)——固定演示样张,可翻页与缩放:
正在加载…
安装
当前以本地包引用(尚未发布到 npm registry):
bash
# 仓库内(monorepo)
npm i rust-ofd@file:../path/to/packages/ofd
# 或 pnpm / yarn 对应的 file: 协议浏览器:10 行代码渲染第一个页面
js
import { init, OfdDoc, playCanvasScript } from "rust-ofd";
await init(); // 加载 WASM(默认同目录 ofd_wasm_bg.wasm)
const bytes = new Uint8Array(await file.arrayBuffer());
const doc = new OfdDoc(bytes); // 解析
const script = doc.renderCanvas(0, 1.5); // 第 1 页 → Canvas 指令流(scale:像素/毫米)
await playCanvasScript(canvas, script, window.devicePixelRatio || 1);PNG 位图方式(不需要 Canvas):
js
const png = doc.renderPng(0, 2.0); // Uint8Array(PNG 字节)
const url = URL.createObjectURL(new Blob([png], { type: "image/png" }));
img.src = url;带进度的加载(推荐用于生产)
init() 默认自行 fetch wasm,无法拿到下载进度。对首次加载体验敏感的站点,可以自己下载并注入:
js
import { init } from "rust-ofd";
import wasmUrl from "rust-ofd/wasm?url"; // 包导出的 wasm 子路径(构建工具会产物化)
// cache: "reload" 必须带上:wasm 是固定文件名,内核更新后若命中
// 浏览器磁盘缓存会一直用旧版本(版本号停在旧内核)
const res = await fetch(wasmUrl, { cache: "reload" });
const total = Number(res.headers.get("content-length")) || 0;
const reader = res.body.getReader();
const chunks = [];
let loaded = 0;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
updateProgressBar(loaded, total); // ← 这里驱动你的进度条
}
const buf = concat(chunks);
await init({ module_or_path: buf }); // 注入已下载的字节在线演示站(demo-site)对渲染内核(约 3.5MB)与中文回退字体(约 3MB)都做了这种逐块进度可视化。
Node.js
Node 下 init() 无法用 import.meta.url 定位 wasm,需手动传入:
js
import { readFile } from "node:fs/promises";
import { init, OfdDoc } from "rust-ofd"; // file: 引用或直接路径 import
const wasm = await readFile("node_modules/rust-ofd/.gen/ofd_wasm_bg.wasm");
await init({ module_or_path: wasm.buffer });
const doc = new OfdDoc(await readFile("sample.ofd"));
console.log(doc.pageCount, doc.docInfo().title);回退字体(电子发票必读)
未嵌入字体的 OFD(数科电票等)需要回退字体才能显示文字。推荐思源宋体 GB2312 子集 TTF(OFL 许可,可分发,约 3MB):
js
import { init, setFallbackFont } from "rust-ofd";
await init(); // 必须先就绪
const font = await fetch("/fallback-font.ttf"); // 站点自备(TTF!)
setFallbackFont(new Uint8Array(await font.arrayBuffer()));注入一次全局生效:渲染与 PDF → OFD 转换共用。建议首次打开文件时懒加载。两个注意点:
setFallbackFont是 wasm 导出函数,必须在await init()完成后调用(弱网下字体可能先于 wasm 到达,需等init完成再注入,vue-demo 即如此处理);- 请用 **TTF(glyf 轮廓)**字体:CFF/OTF 字体在 OFD → PDF 真实文本输出时会使文本退化为轮廓,影响可检索性。
按需加载的三个模块
npm 包内含三个独立 wasm 模块,按需懒加载(也可用 initConvert / initTool 显式预载):
| 模块 | 导入子路径 | 触发加载的调用 |
|---|---|---|
| 核心(解析/渲染/文本/验签) | rust-ofd/wasm | init() |
| 转换(PDF↔OFD 双向) | rust-ofd/convert-wasm | convertPdfToOfd / convertOfdToPdf* / pdfPageCount |
| 工具(合并/拆分/旋转/水印/生成/注释/OFD-A/加解密) | rust-ofd/tool-wasm | mergeOfd / rotatePages / addAnnotation / ofdaCheck / encryptOfd 等 |
initLicense / setFallbackFont / addFallbackFont 会自动补种到后续加载的模块,无需按模块重复调用。
API 总览
| 导出 | 说明 |
|---|---|
init(module_or_path?) | 加载核心 WASM(async) |
initConvert(opts?) / initTool(opts?) | 预载转换 / 工具模块(async,可选) |
new OfdDoc(bytes) | 解析 OFD(详见 API 参考) |
convertPdfToOfd(pdf, textMode?, pages?, password?) | PDF → OFD 字节(async) |
convertPdfDetailed(...) | 同上,附带转换报告(async) |
convertOfdToPdf(ofd, textMode?, pages?) | OFD → PDF 字节(async) |
convertOfdToPdfDetailed(...) | 同上,附带报告(async) |
pdfPageCount(pdf) | 探测 PDF 页数(async) |
mergeOfd / splitOfd / rotatePages / extractPages / deletePages | 页面级文档工具(async) |
addTextWatermark / addImageWatermark / createOfd | 水印与结构化生成(async) |
listAnnotations / addAnnotation / removeAnnotation | 注释增删查(async) |
ofdaCheck / ofdaRepair / collectSystemFonts | OFD/A 归档合规检测与修复(async) |
signOfd(ofd, opts?) | 本地签名(SES v4 + 印章,同步) |
setFallbackFont(bytes) | 注入全局回退字体(init 完成后调用) |
addFallbackFont(bytes) | 追加回退字体(泰文/泰米尔等补位) |
initLicense(token) / licenseStatus() / resetLicense() | 授权 |
wasmVersion() | 读取内核版本号 |
playCanvasScript(canvas, script, dpr?) | Canvas 播放器(详见 指令集) |
完整类型见 API 参考与包内 index.d.ts。