行安装
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 兜底,视觉立刻接近纸质发票。

  1. ZOrder 与模板页
    模板页 ZOrder 有 Background / Foreground 两种。电子发票的「红章框」常常是 Foreground 模板,必须最后绘制,否则会被发票数据盖住。渲染顺序:模板 Background → 主页对象 → 模板 Foreground → 注释 → 签章。

  2. AbbreviatedData 路径的 A 命令
    OFD 路径 A 命令参数是 SVG 风格的 rx ry rotate largeArc sweep x y,但 Canvas 没有 SVG 圆弧 API,要换算成 ellipse(cx, cy, …)。endpoint-to-center 推导那一段代码挺繁琐,仓库里 parsePath.ts 有完整实现。

  3. 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。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐