Skip to content

快速开始(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/wasminit()
转换(PDF↔OFD 双向)rust-ofd/convert-wasmconvertPdfToOfd / convertOfdToPdf* / pdfPageCount
工具(合并/拆分/旋转/水印/生成/注释/OFD-A/加解密)rust-ofd/tool-wasmmergeOfd / 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 / collectSystemFontsOFD/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。

专有软件 · 商业授权(闭源)