each404

导航

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

演示

demo

快速接入

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 反馈实际样本踩坑,任何兼容性问题都会跟进。

posted on 2026-05-25 10:01  递归404  阅读(28)  评论(0)    收藏  举报