夜雨聆风学习资料网

ARTICLE · 1100618

8 个 OFD 转 PDF 库实测:谁最快、谁的全文字没了、谁在 7M 的文档上崩了

8 个 OFD 转 PDF 库实测:谁最快、谁的全文字没了、谁在 7M 的文档上崩了

上一篇讲了双向转换为什么不对称。这一篇进数据,看 OFD → PDF 方向的实测结果。

先给结论剧透:

没有一个人在所有维度上赢。 速度第一的还原度崩了,还原度第一的文本最慢,文本最强的最快只排第二。

更麻烦的是:两个 Python 库在 6 个测试文件上各有 2 个直接失败,成功率 67%。


一、参测的 8 个转换器

⚠️ 先读这段:下表是数据快照,不是库的当前状态。所有结论只对表中版本成立 —— OFD 库多为 0.x 早期版本,迭代很快,新版大概率修掉本文的若干短板。引用前请先核对版本,见 05 篇「局限 9」。

覆盖 5 种语言,全部实测过。「库版本」是数据可信度的关键列,「库更新」是该版本的发布日期:

转换器
语言
底层库
库版本
库更新
许可证
go-zc310
Go
zc310/ofd
v0.1.3
2026-09-28
Apache-2.0
go-ofdgo
Go
xiaoqidun/ofdgo
v0.0.0-20260928111512
2026-09-28
Apache-2.0
rust-easyofd
Rust
easyofd-rust
v0.1.4
2026-09-28
Apache-2.0
python-easyofd
Python
easyofd
20260427
2026-04-27
Apache-2.0
python-ofd2pdf
Python
ofd2pdf
0.0.2
2026-07-08
⚠️ 无许可证
java-ofdrw
Java
ofdrw
2.4.0
2026-08-04
Apache-2.0
node-ofd2pdf
Node
@miconvert/ofd-to-pdf
0.2.3
2026-08-03
Apache-2.0
python-ofdreader
Python
ofdreader
—(未安装)
—
MIT

📌 python-easyofd 实际是 pip install easyofd ofd2img 组合(自动切换库),另一个组件 ofd2img 版本 0.1.2、更新于 2026-05-07。

测试环境:Ubuntu 26.04.1 LTS (64-bit)字体:fonts-wqy-zenhei、fonts-noto-cjk(已装各种中文字体)样本:来自 zc310/ofd 仓库的 test/testdata参考:数科版式阅读器导出的 *.pdf

关于版本时效性,有三点必须说清楚:

  1. 版本跨度很大
    :从 2026-04-27(easyofd)到 2026-09-28(三个主力库),相差 5 个月。同一次测试里,有的库是刚发布的,有的已经半年没更新 —— 后者的"失败"更可能是"没人修",而不是"修不了"。
  2. 失败项尤其容易过期
    :本文最刺眼的 python-easyofd 4 次失败、python-ofd2pdf 4 次失败、python-ofdreader 全 FAILED,都发生在未维护或低维护的库上。作者修个 bug 是随时可能的事。
  3. 许可证是唯一几乎不会变的结论
    :jsyzdej/ofd2pdf 无许可证,这条不会因为升级而失效。

⚠️ python-ofd2pdf 没有许可证,商用项目直接排除。这一条比它的任何性能数据都重要。

另外 python-ofdreader 模块在本环境未安装,该转换器全部标记 FAILED,实际不参与排名。

测试文件:难度是递进的

文件
大小
说明
难点
hello.ofd
1.5K
简单测试文件
最小连通性
999.ofd
30K
带签章文件
简单多页文档
ano.ofd
702K
带签章文件
发票
,版面复杂
1000-pages.ofd
456K
1000 页文件
页数、字体子集复用
zsbk.ofd
1.5M
数科签章文件
签章
intro.ofd
7.2M
介绍文档
复杂图形/多层结构

这 6 个难度完全不同的文件,是本篇所有结论的适用范围。


二、速度:Rust 五胜,但别急着欢呼

转换器
hello
ano
intro
1000-pages
999
zsbk
胜出
rust-easyofd45ms72ms
1.85s
951ms166ms71ms5
go-zc310
125ms
212ms
835ms
4.22s
290ms
421ms
1
go-ofdgo
102ms
504ms
3.18s
7.88s
655ms
522ms
0
java-ofdrw
507ms
759ms
5.11s
2.40s
799ms
5.53s
0
node-ofd2pdf
287ms
711ms
1.21s
8.57s
507ms
801ms
0
python-easyofd
FAILED
1.09s
8.43s
FAILED
1.01s
5.18s
0
python-ofd2pdf
136ms
FAILED
FAILED
41.29s
409ms
201ms
0

三个要点:

① Rust 是 5 胜里的"4 胜 + intro 一败"。 5 个样本第一,只有 intro 掉到 1.85s(被 go-zc310 的 835ms 压过)。5 胜无极端值,是这批数据里最干净的一行。

② 差距在多页场景才成倍拉开。1000-pages 一栏:rust 951ms,ofd2pdf 41.29s,43 倍。而 hello 一栏只有 11 倍。单页测试完全测不出真实吞吐。

③ 两个 Python 库合计 4 次失败。

python-easyofd   hello FAILED,  1000-pages FAILEDpython-ofd2pdf   ano   FAILED,  intro     FAILED

最刺眼的是 python-ofd2pdf:hello 136ms,1000-pages 41.29s。 同 一个库,差了 300 倍。栅格渲染路线在多页上会随页数线性累积,且 intro(7.2M,复杂图形)直接崩。

根因记录在项目文档里:python-easyofd 对 hello、1000-pages 会 crash(list index out of range);python-ofd2pdf 基于图片渲染,部分文件失败。

教训:hello 通过率 100% 的库,遇到你的真实文档可能直接挂。必须用自己业务的样本重测。


三、体积:谁在膨胀,谁在偷内容

转换器
hello
ano
intro
1000-pages
999
zsbk
胜出
go-zc310
9.9KB
77.7KB
7.6MB
1.8MB
73.6KB
1.4MB
0
go-ofdgo
13.8KB
1.4MB
34.8MB
38.8MB
2.1MB
2.0MB
0
rust-easyofd
37.3KB
77.1KB
28.9MB
614KB
83.3KB
77.8KB
2
python-easyofd
FAILED
36.1KB
38.2MB
FAILED
73.1KB
13.6MB
2
python-ofd2pdf
36.8KB
FAILED
FAILED
70.2MB
752KB
129KB
0
python-ofdreader
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
0
java-ofdrw
4.8KB
65.6KB
22.6MB
2.3MB
76.9KB
15.4MB
0
node-ofd2pdf
1.3KB
197KB
1.1MB
7.2MB
82.9KB
559KB
2

python-ofdreader 模块未安装,全部标记 FAILED,不参与排名。

核心矛盾:体积和保真度在打架。

  • intro
    (7.2M 源)一栏:node 1.1MB,go-zc310 7.6MB,ofdrw 22.6MB,easyofd 38.2MB,ofdgo 34.8MB。
  • 1000-pages
    (456K 源)一栏:rust 614KB(全场最小),go-zc310 1.8MB,ofdrw 2.3MB,node 7.2MB,ofdgo 38.8MB,ofd2pdf 70.2MB(全场最大)。

python-ofd2pdf 在 1000 页上输出 70.2MB,是全场最差的压缩表现(⚠️ 456K 是源 OFD,70.2MB 是输出 PDF,属跨格式比较;若按源文档口径与数科参考 PDF 相比,比值为 33.13)。go-ofdgo 的 38.8MB 排第二(对应比值 18.30)。项目文档对这两个库的体积定性分别是"基于图片渲染,部分文件失败"和"文件体积较大",未给出根因。

📌 顺带澄清一个常见误传:项目工程记录里"ofdgo 输出 OFD 写 16 位色值"这条,属于 PDF→OFD 方向(03 篇),说的是它生成的 .ofd 里颜色通道值偏大,与这里 OFD→PDF 的体积膨胀是两回事,别混着引用。

再看和数科参考 PDF 的比值(1.00 = 与数科一致,越接近越说明压缩策略接近,加粗为该列最接近 1.00):

转换器
hello
ano
intro
1000-pages
999
zsbk
go-zc310
2.79
0.09
1.13
0.84
0.92
0.90
go-ofdgo
3.90
1.61
5.19
18.30
26.48
1.26
rust-easyofd
10.52
0.09
4.30
0.281.04
0.05
python-easyofd
FAILED
0.04
5.70
FAILED
0.92
8.62
python-ofd2pdf
10.37
FAILED
FAILED
33.13
9.40
0.08
java-ofdrw
1.36
0.07
3.37
1.070.96
9.78
node-ofd2pdf
0.38
0.220.16
3.40
1.04
0.35

go-zc310 是唯一六项里有四项落在 0.84~1.13(贴近 1.00)的:1000-pages 0.84、999 0.92、zsbk 0.90、intro1.13。离群的两项是 hello 2.79 和 ano 0.09。这说明它的压缩行为整体和数科最接近,不是靠"少转东西"取胜的。

rust-easyofd 的 zsbk = 0.05(1.4M → 77.8K)看着惊人,但第 4 节会告诉你它的 zsbk 像素只有 94.02% —— 体积小是因为丢内容。


四、还原度:像素相似度,暴露了谁在偷懒

以数科版式阅读器导出的 PDF 为基准,96 DPI 渲染,算 1 - 平均归一化色差:

转换器
hello
ano
intro
1000-pages
999
zsbk
胜出
python-ofd2pdf
99.95%
FAILED
FAILED
99.32%
97.74%
94.33%
2
go-zc310
99.54%
99.62%
99.42%
98.00%
98.45%
99.50%
2
go-ofdgo
99.40%
99.35%
99.62%
98.37%
98.49%
99.30%
2
java-ofdrw
99.88%
98.86%
98.94%
98.93%
97.73%
98.45%
0
node-ofd2pdf
99.47%
97.74%
96.29%
98.21%
95.80%
94.40%
0
rust-easyofd
99.79%
97.83%
44.99%73.66%
96.34%
94.02%
0
python-easyofd
FAILED
93.82%
37.74%
FAILED
92.41%
94.41%
0

4.1 三个硬故障

rust-easyofd     intro 44.99%    1000-pages 73.66%python-easyofd   intro 37.74%

intro 这份 7.2M 的文档,是照妖镜。 任何在 hello 上接近 99% 的库,到 intro 都现原形:

node-ofd2pdf     96.29%   ← 掉得最少go-ofdgo         99.62%   ← 几乎无损go-zc310         99.42%java-ofdrw       98.94%rust-easyofd     44.99%   ← 崩了python-easyofd   37.74%   ← 崩得更狠

项目文档写得很直接:

rust-easyofd 在 intro、1000-pages 相似度明显偏低(46%、74%),说明对含复杂图形/签章文件的还原能力弱。

注意 1000-pages 一栏更能说明问题 —— rust 在这一栏的像素只有 73.66%,但它的输出 PDF 只有 614KB(该栏最小)、速度 951ms(该栏最快)。"最快 + 最小 + 还原最差"三件事同时发生在同一个库身上,这不是巧合:它没转该转的东西。

4.2 一个方法论警告

文档里有一条必须记下的注意事项:

intro 的像素对比跳过 14~19 页(数科导出的参考 PDF 这几页背景图丢失,不计入对比)。

也就是说,参考基准本身在这 6 页上是有缺陷的。如果不排除,intro 一栏的所有数字都会被系统性拉低。

复现:python3 tools/compare_pdf.py 96 <file>


五、文本能力:这是最容易被忽略、也最影响业务的维度

转换完了。用户接下来会做什么?

在 PDF 里 Ctrl+F 搜索      → 需要文本层复制一段文字贴进系统      → 需要文本层归档系统做全文检索        → 需要文本层把 PDF 内容喂给 NLP/RAG   → 需要文本层

"转换成功"和"转换出的 PDF 能用",差距就在这里。

5.1 提取字符数

单位:字符数(chars)

转换器
hello
ano
intro
999
zsbk
胜出
node-ofd2pdf
26
6990
9614
42491938
2
go-zc310
22
7280
6072
3971
1162
1
rust-easyofd
28
6553
9602
4046
490
1
python-easyofd
FAILED
1208
10194
3933
1508
1
java-ofdrw
18
6456
1036
2933
181
0
go-ofdgo
8
00
61
13
0
python-ofd2pdf
0
FAILED
FAILED
00
0

两个 0 值来源完全不同,性质天差地别:

go-ofdgo          0  ← 转换成功,但文本层基本是空的(不是提取不出来,是没写进去)python-ofd2pdf    0  ← 走的是"整页渲染成图片"路线,本来就没有文本层

go-ofdgo 的 0 尤其要注意 —— 它的像素相似度 99.62%(intro)、98.49%(999)是全场前列,画面完美,但一个字都搜不到。项目文档:

go-ofdgo 文本提取能力弱(hello=8/25、999=61/3934 字符)。

参考值是 hello=25、999=3934。它提取出 8 和 61,分别只有 32% 和 1.6%。

这就是本文最想传达的一句话:像素相似度 99% 和"能用",是两件事。

5.2 文本相似度(4-gram Jaccard)

字符数只能说明"有多少",相似度才说明"对不对"。

转换器
hello
ano
intro
1000-pages
999
zsbk
胜出
go-zc310
37.93%
99.07%73.85%
69.82%
95.85%98.85%4
java-ofdrw
29.63%
16.20%
7.16%
78.68%
51.29%
5.88%
1
rust-easyofd
45.16%
25.44%
4.78%
0.22%
47.81%
7.01%
1
node-ofd2pdf
30.00%
13.57%
5.88%
62.90%
41.24%
4.86%
0
python-easyofd
FAILED
12.62%
0.08%
FAILED
80.54%
11.20%
0
go-ofdgo
8.33%
0.00%0.00%
0.35%
0.64%
0.64%
0
python-ofd2pdf
0.00%
FAILED
FAILED
0.00%
0.00%
0.00%
0

go-zc310 4 胜,而且是唯一在长文档上稳定的:

zsbk   98.85%   (参考 1150 字符)999    95.85%   (参考 3934)ano    99.07%   (参考 7139)intro  73.85%

go-ofdgo 的 ano 和 intro 是 0.00% —— 和"转换成功"并列出现,冲击力很强。

为什么有些库字符很多但相似度很低? 项目文档点名了 go-zc310 的相反问题:

go-zc310 保留文本对象,文本顺序正确(如「欢迎使用」顺序无误),但字形间插入空格(如 H e l l o)使 4-gram 相似度偏低,内容本身完整。

以及:

文本相似度对提取顺序/换行敏感,得分偏低不代表内容缺失。

所以正确的读法是:

字符数 0        →  确定不能用(无文本层)字符数少 + 顺序对  →  可能是好的(可以优化字距)字符数多 + 相似度低 →  顺序/切分有问题(要修切分逻辑)

复现:python3 tools/compare_text.py <file>


六、质量总结与官方结论

转换器
成功率
页面尺寸
矢量
文本可提取
图片
压缩
中文
兼容
go-zc310
100%
A4
✅
最佳
✅
良佳
最佳
全部
go-ofdgo
100%
A4
✅
部分
✅
较差
良好
全部
rust-easyofd
100%
A4
✅
良好
✅
最佳
良好
全部
python-easyofd
67%
A4
✅
部分
✅
良佳
良好
部分失败
python-ofd2pdf
67%
A4
❌
无
✅
一般
良好
部分失败
java-ofdrw
100%
A4
✅
良好
✅
良佳
良好
全部
node-ofd2pdf
100%
A4
✅
良好
✅
良佳
良好
全部

项目文档给的结论(原文摘录):

  • rust-easyofd
    :速度最快(5 胜),压缩率最好(2 胜),推荐首选
  • go-zc310
    :intro.ofd 最快(835ms),文本相似度最高(4 胜)、像素还原 2 胜(ano、zsbk),文本提取最完整(ano=7280 字符),适合需要搜索/复制文本或处理复杂文档的场景
  • go-ofdgo
    :像素相似度 2 胜(intro、999),但文件体积较大、文本提取较差
  • python-easyofd
    :部分文件转换失败,intro 还原度差
  • python-ofd2pdf
    :基于图片渲染,无法提取文本
  • java-ofdrw
    :兼容性好,但速度较慢;1000-pages 文本相似度最高(78.68%),小文件还原度接近数科
  • node-ofd2pdf
    :兼容性好,文本提取字符数上 999(4249)、zsbk(1938)领先
  • rust-easyofd
    :对含复杂图形/签章文件(intro、1000-pages)还原度差(46%、74%)

⚠️ 文档自己列出的局限性(请一并读完)

  • 本测试仅运行 1 次,无方差/置信区间,无法判断差异是否显著
  • 未记录机器规格
    (CPU 型号/频率/核数/内存/是否 SSD),绝对耗时不可移植
  • Java/Node 未预热(JIT 未达稳态),首次运行吃亏,"Java 最慢"的结论需谨慎
  • 无冷启动/热启动分离,单次测量包含类加载、字体扫描等开销
  • 仅相对排名有意义,绝对毫秒数在不同机器上会完全不同
  • Rust 5 胜为全面领先,无明显极端值

以上结论由 AI 根据测试数据自动汇总生成

建议:手工核对输出 PDF 效果,选择合适的库。不同 OFD 文件结构差异较大,实际效果可能与基准测试结果不同。

测试输出 PDF 文件可在 Releases 页面下载。


七、按场景的实用建议

你的需求
首选
理由
归档/公文,要文本可搜
go-zc310
文本相似度 4 胜,体积比接近数科
发票/签章,保真优先
go-zc310
 或 java-ofdrw
zc310 像素 2 胜;ofdrw 小文件接近数科
复杂图形文档(PPT 导出版式)
go-ofdgointro
 99.62% 最高
大批量、要快
rust-easyofd
5 胜,但必须自己验 intro 类样本
超大页数(1000+)
rust-easyofd
(614KB)或 java-ofdrw
rust 最小最快,但需验还原度
浏览器/Node 环境
node-ofd2pdf
字符数 2 胜,intro 1.1MB 最小
只是要个图片
python-ofd2pdf
接受 0 文本、41s/1000 页即可
商用项目
排除 python-ofd2pdf
⚠️ 无许可证

八、一句话收束

OFD→PDF 的正确问法不是「哪个最快」,而是:  · 我的文档有复杂图形吗?        → 有,先验 intro 类样本  · 转换后要能搜索/复制吗?      → 要,看文本相似度不看像素  · 签章能画出来就够吗?        → 够(信任链另算)  · 能进商用吗?                → 先查许可证

先按这四问筛掉一半选项,再看速度排名 —— 顺序反了就会被榜单骗。


参考资料

  • ofd-benchmark/docs/ofd-to-pdf.md
    (本文全部数据来源)
  • ofd-benchmark/README.md
    (项目索引与复现入口)
  • 库能力全景见 ofd-benchmark/docs/ofd-libraries.md(66 个库)

相关学习资料