npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ofd-lib

v0.1.0

Published

JavaScript/TypeScript 生态中 OFD(开放版式文档)格式的通用操作库,API 设计对标 pdf-lib。

Readme

ofd-lib

JavaScript / TypeScript 生态中 OFD(Open Fixed-layout Document,开放版式文档) 格式的通用操作库,对标 pdf-lib 在 PDF 领域的地位。

  • 跨环境:Node.js / 浏览器 / Deno(全部 I/O 与解析返回 Promise
  • 创建与修改并重:从零创建 OFD,或加载并修改现有 OFD
  • API 简洁:对标 pdf-lib 的 OFDDocument / OFDPage / rgb() / degrees()
  • TypeScript 优先:完整类型定义
  • 扩展能力:标准中文(宋体/黑体等)、表单、数字签名(国密 SM2/SM3)、印章、模板

遵循 GB/T 33190-2016《电子文件存储与交换格式 版式文档》。

安装

npm install ofd-lib

国密 SM2/SM3 为内置纯 TypeScript 实现,零额外依赖。

快速开始

创建文档

import { OFDDocument, StandardFonts, rgb } from 'ofd-lib';

const doc = await OFDDocument.create();

const font = await doc.embedFont(StandardFonts.SimSun);
const page = doc.addPage([210, 297]); // A4(mm)

page.drawText('你好,OFD!', {
  x: 50, y: 200, size: 24, font, color: rgb(0, 0, 0),
});

page.drawRectangle({
  x: 50, y: 50, width: 100, height: 50,
  borderColor: rgb(1, 0, 0), borderWidth: 1,
});

const ofdBytes = await doc.save(); // Uint8Array(ZIP)

加载与修改

const doc = await OFDDocument.load(existingBytes);
const pages = doc.getPages();

doc.insertPage(1, doc.addPage([210, 297]));
doc.removePage(0);
doc.movePage(2, 0);                  // 页面重排

const copied = await destDoc.copyPages(srcDoc, [0, 1]);
copied.forEach((p) => destDoc.addPage(p));

doc.setTitle('我的文档');
doc.setAuthor('作者');

const text = doc.extractText();      // 提取全文文本(可传页索引)

表单

const form = doc.getForm();
form.addTextField('name', { x: 50, y: 100, size: 14 });
form.getField('name').setText('张三');

数字签名 / 印章(国密 SM2/SM3,内置零依赖实现)

import { generateKeyPair } from 'ofd-lib';

// 生成密钥对(或使用已有 SM2 私钥十六进制串)
const { privateKey, publicKey } = generateKeyPair();

// 签名(可同时加盖印章图片);不传 privateKey 时自动生成临时密钥
await doc.sign({ signer: '张三', reason: '审批通过', privateKey, stampImage: stampPngBytes });

const bytes = await doc.save(); // 签名持久化到 Doc_0/Signs/

// 加载后验签(默认使用随签名保存的公钥,可显式传入外部公钥)
const loaded = await OFDDocument.load(bytes);
const results = await loaded.verifySignatures();
// [{ valid: true, message: '签名有效', signer: '张三', time: '...' }]
// 内容被篡改时 valid 为 false(SM3 摘要不匹配)

也可直接使用底层国密算法:

import { sm3, sm2Sign, sm2Verify, generateKeyPair } from 'ofd-lib';

sm3('abc'); // '66c7f0f4...'(GB/T 32905 标准向量)
const kp = generateKeyPair();
const sig = sm2Sign('消息', kp.privateKey);
sm2Verify('消息', sig, kp.publicKey); // true

附件

doc.attach(fileBytes, 'contract.pdf');            // 添加附件
const atts = doc.getAttachments();                // 加载后提取:[{ name, bytes, format }]
doc.removeAttachment('contract.pdf');             // 按名称删除附件

页面模板

const tpl = doc.addTemplate([210, 297]);          // 创建模板页(页眉/边框/水印等)
tpl.drawText('机密文件', { x: 30, y: 10, size: 10 });

const page = doc.addPage([210, 297]);
page.setTemplate(tpl);                            // 页面引用模板,渲染时模板层在下

OFD/A 归档校验

import { OFDArchiveValidator } from 'ofd-lib';

const result = OFDArchiveValidator.validate(doc);
// { valid, profile: 'OFD' | 'OFD/A', issues, summary }
if (!result.valid) {
  // 常见错误:FONT_NOT_EMBEDDED(字体未嵌入)、IMAGE_NOT_EMBEDDED(图像未嵌入)、
  //          MISSING_SIGNATURE(缺少数字签名)、SIGNATURE_EMPTY(签名值为空)
  console.error(result.issues);
}
// OFD/A 要求:所有字体/图像必须内嵌、必须带数字签名、不得含外部引用。

字体子集化(减小嵌入体积)

// 自动收集文档实际用到的字符,仅保留对应字形并重建字体表
const results = doc.subsetFonts();
// 或保存时一次性完成:
const bytes = await doc.save({ subsetFonts: true });
// 仅支持 TrueType 轮廓字体;CFF/OTF 会自动跳过且不影响保存。

大文档流式读写

// 流式保存:逐条目压缩并产出字节块,避免一次性占用全部未压缩内存
for await (const chunk of doc.saveToStream()) {
  await writable.write(chunk); // 写入磁盘 / 响应流
}

// 从可读流加载(无需事先把整个文件读入内存)
// 浏览器:fetch 响应体 / File(Blob) 流都实现了 AsyncIterable<Uint8Array>
const res = await fetch('big.ofd');
const doc = await OFDDocument.loadFromStream(res.body!);
// 或来自用户选择的文件
const doc2 = await OFDDocument.loadFromStream(file.stream());
// 完整文件可直接:
const doc3 = await OFDDocument.load(await (await fetch('doc.ofd')).arrayBuffer());

渲染

import { renderPageToCanvas, renderPageToSVG, getPageDimensions } from 'ofd-lib';

const svg = renderPageToSVG(doc, 0, { scale: 1.5 });
const { width, height } = getPageDimensions(doc, 0); // mm
await renderPageToCanvas(doc, 0, canvasEl, { scale: 1.5 });

API 对照(pdf-lib → ofd-lib)

| pdf-lib | ofd-lib | 说明 | | --- | --- | --- | | PDFDocument | OFDDocument | 文档主类 | | PDFPage | OFDPage | 页面类 | | StandardFonts | StandardFonts | 扩展中文字体 | | rgb() / cmyk() | rgb() / cmyk() | 完全兼容 | | degrees() | degrees() | 完全兼容 | | PDFForm | OFDForm | 表单 | | — | OFDSignature | 数字签名(国密) | | — | OFDStamp | 印章 |

目录结构

src/
├── index.ts                 # 公开 API
├── ofd/                     # 文档/页面/字体/图像/表单/签名/印章
├── parsing/                 # OFD.xml / Document.xml / Page.xml / 资源 解析
├── rendering/               # Canvas2D 与 SVG 渲染器
├── objects/                 # Text/Image/Path/Composite 内容对象
├── fonts/                   # 字体嵌入与度量
├── crypto/                  # 国密签名验证
└── utils/                   # ZIP / XML / 坐标 / 颜色 工具

开发路线图

  • [x] Phase 1:基础解析与渲染(MVP)
  • [x] Phase 2:文档创建与修改、绘制、字体嵌入、保存
  • [x] Phase 3:跨文档复制、表单、元数据、SVG 渲染
  • [x] Phase 4:国密签名(内置 SM2/SM3)与验签、印章、附件、页面模板
  • [x] Phase 5:OFD/A 归档校验、字体子集化(TTF)、大文档流式读写

构建与测试

npm run build      # tsup 构建 ESM + CJS
npm run typecheck  # tsc 类型检查
npm test           # vitest

License

MIT