ofdkit-harmony 0.2.0 发布:鸿蒙原生 OFD 阅读库,已上架 ohpm
软件简介
ofdkit-harmony 是一个面向 HarmonyOS NEXT 的原生 OFD 阅读 / 渲染库,完全用 ArkTS 实现,不依赖 WebView、JNI 或 native 桥接,遵循国家标准 GB/T 33190-2016。适用于电子发票、电子公文、电子合同等典型 OFD 场景。
- 项目地址:https://gitee.com/notcoder/ofdkit-harmony
- 开源协议:Apache License 2.0
- ohpm 包名:
ofdkit-harmony - 当前版本:0.2.0
- 运行环境:HarmonyOS NEXT(暂不支持 OpenHarmony,库内使用
@kit.*Kit 命名空间 API)
主要功能
- 多页 OFD 文档打开(电子发票 / 电子公文 / 电子合同)
- 模板页 + 注释 + 路径 / 图片 / 文字对象完整解析
- 双指捏合缩放 / 单指拖动 / 双击复位
- 侧滑切页 + 多页连续滚动 + 缩略图条
- 跨页全文搜索 + 命中高亮 + 上下跳转
- 长按选中文字 → 复制到剪贴板
- 嵌入字体注册 + 全局兜底字体 API
一行安装
ohpm install ofdkit-harmony
演示

快速接入
import {
OFDParser,
OFDPageView,
installDefaultExtensions
} from 'ofdkit-harmony';
// 应用启动时调用一次
installDefaultExtensions();
// 解析
const parser = new OFDParser({ workDir: '/path/to/cache' });
const doc = await parser.parse('/path/to/file.ofd');
// 渲染
@Component
struct Reader {
@State doc: OFDDocument = doc;
@State pageIndex: number = 0;
build() {
OFDPageView({ page: this.doc.pages[this.pageIndex] })
}
}
OFDPageView 内部自动处理字体注册、图片解码、Canvas 绘制和手势。
架构
四层:UI 组件 → Renderer → Parser → 扩展点。三类扩展点向上开放(ObjectParserExt / ObjectRendererExt / DocumentExtension),验签 / 转换 / 表单等能力都能用同一套机制接入,不必侵入核心库。
几个值得说的实现细节
1. DeltaX/DeltaY 定位的几个坑
OFD 文字对象用 DeltaX 数组定位每个字符。规范里有一个 g N v 重复格式(如 3.5 g 5 4.2 1.8 表示 [3.5, 4.2×5, 1.8]),实际样本里还经常出现 DeltaX 数组比字符串短(要重复最后一个值兜底)、负 DeltaX(密码区多行布局)等情况。早期把「DeltaX 太小就用字符自然宽度兜底」的逻辑加进去,结果把负值也覆盖了,密码区直接错乱——后来改成负值跳过兜底。
2. 字体处理:嵌入 ttf + 全局兜底 API
嵌入字体直接用 font.registerFont 注册到 ArkUI 字体表,Canvas 直接用字体名。但大多数电子发票只声明 FontName="宋体" 不嵌入文件——HarmonyOS Sans 是无衬线,跟纸质发票的衬线宋体差别明显。所以加了:
OFDRenderer.setFallbackFontFamily('OFD-Fallback');
调用方自己注册字体到系统表(避免授权风险),然后告诉 Renderer 用这个 family 兜底,视觉立刻接近纸质发票。
3. ZOrder 与模板页
模板页 ZOrder 有 Background / Foreground 两种。电子发票的「红章框」常常是 Foreground 模板,必须最后绘制,否则会被发票数据盖住。渲染顺序:模板 Background → 主页对象 → 模板 Foreground → 注释 → 签章。
4. AbbreviatedData 路径的 A 命令
OFD 路径 A 命令参数是 SVG 风格的 rx ry rotate largeArc sweep x y,但 Canvas 没有 SVG 圆弧 API,要换算成 ellipse(cx, cy, ...)。endpoint-to-center 推导那一段代码挺繁琐,仓库里 parsePath.ts 有完整实现。
5. OFD 套 OFD:矢量印章的递归解析
最有意思的一个细节——电子发票的红章本身就是一个嵌入的 OFD 文件(picture.type='OFD')。完整结构:
主 OFD 文档
└── StampAnnot
└── SES_Signature
└── picture
├── type='png' → 光栅印章,PixelMap 直绘
└── type='OFD' → 矢量印章,递归解析渲染
递归渲染时字体命名要做命名空间隔离(嵌入文档的字体注册加 stamp_${stampId}_ 前缀),否则跟主文档同名字体(都叫 Font_F1)会互相覆盖。
实际踩过的小坑
- LongPressGesture 被 PanGesture 抢占:长按选字 500ms 触发不了 → 用
priorityGesture独立挂 LongPress,比子组件 / 兄弟手势先识别。 - 模板页常常不带 PhysicalBox:规范说应有,实际样本里大多没有 → 模板页 Area 改为可选,缺省继承主页。
- OFD 包路径大小写不一致:XML 写
Doc_0/,实际文件名是doc_0/→ 加resolveCaseInsensitive兜底。 - 部分电子发票对象不带 ID:早期当必需字段直接报错 → 改可选,用对象顺序号兜底。
API 概览
OFDParser— 解析入口OFDPageView— 单页渲染组件OFDDocumentScroll— 多页连续滚动视图OFDThumbnailStrip— 缩略图条SearchController+OFDSearchBar— 跨页全文搜索OFDRenderer.setFallbackFontFamily— 全局兜底字体
完整 API 与示例见仓库 README。
后续路线
- CMYK / Pattern / Gradient 色彩空间补全
- 表单填充
- 国密 SM2/SM3 签章验签(已在企业版闭源分支验证,开源版规划中)
欢迎在 Gitee 仓库提 Issue 反馈实际样本踩坑,任何兼容性问题都会跟进。
浙公网安备 33010602011771号