更新日志

本页按版本号记录一凡OFD(npm 包名 rust-ofd)每次修改与升级的内容,新版本在上。

版本号与 WASM 内核一致:页面端调用 wasmVersion() 可读取当前实际加载的内核版本(阅读器示例顶栏徽标与「授权」面板均有展示,可用于核对部署产物是否为预期版本)。

记录约定

  • 内核(Rust crates / WASM)发生功能新增或缺陷修复时,workspace 版本号同步升级。升号须同步四处(缺一处即坏,详见根目录 AGENTS.md「版本号策略」):① 根 Cargo.toml 的 [workspace.package].version;② 同文件 [workspace.dependencies] 中内部 crate 的 version(与全局一字不差,v0.10.0 起新增 ofd-jpx、v0.20.0 起为 10 个——含 ofd-license 自身条目,漏改该条目时次版本升级 cargo 解析直接报错);③ packages/ofd/package.json;④ Cargo.lock(随构建自动更新)。各 crate 均为 version.workspace = true,勿单独改。升号后在本页顶部追加条目,WASM 授权产物须重建才会显示新版本;
  • 条目须包含:版本号、日期、本次修改与升级的内容(面向使用者的表述,而非提交记录罗列);
  • 主站(astro-site)/ofd-batch 等前端与工具载体、文档站自身的 noteworthy 改动,随所在内核版本一并记录(vue-demo 已冻结维护,仅其罕见回归修复才记);
  • 纯文档错别字等无行为变更可不升版本,直接在对应版本条目下补记。

v0.24.0 — 2026-10-31

ofd-license 升级为唯一签发+验签核心(签发侧 Rust 化第一步):

  • ofd-license 新增签发 API(issue_token/IssueSpec/IssuedLicense/load_signing_key 等):与 Node 签发服务 lib.mjs issueToken 字节级兼容——同一把私钥、同一组参数产出完全相同的 token(payload 字段序 v,lic,env,typ,hosts,iat,exp,nonce,scope[,did]、Ed25519 确定性签名、紧凑 JSON 序列化逐字节一致),签发与验签从此收敛到同一 crate 单一真源(TTL 分档/scope 档位/hosts 校验不再三处镜像);

  • 签发校验全量移植并逐条对拍:trial 固定 localhost, 127.0.0.1、online 禁部署 ID/环回私网地址/泛域名、offline hosts 与 did 至少一项、TTL 按 typ 分档含下限(online 1168h / offline 19600h / trial 1~720h)、hosts 归一化(小写/去空白/::ffff: 映射形式转纯 IPv4)与模式校验(域名/*/*.suffix/IPv4/IPv6/CIDR)、modules_to_scope 模块组合→功能档位(空集兜底 full,存量授权不降级)——每条语义均为「Rust 单测 + Node 冒烟断言」双实现同输入同结论;

  • 零新增第三方依赖(wasm32 构建图红线):熵与时钟全由调用方注入(iat/nonce_hex 外置,lic 缺省自动编号取 nonce 前 4 字节),纯函数可确定性单测;配套 gen_no/gen_local_lic 编号生成(31 字符集去 0/O/1/I,对齐签发服务 genNo);

  • 对拍测试 tests/issue_compat.rs:以现网统一密钥经 Node 实产的 5 组参考 token(在线/离线绑 did/trial/prod+convert 边界 TTL/desktop 档)固化为常量,Rust 签发逐字节 assert_eq! 且过 verify_with_key 全闸门链;私钥不入库,运行时从 .license-keys/ 备忘按公钥配对扫描(Node 侧冒烟 87 项全过);

  • 本版为授权体系统一架构(docs/business/授权体系统一架构Rust化方案.md)批次 1:Node 签发链路原样保留(对拍金标准+应急兜底),后续 Rust 版签发服务与授权构建台直接复用本核心;WASM API 面无变化(三模块 .gen 已随内核重建同步)。

  • 授权构建台(ofd-license-builder 0.2.0,补记 2026-10-31,随本版本一并交付):前端整体重构为 Vue 3 + Ant Design Vue 4 管理后台(Vite 构建产物编译期嵌入,原纯手写三件套退役),侧栏七域布局(新增总览域:工具链/server 连接/outbox 待报/待审申请/新反馈一屏聚合 + 最近构建与快捷入口);授权构建改为列表优先 + 分步弹窗——授权列表由客户申请(server 只读带入)与手动添加条目合并,点「构建」先进参数步(按授权行预填,可改档位/环境/密钥/部署ID/激活码签发)再进执行步(实时日志),构建异步可后台(关弹窗不中断,列表行标「构建中/已出包」),同一时刻单作业;表格/表单/弹窗/消息全量换 antd 组件(日志查看器带自动滚动与复制、服务连接改抽屉并新增「测试连接」、反馈改展开行流转);配置 Vite 热更新(npm run dev + cargo run,改前端免重编 exe);签发/构建后端口径(29 个命令、6 个事件、TTL 上限、scope 映射)与旧版 1:1,无行为变化。布局与自适应(同日二轮补记):七域统一「页头(标题/副标/操作区)+ 卡片内容」结构(PageHeader 组件,操作从卡片 extra 上移);侧栏 <1280px 断点自动折叠(状态记忆)+ 顶栏手动手折钮,窄窗顶栏依次隐藏工具链灯/版本徽标;全部弹窗居中(含确认框)且宽度 min(px,96vw) 自适应、抽屉 min(px,92vw);表单栅格窄窗自动折单列(签发/审批/构建参数/手动授权表单),统计四卡等响应式重排;文案统一「授权构建」。桌面授权同模式重构(同日三轮补记):改为「客户列表优先」——手动建档(localStorage)+ 台账历史按客户名自动归并(已签发数/最近签发/指纹补缺),点「签发激活码」进分步弹窗(第一步客户信息与签发参数、第二步激活码回执与一键复制);签发表单从常驻卡片收入弹窗,分发包构建卡瘦身为单行工具条;指纹格式等后端校验错误就地回显。用户邮件同模式收口(同日四轮补记):页面只留用户聚合列表,写邮件收入分步弹窗(撰写→逐封投递回执,含「再写一封」);列表「写信」带入收件人可在页头按钮显示已带人数,发送成功自动清空。上线前联调收口(补记 2026-11-01,随本版本一并交付):修复构建台与 Rust 授权服务(license-server-rs)的跨端断链——① 访问统计 /admin/stats/site 响应形状对齐构建台钉住的契约(cards/trend/count,此前两侧各说各话导致四卡/趋势/榜单计数全空);② 桌面分发包构建的实时日志事件通道打通(desktop-build-log 此前后端从未发出,日志区全程空白);③ 「目录/重排/激活码文件」三个按钮断链修复(参数形状错误必败,激活码文件改走新命令 reveal_path 且限作业目录内、失败不再伪装成功);④ 在线授权+部署 ID 组合提交前拦截(此前要白跑整轮 wasm 构建才报错);⑤ 反馈附件中文名按 RFC 5987 解码落盘(此前折叠成不可读的 '----' 串);⑥ outbox 上报批内单条失败不再中断整批;⑦ 邮箱校验对齐 Node 正则回溯语义(IP 形态域名不再误拒);另有历史日志查看不清实时流、构建完成自动刷新台账、LLVM 目录改失焦保存、剪贴板失败明示等交互修复。macOS/Linux 开发适配:补 icons/icon.png(非 Windows 平台编译必需)、新增 mock 版 UI 验收脚本 scripts/ui-mock-acceptance.mjs(无 WebView2 环境跑前端回归,22 项断言含关键调用参数形状);登录限流测试确定化(慢机跨 60s 窗口抖动)。macOS 兼容收口(同日五轮补记):修复启动横幅误报「LLVM 目录不存在」——空白目录一律按未配置处理,macOS/Linux 留空即用系统 PATH 的 clang(Windows 缺配仍提示);子进程环境注入平台化:PATH 按平台以 :/; 分隔(此前 POSIX 上被按 Windows ; 拼接写坏)、build-env.sh 的 MSVC PATH 段与 LIB/INCLUDE 仅 Windows 注入;macOS 自动探测 Homebrew LLVM 安装位(Apple 的 /usr/bin/clang 无 wasm 后端,GUI wasm 构建编译 ofd-jpx 的 C 源需带 wasm 后端的 clang,未装 brew llvm 时装之并在总览域配置目录);scripts/build.mjs 的 cc 口径平台化(Windows 用 clang-cl、其余用 cc),非 Windows 构建脚本原生可跑,直调 cargo 用 CC=cc 覆盖(cargo [env] 无法按平台条件化,clang-cl 仅 Windows 存在)。

    授权构建台 UI 专项走查修复(ofd-license-builder 0.3.0,补记 2026-11-01,随本版本一并交付):① 修复错误提示全部渲染成普通颜色的问题(antd Typography 的 type 枚举无 error,全量 error→danger,此前构建/签发/审批失败文案与正文同色无感知);② 桌面授权域重构为客户/授权台账同卡 Tabs、outbox 收纳为摘要条(有待报/冲突自动展开明细),总览六卡改一行三张且与统计四卡统一为同一指标卡组件;③ 构建反馈补强——执行步新增模块阶段进度(按构建日志识别已进入的模块)、实时耗时计时、成功回执改 Result 卡片(产物名/大小/sha256 + 下载/打开目录),日志查看器新增空态占位、warn 着色、错误行计数、下载与清空;④ 表单校验内联化(构建参数/桌面签发/手动授权/客户档案/写邮件均在字段下方红字定位),申请审核选中行后平滑滚动到审批卡并补选中行高亮(此前高亮类名有引用无样式定义);⑤ 语义色收敛为 CSS 变量亮暗两套值(此前约 40 处硬编码 hex 不随主题切换,修复暗色下徽标刺眼/趋势图过暗/辅助文字对比不足),侧栏待办徽标统一琥珀色(红只留给失败/冲突);顶栏工具链三灯收纳为单胶囊(悬停看逐项明细,窄窗不再整体隐藏)、连接胶囊点击直达服务连接抽屉、待报数拆独立胶囊跳桌面域、LLVM 目录配置迁入服务连接抽屉(总览只读状态);表格低频操作收进「更多」下拉、单页数据隐藏分页器、微字号收敛(10.5/11/11.5px→12px);⑥ 服务连接保存空口令先确认(防误清登录凭据)、统计趋势图补峰值参考与悬浮明细且数据点≤3 时直接标值、兴趣榜加排名徽标与比例条;mock 验收断言同步扩展(工具链胶囊/连接抽屉/榜单排名等,37 项)。

  • 授权链替换收尾(补记 2026-11-01,随本版本一并交付):授权服务 Rust 版(license-server-rs 0.2.0,examples 独立工程)补齐三件对外行为——① /portal 门户页改由服务二进制直接托管(静态资源编译期嵌入、响应 no-cache,门户不再依赖 Node 服务在位);② 签发侧 iat 宽限 ISSUE_IAT_GRACE_SEC(默认 60 秒、设 0 关闭,token 的 iat 整体回拨该秒数而时长不变——防客户端时钟略慢导致 token 一到手即「尚未生效」,配置语义与 Node 服务一致);③ 启动自检必备表清单补 v3 两表(site_events/outbox_dedupe),缺表即拒绝启动(fail-fast,修复路径为重放 upgrade-v3.sql)。授权构建台(license-builder)承接授权台账管理(列表/详情/编辑/续费/吊销/归档/回收站),本地签发同样带 iat 宽限。Node 版 license-server(Express)冻结维护(2026-11-01 起,承接方与生效口径见其 README 横幅)。内核 crates 无行为变更,workspace 不升号。

  • Rust 版替换上线终验通过(补记 2026-11-06,随本版本一并交付):新增 parity-node.mjs 双服务并行对拍——同一随机私钥分别起 Node 版与 Rust 版(两套独立内存库、同 schema+v3),68 步同序列请求(renew 全错误矩阵、admin 全生命周期、门户全链现签激活码、stats/feedback、CORS 预检三步)带业务成功断言(防「两侧同样 401」的假阳性一致)逐字段 diff,收尾对 licenses/applications/issue_logs/license_events/access_stats 五表做全行终态对拍,当前基线 68 步全过、五表终态逐行一致;修复两处切流阻断项——① 嵌入门户静态资产行尾随 checkout 平台漂移(Windows CRLF 下 include_bytes! 嵌入字节与 Linux 生产构建不一致、测试字节基线跨平台翻车,根 .gitattributes 精确钉 6 个文本资产 eol=lf,二进制 png/ico 不标 text 防入库规范化损坏);② smoke 冒烟被本目录 .env 遗留 SMTP 配置干扰(devCode 分支须 SMTP 缺失,spawn 时显式注入 SMTP_HOST="" 屏蔽)。部署面验证:cargo zigbuild --release --target x86_64-unknown-linux-musl 交叉编译通过(6.3MB 静态 ELF、零运行时依赖,生产机无需编译环境),license-server-rs README 补部署节(systemd unit 样例沿用 ofd-license 单元名使 monitor.mjs 巡检/自愈零改动、.env 原样复用、切流五步与 Node 版一周回退保留期)。已于 2026-11-06 执行切流上线:线上并行对拍(同一生产授权 DESK-261031-22PP 双侧 renew,九字段与 token payload 逐字节一致,签名段差异仅为随机 nonce);systemd 单元 ofd-license 仅换 ExecStart(monitor 巡检/自愈零改动全绿,cert 44 天/disk 48%);常驻内存 73MB→5.7MB;lettre→网易 465 实投递验证通过;主站真实授权 renew 与门户/管理 API/静态页全链冒烟通过,Node 版目录与 unit 备份(ofd-license.service.bak-node-20261106)保留一周回退。

v0.23.0 — 2026-10-24

系统字体索引可序列化(桌面字体索引缓存的地基):

  • ofd-font 的 SystemFontIndex/FaceRef 补 serde 序列化支持(索引仅存字体路径与 name 表元数据,不常驻字体字节,单面约 0.5–2 KB);ofd-font 新增 serde 依赖;
  • 首个消费方为桌面批量工具(ofd-batch 0.4.0):扫描完成后索引落盘缓存,下次启动目录指纹未变化时直接反序列化就绪,免去每次启动的全量字体文件读取与解析(秒级~十秒级 → 亚秒级);
  • WASM 产物随内核源码变化重建(三模块 .gen 已同步),API 面无变化。

元数据(DocInfo)预览与编辑:内核 API + 网页双形态 + 桌面批量(对齐 GB/T 33190 DocInfo 全字段、PDF Info/XMP(Dublin Core)与 DA/T 46 档案元数据承载实践——自定义键值对作为跨格式扩展载体):

  • 内核(ofd-tool 新增 meta 模块):DocInfoPatch 补丁语义(标准字段三态:缺省不动 / 清除 / 设值,trim 后空串视为清除;customDatas 整体替换、重复键后者覆盖)+ edit_doc_info(作用于首个 DocBody,与 docInfo() 读口径一致;日期格式 YYYY-MM-DDThh:mm:ss 形状+范围校验、任一非法整体报错不动模型;autoModDate 缺省开——保存时自动刷新修改时间,显式给出时以显式值为准)+ read_meta 只读快照;DocID 只读不参与编辑(OFD-A metadata-docid 合规依赖,缺失由 ofdaRepair 补全);报告含变更明细、自定义键增删数、签名存在性与多文档提示;
  • WASM editOfdMeta(ofd, patch)(工具模块,全功能档):返回 { ofd, report },补丁 JSON 为 camelCase(null 清除 / 缺 key 不动 / 字符串设值),加密件拒绝;修改元数据会使既有签名摘要失配——report.hadSignature 为 true 时应提示重新签名。顺带纠错:DocInfo 类型声明与 api.md 此前按 snake_case 手写,实际序列化一直是 camelCase(vue-demo/astro-site 消费实证),.d.ts 已改为与运行时一致,消费方不受影响;
  • astro-site 双形态(单文件):① 阅读器「信息」面板升级为预览+编辑(分组展示:基本信息/时间/自定义/系统留痕,OFDA-Repair-* 留痕键带标记、删除需确认;保存走热替换——保留页码/缩放的全部视图状态,产物留存状态栏下载槽不自动下载);② 新增 /meta 独立工作台(上传→预览→编辑→手动下载/去阅读器预览,SEO 入口),首页工具目录与全站导航同步加入;
  • ofd-batch 新增第 4 模式「元数据编辑」(模板式批量:留空字段不动、填写即修改,自定义键值可承载档号/保管期限等档案著录字段):免费版单次 1 份、不限日次数,激活桌面授权后批量不限份数(不占转换日额度);文件行内「查看」免费预览当前元数据(含签名/多文档警示);已签名件默认跳过(勾选「包含已签名件」才处理,报表标注签名失效);加密件拒绝、断点续跑与 CSV 报表沿用;
  • 阅读器「文档信息」面板此前对 customDatas/permissions 原样平铺(数组/对象显示为字符串),本轮起为分组结构化展示。
  • ofd-batch 操作逻辑优化(补记,2026-10-24,工具版本 0.1.0→0.2.0):转换完成行内「定位」改指产物文件(此前打开的是源文件);转换运行中锁定选项区;免费额度超限前置为按钮禁用 + 行内预警(每批超 5 份 / 今日额度不足 / 元数据编辑超 1 份,不再点击后才报错);输出目录记忆上次选择、添加文件后未选目录时自动预填首个文件所在目录;模式切换过滤文件时弹提示;新增「只看失败」筛选与「完成后打开输出目录」选项;请求取消后即时显示「当前文件处理完成后停止」(与文件边界取消语义一致);元数据编辑模板实时回显「将修改 N 处」;已签名件默认跳过不再计入汇总「失败」数(分桶口径修正,报表 CSV 不受影响)。
  • ofd-batch「单文件免费 / 批量付费」商业模型重构(补记,2026-10-24,工具版本 0.2.0→0.3.0):
    • 统一免费/付费分界:行内单文件操作(一次一份)免费不限次、不占额度——OFD↔PDF 转换、OFD/A 检测与修复(原需授权的修复单文件免费)、元数据读取与编辑;批量操作免费试用统一为每批 5 份 / 每日 20 份(元数据批量从「单次 1 份」并入统一口径),桌面授权后批量不限量;OFD/A 批量检测免费不限量;
    • OFD/A 检测→修复引导闭环:头部拆「批量检测(免费)/ 批量修复」两按钮,检测结果以「合规/不合规」状态写入文件列表;行内「检测」「转换」并列(检测不阻断修复),检测报告不合规时提供「修复此文件(单文件免费)」入口;
    • 行内元数据编辑:原只读「查看」弹窗升级为编辑表单——回显当前值,diff 提交语义(改过的字段才提交、清空字段 = 删除该字段内容、未改动保持原值),自定义键值行可增删(整体替换),就地原子保存到原文件(临时文件 + rename 替换,失败不留半成品),已签名件须显式勾选确认(保存使签名失效);
    • 单文件操作零配置:输出默认写到源文件所在目录(同名产物自动 -2 递增),无需选择输出目录;
    • 付费提示统一点击时:修订 0.2.0 的前置预警口径——界面不再预禁用批量按钮、不再前置展示红色购买预警,额度不足/超限在点击批量按钮时以 toast 引导(「单文件操作免费不限次,或购买桌面授权解锁批量」);顶栏额度信息保留为中性状态展示;
    • 后端新增 convert_file(单文件转换/修复,复用批量内核管线与 panic 容错)与 meta_edit_file(就地编辑)命令;额度计数仅作用于批量管线。
  • ofd-batch 0.4.0:元数据模板 + SQLite 配置 + 导入导出 + 微信二维码 + 字体缓存(补记,2026-10-24):
    • 元数据批量编辑模板:批量表单可保存为命名模板(同名覆盖)、下拉加载、可删除,随配置一并导出迁移;
    • 本地配置 SQLite 化(%APPDATA%\ofd-batch\config.db):模板 + 自定义字体目录 + 输出目录记忆(自定义字体目录此前重启即丢,本轮修复);
    • 配置导入导出:设置页一键导出/导入 JSON(含全部模板与设置,按名覆盖、可跨机器迁移;导入后字体目录自动重扫);
    • 付费引导弹窗:额度不足点击批量按钮时弹窗展示微信二维码(添加好友请备注「工具授权」)+「打开授权面板」入口,替代纯文字 toast;
    • 字体索引缓存:内核 ofd-font 索引补 serde(见 v0.23.0),工具侧扫描完成后索引落盘,下次启动快速指纹校验命中即亚秒级就绪(此前每次启动全量重读字体文件,秒级~十秒级);字体文件增删或目录变化自动重扫。
    • 缺陷修复(补记,2026-10-24 当日二轮):① 勾选「强制修复已签名件」仍修复失败——工具级闸门放行后未向内核传 strip_signature,内核签名闸门继续拒绝;现随勾选显式剥离签名(印章外观尽量转页面图像保留),已合规的已签件也改走修复管线(to_ofd_a 对已签件直接拒绝);② autoModDate 静默失效——批量模板/编辑补丁的 serde bool 缺省为 false 与「缺省开」文档口径不一致,现 serde 与 Default 双侧对齐为 true;③ e2e 测试静默空跑——测试助手资源路径算错一级(examples/examples/resources),所有依赖样例文件的端到端测试从未真正执行(0.02s 全绿假象),修正后真跑 4.6s 并暴露修复了前两个缺陷,强制修复链路补「剥离动作记录 + 产物无签名」断言。多文档包(DocBody>1)不支持归档为内核硬约束(需先拆分/合并为单文档),报错文案不变。
    • 设置与模板交互重构(补记,2026-10-24 当日三轮):设置弹窗分区化(元数据模板 / 字体目录 / 配置备份 / 额度说明)——模板管理整体迁入设置:选择即预览编辑、新建、保存(同名覆盖)、删除;批量元数据模式主界面只留「应用模板」下拉(含摘要提示:修改处数 / 自动更新时间),未选模板点击开始时引导;付费引导窗与授权面板新增定价说明(每台设备每年 ¥199,版本升级不影响已购授权)。
    • 元数据批量编辑双模式 + 按文件 Excel(补记,2026-10-24 当日四轮):元数据模式分「统一设置」与「按文件设置」——统一模式下页面按所选模板预填表单(可微调),所有文件使用同一套元数据;按文件模式走 Excel 闭环:「导出 Excel」按导入文件列表生成逐行填写表(列 = 文件名 + 8 标准字段 + 模板自定义键,附填写说明页;空单元格 = 不修改)→ 在 Excel 中逐行填写 →「导入 Excel」按文件名匹配回填并展示逐文件摘要 →「批量编辑」(原「开始转换」按钮在该模式下更名);未在表格中出现的文件自动跳过(状态列「按文件设置中未找到该文件的元数据行」)。后端 JobConfig 新增 meta_patches 逐文件补丁;Excel 读写为纯 Rust(rust_xlsxwriter 写 + 树内 zip/轻量 XML 读,calamine 与 zip≥2.6 不兼容故自实现首表解析);导入按文件名匹配,同名文件以列表靠前者为准。
    • 行内预览(补记,2026-10-24 当日五轮,方案一 native 渲染):文件行内新增「预览」按钮(OFD 输入的三种模式;免费通道)——新增 preview_render 命令调内核 ofd-render 渲染当前页为 PNG,弹窗内翻页(上一页/下一页/左右方向键)+ 页码指示;加密件拒绝。文件不出本机、离线可用。清晰度根治(当日八轮):放弃固定倍率——前端按弹窗实际显示宽 × devicePixelRatio 上报精确目标像素宽,后端按页面毫米宽换算 scale 逐像素对齐物理像素网格(任何屏幕缩放/窗口尺寸都锐利),窗口 resize 防抖重渲;缺省回落 3×96dpi。
    • UI 文案与操作习惯打磨(补记,2026-10-24 当日七轮):选项标签的括号说明全部改为 ⓘ 悬停提示(覆盖已有输出/图像转码/强制修复已签名件/统一与按文件设置/应用模板/自定义元数据等 10 处),标签只留短文案;行内标签不再换行(应用模板等);授权面板补微信二维码与购买说明(扫码备注「工具授权」→ 发机器指纹换激活码,与激活流程说明合并去重)。
    • 验收修复(补记,2026-10-24 当日六轮,全面验收发现):① 统一模式元数据批量整批 skip(按文件分支未回落统一模板,P0,抽 resolve_meta_patch 纯函数+回归测试锁定);② 行内修复 forceSigned→forceRepairSigned 键名错位致强制修复勾选静默失效;③ 单文件编辑弹窗「自动更新修改时间」勾选框未接线;④ Excel 同名文件匹配方向与说明页口径不一致(改为先到先得);⑤ 文件列表变化后按文件 Excel 数据自动失效;⑥ 统一表单实时摘要提示(元素缺失);⑦ Excel 解析健壮性(空表/无属性单元格/自定义键列序稳定/64MB 解压上限);⑧ 字体缓存去双序列化、临时文件加 pid 防双实例竞态;⑨ 配置导入单事务化;⑩ meta_templates 返回 null 防御。
    • 授权激活闭环修复(补记,2026-10-24 当日七轮,真机 CDP 全链路验收发现):闭环构建下激活恒报「License 公钥配置错误」——build.rs 手写 base64url 编码器存在变量遮蔽缺陷(let chunk = [...] 遮蔽原始 chunk),尾部不满 3 字节的组按满组编码,32 字节公钥被编成 44 字符(应 43),运行时解码得 33 字节 ≠ 32、公钥还原失败。该缺陷仅存在于桌面工具这份 build.rs(cli/ofd-wasm 两份同步拷贝用独立变量名、无恙——此前 wasm/CLI 授权一直正常的原因)。修复 + 编码长度/往返单测;另修 --keys 构建必需的密钥备忘 OFD_LICENSE_ENV 标记缺失。验收方式:WebView2 CDP 驱动真实 GUI 全链路——免费版初始态 → 读机器指纹 → issue.mjs 签发 desktop 离线码(365 天)→ 粘贴激活 → 徽章「已授权·剩 365 天」+ 批量不限量 → token 落盘 exe 同目录 → 重启持久化 → 删除激活码回退免费版,全程通过。
    • 主站批量工具落地页暗主题修复(补记,2026-10-24):ofd.11m18.cn/batch.html 页内样式误用未定义的 --card/--brand 变量名,恒回退浅色 fallback——暗主题下锚点导航条、能力卡、价格卡与 code 底色全为白色刺眼。已对齐全局设计 token(--panel/--accent/--ink/--muted/--line),暗主题随 [data-theme="dark"] 正确联动;亮主题观感不变。
  • 主站 SEO 专项(补记,2026-10-24,随本版本一并交付):据热词检索(OFD转PDF/OFD批量转换/电子档案四性检测/数电票等竞品格局)升级全站 SEO——
    • 新增两个工具落地页:/ofda.html(OFD/A 检测与修复·电子档案四性检测:GB/T 42133 十四项检查清单、DA/T 70 口径、检测免费不限量引导)与 /invoice.html(数电票 OFD 专页:三格式对照/在线预览/OFD转PDF/验真/字段提取);知识中心新增第 7 篇《电子档案"四性检测"是什么?》;
    • /view.html 去"SPA 空壳":补视觉隐藏 H1 与 noscript 兜底文案+站内内链(页面视觉零变化,不执行 JS 的爬虫可见正文与内链);
    • 修复桌面工具下载直链线上 404——zip 自 docs.11m18.cn/downloads/(该域为重定向别名、不服务文件)迁至主站 /downloads/ 随 dist 部署;下载按钮新增点击统计(百度统计事件 + 自建 beacon path 带 event=tool-dl 标记,零服务端改动);
    • 全站元数据补齐:learn 7 篇与 docs 16 个公开页补 keywords(docs 副本 9 页空 description 一并补实),Docs/Article 布局补 keywords 输出与品牌 WebSite/Organization @graph、twitter:card,工具页 keywords 补数电票/四性/CRL/时间戳等长尾词,llms.txt 收录全部工具页与文章;
    • 首页工具目录 7→9 卡(3×3)、全站导航新增「OFD/A 检测」「数电票」入口;audit/design-audit 计数断言同步。纯前端/文档改动,内核无变化,不升版本。
  • 线上「未授权」事故修复(补记,2026-10-25):部分终端访问主站时功能入口报「未授权:请先通过 initLicense(token) 激活」。根因为三因叠加——授权服务签发 token 的 iat 零宽限(=服务器当下时刻),wasm 验证严格 now>=iat,而客户端时钟比服务器慢几秒(NTP 被网络屏蔽的终端常见,实测慢 3~4s)时 token 一到手即「尚未生效」;此前前端在线激活失败后不再重试,刷新页面也无济于事。排查确认密钥、env、hosts、did、CORS 与授权记录均正常。修复分两层:① 签发侧(license-server):issueToken 的 iat 回拨 ISSUE_IAT_GRACE_SEC(默认 60s,环境变量可调/可关),有效窗口整体前移、时长不变(不影响 TTL 分档上限)——重新部署 license-server 即刻对全部存量部署生效;② 前端(astro-site/vue-demo):续期调度对「激活失败」也走 1m/5m/30m 退避重试(此前一败即终止,旧 token 到期后整站失活),启动激活失败 60s 后静默重拉一次。回归测试:签发侧 node examples/license-server/test-iat-grace.mjs(iat 回拨量/窗口不变/env 开关)+ 验证侧 cargo test -p ofd-license 新增 2 项时钟偏差契约测试(宽限内慢钟通过、零宽限慢钟拒绝,锚定两端职责:宽限只在签发侧,验证端保持严格)。内核/wasm 无改动,不升版本。随事故处置,线上授权记录同日轮换:L2609223Q6CUU(preview 档)→ L2610255J5GXF(full 档,至 2027-10-25),主站以新号重建部署——wasm 三模块复用线上 prod 构建(字节一致、资源 URL 因内容哈希不变),顺带带上本条目前端重试加固;仓库内 build-dist.mjs prod-online 档、VitePress Playground 与 license-server ui-shot 的引用号一并更新。

v0.21.0 — 2026-10-23

OFD/A 合规检测与修复面向档案四性验证的完善(补记,2026-10-24 随本版本一并交付):

  • 检查项 10 → 14:在 GB/T 42133 九条格式规则之上新增四性文件级检查——refs-integrity(引用完整性:媒体/附件/外链模板/注释外观/大纲目标悬空逐类检出)、metadata-docid(DocInfo/DocID 存在,DA/T 70 元数据检测项)、extensions-absent(Extensions 扩展信息残留,6.12)、signature-valid(签名有效性:无签名或全部签名值+文件摘要验证通过;报告新增 signatures 逐签名明细,只验值与摘要、不判证书链信任)。no-video/非 Goto 动作/页块深度的扫描范围扩到内嵌模板、外链模板内容与注释外观文件(此前只扫页面正文);
  • 已签名件默认拒绝修复(GB/T 42133 6.13.1 标准路径):修复重写受保护文件会使验签必败,此前版本对已签件静默重写、产出「复检合规」但验签必败的假件;现在拒绝并按验签结果区分提示(有效签名=修复将破坏 / 已无效=取证进一步受损),--strip-signature(CLI)/ stripSignature: true(wasm)显式剥离——SES 内嵌印章图片(PNG/JPG)按 StampAnnot 边界转为页面图像保留视觉(stamp-visual-preserved),矢量/外置 Seal.esl 形态诚实记 stamp-visual-lost 警告;ofdaConvert 同步拒绝加密/多文档/已签件(与 repair 同口径);
  • 修复链路补齐:视频/音频资源移出时孤儿 MultiMedia 与媒体字节一并清理并记 SM3 摘要(media-removed-with-digest,DA/T 70 固化信息);音视频附件移出留摘要、悬空附件引用移除声明(6.15.2,文本附件保留);悬空媒体/模板/注释外观引用清理声明;无效大纲跳转目标移除且子条目上提(6.2.5b);Extensions 残留检测记动作;DocID 缺失自动生成 UUID(metadata-docid-generated);外链模板与注释外观的用字计入子集嵌入(此前漏计导致修复后模板缺字形);修复留痕默认写入 DocInfo/CustomDatas 三键 OFDA-Repair-Time/Tool/Summary(DA/T 70 处理过程记入元数据,重复修复覆盖同键,--no-trace/noTrace 关闭);
  • 引用存在性判定统一走大小写不敏感路径(Image_4.JPEG vs 包内 image_4.jpeg 此前被误判悬空,contains 精确匹配与 reader 实际加载行为不一致);
  • 合规率口径更新(133 份兼容矩阵,14 项检查):valid 90 份修复后 100% 全项通过(精确与回退两模式一致、字体嵌入 100%);signed 40 份已签件正确拒绝(单列不计失败——此前这 40 份被静默重写为假合规件);missing-asset 3 份结构性损坏。评测脚本 ofda-compliance.mjs 新增 signed 分类;
  • 前端:vue-demo 与 astro-site 归档面板展示签名状态、新增「剥离签名并修复」路径、新检查项/修复动作中文标签;packages/ofd 的 ofdaRepair 增加第三参 options(stripSignature/noTrace),.d.ts 类型同步。

桌面批量转换工具(ofd-batch)与授权体系 desktop 档:

  • 新增桌面工具 examples/ofd-batch(Tauri 2,Windows 绿色单文件,WebView2 渲染):批量 OFD↔PDF 双向互转与批量 OFD→OFD/A 归档三模式;拖拽文件/文件夹、断点续跑(输出较新自动跳过)、逐文件 CSV 报表、进度与失败容错(单文件异常不中断整批,零 panic 红线延续到桌面端);
  • 批量内存纪律:串行执行、每文件独立作用域(读取→转换→立即写盘→释放),跨文件零缓存(回退字体每文件按需重读、字体索引只存路径元数据)——大批量转换峰值内存≈单文件峰值,任务面板实时显示进程工作集;
  • OFD/A 归档流水线:合规检测免费不限量(逐文件「检测」按钮,10 项检查规则明细);不合规项修复(嵌入合规字体/图像转码含 GBIG2 兜底/设归档标识)需授权,修复后自动复检并给出 before/after 合规性;加密件与多文档包逐行报错拦截,已签名件默认跳过(修复将使原签名失效,可强制);
  • 授权体系新增 desktop 档:ofd-license 验签放行 scope=desktop(桌面批量工具专用档,绑机器指纹、仅离线激活码、TTL ≤400 天);该档不参与 preview/convert/full 三档排序——wasm/CLI 宿主对 desktop token 依旧 fail-closed 拒绝,一端认领、他端不认;签发侧 issue.mjs --scope desktop 支持直签;
  • 免费与授权(仅作用于桌面工具 UI 层,内核与付费 SDK 零限制):免费版每批 5 份、每日 20 份(本机日期滚动计数);授权后不限量并解锁 OFD/A 修复。构建脚本 scripts/build.mjs --keys <备忘> 产出授权闭环版本(开发构建界顶标注、功能全开、不得外发);
  • ofd-font 微增:SystemFontIndex::file_count() 索引统计查询(宿主状态展示用),行为无变化。

v0.20.0 — 2026-10-23

CLI 接入统一商业授权:与 WASM 同一授权体系(时长 × 授权实例 × 功能档三要素在命令行形态落地,离线桌面与浏览器同一套授权):

  • 新增 ofd-license crate:从 WASM 授权模块拆出的授权验证核心(Ed25519 签名验证、scope/时长/机器指纹校验),三个 WASM 模块改为薄壳复用同一实现——CLI 与 WASM 单一真源,防两侧授权口径漂移;对 WASM 使用方完全透明(接口与行为不变);
  • CLI 全命令授权门控:命令按域分三档——预览档(info/list/render/extract)、转换档(另含 pdf2ofd/ofd2pdf/batch)、全功能档(文档工具与签名/加解密/OFD-A 等其余全部);help/genkey/machine-id/license 免检;新增子命令自动落入全功能档(fail-closed);每次命令运行时重验授权,进程短命天然防过期;
  • 激活与查询:license activate <激活码|@文件> 验证并保存授权(离线机器将 machine-id 机器指纹报给供应商签发),license status 查看档位/到期/机器指纹;授权 token 依次从环境变量 OFD_LICENSE_TOKEN、可执行文件旁 ofd-cli.license、~/.ofd-cli/license.txt 读取;
  • 构建与分发:scripts/build-licensed.mjs 新增 --with-cli 一并产出授权版 CLI(机器指纹门控编译进二进制);新增 scripts/cli-license-smoke.mjs(22 断言)与 wasm 授权冒烟脚本配套。

v0.19.0 — 2026-10-23

OFD-A 合规工具链:系统字体解析、检查/修复/批量 CLI 与合规率实测:

  • 系统字体解析:ofd-font::system 按名扫描 Windows/Linux/macOS 系统与用户字体目录,支持 TTC 多面、名称归一化与精确中英文别名(宋体↔SimSun、TimesNewRomanPSMT↔Times New Roman、KaiTi_GB2312↔KaiTi 等);字体字节按需读取,索引进程级缓存。渲染端系统字体回退复用同一索引;
  • 修复增强:ofd-archive 修复报告新增 warnings,支持 RepairOptions.fallback_font 显式回退字体(动作 font-substituted,不静默替换);字体用字收集覆盖模板、注释外观与复合对象 VectorG;无文本字型改为整包嵌入,避免 0 字符子集;多 DocBody 输入直接拒绝(OFD/A single-doc 不可无损修复);同包同名字体复用(包内任一声明带 FontFile 即可复用原字节,解决 sample2 的 DB* 分散声明);
  • 字体名兼容:XML 解析支持声明 UTF-8 实为 GBK 的政务/发票件(恢复 小标宋体/方正小标宋简体/仿宋_GB2312 等中文名);FZHei-B01/FZKai-Z03/FZFangSong-Z02/FZXiaoBiaoSong-B05/方正小标宋 在系统无厂商字体时映射到同风格通用族(黑/楷/仿宋/宋),精确模式无需 --fallback-font;
  • CLI:新增 ofda-check / ofda-repair / ofda-batch,支持 --font-dir、--no-system-fonts、--fallback-font、JSON/CSV 报告与 --fail-on-noncompliant;WASM ofdaRepair 对注入字体名与资源名使用同一归一化规则,报告含 warnings;
  • 合规率:新增 scripts/ofda-compliance.mjs(精确/回退双模式、valid 分母排除残缺/缺图/多文档),133 份矩阵 valid=130 精确模式 130/130(100%)、字体嵌入 130/130;实测数字与复现命令见 标准与合规;
  • 文档修正:rust-core 版本、signature.md 离线 CRL/RFC3161 边界、limits.md 多文档与加密元数据明文边界、CLI/API OFD-A 用法;修复文档站 changelog 裸 </> 与失效链接导致的构建失败。

v0.18.0 — 2026-10-23

CMS/PKCS#7 真验签 + 离线吊销/时间戳:政务签章验签补全(此前 CMS 形态按「格式不支持」区分,无法给出结论;topsec/云南 CA 等真实政务签章即此形态):

  • CMS SignedData 真验签:SM3withSM2、SM2-1(GB/T 35275 国密变体)与 SHA 族 RSA(sha256WithRSA / sha1WithRSA);detached 场景被签数据为 Signature.xml 字节,messageDigest 属性逐签名比对;证书集内按 IssuerAndSerialNumber 精确匹配签名者证书。真实样本(testImageNotFound.ofd,云南 topsec 设备证书签章)验签全通过——v0.17.0 时还是「格式不支持」;
  • CRL 离线吊销检查:verifySignatures(trustedKeyHex?, anchors?, crls?) 第三参接受 -----BEGIN X509 CRL----- PEM(可多块)或 base64(DER);CLI verify --crl <文件>。CRL 须由签发者证书(信任锚/签名包证书集内)验签后才采信(伪造/验不过的 CRL 不采信),命中吊销 → chainStatus="revoked";
  • RFC 3161 时间戳:CMS unsignedAttrs 携带 TimeStampToken 时验证 messageImprint(对签名值摘要)与 TSA 签名,报告新增 timestamp 字段(present/verified/genTime);verified=true 时 genTime 为 TSA 可信时间并作为报告的 signTime(不可被签名者自报操纵)。

验签信任语义修正(行为变化):

  • 显式信任公钥优先:传入 trustedKeyHex 时签名值只按它判定(usedEmbeddedCert=false)——此前优先用签名内嵌证书验签,运营以「签名值有效」作可信判据时可被自签证书蒙混(signatureValid=true)。不一致时在新增的 warnings 字段告警(「内嵌证书自验通过,但其公钥与传入的信任公钥不一致」);未传 key 时维持内嵌证书优先;
  • 签名时间时区修正:Signature.xml 自报的无时区时间按本地墙钟口径判「未来」(此前东八区机器上本地写法恒被判超前 8 小时,错误触发「改按当前时间校验」降级)。

多 DocBody 归档包非页级 API 补全(v0.17.0 页级全局索引的延续):outline()/attachments()/customTags()/annotations() 聚合全部文档(此前只取首个文档,多文档归档包的书签/附件/标引/注释只能看首份),条目新增 docIndex 字段;attachmentBytes(index, docIndex?)/customTagBytes(index, docIndex?) 支持指定所属文档(缺省 0,旧调用不变);search() 命中新增 globalPageIndex(跨文档连续页号,可直接喂页级 API)并配套 docPageCounts()(逐文档页数)。

自建国标样本体系(examples/resources/自建样本/,中文命名独立目录,不入兼容性矩阵统计):按「无真实样本即冻结」原则此前不做的能力,本轮以自建符合 GB/T 33190/38540/35275 的样本解锁——多 DocBody 全要素件、图像无 @Format 声明件、斜体声明件、DocUsage/封面件、CMS 签名(SM2/RSA)、时间戳签名、CRL 吊销对照件;全部由入库生成器产出(cargo run -p ofd-tool --example gen_selfbuilt_samples / cargo run -p ofd-sign --example gen_sign_samples),配套回归测试防退化。文档生成器(OfdBuilder)新能力:多 DocBody(add_document)、附件/大纲/自定义标引、DocUsage/封面、文本样式 Italic/Weight、无 @Format 图像。

同步解锁的渲染/导出能力:

  • 合成斜体:Italic=true 声明而所用字体非斜体时以基线为轴右倾 12°(浏览器 oblique 同语义;语料 Italic 声明全为 false,自建样本驱动);
  • DocUsage/Cover 导出:docInfo() 新增 docUsage/cover(GB/T 33190 第 7 章可选元数据)。

OFD-A 检核/修复口径统一(数据安全):无 @Format 声明的图像,ofdaCheck 此前跳过(放行)、ofdaRepair 却视为不合规直接转码——同一文件「检查通过、修复却改图」且转码破坏签名。现统一为魔数嗅探(PNG/JPEG 等)+ 扩展名兜底判定:合规则修复侧只补写声明(新动作 image-format-filled,不转码不破坏签名),确属非白名单格式才转码。

PDF→OFD 迁移件伪文本修复(GBT 42133 迁移件归因):书生 SEP 拆分子集字体(每字体一字形)的 ToUnicode 为共用模板(全部码映射同一字符,如 <1581>→<4e66>「书」)——遵循它会让整页输出「书书书…」伪文本;方正 FzBookMaker 私有 G+hex 字形名此前被 StandardEncoding 码位语义冒充(输出 !"#$…)。两路现都降级为占位符并在转换报告警告(「ToUnicode 为拆分子集共用模板,文本层按占位符输出」),渲染(字形轮廓)不受影响。归因结论:这类源文件的文本语义在 PDF 数据层面不存在(Adobe 阅读器同样显示乱码),迁移需向源系统索取可检索文本。

其它修复(v0.17.0 复验清单闭环):

  • pageText(page)/pageLinks(page) 越界统一抛中文错误(此前静默返回空串/空数组,前端越界 bug 被吞);
  • 低档授权行为统一:attachmentBytes 档位不足时与 attachments() 同口径抛错(此前静默 null);tool/convert 模块在低档 token 激活失败后的调用报「当前授权为 X,不含本功能」而非误导性的「请先激活」;
  • WASM 入参/生命周期人机工程:new OfdDoc(arrayBuffer) 给出「请先 new Uint8Array(buf) 转换」提示(此前报无关的 ZIP 容器错误);free() 后误用抛中文「OfdDoc 已释放」(此前英文 null pointer passed to rust);
  • vue-demo 大纲跳页修复(与 astro-site 同款 pageIds() 优先解析 #页ID 锚点,ReaderManual 等 0 起命名页文件不再 off-by-one);
  • CLI verify 新增 --crl;usage() 补漏列的 ofda-convert。

⚠ 行为变化速览(升级注意):① verifySignatures 显式传 key 时 signatureValid 语义收紧(内嵌证书不再蒙混);② attachmentBytes 低档从静默 null 改为抛错;③ pageText/pageLinks 越界从静默空改为抛错;④ CMS 形态签名从 unsupportedFormat=true 变为真验签结论。

v0.17.0 — 2026-10-22

政务档案阅览专项:真实产物中「API 有出口、数据读不出」的五类数据链修复(附件、大纲、页内链接、权限、语义标引——内核此前按属性解析,而国标与真实厂商产物(数科、福昕、OFD R&W、税务电票)用子元素写法)。全部为属性/子元素双读兼容,既有版本产物不受影响:

  • 附件:解析国标 Format 属性与 <FileLoc> 子元素(此前只读 @FileFormat/@File,58 份真实样本 118 个附件全部读不出);路径基准按「包内实际存在」在 Attachments.xml 目录、文档根、包根三级中择优,自带文档目录前缀的写法不再产生双前缀路径;
  • 大纲/书签:解析国标 <Outlines> 包裹与 Actions/Action/Goto/Dest@PageID 跳转(此前取单数 <Outline> 且要求 @Dest 属性,7 份真实含大纲样本全部为空);outline() 把 #页ID 形态解析成页内容路径 + 锚点,前端既有解析无须改动;
  • 页内链接:注释外观解析 <Annot><Appearance@Boundary> 包裹(此前只认直接 PageBlock 子元素),外观边界平移烘焙成页面绝对坐标——真实链接注释(OFD R&W 53 处 Goto)此前 pageLinks() 全语料 0 命中;
  • 文档权限:解析子元素写法(<Edit>true</Edit>、<Print Printable="true"/>、<CopyText> 等,此前只读 @属性 致真实权限全空);写出改为国标子元素形态;
  • 语义标引:解析 TypeID 属性与 <FileLoc> 子元素;新增 customTagBytes(index) 读取标引内容文件字节;数科系产物把 CustomTags.xml 放在 Doc_0/Tags/ 而 Document.xml 不引用的约定路径按发现式加载。

文本层与全文检索口径统一:pageText/search 纳入模板页文字(按渲染同序:背景模板 → 页层 → 前景模板)——政务红头/版头/表头/发票栏目普遍在模板里,此前 31/37 份含模板样本漏检、2 份整页文字全丢;pageTextLayout 与检索文本源一致。仅 addFallbackFont 未 setFallbackFont 时首个追加字体自动兼任主回退(此前主回退缺失时未嵌入字体文档整段文字叠在原点、span 宽度塌陷)。

多 DocBody 归档包:全部页级 API(pageSize/renderPng/renderCanvas/exportImage/exportLongImage/pageText/pageTextLayout/pageImages/pageLinks/pageVideos/pageLayers/formFields/extractFields)改为全局页索引(跨文档连续编号,与 pageCount、检索命中的 docIndex 同口径)——此前 pageCount=4 而 renderPng(2) 报「页索引 2 不存在」、检索命中无法跳转。

加密防护(数据安全):

  • encryptOfd 禁止二次加密(此前密文再加密「成功」但解码报密文长度非法,包报废);
  • ofdaRepair 拒绝加密件(此前修复会把密文重写为空白页并删加密描述,复检报「合规」但数据已丢);
  • new OfdDoc(密文) 显式报错而非静默打开渲染空白页(前端用 isEncryptedOfd 分流解锁);工具模块全部写操作(拆分/合并/旋转/水印/注释等)同样拒绝加密件;
  • 对密文 verifySignatures **报「加密文档无法验签」**而非返回空数组(UI 不再误显示「未签名」);CLI render/ofd2pdf 对密文报错而非输出空白图,verify 显式提示「加密文档无法验签」。

签章互操作:CMS/PKCS#7 SignedData 形态的 SignedValue.dat(topsec/云南 CA/淄博 CEGN_SEAL_CA 等真实政务签章,国密 OID 1.2.156.10197.6.1.4.2.2)按**「格式不支持」**区分,不再误报「签名值无效」——verifySignatures 报告新增 unsupportedFormat 字段,CLI 显示「格式不支持(CMS/PKCS#7,无法验签)」。本内核暂不支持 CMS 验签本身(真实签章此前 6 个签名全部假阴性,现在结论诚实且可区分)。

渲染加固:Rule="Even-Odd" 填充规则全链路消费(canvas 播放器新增 fillEvenOdd 指令、ofd2pdf 出 f*、pdf2ofd 从 f* 读回——同向嵌套子路径挖洞的圆环/徽标此前被填实);水印注释图像 CTM="40 0 0 30 0 0" 按单位方形语义画 40×30mm(此前 e=f=0 一律忽略 CTM 被拉成 60×60 边界盒);renderPng/renderCanvas 对 scale≤0 与 0×0 页面报中文错误而非静默输出 1×1 图;CLI info 在页尺寸与文档级不一致时逐页列出(发票类混排件此前误报纸张)。

样本驱动修复(补记,2026-10-22 同日第二批):按「有样本才实现」规则对上批遗留逐项检索样本后修复——① ImageMask 图像蒙版:引用同尺寸二值图,白色(RGB 均值 >244)处保留原图、其余透明(抠非白背景,极性对照 ofdrw ImageUtils.renderMask;语料基准 2.ofd),此前属性未解析致彩色底图露白底方块;② 描边文本进文字层(fill||stroke 发文本运行,位图后端仍回退轮廓视觉不变;基准 内嵌stroke字体.ofd);③ 任意角旋转/斜切文本产出整 run 轴对齐近似盒 span(基准 ano.ofd 45° 保密资料水印,此前恒跳过不可选中);④ 旧式注释条目元数据回填:类型/作者/时间取自聚合文件首个 Annot 元素(基准 ano.ofd,此前 60/61 条类型为 Other);⑤ OFD-A 图像白名单补 JBIG2 生态别名 gbig2/jb2(语料 8 处声明此前误报);⑥ 新增 OFD/A 转存出口:CLI ofda-convert 与 wasm ofdaConvert(加密件拒绝)。

复验闭环修复(补记,2026-10-22 同日第三批,据 REVERIFY 复验报告):① P0 加密写守护补全——CLI watermark/rotate/extract/delete-pages/split/merge/sign 与 WASM addAnnotation/removeAnnotation/fillFormFields/signOfd 对密文统一拒绝(此前对密文「成功」但产物不可再解密=不可逆损坏,或产出摘要失配的误导性签名);② 修链接热点双重平移回归:外观子对象 Boundary 已是页面绝对坐标时不再烘焙平移(xilou URI 热点 57.6→28.8 恢复可点,ano.ofd 相对写法回归通过);③ 前端大纲跳页改用内核 pageIds() 精确映射(ReaderManual off-by-one 修复);④ 复合对象(CompositeObject→VectorG)文字/图像进 pageText/search/pageImages(y.ofd 三页 0→1000+ 字、「海关总署」可检索);⑤ pageImages 展开模板 BaseLoc 外链内容(红章/图案类模板图不再漏);⑥ CLI info 页级与文档级不一致即逐页列出(发票件 210×140 不再误报 210×297);⑦ CLI verify 对密文 exit 3(非零信号);⑧ 页级 API 越界口径统一为中文报错(pageText/pageSize/pageLinks/pageImages/pageVideos/pageLayers/formFields,此前静默空吞掉前端 bug);⑨ renderCanvas 补画布守卫(超大 scale 不再产巨型指令流);⑩ attachmentBytes/customTagBytes 越界显式返回 null(与类型契约一致);⑪ 权限补生态扩展字段 PrintScreen/ContentRegist(docInfo 透出);⑫ 大纲标题 trim 行尾换行(z.ofd 书签树多余空行)。

字体两则(补记,2026-10-22 同日第四批):① Weight 合成粗体——Weight≥600 声明而所用字体无粗字重时以 fill+同形描边(线宽≈3% 字号,浏览器 faux bold 同语义)合成,字体本身有粗字重则不叠加(语料 1255 处 Weight 声明生效;keyword.ofd 墨量对比回归);② 修未嵌入比例字体(Arial/Times)自动字距错乱——无 DeltaX 时步进此前误用「当前字符」的自然宽度(x_i = x_{i-1} + adv(char_i)),比例字体下每字符拿后继宽度造成 "A document, rendered." 渲染成 "A docu mert, r ender ed";现改为标准排版语义 x_i = x_{i-1} + adv(char_{i-1)}(前字符步进)。等宽字体(宋/黑,步进处处相同)与显式 DeltaX 场景数学上零变化、无回归面(ofdjsviewer-sample.ofd 目检恢复 "A document, rendered.",n→t 间距回归测试固化)。

兼容性矩阵复跑(补记,2026-10-23,release CLI 实测):语料扩至 133 份(resources 65 + corpus 68),七项实测——info/list/verify/sign+verify 各 132/133(唯一失败 = 负向损坏样例 sample-hidden.ofd,预期拒绝)、render 131/133(另加 sample0.ofd 0×0 页面按本版守卫中文报错,预期行为变化)、encrypt + decrypt 回环 133/133 全过、decrypt 语义一致 131/131(2 负向样例 N/A)。文档站矩阵表与样例数已按实测同步。Italic 合成斜体——语料 38 处 Italic 声明经逐份核验全部为 false(无斜体正样本),按「无样本不实现」规则不做合成,现有解析行为已正确、TSA 时间戳与 CRL/OCSP(无带时间戳 SES 样本/无国密 CRL 样例,ofdrw 验证端亦不解析)、加密件元数据明文(我方形态自洽无第三方样本;GB/T 33190 加密有 ofdrw-crypto 参照可后续立项兼容读)、DocUsage/Cover 暴露(语料 0 份有值)。

v0.16.0 — 2026-10-21

授权档位细化:注释与附件读写、签名与签章校验划入全功能版。这五项能力此前随 core 模块对预览版开放,本版起仅全功能版(full 档)授权可调用:

  • 涉及接口:verifySignatures(签章校验)、signOfd(数字签名)、annotations(注释列表)、attachments / attachmentBytes(附件读取);注释写入与附件写入所在的 tool 模块(addAnnotation / removeAnnotation / listAnnotations 等)本就整体要求全功能档,行为不变;
  • 预览版与转换版授权调用上述接口时抛错,提示「当前授权为预览版/转换版(…),不含注释/附件读写与签名/签章校验(全功能版能力)」,licenseStatus() 的 reason 为 scope_denied;未激活时的行为不变;
  • 不带 scope 的存量授权(v0.14.0 之前签发)视为全功能,不受影响;前端无须改动,错误经既有异常路径呈现;
  • 各档位能力对照见价格与授权。

另含阅读器示例(astro v2 站)两处体验修复:直线标注此前无论从哪个方向拖画,落盘后一律被折成「左上→右下」(坐标归一化抹掉了线段方向,现按绘制方向保留端点顺序,水平/垂直直线也不再被误判为「拖动距离太短」);打开文档新增加载遮罩——大文件解析期间(同步 wasm 调用冻结主线程)此前页面无任何反馈,现从选择文件起显示「正在打开…」直至首页渲染完成。

PDF 转换兼容修复:预定义 CMap 字体文本乱码。部分由 iText 等生成器产出的 PDF(典型如电子发票:非嵌入 SimSun/KaiTi 配 GBK-EUC-H 或 UniGB-UTF16-H 编码写发票号码、日期、公司名、备注),转换后文本整段乱码——数字错位成无关字符(0.52 显示为 MKRO 级)、汉字变菱形占位(标题「普通发票」缺字)。根因是这类字体的编码声明为 CMap 名而非内嵌映射表,解析端缺省按 Identity 语义把码位当字形编号直通错查;现按 Adobe 规范直接给出码宽与语义(GBK-EUC 系按 GBK 码位表恢复、UniGB-UTF16/UCS2 系码位即 Unicode),文本与可检索性恢复正常。

PDF 转换兼容修复(补充):发票号码显示不全(尾部字符被裁)。上一修复解决了乱码后,用户实测发现同一批发票转换出的 OFD 里发票号码 20 位只显示 17 位。根因是文本步进度量:这类字体的字符宽度由 PDF /W 表按字符编号(CID)声明,而半角数字/字母所在区段的 CID 映射解析未穿透间接引用(/CIDSystemInfo 44 0 R 形态),导致步进退化为按替代字体的宽度累计——替代字体数字比原字体宽约 11%,20 位号码总宽超出原文裁剪框,尾部被裁。现完整穿透间接引用并按 /W 声明恢复步进(半角区按 CMap 规范映射到 CID 1-95),号码、日期、金额等全部完整显示。

补记(2026-10-22,随本版本一并交付):网页阅读器默认适高显示与高清屏清晰度。主站(astro-site)阅读器打开文档后默认按适高呈现——整页高度放入可视区(此前竖版默认适宽);首次渲染即按适高尺寸直接输出,不再出现「先渲一版小图再跳变成适高」的两段闪变;单页/连续视图同口径,双页保持适宽对开;编辑写回(加注释/水印等重开)保留当前缩放,视图模式互切不再覆盖用户手动缩放。清晰度:canvas 的 CSS 尺寸改按设备像素网格对齐(物理像素 ÷ DPR),Windows 125%/150% 等非整数缩放场景下画布与屏幕设备像素 1:1,消除浏览器对整页做亚像素重采样导致的文字发糊(playCanvasScript 在返回的画布上挂 cssSize 供调用方布局同步,npm 包 index.d.ts 类型同步)。

补记(2026-10-22,随本版本一并交付):部分数电票 OFD 打开报「数据解析错误」。有电子发票生成器在文字对象等元素上写非数字 ID(如 <TextObject ID="9527_note">,违反国标 ST_ID 正整数约定),此前打开整份拒开,报「元素 <TextObject> 属性 'ID' 不是正整数」。现按主流阅读器语义将非法 ID 与引用降级为缺失处理(覆盖图元/层/页/模板/资源/注释/附件/标引的自标识 ID,及 DrawParam、Font、ResourceID、PageRef 等对象引用):文档正常打开与渲染,仅引用失效处按缺省样式绘制;编辑写回时不再携带非法 ID。

补记(2026-10-22,随本版本一并交付):渲染兼容两则(对照 ofdrw 参考实现)。① 价税合计(大写)金额前的「圈X」符号显示为实心圆——该符号在票据模板中是一条 Fill="true" 但未声明填充色的圆+叉路径,此前按缺省黑色填充涂成实心;现对齐 ofdrw 语义:Fill="true" 只使能填充,还需 FillColor(自身或 DrawParam 继承)可解析出颜色才填充,解析不到时仅描边(描边缺省黑不变),符号恢复为空心 ⊗,ofd2pdf 导出同此语义;② 票面中部多出虚线框——发票引用的 redInvoiceWatermark.png 在包内缺失,此前渲染为灰色虚线占位框;现缺失/不支持的图像一律跳过不画(ofdrw 同语义),不再出现官方版式没有的占位框。

补记(2026-10-22,随本版本一并交付):CNKI 电刊复合单元文字/插图缺失。以 CompositeGraphicUnits 组织版面的期刊类 OFD(知网下载件等)此前部分文字只剩行尾碎片、页内插图整幅不显示——这类生成器的子图元裁剪窗(Clips)以单元空间书写、坐标只对复合 CTM 镜像补偿,渲染时误乘子图元自身 CTM(文字 ×9、大图 ×346)后裁剪窗被推出页面,窗口内内容整段被裁。现复合链在烘焙阶段预乘进 clips、裁剪窗跳过子图元 CTM,文字、刊头、插图恢复完整;直连对象的裁剪语义(数科整页裁剪窗等)不受影响。

v0.15.0 — 2026-10-21

注释绘制样式扩展(自定义颜色/填充/虚线/透明度,绘制内容不设边框):addAnnotation 的 spec 新增三个样式字段,覆盖签批与标注的主流自定义诉求:

  • fillColor("r,g,b"):形状填充色 / 高亮底色 / 水印字色。缺省按类型取缺省值——形状无填充、高亮半透明黄、水印灰 190(行为与旧版一致);
  • stroke(布尔,仅 Path):描边开关,缺省描边(向后兼容);false = 纯填充无边框;
  • dash(mm 段长数组,仅 Path):虚线样式,缺省实线;
  • Path 类注释此前不读 alpha,本版起描边/填充透明度均落盘;ink(手写签批)外观固定圆帽/圆角(Cap="Round"/Join="Round"),与前端预览观感一致;
  • 缺陷修复:高亮注释不再带黑细边框——国标 Path 缺省描边为 true,此前高亮外观漏写 Stroke="false",渲染端按缺省勾了一圈 0.353mm 黑边;本版显式关闭;
  • 水印注释字色可自定义(此前固定灰)。

配套前端(astro v2 站,vue-demo 不随本版变动):新增「注释样式条」——绘制工具激活时出现在顶栏下方(注释面板表单同源共享),提供颜色盘(8 预设+自定义取色)、线宽三档、不透明度滑条、形状填充色、边框开关(缺省关)、虚线开关;矩形/椭圆缺省半透明填充+无边框;拖画预览按真实样式绘制;样式选择本地持久化。另修复阅读器两处旋转问题:①单页视图旋转缺平移补偿导致内容错位出页框(与「整理」面板旋转后的正确居中渲染不一致);②viewRot 在换文档/编辑写回后不清零,顶栏预旋转与整理旋转视觉叠加(90°+90°=180°、270°+90° 观感「没转」);「整理」旋转后现停留当前页;签批/标注进行中禁用顶栏旋转。

v0.14.0 — 2026-10-20

授权功能档位(scope):商业三档(预览版 / 转换版 / 全功能版)从"分发包物理裁剪"升级为token 烘焙 + wasm 内核强制——此前授权记录的模块子集只决定分发包里放哪些 wasm 文件,token 本身在三个模块里都验签通过;现在档位写进 token,由内核在运行时按模块身份校验。

  • token(v2)新增可选字段 scope:preview(仅 core 基础预览)/ convert(预览 + PDF↔OFD 转换)/ full(全部,含文档工具),与价格页三档一一对应;
  • 向后兼容:不带 scope 的存量 token 一律视为 full——已售授权升级 wasm 后行为不变,无须重新签发;
  • 强制点在内核:模块身份由 crate 名编译期推导(core/convert/tool 各自的最低档位要求 preview/convert/full),initLicense 激活时 fail-fast 拒绝档位不足的模块、每次功能调用复验(原因码 scope_denied);非法 scope 值整枚 token 拒绝(scope_invalid)——即便预览版客户拿到 convert/tool 的 wasm 文件,也无法用低档 token 激活;
  • licenseStatus() 新增 scope(当前档位)与 module(本 wasm 模块标识)字段;授权面板(vue-demo)显示"功能档位",档位不足时有明确中文提示;
  • 文档同步:API 参考的 licenseStatus 字段表与授权提示块、常见问题新增「三档差在哪/怎么查档位/低档调用会怎样」两条问答(astro 文档中心副本随 check-docs-sync 同步);
  • 签发侧(授权服务 v2.6):签发 token 时由授权记录的模块子集推导档位(core→preview、core+convert→convert、含 tool→full);CLI issue.mjs 新增 --scope / --modules 参数;存量库升级脚本 upgrade-v2.6.sql 把 v2.4 上线(2026-10-08)前创建的授权记录显式置为全模块(当时售出的即全功能,避免被 v2.4 迁移的默认值降级)。

补记(2026-10-20,随本版本一并交付):三站 SEO/GEO 标题与结构化数据优化

  • 主站(vue-demo):标题改为「OFD在线预览 - OFD阅读器/OFD转PDF 在线工具」(核心词前置、按百度 22–30 字口径);描述以问句「OFD文件怎么打开?」开头(AEO 问答式句式);关键词补「OFD文件怎么打开/OFD是什么文件/增值税电子发票阅读器」等真实搜索词;JSON-LD 增强(WebApplication 补 featureList/alternateName,新增 FAQPage 三问)并新增 llms.txt(AI 抓取入口,含关键事实与可引用统计数字);
  • astro v2 站:五个功能页标题/描述/关键词逐页重写(每页唯一、意图词前置);BaseLayout 新增 keywords/faq props——页面级输出 FAQPage 结构化数据;补齐 og:image 与 twitter:card;llms.txt 补工程质量数字;
  • 文档站:titleTemplate 全局尾缀「— 国标 OFD 引擎文档」(首页独立标题不受拼接);站点描述重写;新增 public/llms.txt(接入/背景/商务页结构化索引)。

补记(2026-10-20,随本版本一并交付):验签完整化——证书链候选与信任锚输入

电票/签章文件通常只嵌签名者证书,上级 CA 不在包内,验签长期停在 no-anchor / incomplete。本轮回补"喂齐链与锚"所需的全部免费能力(纯本地计算,不依赖任何在线验签 API、CA 认证或时间戳服务):

  • 信任锚输入支持 PKCS#7 证书包(.p7b / .p7c):CLI --anchor 与 WASM verifySignatures 的 anchors 参数均可直接喂 CA 发放的根证书包(DER 文件 / -----BEGIN PKCS7----- PEM 块 / base64 文本),包内证书自动展开,无须先在系统证书管理器手工导出 PEM;锚列表同时作为链上缺失环节的证书来源——中级与根一并喂入即可补全链(拿不到根证书时也可把可信中级直接当锚);
  • SES 内嵌证书全收集:深度扫描 SES_Signature 全部嵌套结构,把嵌在非标准位置(TBS 内嵌套 SEQUENCE 等,BW 等厂商电票的制章者证书即在此处)的证书一并纳入链候选,此前只收印章 certList 标准字段;
  • 签名者证书密钥用途检查:keyUsage 明确不含 digitalSignature / nonRepudiation 时在 chainErrors 给出「不具备签名用途」告警(不影响链结论);
  • 验签文档新增「信任锚与证书链配置」实战章节(wasm/signature.md):电票只嵌签名者证书的原因、锚的四种来源(含从可信发票提取中级的逃生门)、实测全国查验平台 taxca.p7b 仅含 RSA 体系两张(SM2 链需另寻渠道)、以及费用边界——业界付费能力(在线验签 API 按次计费、签章服务器、TSA)均不需要也不在范围;
  • 测试样本库新增真实数据:税务根证书包 tax-root-ca.p7b(查验平台原样下载)与 BW 厂商测试发票 invoice-seal-chongqing.ofd(SESv4 + TBS 内嵌制章者证书 + 自带 Document.xml 摘要失配的已知瑕疵,端到端回归断言签名值验证通过、失配文件精确指认、证书候选含非标位置证书)。

v0.13.0 — 2026-10-13

pdfjs 官方语料遗留问题专项(依据 docs/business/pdfjs-语料遗留问题与解决方案.md 的优先级清单)。98 份字符错乱、85 份内容丢失、14 份混合透明、9 份渐变、字体与 JBIG2 等长尾逐项归因修复;每一项都带最小复现回归测试,并按 scripts/pdf2ofd-page-verify.py 逐页严格对拍记录前后数字。982 份全量转换两次跑批 panic 0;72 份重点子集 pass/warn/bad 7/12/46 → 21/15/30、bad 页 77 → 61。

混合模式与透明组

  • ExtGState /BM 16 种混合模式落地:OFD 无混合模式,按业界 transparency flattening 把受混合影响的绘制序列连同背景栅格化为位图嵌入;失败/嵌套/超预算一律保持原对象(等价 pdf.js 的 source-over 降级 + 告警),因此不可能因扁平化丢内容。knockout_inner_backdrop(红×蓝 Multiply)SSIM 0.2883→0.9979、墨量比 0.6832→0.9970;blendmode 0.8493→0.9496;
  • 软掩码 /TR 与 /BC:smask_alpha_oob 墨量比 0.0574→1.0017、smask_alpha_oob_transfer→1.0016、smask_luminosity_oob_transfer→1.0019(三件转 pass)、smask_alpha_bc bad→warn;并修正"整页位图被压进内容小包围盒"的漂移缺陷;
  • ca/CA 透传为图元 Alpha(异值时路径拆双对象、描边文本取 CA);
  • 性能守卫:扁平化按"单次重算对象数 / 单页累计工作量 / 单页栅格化像素"三重预算记账,超限降级 source-over 并告警——修掉一批含数千次 gs 的页面出现的分钟级 CPU spin(bug1721218_reduced 240s 超时 → 1.0s,输出与基线同量级)。

渐变与色彩空间

  • 径向渐变补全 poppler doRadialShFill 同构几何(apex/切线角/clip bbox 切线裁剪 + Extend 组合),OFD RadialShd 无法表达时逐像素栅格化兜底;radial_gradients p1 SSIM 0.7446→0.8824、p3/p5 墨量比 1.6~1.9→0.80;
  • 轴向渐变补上 Extend 缺省 [false false] 的语义(此前端点色被涂满整个填充区),用几何条带裁剪实现(sh 与 pattern 两条路径都覆盖):ShowText-ShadingPattern SSIM 0.9443→0.9825、墨量比 1.0981→1.0458,shading_extend 0.8725→0.9361;
  • Type 4/6/7 mesh:修正「flag 每顶点一个」(ISO 32000-2 表 84,此前每三角形只读 1 个,位流从第二个三角形起错位)与栅格化垂直镜像。issue2948 墨量比 0.0907→0.9994、SSIM 0.6761→0.9800;mesh_shading_empty 0.8747→0.9944;coons/tensor allflags 两件转 pass;
  • Type 1 function-based shading 行序修正(图像行 0 = bbox 最小 y,与 OFD 图像原语一致):function_based_shading 0.8203→0.9281;
  • Indexed 越界索引 clamp 到 [0,hival] 照常绘制(poppler/pdfium 口径,不再丢对象),Indexed 图像通道样本值直接作下标(修正 4bpc 下放大 17 倍取错色):IndexedCS_negative_and_high SSIM 0.9109→0.9760、墨量比 0.6357→0.9247;
  • DeviceN/Separation:非法/退化 /Domain 按 poppler「色彩空间非法 → 忽略 scn、保持当前色」处理,devicen-none-zero-domain bad→pass(SSIM 0.6483→0.9982);
  • shading 的 /ColorSpace N 0 R(间接引用)与 Separation/DeviceN tintTransform 下标(4 应为 3)两处解析缺陷修复——此前相关渐变/色彩空间被判非法而整块静默丢画(personwithdog /Sh6,/Sh8、bug1703683_page2_reduced /Sh31);
  • ExtGState 级 /TR 传递函数按 ISO 表 58「同一函数对每个分量各作用一次」逐分量应用(作用于填色/描边/图像/pattern/shading):transfer_maps 墨量比 1.4134→0.8486、SSIM 0.6821→0.7376。

字体与文本

  • Type1 seac 复合字形(endchar 5 操作数:asb adx ady bchar achar)按 FreeType t1operator_seac 合成,并补 bare CFF ISOAdobe 缺省 charset 字形名、容忍废弃 dotsection:endchar SSIM 0.1565→0.6355(墨量比 0.1383→1.2249)、glyph_accent→0.9410、mmtype1 0.6819→0.9394;
  • TrueType 复合字形环检测(visitedGlyphs 当前路径集合 + 深度上限),只跳过环组件、保留合法组件,菱形引用不受影响;转换产物内嵌字体环数 1→0;
  • 嵌入 CMap 的 <hex> 与裸十进制 token 配对错位修复(影响所有「嵌入 CMap + CID 十进制」的 Type0 字体):issue7901 文本相似度 0.7308→1.0000(不再有替换字符),SSIM 0.3509→0.6374;
  • 无 ToUnicode / 无 cmap 的兜底:符号 (3,0) 的 0xF000 段与 (1,0)/(0,x) 直映射放宽到 cmap format 4/6、TrueType post 表字形名寻形:bug1027533 0.7183→0.9291(墨量 0→1.0179)、TrueType_without_cmap 0.9359→0.9714;
  • 非嵌入字体替换链:Symbol/ZapfDingbats 标准编码码表、ArialNarrow/Helvetica Narrow 按名窄体候选、替代子集来源字节登记(族正确优先于与参考替换字体字重一致);
  • 7 Tr(文本作裁剪路径)遇到环字形时按参照实现不设裁剪(此前按字形轮廓裁剪导致整页近乎空白):recursiveCompositGlyf bad→pass 1.0000(Tr 4-7 使用者全语料穷举仅 5 份,合法 7 Tr 件逐位不变);
  • 西文连字(fi/fl/ff)重复显示修复:连字保持一个逻辑字形,写出端按 GB/T 33190 11.4 CGTransform 收缩组表达"多字符共用一个字形"(文本仍逐字符展开、检索/复制语义不变),渲染端收缩组内只画一次整字;同时修正 cmap 用字对与轮廓退化路径的同族问题。夹具 tests/data/css4-ligature.pdf 入库。

JBIG2 与解析容错

  • JBIG2 halftone:图案字典 GRAYMAX 上限 255→0xFFFF(10bpp 图案字典整段曾被拒)、MMR 黑终止码上限按码表取最大值、T.6 行模型重写(a0/b1/H 模式/pass)、灰度平面间 EOFB + 字节对齐。bitmap-halftone-10bpp 与 -10bpp-mmr 从完全不画(ink_cur=0)到 pass(SSIM 0.9919、墨量比 0.9979),且 MMR 与算术编码的 10 张灰度平面逐位一致;
  • 安全硬化:halftone 网格尺寸 grid_w/grid_h 增加上限预检——此前畸形 4 字节头可让 1.8KB PDF 进入 4.29e9 次循环(11.3s),取 0xFFFFFFFF 组合即单文件挂死;
  • PDF 解析容错:无法解析的 /Encrypt 字典降级为按未加密打开(真密码保护仍以 Invalid password 拒绝,8/8 逐件复核)、解析器字典/数组的残渣 token 跳过、/Kids 非数组按单元素处理、非 UTF-8 键名 lossy、表单字段树类型不符不再让 Catalog 失败。PDFBOX-4352-0 由打不开变为可转换;全量扫描新增可打开件 1 份、失去可打开件 0 份。

注释

  • /AP 缺失时按 ISO 32000-1 §12.5.6.8 合成 Square/Circle 默认外观(/Rect 内缩 W/2、/C 缺省黑、/IC 不透明填充、表 166 缺省 1pt、W=0 不画、4 段 Bézier 椭圆)、FreeText 的 DA 经 AcroForm /DR /Font 解析、Widget 列表框高亮去重;并修掉"边界相对化只平移 MoveTo/LineTo、曲线控制点留在绝对坐标"的缺陷。annotation-square-circle-without-appearance 墨量比 0.5452→1.0066、SSIM 0.9429→0.9971(转 pass)。

独立复核发现并修掉的既有缺陷(每项都有回归测试)

  • FuncSpec::Sampled 固定 [f64;2] 暂存网格坐标 → ≥3 维 DeviceN tintTransform 索引越界 panic;read_bits/idx*stride 在 /Size 饱和时整数溢出;均改为 checked/saturating 并加采样位上界;
  • 畸形 CFF INDEX 偏移越界直接索引 panic(fuzz 发现);
  • 单函数形式 /TR 被当作「n 输入一次调用」而静默失效。

已知未达成(如实记录)

  • radial_gradients p3/p4/p5 仍 bad(两套口径逐像素探针比较:现口径平均 |Δ| 18.35/255 vs 备选 32.45/255,判定已逼近 poppler 环近似极限);
  • transfer_maps 墨量比 0.8486 未进带(poppler 在同一文件内对 /TR 自相矛盾,未对单文件过拟合);
  • hybrid:混合组内逐对象混合、knockout 组 /K、/BM 嵌套/超预算场景按 source-over 降级;Type3 完整渲染模型(建议单独立项);
  • poppler-742-0-fuzzed 仍打不开(与 poppler 一致拒绝,非回退);bitmap-halftone-skip-grid 已知不画;jpx_smaskindata 判定为参考渲染口径异常;
  • 字体替换的字重与参考渲染使用的替换字体不同,个别文件墨量比在带外(族已按名正确,属口径类)。

v0.12.0 — 2026-10-12

PDF→OFD 字体保真专项:解决「转换后预览的字体和 PDF 不一样」的三类根因——风格被统一替代、部分字静默丢失、非嵌中文字体全部变同一字形。

转换内核(pdf2ofd)

  • 非嵌中文字体按名精准替换:PDF 只带字体名(中文发票/公文极常见)时,此前统一落到回退字体(宋体样)。现按字体名匹配系统同族字库:宋体/新宋体/华文宋 → SimSun、仿宋 → SimFang、楷体 → SimKai、黑体/华文黑 → SimHei、微软雅黑、等线,含粗体派生与 macOS/Linux 常见路径;替代子集与系统同族同源——本机/服务端转换后,任何阅读器预览的字形风格即与 PDF 一致。西文 Base14(Times/Helvetica/Courier/Nimbus/LaTeX CM)的替代子集源同步修正为同名系统字体(此前 OFD 内嵌的 FontFile 仍是回退字体子集,字形风格不符);
  • 风格化回退字体槽(WASM 新 API setStyledFallbackFont):网页端无系统字体,新增按风格注入回退字体("song"/"serif"、"hei"/"sans"、"kai"),转换时按 PDF 字体名选风格匹配者做替代子集——不注入则宋/黑/楷统一为单一回退字形的旧行为不变(槽缺省自动落 setFallbackFont 字体);vue-demo 阅读器示例已接入黑/楷风格字体懒加载(预置字体为 OFL 开源子集,生成方式见文档站 wasm/convert);
  • 缺字轮廓兜底:嵌入字体中个别字形不存在(解码失败/字形 ID 越界引用)时,该字符从回退字体轮廓补画(与正文同色同变换、连续缺字合并为单一路径对象),其余字符不受影响——消灭「部分字显示空白」;
  • 替代子集源覆盖统计:转换报告新增 substituteMissing(字体名 → 子集源不含的字形数),「部分字丢失」从静默变为可定位;
  • 修复 Type0 外层字体的 /W 步进表与 CIDSystemInfo 从未被读取的存量缺陷:主流 Type0 文件(外层 /Type0 + DescendantFonts 结构,即全部 Adobe-GB1 中文文件)的声明步进与 Ordering 此前解析不到——GB1 语义恢复整体失效、/W 与字形面度量不一致时步进错位。现穿透 DescendantFonts 读取,GB1 码位中文文本在无 ToUnicode 时正确恢复可检索语义;Ordering 为 string 形态(非规范 name)的文件同步兼容。

渲染内核(ofd-render)

  • 裸 CFF 内嵌字体兜底:其他工具生成的 OFD 内嵌裸 CFF 字体(无 sfnt 容器,如数科部分模板文件),此前解析失败整体落回退字体顶替;现自动合成最小 OTF 容器后按原字形渲染,预览风格与文档意图一致;
  • 回退链风格匹配:文档声明的字体名呈宋/黑/楷特征时,回退渲染优先选族名同风格的回退字体(字体风格经 name 表自动识别,无需调用方标注)。

转换报告(诊断)

  • report.fontDetails:逐字体处理明细(embedded 原样嵌入 / substituted 替代(含来源 system/fallback)/ outlined 轮廓化 / skipped),「预览字体和 PDF 不一样」的第一排查入口;wasm convertPdfDetailed 的 report 对象同步携带。

测试与质量

  • 新增 7 项回归测试(中文族子集源、风格槽匹配、缺字轮廓兜底、GB1 替代缺字统计、裸 CFF 渲染兜底等),workspace 431 项测试全绿(2026-10-12 实测 cargo test --workspace:431 passed / 0 failed,日志 audit/logs/latest-test2.log;口径与门禁见 scripts/ci-check.mjs;下方 10-13 互操作补记再增 11 项、发布验收轮再增 3 项,最终 445,对外文档以 445 为准)、clippy 无告警;1018 份公开语料评测(2026-10-12 审计整改后全量复跑,100 DPI、5 页/份、-cropbox):rc=0 成功率 98.4%(1002/1018)、非空产物 98.0%(998/1018)、零 panic,SSIM≥0.95 542→557、文本层≥0.9 431→437、丢文本层 33→29、平均分 57.61→59.19,逐份对账无新增转换失败(口径说明、empty_output 计数与并行字体编码工作线的影响见达标线文档);
  • 内部重构:CFF 解析/包装模块(裸 CFF→OTF、CID identity 重写)从 ofd-convert 下沉至 ofd-font 共享,ofd-render 复用同一实现(无行为变化)。

补记(2026-10-12,随本版本一并交付):加密 PDF 兼容性修复(vendor/pdf 补丁)

  • AES-256 加密 PDF 打不开:/CFM /AESV3 的解密密钥是 32 字节文件密钥,此前统一按 RC4 / AES-128 的 16 字节截断取用,AES-256 解码器因密钥长度非法直接失败——Acrobat / 新版 Office 导出的 AES-256 加密件此前整份转换失败,现可正常打开并转换;
  • Identity 加密过滤器被误判为「密码错误」:写入方把 /StmF(或 /StrF)声明为 Identity 时内容流其实并未加密,此前仍强制走密码校验,出现「其他阅读器能打开、本库报 Invalid password」的伪失败;现与 Acrobat / poppler 同策略,直接按未加密文档处理;
  • 损坏文件的修复路径不再覆盖可用解密器:xref 损坏的文件走修复路径时,扫到的 xref 流 trailer 可能来自同一文档的另一段(/ID 不同),重新派生密钥会覆盖常规路径已装配好的可用解密器(issue19484 的 RC4 文件)——现仅在尚未装配解密器时才派生。

补记(2026-10-12,随本版本一并交付):PDF 链接注释完整迁移(arXiv 113 条链接不再丢失)

arxiv-1706.03762.pdf 的 113 条链接(95 条文档内跳转 + 18 条外部 URL)此前转换后只剩 <Annot Type="Link" PageRef="…"/> 空壳:没有可点区域、没有跳转目标,各类 OFD 阅读器 (含本库预览)都当它不存在。根因与修复:

  • 链接语义没有落地:PDF 链接注释无可见外观,语义全在 /A 动作(GoTo/URI)或 /Dest 上。现按现实 OFD 产物(数科/ReaderManual 等,语料实测同款写法)与本库 ofd-tool 的 既定约定落地——外观文件内放不可见图元,其 <Actions> 携带 Goto/URI,图元 Boundary = 链接矩形(页面 mm,左上原点),阅读器据此做命中跳转;
  • 跨页引用延后回填:文档内跳转的目标页可能在源页之后才转换,外观内容改为收尾阶段 统一生成,Goto 按目标页 OFD 页 ID 引用(<Goto><Dest Type="XYZ" PageID="…"/></Goto>);
  • 命名目标两种写法都认:/D 按规范可为字符串或名字,此前 pdf-rs 只解析字符串形态, /D /chapter2(阅读器与工具产物常见)会让整条动作解析失败——vendor/pdf 补齐 Name 形态 (顺带修好书签目标的同类漏读);Catalog /Names /Dests 名字树按需构建并缓存;
  • 动作事件容错 + 标准值:现实语料 206 处事件全是 CLICK,此前枚举只认 MouseUp 等四种且解析失败会向上传播,连整个图元一起丢掉。现补 CLICK(书写缺省)并对其余 未知值容错回落;<Goto> 写回统一为标准式 <Dest PageID=…>,数科/ofdrw 亦可跟随。

逐链接核对(源页 + 矩形 + 目标页/URL,脚本 target/realworld-final/verify_links.py): arXiv 113/113、css4 3/3 全部一致;页面渲染像素对拍与修复前逐页一致(链接图元不可见, 不影响版面)。回归测试:ofd-convert 新增链接注释迁移用例(含命名目标 + URI + 链接矩形 mm 换算),ofd-core 新增 Goto 两种方言与 CLICK 事件 round-trip 用例。

补记(2026-10-12,随本版本一并交付):real-world 四份样例逐页对拍的字形错位与符号丢失修复

以 corpus/extra/real-world 四份真实 PDF(Adobe 官方样例 / arXiv 1706.03762 / css4.pub / W3C) 逐页 100 DPI 严格对拍,修掉三类「预览与原 PDF 不一致」:

  • 嵌入面与回退面字形编号混用(arXiv 整篇错字、css4 标题错字):嵌入字体没有 cmap 时 (LaTeX 系的裸 Type1 经 CFF 包装、只留 (1,0) 字节表的老子集 TrueType),码位→字形此前会 落到回退字体的编号空间(arXiv 正文数字直接取到宋体编号:28.4 画成 y.u)。现新增 「码位 → GID」权威表:码位取自 PDF Differences 与字体内置 StandardEncoding,字形名经 嵌入面 CFF charset 精确反查;ToUnicode 分支同样只认嵌入面自身字形空间。回退面编号仅在 「无嵌入面」(替代字体)时使用;
  • AGL 未收录字形名(数字、连字、CM 数学名):zero..nine、summationtext 这类名字 不在原 AGL 子集内,按名寻形整链断路——现以上表的逐名精确匹配替代 Unicode 猜测, arXiv 摘要的 28.4 / 41.8 / 3.5 / 8 GPUs、页脚 31st/(NIPS 2017) 全部复位;
  • 非嵌入 Symbol 字体的项目符号整段丢失:系统 Symbol.ttf 的 cmap 是符号表 ((3,0) 按 U+F000|code、(1,0) 字节直映),按 U+2022 之类 Unicode 码位寻形必然落空。 现补 Adobe Symbol 内置编码表(ofd-convert/src/symbol.rs,码位 ↔ 字符双向), Adobe 官方样例(adobe-c4611)四个项目符号由「整段跳过」恢复为正常绘制。

修复后四份样例逐页对拍:arXiv 正文内容与位置逐字对齐(最差页 SSIM 0.65→0.84), css4、adobe 文案/标题/项目符号与原件一致;剩余像素差为轮廓渲染与 FreeType 抗锯齿 差异(墨量比 0.971.01,属已知非缺陷带)。已知遗留:arXiv 图 4/5(第 1315 页)的 嵌套 Form 透明组(ExtGState ca)尚未落到对象 Alpha,图内半透明色块偏实。

补记(2026-10-13,随本版本一并交付):hyperref 边框模式链接的可见边框(arXiv 绿框)

arXiv 论文页面上围绕引文/URL 的彩色矩形框(hyperref 边框模式:/Border [0 0 1] 线宽 1pt + /C [0 1 0] 绿色等,LaTeX 论文极常见)此前转换后全部消失——链接注释被 一律落地为不可见图元,边框视觉丢失。现转换时解析 /BS /W(优先)或 /Border 数组 线宽与 /C 颜色(RGB/CMYK/Gray),线宽 > 0 且颜色可解析的链接外观图元改为可见描边 (Visible="true" Stroke="true",颜色即边框色、线宽按 pt→mm 换算),无边框链接维持 不可见图元不变。arXiv 1706.03762 第 2 页 29 个绿框、第 11/12 页 14 个青色 URL 框 逐框位置与 poppler 基准对齐(12/12 行带匹配,误差 ≤1px 抗锯齿级);点击跳转语义 不变。回归测试扩展链接注释用例(hyperref 边框 + 无边框对照断言)。

补记(2026-10-13,随本版本一并交付):透明度组调用时 α 落对象(arXiv 图 4/5 深色块消失)+ 阅读器链接点击/标注坐标修复

  • 转换内核(pdf2ofd)——透明度组以调用时 ca 合成:透明度组(/Group /S /Transparency) 整体按「调用时生效的 /ca」合成到背景(ISO 32000-1 §11.7.4.4)——组内绘制自身的 ca/CA 只管组内合成,组落纸再乘调用时 α。Illustrator 导出的隐藏图层(arXiv 1706.03762 图 4/5,第 1315 页注意力热图)即靠「调用前 gs ca=0 + 组内正常绘制」实现不可见, poppler/pdfium/MuPDF 三引擎一致丢弃;此前转换器忽略组语义把整层深色格子画成实色 (墨量达原件 78.7 倍、大片杂色)。现组内对象 α 一律乘调用时 ca(BM Normal 下与组 语义等价),α=0 的绘制不留痕不产出对象,0<α<1 落 Alpha 属性;非透明度组普通表单 无此边界效应、保持原行为。修复后 13~15 页墨量比 8.69→1.04,全 15 页逐页对拍正常。 回归测试钉住三种形态(ca=0 丢弃 / ca=0.5 → Alpha / 普通表单组内 gs 生效);
  • 阅读器示例(vue-demo)——事件坐标换算两处缺陷:多视图重构后 localToMm 漏乘 页面 mm 尺寸,事件坐标退化成 0~1 归一化比率,页内链接点击/hover 手型/拖画标注/ 签批落笔全线失准(标注被「拖动距离太短」恒拦);且 pageSize 返回的是数字键对象 而非数组,Array.isArray 判定误落 A4 兜底。修复后 arXiv 第 2 页绿框点击正确跳转 (目标第 11 页)、hover 手型恢复。

补记(2026-10-13,随本版本一并交付):转换产物互操作与「字母窄一半」渲染修复

  • 修复预览时字母窄一半(如 PDF BOOKMARK SAMPLE 的 B、Sample Date: 的 D/e): 渲染器的字形「按格宽收紧」逻辑对 TextCode 末字符没有后继步进可用,此前拿「前一 个同类字符的步进」外推末字符格宽——比例排版(PDF 转换产物按字形真实步进写 DeltaX) 下这个外推是错的:B 拿到前面空格的小步进当格宽,被压到 0.5 宽下限(正好「窄一半」, B/D/e/T/o/g 全是各自文本段的末字符)。现包内嵌入字形的末字符直接信字形自然步进; 电票等宽格排版(替代字形须贴格子)的既有行为保持不变;
  • 转换产物全面对齐 GB/T 33190 生态写法:实测 64 份真实厂商样本,XML 部件带 ofd 命名空间、图层包在 <Content> 内、ID 引用用裸数字三项均为 100% 惯例,我们的 产出此前三项全缺且 Font 资源缺 FontName、FamilyName 带 PDF 子集前缀——严格阅读器 (ofdrw 等)会把整页解析为空白,宽松阅读器按名匹配系统字体失败后退化为替换字体。 现写出器统一输出命名空间与 Content 包裹、引用写裸 ID、字体声明带清洗后的 FontName/FamilyName(/HOEPNL+Arial,Bold → Arial,Bold),自家解析器两种形态都兼容 (旧文件不受影响);
  • 搬运子集字体自动补 Unicode cmap((3,1)):Accelio 等生成器的 PDF 内嵌子集只有 (1,0)/(3,0) 编码表(部分还带子表长度越界的畸形声明),OFD 的 TextCode 文本是按 ToUnicode 还原的 Unicode——按 Unicode 查 cmap 的阅读器全部落空。现转换收尾按实际 用字「字符 → 字形编号」为这类字体追加 (3,1) 子表(既有子表原样保留、表目录与整体 校验和重算,补表后字体不可解析时自动回退原字节),第三方阅读器按文本寻形命中真实 字形。ofdrw 渲染验证:修复前整页空白,修复后 4 页全出、标题字母宽度与 poppler 渲染 原始 PDF 一致;
  • 测试:新增 11 项回归(互操作结构 3 项、cmap 补表、末字符不收紧等),workspace 442 项测试全绿(2026-10-13 本轮时点实测 cargo test --workspace;终值 445 见下方发布验收轮补记)、clippy 无新增告警; 回归样本入库 tests/data/c4611-page1.pdf(Accelio 生成器产物)与 tests/data/c4611-arialbold-subset.ttf(畸形 cmap 子集字体)。

补记(2026-10-13,随本版本一并交付):发布验收轮——三路代码审查 13 项修复

发布前对全部未提交改动做三路独立审查(转换内核 / 渲染+WASM+vendor / 新文件与 文档一致性),修复清单(全部带回归测试或注释说明):

  • wasm32 必崩缺陷(P0):裸 Type1 的 PFB 段长解析在 32 位平台 (即线上 wasm 产物)上可被畸形 /FontFile 构造整数回绕、绕过长度检查后切片 panic——64 位本机测试发现不了。段长改按 u64 比较;
  • 连字用字对错位(P1):ToUnicode 多字符映射(连字 ffi 等)时,(3,1) cmap 补表的「字符 → 字形」配对整体错一位,首个连字之后的字符全部映射到错误字形 编号(第三方阅读器画错字)。改为按「uni 各字符 × 对应字形」成对收集;
  • StandardEncoding 布局位文本层错字(P1):显式声明 StandardEncoding 的 Type1 字体, \ { | }` 等布局位的 Unicode 恢复误走 TeX OT1 布局表(反斜杠 给成左双引号等)——检索/复制层错字。现按标准编码字形名走 AGL 恢复,OT1 表 仅保留给低段连字(两编码同义区)与无编码声明的猜测场景;
  • JBIG2 halftone 外部 REPLACE 算子被折叠(P2):半调区域→页的组合算子 取位掩码写成 &3,REPLACE(4) 退化为 OR(清零像素丢失),改与 generic 分支 一致的 &7;
  • Type1→CFF 产物两处规范偏差(P2):StandardEncoding 前缀判定的恒真条件 (!len &gt;= 16 优先级问题);合成 CFF 的 Encoding format 0 缺 nCodes 字节 (按规范补齐并按 255 截断);
  • CFF 解析边界加固(P2):charset 展开与自定义 SID 的三处 u16 加法溢出 (畸形 CFF 在 debug 构建直接 panic)改 checked/saturating;contains_seac 遇 hintmask/cntrmask 掩码字节无法安全跳过时保守拒绝 CID 化(防漏检 seac 导致组合字形寻址错位);
  • 楷体风格关键词缺 wenkai(P1 级实测缺陷):前端实际注入的楷体回退字体 (霞鹜文楷 LXGW WenKai)族名不在宋/黑/楷关键词表——楷体文档的风格化回退 匹配落空(整体回落黑体/链首)。渲染端与转换端关键词表同步补 wenkai, 并新增渲染端风格回退三层回归(分槽表驱动 + 族名自动识别 + 端到端楷槽选中 断言);
  • JS 包 setStyledFallbackFont 增加风格校验:无效风格名(拼错 songti 之类) 此前静默吞掉且字体字节常驻内存,现提前拒绝并 console.warn(与 wasm 端 同一合法集);
  • 工程清理:fuzz 渲染种子补真实 JBIG2 样本(此前夹具缺失、只走全零解码失败 路径);check_no_dotdot 等 6 处存量 clippy 告警清零(本轮改动相对 HEAD 基线新增告警数为 0,净消除 9 条);分发包/文档多处的版本与测试数口径统一 (本轮终值 445 项测试全绿,33 个测试二进制,2026-10-13 实测)。

v0.11.1 — 2026-10-10

缺陷修复(pdf2ofd 转换):

  • 修复 ICCBased 色彩空间图像被错误按灰度处理:PDF 图像的 ICCBased 色彩空间由流字典 /N 声明分量数(1=灰度 / 3=RGB / 4=CMYK),此前转换器无条件按灰度(1 通道)处理——扫描件的 ICC RGB 全幅图像被按 1/3 行宽逐行错切,转换产物呈现「内容放大 + 横向重复」的错乱图,且整体灰度化(红色印章消失)。现按 /N 正确取分量数(缺失或非常规值时退回旧行为),Indexed 基底的 ICC 同步修正。附真实扫描件(住院病案首页)端到端回归测试;1018 份公开语料评测成功率 98.1% 持平、零 panic。
  • 修复裸 Type 1 嵌入字体导致整页文本不可见:pdflatex 时代(约 2020 前的 LaTeX 论文等)的 PDF 嵌入字体是裸 Adobe Type 1(/FontFile),此前解析器不支持——本地端退化为系统字体替代(缺字、CM 数学字体整段跳过);网页端(wasm 无系统字体)更会在回退字体未就绪时把整页文本静默丢弃。现内核将裸 Type 1 解密并转换为 CFF 嵌入(eexec/charstring 解密、flex/hint/seac 语义、Subrs 展开、PFB/PFA 两种封装),LaTeX 论文(CM/Nimbus 系字体)转换后以原始字体真实嵌入、文本完整可检索;另兜底:任何字体字形全部缺失时并行输出不可见文本层,检索/复制不再静默丢失。1018 份语料评测:成功率 98.1% 持平、零 panic,SSIM≥0.95 文件数 535→541、平均分 57.21→57.43(LaTeX 系样本以真实嵌入字体参与对拍)。

v0.11.0 — 2026-10-10

对标数科阅读器的可感知差距补全:内核把一批「模型已解析但未导出/未消费」的能力开放出口,阅读器示例(vue-demo)升级为多视图阅读器(默认仍为单页,既有交互零变化)。

渲染内核与 WASM API(core 模块)

  • 图层运行时切换:新增 pageLayers(page) 枚举页面图层(含声明隐藏层)、setLayerVisible(id, visible) / resetLayerVisibility() 即时开关——对 renderCanvas / renderPng / exportImage 等全部渲染出口生效;Rust 侧同能力经 RenderOptions::layer_overrides 开放(默认空表 = 按文档声明渲染,不传即旧行为);
  • 视频对象封面渲染:VideoObject 的 PosterID 封面图照常呈现(数科同观感;此前只画灰色占位框;无封面或封面损坏保留占位框,零 panic);
  • 多媒体出口:medias() 列出图像/视频/音频资源(类型/格式/时长/自动播放),mediaBytes(id) 取任意媒体文件字节;pageVideos(page) 导出页内视频区域(供阅读器放播放按钮);
  • 文档信息补全:docInfo() 新增 permissions(打印/拷贝/导出/修改/注释/签名/水印/全屏 8 项国标权限声明)与 vPreferences 字段;customTags() 导出语义标引条目(GB/T 33190 第 16 章——电子发票字段标引等厂商扩展的数据入口)。

工具模块(tool)

  • 加密 OFD 浏览器端闭环:isEncryptedOfd(bytes) 检测、decryptOfd(bytes, 私钥hex) 解密、encryptOfd(bytes, 公钥hex[]) 加密(SM4 内容加密 + SM2 数字信封,与 CLI 同底座,字节级一致);npm 包装器 rust-ofd 已同步导出。

阅读器示例(vue-demo)

  • 视图模式:单页(默认,与旧版一致)/ 连续滚动 / 双页对开——流式模式按可见性懒渲染、远离视口自动回收画布,滚动位置实时同步当前页与键盘翻页;
  • 视图旋转(90° 步进会话级预览,不改动文档字节;「整理」页的旋转仍是文档级编辑)、夜间模式(界面深色,纸面内容保持原样)、全屏阅读、缩略图导航栏(懒渲染小图,点击跳页);
  • 检索导航:上一处 / 下一处顺序巡览命中 + 当前命中行高亮;页码输入框回车跳页(此前为死代码);
  • 页面标注工具:在页面上直接拖画高亮 / 矩形 / 椭圆 / 直线(颜色、线宽、透明度随「注释」表单),松开即写回标准注释——替代「表单填 mm 坐标」的旧交互(表单保留可用);连跨页签批不变;
  • 信息页补全:主题 / 关键词 / 创建与修改时间 / 制作者 / 自定义数据;文档权限声明徽章(仅展示提示,不拦截);发票字段一键识别卡(extractFields);图层面板(配合 pageLayers 即时开关);
  • 导出长图入口接入 exportLongImage(此前 API 已有、界面无入口);打开加密 OFD 失败时自动识别并弹出密钥解锁(配合 tool 模块新导出)。

勘误与文档(不改行为)

  • Clips/@TransFlag:语料实证 60 文件 / 6788 处几乎全为 false(ofdrw 写出件缺省如此)且 ofdrw 读取端不消费——渲染变换链对 true/false 一致为正确口径(94 语料对拍锁定),修正模型注释中「false = 不随宿主变换」的误导描述;
  • README「已知边界」删除「Canvas 指令流后端不解释图像 CTM」的过时表述(该后端实际已支持图像仿射);
  • 文档站全站数字与能力口径随本版本校准:crate 数(13,含 ofd-jpx)、测试数(391)、内核体积(约 3.5MB)、能力矩阵「加密」补 WASM 出口、快速开始页工具模块补加解密触发;价格页企业版不再列示「优先 / 驻场支持」档位;文档站内嵌 Playground 演示内核同步刷新为 0.11.0;
  • 测试:workspace 全绿(ofd-render 新增图层覆盖与枚举、视频封面渲染回归测试);vue-demo 生产构建通过。

v0.10.0 — 2026-10-09

OFD 阅读方向两大图像能力补齐(JBIG2 精化全路径 + JPEG2000 渲染):

  • OFD 文档内嵌 JPX/JPEG2000 图像可渲染:此前仅 PDF→OFD 转换方向支持 JPX(v0.6.0 起),阅读 OFD 文档时遇到 JPX 图像只画灰色占位框;现解码能力自 ofd-convert 下沉为独立 ofd-jpx 模块(vendor openjpeg 2.5.3,含 wasm32 垫片与空 tile 崩溃围堵),渲染管线在 PNG/JPEG/JBIG2 皆不识别时按魔数嗅探尝试 JPX 解码,失败仍回落占位框(零 panic)。印章(SES)位图路径同步受益;core 模块 wasm 体积 gzip 增加约 80KB(feature 门控,可关);
  • JBIG2 refinement(精化)全路径:此前 symbol dictionary 的 SDREFAGG 聚合与 text region 精化实例降级为「直接复用参考符号」(占位来源之一)。现按 T.88 6.3/6.4/6.5 完整实现:
    • 泛型 refinement 区域的 TPGRON 典型预测(SLTP 上下文按 jbig2dec 权威值 0x100/0x40,隐含值九宫格判定;此前两次「失步」实为 pdf.js 位序值未换算 + 模板 1 段头多吞 2 字节 AT 的组合误诊——后者为本轮修复的既有缺陷);
    • 符号字典 SDREFAGG=1 单实例聚合(IARDX/IARDY 锚定 + GR 算术重构)与 IAAI>1 内嵌文本区域(与外层共享算术流);
    • 文本区域 SBREFINE=1 精化实例(IARDW/RDH/RDX/RDY + GR 重构),以及 SBCOMBOP 全部组合算子(AND/XNOR 此前降级为 OR);
    • 已知限制(与 pdf.js 口径一致):SDHUFF=1 × SDREFAGG=1 组合仍拒绝(生态零实现:jbig2enc 不产生、pdf.js 拒绝、jbig2dec 实现的算术流位置语义无法自洽);halftone 区域的 HSKIP/灰度 MMR/非 OR 灰度拼贴仍跳过;
  • 迁移与体例:ofd-convert 切换为依赖 ofd-jpx(解码行为零变化,单测像素断言锁定);OFD 包写出的 jp2/jpx/j2k/j2000 媒体改 STORED 打包(已压缩格式不重复 deflate);
  • 测试:workspace 全绿(ofd-core 105,新增 TPGRON roundtrip、SDREFAGG 单实例/多实例 e2e、文本区域精化实例 e2e、AND/XNOR 组合算子 e2e、JPX 渲染 e2e);pdf2ofd 语料评测 JBIG2/JPX 相关文件全部转换成功、零 panic、无退化。

v0.9.1 — 2026-10-09

v0.7.0~v0.9.0 三批次代码审查专项修复(双审查代理深读内核与前端,1 项 P0 + 6 项 P1 + 10 项 P2,全部 P0/P1 与 7 项 P2 落地):

  • 修复「关闭文档」失效(P0):关闭按钮因一处变量改名残留(drawPts 未声明,严格模式赋值抛错中断后续全部清理)导致界面卡在「幽灵文档」状态——修为当前笔画变量并连带补齐多笔签批与表单状态的清理;
  • 表单卡时序与翻页(P1):打开新文档时表单字段读取早于页码归位(读到旧文档页码,小文档越界返回空表),且翻页不刷新字段(在错误页填写)——读取移到页码归位后并随翻页刷新;
  • 「打印」按钮弹窗拦截兜底(P1):转换耗时超过浏览器用户手势窗口后新标签几乎必被拦截且无提示——拦截时自动降级为下载打印版 PDF,始终有出口;
  • 文档站 Playground 三处加固(P1/P2):wasm 改强制绕过缓存(防内核升级后胶水与旧 wasm 版本错配——项目缓存纪律);渲染加序号令牌(快速翻页时旧页后到不再覆盖新页);页面卸载释放 wasm 侧文档内存;
  • 手写墨迹防丢失(P2):缩放/字体就绪重绘不再清空未保存笔画(同页保留并按新比例重画预览,仅翻页清空);保存失败时笔画还回累积区可直接重试;
  • 内核健壮性(P1/P2):表单字段值拼接的排序比较器改为全序(非全序比较器在新版 Rust 排序实现中可能 panic——零 panic 红线);发票日期扫描去掉平方级后缀复制(超长单行不再卡死);表单填写补正文层的对象 ID 漏递增(防 ID 撞号);填写区坐标 NaN/inf 守卫(防产出损坏文档);长图导出改两遍法(峰值内存约减半);JPEG 质量 0/越界统一归默认 90(JS 传 0 不再得到质量 1 的废图);
  • CLI 加固(P2):批量报表 CSV 防公式注入(=+/−@ 开头前置引号);同主名消解改哈希查重(数万文件不再平方级);--scale 与 --dpi 两种出现顺序都正确互斥;
  • 全 workspace 测试通过(新增:表单值阅读序拼接/交错行场景回归);
  • 补记(2026-10-09,随本版本一并交付,仅前端界面、内核与 API 无变化,不升号):阅读器示例(vue-demo)界面全面优化并上线——「获取授权」弹窗移除收款二维码,改为联系方式直达(微信 / 邮箱两张联系卡,点击复制、一键发信,portal 在线申请入口保留);侧栏六个功能面板表单布局重构:标签统一上置、控件换行对齐不再参差,数字输入的占位提示不再被步进箭头截断,超长占位规则移至行下方完整可读,面板内容超出一屏时改为内部滚动;另统一表单定焦高亮、检索框弹性伸缩、注释列表长文本不再互相挤压、授权管理弹窗超高内容可滚动;
  • 补记(2026-10-09,文档站 SEO 修复):文档站首页标题补齐关键词(原仅"rust-ofd")并修复栏目页 og:title 为空的问题(新增页面无 frontmatter title 时全站兜底);主站 sitemap 更新日期改为构建期自动刷新(原停在首次上线日);清理服务器内部页(授权/部署手册)历史残留 html,并修复文档站不存在路径返回 200 的软 404(现正确返回 404 状态码与 404 页)。SEO 整体方案见 docs/business/SEO优化流程与方案.md。
  • 补记(2026-10-09,随本版本一并交付,仅对外品牌与文案,内核/API 无变化,不升号):项目对外定名「一凡OFD」——阅读器示例、文档站与 README 的品牌展示层(站点标题、og/JSON-LD 元数据、顶栏/页脚、正文叙述)统一由 rust-ofd 换为「一凡OFD」;npm 包名、Rust crate 与全部技术标识保持 rust-ofd 不变,已安装用户的集成代码不受影响。品牌域名 z1fan.com 已注册,暂不切换站点域名。

v0.9.0 — 2026-10-09

易用与转化批次(《价格与功能优化路线图》v0.9.0,路线图收官):

  • CLI 批量转换(ofd-cli batch):面向档案迁移的非技术用户包装——目录/文件列表批量 PDF↔OFD(按扩展名自动定向,--to 可强制方向、--recursive 递归);断点续跑(已存在且不早于输入的输出自动跳过,中断重跑零重复劳动);逐文件容错续转(失败记报表不拖垮整批,--stop-on-error 可改为即停);CSV 报表(path/direction/status/seconds/error,带 BOM 可直接双击打开)与逐文件进度显示;同名输出自动消解(递归目录撞名加 -2/-3 后缀)。验收:500 份混合语料实测零 panic(493 成功 + 7 份 vendor 层已知损坏件正常记 fail,进程干净退出);
  • 打印保真出口(阅读器「打印」按钮):打印专项验收实证「直接 Ctrl+P 打印网页 = 打印应用界面而非文档」(无打印 CSS、单页 canvas 形态),正确出口 = OFD→PDF 矢量转换后打印:一键生成多页全保真、文本可检索的 PDF 并新标签打开,Ctrl+P 即所见即所得。FAQ 补「怎么打印」口径;
  • 文档站内嵌 Playground:WASM 快速开始页新增「在线演示」——页内真实内核渲染(非截图),固定演示样张(数科版式文档)可翻页/缩放、显示内核版本与渲染耗时;演示授权与主站同域复用在线续期,本地预览时优雅降级为提示。转化价值:访问者零安装即见内核真实渲染效果;
  • 全 workspace 测试通过(本版新增 4 项:batch 规划定向/同名消解、断点续跑 mtime 规则、混合目录端到端含坏文件与续跑断言);docs 站点新增 rust-ofd 本地包依赖(Playground 用),wasm 演示资产(3.3MB 内核 + 回退字体 + 样张)随 public 目录分发;
  • 注意:WASM 内核本版无新 API(wasmVersion() 仍 0.9.0 需重建生效);「打印」按钮依赖转换模块(已授权部署自动可用)。

v0.8.0 — 2026-10-09

交互双件(《价格与功能优化路线图》v0.8.0 批次):表单填写 + 原笔迹手写签批。

  • 表单字段与填写(formFields / fillFormFields):前置调研确认 OFD 国标(GB/T 33190-2016)没有表单域标准——生态事实标准是数科 AreaHolderBlocks.xml 命名填写区(区域名+边界,Document.xml 不引用、按约定发现式加载)。本版落地完整闭环:内核解析填写区(宽容降级,缺失不影响文档打开)→ formFields(page) 导出字段(区域 ∩ 文本层求交,值文本未嵌入时须先 addFallbackFont,与发票提取同因)→ fillFormFields(ofd, fills) 把值以 TextObject 写入所属页正文层(数科语义:跨阅读器可见、可检索、写回后 formFields 可取回)→ 未命中字段名明确报错并列出文档全部填写区。填写会修改页内容文件:已签名文档的既有签名摘要将失配(等同修改正文),应在填写后重新签名(前端已提示)。数科真样本往返实测通过(tests/data/areaholder-fields.ofd);
  • 手写签批(addAnnotation 新增 type: "ink"):前置调研确认墨迹的标准表达 = Type="Path" 注释 + 外观内 PathObject 线段(GB/T 33190 第 15 章原生表达,数科/ofdrw 均按此渲染注释外观,无需自造格式)。ink 为输入别名(落盘标准 Path 注释):strokes: [{points, width}] 多笔一次落成一条注释,每笔独立 PathObject 与线宽(压感由前端烘进笔级 width——触屏/手写板轻压细、重压粗),全部笔迹共享总包围盒 Boundary,单点点触自动扩为微线段。阅读器示例「签批」按钮即此形态:页面级 overlay 画布 Pointer Events 采集(指针捕获跨画布书写)、压感实时预览、多笔累积、「保存墨迹 / 清空」操作(原「手绘」单笔即时提交模式升级为多笔累计保存);
  • 实现落点:ofd-core 新增 form.rs(AreaHolder 解析 + reader 集成);ofd-render 新增 form.rs(page_form_fields[_with] 区域∩文本层);ofd-tool 注释 AnnotationSpec 新增 strokes: Vec&lt;InkStroke&gt;(Path 分支多 PathObject 输出)与 form.rs(fill_form_areas 值文本写回,复用水印字体声明机制);wasm core formFields / tool fillFormFields + addAnnotation.strokes;
  • 全 workspace 376 项测试通过(本版新增 9 项:墨迹多笔 PathObject/总包围盒/笔级线宽/点触扩线/可删除、AreaHolder 解析两用例、reader 发现式加载、字段导出与无表单降级、填写往返取回/未命中报错/空值拒绝);node 三模块冒烟扩至 v0.8.0 三链路(formFields 空值 → fillFormFields 往返回读 → ink 注释落盘);
  • 注意:WASM 授权产物需重建后 wasmVersion() 显示 0.8.0;工具模块(fillFormFields/ink)按需懒加载,core 模块仅增 formFields(含 AreaHolder 解析,体积增长极小,体积表随部署更新)。

v0.7.0 — 2026-10-08

转换增值双 API(《价格与功能优化路线图》v0.7.0 批次):

  • OFD→图片产品化(exportImage / exportLongImage,WASM 新 API + CLI 增强):单页导出支持 PNG / JPEG(jpeg-encoder 纯 Rust 编码器,无 C 依赖)、自定义 DPI(默认 96、上限 2540,与渲染守卫同口径)、JPEG 质量 1–100;多页长图竖向拼接(白底、水平居中、窄页居中留白),总画布沿用零 panic 守卫(边长 ≤16384px、总像素 ≤64M,超限返回含实际尺寸的错误);JPEG 单边 65535px 上限(JPEG 标准约束)。CLI render 新增 --format png|jpeg、--dpi(与 --scale 互斥)、--pages 1-3,5(1 起,优先于 --page/--all),输出扩展名随格式联动(目录模式/模板模式/缺省命名三路径全覆盖)。renderPng 渲染路径重构为公共 render_page_skia_target(行为零变化,355+ 既有测试护住);
  • 电子发票字段提取(extractFields,WASM 新 API):基于文本层布局的模板化提取——行聚类(y 邻近)→ 标签匹配(行级拼接支持「名」+「称:」拆分书写、排除「项目名称」表头)→ 值提取(列对齐优先 + 度量漂移正则兜底:税号行内 15–20 位大写段、「称:」后段截数字前、代码+号码粘连段前缀切分)。字段覆盖票种/号码(旧 8 位/全电 20 位)/代码/日期/校验码/机器编号/购销方名称与税号(版式顺序分配)/合计金额/税额/价税合计大小写共 15 项;非发票文档不报错(matched=false 空字段降级)。百望增值税电子发票实测全部字段正确(native 系统字体与 wasm 回退字体双度量路径均验证;样本 tests/data/invoice-baiwang-vat*.ofd 入库)。发票字体普遍未嵌入,调用前须 addFallbackFont(文档已明示);
  • 新增依赖:jpeg-encoder 0.6.1(MIT/Apache-2.0 双许可,纯 Rust)仅进 ofd-render;
  • 全 workspace 367 项测试通过(本版新增 12 项:图片导出 6——PNG/JPEG 魔数与尺寸、格式别名、长图高度=各页和、空页拒绝、DPI 缩放;发票提取 6——百望双样本字段断言、非发票降级、日期/大写金额/金额正则工具函数)。node 三模块冒烟扩至含三个新 API(examples/node-demo/smoke.mjs);
  • 注意:WASM 授权产物已重建部署(线上 wasmVersion() 核对 0.7.0);core 模块因 jpeg-encoder 与发票模块体积增长约 70KB(gzip,体积表已更新:core 3.26/1.13 MB、convert 4.68/1.68 MB、tool 2.02/0.71 MB)。

v0.6.1 — 2026-10-08

公网健壮性专项(商业化就绪审查修复):针对「不可信来源文档」场景的两个可崩溃缺陷与授权/交付链路的一批真实缺口。受控输入场景(内部系统、可信语料)本就达标,本版把同一标准补齐到公网/嵌入宿主场景。

  • 渲染画布尺寸守卫(零 abort):恶意或损坏 OFD 声明超大 PhysicalBox(如 30000×30000mm,A0 的 100 倍)时,主渲染入口此前无尺寸上限(图案瓦片/转换侧 mesh 均有,唯独主入口缺失),96dpi 下请求约 51GB×2 分配直接 abort 进程——FFI/C#/Java 宿主与 wasm 阅读器全部可达。现与图案守卫同口径(边长 ≤16384px、总像素 ≤64M),超限返回明确错误信息(含实际请求尺寸与上限);印章子文档(OFD 型印章外观)同守卫,超限降级为无印章外观;CLI render --scale 补上限 100px/mm 与非法值校验;
  • 渐变函数/色彩空间递归深度守卫(防栈溢出):恶意 PDF 以间接引用构造 FunctionType 3 自引用 / Separation 交替空间自引用,此前解析为无限递归栈溢出段错误(catch_unwind 与字节变异 fuzz 均无法拦截)。现函数/色彩空间解析统一携带深度上限 16,超限返回 None 走对象级跳过降级(SkippedObject 警告不变),合法嵌套(两层 stitching 等)不受影响;
  • 模糊测试双向化:此前 fuzz_smoke 只覆盖 PDF→OFD 单向,OFD 解析→渲染方向(阅读器主业输入面)零 fuzz——本次两个崩溃缺陷恰在此路径。新增 ofd-render/tests/fuzz_ofd_smoke(真实样本 + 超大 PhysicalBox 守卫形态种子,canvas/PNG 双后端,默认 800 次、FUZZ_ITERS 可加压),并在 PDF 侧种子中加入自引用渐变构造形态;
  • C ABI 边界 panic 兜底:ofd-ffi 的解析/渲染/转换/验签导出统一经 ffi_guard 捕获 panic 转为可读错误(此前 panic 穿越 extern "C" 边界直接打死 C#/WPF、Java/JNA 宿主进程且无法恢复);
  • WASM 核心模块授权逐次复验:OfdDoc 的渲染/文本/图像/检索/附件/注释/验签等 14 个内容方法入口补授权复查(此前仅构造时校验一次,token 过期或吊销后已打开的文档可无限期继续用),与 convert/tool 模块及授权文档「每次功能调用复验」的声明对齐;pageText/pageImages 在授权失效时抛出授权错误而非静默返回空值,元信息 getter(pageCount 等)不受影响;
  • 验签「未保护文件」分级:签名后新增的注释/附件文件此前与真实篡改同权重报 digestValid=false(较国标更严的过严规则),线上验签告警噪声大。现此类文件单列新报告字段 unprotectedFiles(WASM/FFI/CLI 同步),digestValid 只对「References 列举文件摘要不匹配/缺失」判 false;篡改受保护文件仍正确判无效。阅读器验签面板同步展示「签名后新增 N 个未保护文件」;文档补充两条明示:未配信任锚时 signatureValid 不能单独作为可信结论(内嵌证书可自造)、非浏览器宿主(Node 等)可伪造 hosts 绑定(高价值离线交付应走 --did 定制构建);
  • 交付脚本安全化:build-dist.mjs 不再硬编码生产服务器 root 账号与 IP(改为环境变量 / .license-keys/deploy.env 读取,示例见 scripts/deploy.env.example,缺失时明确报错并建议非 root 受限账号);非 Windows 平台 zip 打包改用系统 zip(此前 PowerShell 专属命令必失败);分发包输出根目录跨平台默认到仓库 target/(不再绑定特定开发机盘符);
  • 小修:CLI pdf2ofd 默认输出名只替换末尾 .pdf 后缀(此前全量替换把 a.pdf.and.pdf 错改为 a.ofd.and.ofd,现保留原文件名大小写);wasm 授权模块锁中毒降级(持锁路径 panic 不再连锁);自签证书生成移除可达路径上的 expect 改为错误传播;openjpeg 构建脚本的头文件变更也触发重编;
  • 文档口径:首页与阅读器示例的语料成功率数字更新为 98.1%(与 0.6.0 实测一致,此前的 97.9% 为旧轮数字);「能力边界」页 JPX 条目区分转换端(已真解码)与渲染端(仍占位);
  • 全 workspace 355 项测试通过(本版新增回归 9 项:超大画布拒绝与边界、自引用函数/色彩空间拦截与合法嵌套、OFD 方向模糊、FFI panic 兜底与超大画布链路、CLI 输出后缀;样本无需入库——全部运行时构造)。

补记(2026-10-08,商业化定价与运营落地,随本版本一并交付;内核无变化):商用定价调整为公开三档——线上阅读器「获取授权」弹窗由单一 ¥1,999 一口价改为三档价格卡:预览版 ¥999 / 转换版 ¥1,999 / 全功能版 ¥2,999(均在线授权 · 元/年/域名,含子域与标准支持,授权期内次版本与修订版本免费升级),30 天免费测试授权不变;内网离线、多实例、合同发票与 SLA 走企业版询价(portal 申请或微信联系)。文档站新增「价格与授权」与「常见问题」两页(含购买、联系方式与报障材料说明)。免费在线站同日明确使用边界:供个人评估使用(页脚提示 + 单文件 50MB 上限),商用授权的 SDK 与部署产物无任何此类限制。10-08 前按旧价付费的客户按原价规则过渡。

注意:WASM 授权产物需在具备 wasm32 clang/LLVM 工具链与授权密钥的机器上重跑 build-licensed.mjs 才会显示 0.6.1(本版为缺陷修复,转换行为与 0.6.0 一致);部署后以 wasmVersion() 徽标核对。

补记(2026-10-08 晚,阶段三运营加固,随本版本一并交付):

  • CI 最小门禁:新增 scripts/ci-check.sh(cargo test --workspace + clippy,Windows 自托管自动 source build-env.sh)与 Gitee Go 流水线 .workflow/rust-ci.yml(push main 触发;字段如与本版本面板有出入,按等价步骤可视化配置即可,门禁实体在脚本);
  • 评测脚本可移植化:pdf2ofd-eval.py / pdf2ofd-page-verify.py 的 poppler 与 xpdf pdftotext 路径改 OFD_POPPLER_BIN / OFD_PDFTOTEXT 环境变量优先(CI/异地环境免改码),本机安装位回落不变、历史口径可比;
  • 授权服务监控告警上线(服务器 root cron,examples/license-server/monitor.mjs,每 5 分钟):healthz 可达与延迟、HTTPS 证书剩余天数(<14 天告警)、/www 磁盘占用(>85%)、服务进程存活(pgrep,pm2 托管自动识别);OFD_ALERT_WEBHOOK 配置后推送到群机器人,OFD_ACCESS_LOG 配置后启用 429 错误率检查;
  • 备份恢复演练闭环:数据库每日备份(mysqldump→gzip 校验→滚动 30 份)+ 恢复演练脚本(最新备份恢复进临时库逐表行数对比,每周一自动跑)。首次演练记录(2026-10-08):12 张表行数全部一致,恢复路径验证可用,详见文档站内部页《宝塔部署》监控与备份节;
  • 口径与合规:首页测试计数 250+→340+;阅读器示例新增隐私说明页(匿名统计的字段清单、不含文档内容声明、关闭路径)与首页告知入口;THIRD-PARTY-NOTICES.md 完成 OFL-1.1 全文 + 216 项 Rust 依赖完整清单(cargo metadata 全量清点,9 个许可族全文随附),build-dist.mjs 打包强制随包 LICENSE 与声明文件(缺失即中止);
  • WASM 三模块产物已重建至 0.6.1(prod 公钥形态,体积与 0.6.0 持平:core 3.01/1.06MB、convert 4.57/1.65MB、tool 1.97/0.70MB gzip),.wasm.gz 预压缩同步生成(该做法自 0.12.0 起调整:构建脚本不再产出预压缩件,改由部署侧在同一次构建的产物目录按需生成,以免静默分发旧内核——见 wasm/license.md);仍待部署上线(线上徽标届时核对 0.6.1)。

v0.6.0 — 2026-10-08

JPX/JP2(JPEG2000)真解码接入 + 渲染语义与零 panic 防线强化:

  • JPX/JP2 原生解码:PDF 内嵌 JPEG2000 图像此前走占位图,现以 openjpeg 2.5.3 静态链入转换内核原生解码(灰度/RGB/CMYK/SMaskInData 透明度)。色彩空间按 JP2 colr 声明定位(enumcs=12 走 CMYK 合成)、不透明度通道按 cdef 标注定位(不假定首位——主流编码器在末位、Adobe 系在首位均正确);超大声明的恶意流按尺寸上限拒绝、wasm 下输出缓冲分配失败优雅降级为零 abort 路径。另前置码流结构校验:按段长走查 tile-part,任一 tile 数据段为空或声明越界即拒绝解码走占位——openjpeg 对"结构完整但 tile 数据为空"的码流(pdf.js 模糊样本 bug_jpx 形态)存在堆损坏写(分阶段二分定位在 opj_decode 内部,引爆点随堆布局漂移),结构级围堵零误伤(合法空包也至少 1 字节)。语料 14 份 JPX 文档全部真解码;
  • Tr 5/6 描边语义(文本渲染模式):描边+裁剪、填充+描边+裁剪此前按纯填充近似,描边色文本会被画成填充色;现输出带 Stroke 属性与描边色的文本对象(与 Tr 1/2 同形,裁剪行为不变)。另评估了「裁剪不被 Q 恢复」的规范语义修正并暂缓:OFD 将裁剪嵌入每个对象,持久裁剪在生成器典型文件(每对象 q+W+Q)上会按 对象数×累积裁剪数 放大内存,表示法改造前维持与生成器惯例一致的近似;
  • 零 panic 防线机制化:新增确定性变异模糊回归(tests/fuzz_smoke,固定种子对代表性 PDF 做字节翻转/截断/片段复制,常态随测试运行,FUZZ_ITERS 可加压);mesh 渐变解码总量守卫(Type 6/7 最小位宽流约 2400 倍内存放大,顶点/三角形超限弃画并告警);字形与路径裁剪统一守卫管线(字形裁剪 64k 独立命令预算);ConvertReport 新增守卫遥测字段(max_clip_cmds/max_mesh_verts 单页峰值),配套校准工具 cargo run -p ofd-convert --example guard-stats -- &lt;PDF 目录&gt; 输出语料分布,阈值是否有余量从此有数据可依;
  • 评测工具:pdf2ofd-eval.py 与 pdf2ofd-page-verify.py 的 SSIM/文本口径抽取为公共单源 scripts/verifylib.py(两套脚本数字必须可比的部分不再双维护);
  • 全 workspace 347 项测试通过(本版新增回归 12 项:JPX CMYK/SMaskInData 两分支、空 tile 码流围堵、变异模糊、mesh 守卫、lattice 宽行、clip 守卫、CMYK JPEG 像素、Type0 变长切分 4 项;样本 jpx-smaskindata-sample.jp2/jpx-cmyk-sample.jp2/jpx-emptytile-crash.j2k 入库)。

补记(2026-10-08,WASM 重建闭环,随本版本一并交付):三模块 WASM 授权产物已按 0.6.0 重建(本机安装 LLVM/clang 后 build-licensed.mjs test+prod 双构建)。wasm32 首次实际编译 openjpeg,为此补齐 freestanding libc 垫片(build.rs 按 wasm 目标注入:标准头、stdio/ getenv 桩、真实现 qsort/strcmp、手工 math——__builtin_pow/floor 等在 wasm32-unknown-unknown 上会降级为对同名符号的库调用,转发即无限自递归,两代踩坑均以 2×1 图/整本文件栈溢出显形),并修正 wasm 分配器垫片一处从未编译过的 realloc 参数错位。验证:wasm wasmVersion()=0.6.0;13 份 JPX 语料 wasm 侧全部转换成功,其中 S2/jpx_smaskindata/issue5481 与原生 CLI 输出字节级一致(手工 sqrt 定点收敛至 IEEE 正确舍入、lrint 半偶舍入后达成);node 三模块冒烟全过(smoke.mjs 补 location 多态与 token 前置激活)。体积(gzip -9):core 3.02/1.06 MB、convert 4.57/1.64 MB(openjpeg 净增约 +190KB)、tool 1.97/0.70 MB,体积表已同步。注意:产物尚在本地 .gen,未部署上线——部署需按既有流程上传(含 .wasm.gz 预压缩产物与配套 gzip_static),wasmVersion() 徽标核对后方可视为 0.6.0 上线。

v0.5.2 — 2026-09-30

PDF→OFD 转换质量专项:对 1018 份语料建立单文件全页面逐页严格对拍管线(scripts/pdf2ofd-page-verify.py,与既有抽前 3~5 页的整体评测互补),基于逐页证据定位并修复一批"整页评测分数不敏感但逐页肉眼可辨"的渲染不一致。样本级验证(css4 配色书、issue3584、text_clip_cff_cid、cmyk-jpeg、annotation_hidden_print 等)全部从 bad 修复至与参考渲染一致;全量复跑与基线对比随并行专项(JPX 解码接入)稳定后另行交付。

  • 文本作裁剪(Tr 4~7)语义落地:PDF 文本渲染模式 6/7(填充+描边+裁剪、仅裁剪)此前解析直接报错被跳过,文本按普通填充画出、后续彩色矩形整块盖住版面(css4 配色书页、issue3584 "整页涂蓝")。现字形轮廓按完整变换写入裁剪区,模式 7 的文本本体不再产出绘制对象——"字形颜色的标题/艺术字"版式正确呈现;
  • CID/子集字形映射修复(字符大面积变乱码族):
    • CIDFontType0 的 CFF charset(GID→CID)反查接入,含裸 CFF 包装路径与 OTF 容器路径——转换型打字机/出版字体的 CID↔GID 不再按 Identity 瞎猜;
    • CID-keyed CFF 的 FontMatrix 位于 Font DICT(FDArray 内),此前只读 Top DICT 导致 2000 units 字体按 1000 缩放、字形整体放大 2 倍;
    • 嵌入子集字体查形优先级修正:嵌入面(Unicode cmap → 字节 cmap)先于回退字体——此前回退字体的字形会顶掉嵌入字体自身的正确映射(文本层对、视觉错字);
    • Unicode 查形只查真正的 Unicode 子表:不再用字符码位去巧合命中 (1,0) 字节映射表('F'=0x46 查到字节表第 0x46 项的相邻字形);
    • Type0 混码长 CMap(1/2 字节码并存)按"2 字节命中优先、否则 1 字节"变长切分,替代固定码宽的整段错位;
  • 隐藏注释不再渲染:/F 标志位 Hidden(0x2)/NoView(0x20) 的注释此前照常迁移渲染(参考渲染为空白页而我们画出墨迹),现与 pdf.js/poppler 一致不迁移;OCG 隐藏逻辑不变;
  • CMYK/YCCK JPEG 转换端转码:4 分量 JPEG 此前原样透传,渲染端 jpeg 解码器对 Adobe CMYK/YCCK 的 RGB 转换存在 K 合成缺陷(色块过饱和纯色,cmyk-jpeg 语料 diff 100%)。现转换端用 zune-jpeg 原样解出 4 分量(YCCK 请求原样输出旁路其缺陷路径),按 Adobe 语义自行合成 RGB 后以 PNG 嵌入;带 SMask 的 4 分量图像同管线正确合成透明度;
  • 附带:ofd-cli render --all 目录输出的页序按页号数字排序(此前 _p10 字典序排在 _p2 前,多页文件逐页对比工具会错位配对)。

全 workspace 328 项测试通过(新增回归:Tr 7 字形裁剪、隐藏注释标志、CID Font DICT FontMatrix、JPEG CMYK 探测各 1 项;tests/data/issue-tr7-text-clip.pdf 最小复现件入库)。

补记(2026-10-08,随本版本一并交付):授权服务 v2.4 上线 + 管理台登录优化(examples/license-server,服务端配套组件,内核无变化):授权服务 v2.4(申请选择授权模块、审核后一键构建分发包、双端界面重构,见 v0.4.6 补记)随本次首次部署生产(license.11m18.cn,存量库执行 upgrade-v2.4.sql)。管理台登录页口令框新增明文显示切换(显示/隐藏按钮);/admin 与 /portal 页面响应统一 Cache-Control: no-cache(每次向服务器再验证,页面更新即时生效,不再受浏览器旧缓存干扰);管理端口令最小长度由 8 位放宽至 6 位;新增运维脚本 server/reset-admin-password.mjs(忘记口令时在服务器上直接重置,重置后作废全部会话)。本地冒烟扩至 69 项断言全过。

补记(2026-10-08,代码审查修复,随本版本一并交付):对上述未发布改动做四路分区代码审查(FFI/渲染/字体/脚本),修复全部高优先级发现,全 workspace 341 项测试通过:

  • 注释 /F 位值勘误:NoView 应为 0x20(0x4 是 Print)——按错误位值,生成器默认输出 /F 4 的普通可打印注释会被整体丢弃;
  • Type0 变长切分缺省修正:2 字节码与 1 字节码都无映射证据时按声明码宽(Identity-H 缺省 2 字节)切分——此前缺省 1 字节,Identity-H 无 ToUnicode 的中文文本整段被逐字节错切成乱码;
  • JPX 色彩空间常量对齐上游 openjpeg 枚举(CMYK=5 等,此前臆写值使 CMYK 分支不可达、真 CMYK 图被当 SMaskInData 处理);JPX 的 SMaskInData 不透明度通道改为按 cdef 标注定位(不再假定首位);wasm 下超大图输出缓冲分配失败优雅降级;
  • 字形裁剪接入 clip 守卫管线(Tr 4-7 与路径裁剪共用去重/深度/命令数三层防御,字形用 64k 独立预算)——"重复画字当镂空模板"类病态输入不再能无界撑爆内存;
  • mesh 渐变三处修正:Type 5 lattice 按规范"每行 VerticesPerRow 个顶点、行数由流长决定"解码(此前行列语义颠倒、宽行顶点被静默丢弃);解码加顶点/三角形总量守卫(Type 6/7 最小位宽流约 2400 倍内存放大,超限弃画告警);fill+stroke 双 mesh 底纹不再互相覆盖;
  • 工具修复:pdf2ofd-page-verify.py 改用分带 SSIM(超大页不再整图 float64,O(width) 内存)、断点记录按文件内容寻址(--only 不再误跳)、补"OFD 多出页面"检测方向;pdf2ofd-eval.py SSIM 同步真分带;管理台"最近访问记录"分页步长对齐(limit 20);vue-demo 文档打开统计改会话级 Set 去重(A→B→A 往返不再重复计数);
  • 新增回归测试 8 项(变长切分 4、lattice 宽行、mesh 守卫、clip 守卫、CMYK JPEG 像素断言,样本 tests/data/cmyk-ycck-sample.jpg 入库);is_cff_without_cid 判定结果按面缓存(损坏 charset 不再逐字形放大到分钟级)。

补记(2026-10-08,文档站自身改动,随本版本一并交付):「闭源构建与授权发布」「宝塔面板部署手册」两篇运维文档转为内部交付,不再对外发布——线上文档站不再包含这两页(侧边栏、搜索与 sitemap 同步移除),相关页内入口同步下线,完整内容随授权交付。首页「获取商用授权」按钮改指授权申请站 license.11m18.cn。

v0.5.1 — 2026-09-29

渲染管线两处标准符合性修复(用户报告:intro-数科.ofd 第 9 页渐变进度时间轴整组不可见,WPS 参照正常):

  • Clip 内层 Path Boundary 锚定:国标 &lt;Clips&gt;&lt;Clip&gt;&lt;Area&gt;&lt;Path Boundary="…"&gt; 形式下,裁剪路径数据为该 Boundary 的局部坐标,渲染时须先平移再参与后续变换。此前该属性被解析丢弃,数科 intro 第 9 页负坐标时间轴(对象 Boundary/CTM 左移一个页宽、靠整页矩形裁剪窗锚定)的裁剪窗被推出页外,进度条/圆点/箭头整组渐变对象被裁没;
  • 渐变轴坐标为 Boundary 局部 mm:AxialShd/RadialShd 的 StartPoint/EndPoint 遵循 OFD 子对象坐标惯例(同 TextCode X/Y),渲染时只平移到 Boundary 原点,不乘宿主 CTM。此前误乘 CTM 全量在正坐标模板上因 CTM.e≈Boundary.x 巧合未暴露;负坐标页渐变轴整体出页、进度条退化为平铺终止色。同文件第 8 页时间轴右半段此前实为平色,现恢复全程渐变;
  • 附带:Clips 容器 TransFlag 属性解析与 round-trip(此前整属性丢弃);CLI release 构建产物校准。全 workspace 321 项测试通过(含新增解析 round-trip 与渲染行为回归各 2 项),ofdrw 无渐变渲染能力、此类页面对拍不覆盖,语义依据国标坐标惯例与 WPS 实测裁定。

v0.5.0 — 2026-09-29

OFD→PDF 转换商业化完善(68 份真实语料入库评测驱动,逐项对比 ofdrw/Spire/ofdbox 等方案,详见 docs/business/ofd2pdf-商业化对比与达标线.md):

  • 转换性能:修复字体去重键逐字形重复哈希整份字体字节的缺陷,2 页文档转换从 224 秒降至 0.1 秒;
  • 水印/平铺图案:OFD 平铺图案(数科水印注释等)此前整体丢失,现以 PDF TilingPattern 矢量迁移(瓦片 288dpi 栅格 + 图案空间归一化,45° 旋转水印在 poppler/pdfium 下均正确);
  • 文本层全覆盖:CFF/OTF 字体、描边文本、渐变文本此前轮廓化后不可检索,现一律叠加 PDF 隐形文本层(Tr 3 + ToUnicode,同扫描件 OCR-PDF 形态)——可检索、可复制、位置真实,视觉仍为矢量轮廓(视觉零回归)。数科 intro 手册(42 页)文本召回 0.898→0.9998;
  • CMYK JPEG 防错色:4 组件 JPEG 原字节透传会被阅读器按 RGB 解码导致全黑(数科手册照片页实测),现探测 JPEG 组件数,仅 1/3 组件透传、CMYK 走解码像素重编码;
  • 损坏包降级:DocRoot 目录形式(如 Doc_0)可读;全零字节资源/页内容降级为空资源/空白页并记警告,页数保持 1:1,不再整体失败;
  • CLI:render 命令回退字体与转换命令同源(Windows/macOS/Linux 系统字体自动探测)并新增 --fallback &lt;字体文件&gt; 参数,无嵌入字体文档在非 Windows 环境不再渲染成空白页;
  • rotate 修复:旋转操作的图像 CTM 写出改用 Boundary 局部语义(与真实文件一致),修复旋转后图像丢失;
  • 测试:新增真实语料回归与上述功能的单元测试,全 workspace 314 用例 100% 通过。

补记(2026-09-29,随本版本一并交付):合并后全面回归验证中的修复——CLI render 的 --out 指向已存在目录时,按 &lt;文件名&gt;_p&lt;N&gt;.png 编号写入该目录(修复:此前目录被当作输出文件路径,Windows 下报「拒绝访问」且报错不含路径;现与文档表述一致,附带 CLI 单元测试);输出路径穿越(..)防护与写失败报错带路径。授权服务本地联调启动器 dev-memory 中和 server/.env 中的真实 SMTP 配置——本地冒烟验证码一律开发模式返回,绝不真发邮件(真实 SMTP 连通性测试走 server/test-mailer.mjs);本地全链路冒烟 56 项断言全过。文档站硬编码数字同步 0.5.0(能力页/性能表/评测口径)。

v0.4.6 — 2026-09-29

PDF→OFD 转换质量专项(评测驱动,十一轮迭代):建立 1018 份公开语料(pdf.js、pypdf、pikepdf、chromium 等来源,覆盖中文正文/嵌入子集字体/扫描件/表单/注释/加密等矩阵)的自动化评测管线与商业化达标线(scripts/pdf2ofd-eval.py + docs/business/pdf2ofd-商业化达标线.md),围绕成功率、视觉还原(SSIM)、文本层保留三项硬指标迭代修复。全量口径下:转换成功率 95.0% → 97.9%、panic 保持 0;视觉还原达标(SSIM≥0.95)文档 494 → 517;文本层保留(相似度≥0.9)文档 356 → 432,丢文本层文档 29 → 30;平均分 52.02 → 56.14。主要能力升级:

  • 文本层恢复与反查:嵌入字体 GID→Unicode 反查、BaseFont UTF-16 解码、GBK 全表/GB2312 双字节解码、CID 桩字体按 BaseFont 名反查系统字体(以 /W 宽度校验 ±12% 防错配,宁缺勿错)、ZapfDingbats 符号字体专项——此前转换结果"看着对但复制/检索不出来"的文档大幅减少;
  • 值文本可检索化:表单域/注释的值文本由几何绘制升级为真实 TextCode(Visible=false 语义),输出 OFD 中表单已填值、注释文本可复制可检索;Type3 字体双输出(CharProc 轮廓保形状 + 不可见 TextCode 保检索)、旋转/非均匀缩放文本走轮廓退化时并行输出不可见文本层——文本层完全丢失的形态显著收敛;
  • 图像保真:JBIG2 refinement 与 halftone(Pattern Dictionary + 灰度图 + 图案拼贴)区域解码、CalRGB/CalGray 线性分量 sRGB 编码、JPX 无法解码时输出占位图并记警告(降级而非静默丢内容);
  • 损坏文件解析容错:Catalog/页树三层容错(悬空 /Pages、/Pages 直指 Page、短 xref 表)、截断对象容忍、截断 zlib/ObjStm 部分解压拯救(限修复路径,常规路径保持增量 xref 链干净语义)、内容流逐流容错;修复途中发现的 panic(红线指标)归零;
  • 评测语料库 corpus/(1018 份,129MB)随仓库管理,来源与许可见 corpus/README.md。

补记(2026-09-29,随本版本一并交付):授权服务 v2.3——管理端与申请端全面升级(examples/license-server,服务端配套组件,内核无变化):

  • 管理端改用户名密码登录:首启自动创建管理员(ADMIN_PASSWORD 或随机打印一次),会话 token 仅存 SHA-256 摘要(12h),登录/改密/操作全留痕;原 ADMIN_API_KEY 降级为可选运维/脚本通道;
  • 授权记录全生命周期管理:新增编辑(站点/部署 ID/备注/到期日)、归档(过期/吊销记录移出主列表可还原)、回收站(软删除 → 还原/彻底删除,保留 90 天自动清理;生效中授权须先吊销方可删除);每步动作写 admin_audit 操作审计;
  • 授权日志:新增 license_events 事件流(创建/编辑/续费/吊销/回收站/在线离线签发与拒绝,按授权/事件/通道过滤),与 token 粒度的签发审计互补;
  • 申请提醒与到期预警:新申请即时站内通知(顶栏铃铛 + 待审角标);生效授权 7 天内到期与已过期每日扫描提醒(同一到期日只提醒一次);
  • 申请端(客户端)邮箱验证码登录:图形验证码(一次性)→ 邮箱验证码(6 位/10 分钟/5 次尝试)→ 会话(7 天);发码限流:同邮箱 60s 冷却/每小时 5/每日 10、同 IP 每日 20;申请须登录提交(联系方式自动绑定邮箱),新增「我的申请」;老申请仍可凭编号/邮箱/手机号/域名免登录查询领取;
  • 访问统计:vue-demo 访问打点(POST /api/v1/stats/beacon,匿名)+ 客户端每次在线续期记录 + 申请端登录记录,管理台「访问统计」页提供按天趋势、今日访问、热门路径、活跃授权 Top 榜;vue-demo 构建配置 VITE_OFD_STATS_URL/VITE_OFD_STATS_LIC_NO 即启用打点;
  • 存量库升级执行 upgrade-v2.3.sql(可重复);门户发码需配置 SMTP(SMTP_*),联调可显式开 EMAIL_DEV_MODE=yes;本地全链路冒烟 41 项断言全部通过(examples/license-server/smoke-v2.3.mjs)。

补记(2026-09-29,随本版本一并交付):授权服务 v2.4——申请选择授权模块 + 审核后一键构建分发包 + 双端界面重构(examples/license-server,服务端配套组件,内核无变化):

  • 申请选择 WASM 授权模块:申请端提交时勾选预览核心/格式转换/文档工具(core/convert/tool,至少一项),审批时管理员可再调整;模块组合随授权记录落库并在构建链路透传,scripts/build-dist.mjs 新增 --only 参数支持按模块组合出包;
  • 审核通过直接构建分发包(仅本地实例):管理台对已批准申请一键构建——申请参数自动映射构建配置(trial→测试分发包 / online→正式在线包 / offline→离线激活包;模块→--only;部署 ID→--did;在线授权自动现签 token 与续期地址;--env 双覆盖杜绝混血包),构建任务实时日志、产物 zip 经 /builds/&lt;任务号&gt;/ 下载,构建与领取写授权事件与操作审计;开关 ENABLE_LOCAL_BUILD=yes(生产公网实例保持关闭,构建只在本地/内网实例执行);
  • 管理台与申请端界面全面重构:统一"公文纸墨印"设计语言(暖纸底、衬线标题、票券式编号条、印章状态章),管理台改左侧栏导航(签发/记录/账号分区)+ 大数字统计带;申请端为三步引导(图形验证码 → 邮箱验证码 → 填写申请)+ 授权模块三卡选择;
  • 存量库升级执行 upgrade-v2.4.sql(可重复,为 licenses/applications 补 modules 列);本地全链路冒烟扩展至 52 项断言全部通过。

补记(2026-09-29,随本版本一并交付):文档站全面核验修订——以 packages/ofd/index.d.ts、运行时导出、vue-demo 实际用法与 CLI 源码为准逐页校对:API 参考补齐检索/文本布局/附件/注释/文档工具/OFD/A/签名生成等此前缺失的成员,修正 DocInfo 键名(snake_case)、pageSize 语义与内存释放(显式 free())说明;转换页补 OFD→PDF 方向并修正异步示例;Canvas 指令集修正为 23 种(补 createPattern);验签报告 chainStatus 取值表按实现重写;命令行文档修正密钥参数(hex 而非 PEM)并补 machine-id/subset-font 子命令;能力矩阵、crate 清单(12 个)、体积与测试数等过时数字统一。vue-demo 到期提示文案与内嵌 API 文档同步修正;C 头文件补 ofd_machine_id 声明。

同日 vue-demo 产品化改版(阅读器示例站,随本版本交付):主页重构为商业化落地页——三张功能卡直达核心能力(OFD 预览阅读 / PDF→OFD / OFD→PDF)+ 引擎指标条 + 页脚导航(授权入口在顶栏「授权」按钮与页脚「获取授权」,不设功能卡);「使用文档」从站内弹层改为浏览器新窗口打开线上文档站(缺省 https://ofd.11m18.cn/docs/——本地开发与独立部署的分发包没有 /docs/ 目录,相对路径会 404;自建文档站用 VITE_DOCS_URL 覆盖),站内 WASM 文档弹层移除;阅读器顶栏新增「关闭」按钮(释放文档回主页);获取授权弹窗的文档入口同步指向文档站。

同日 阅读器显示修正(vue-demo + npm 包播放器):打开文档的默认视图由"一律适高"改为按纸型智能选择——竖版公文/报告适宽阅读(宽度撑满、纵向滚动,此前 A4 被压到 435px 宽字小不可读),横版发票等宽纸整页可见且最大化(不再留大片空白);缩放百分比改为物理尺寸口径(相对纸面 96DPI),新增「100%」按钮按真实纸面大小核对发票/印章;可视区右下角新增纸型角标(如 240×160mm · 122%),不同纸型一眼可辨。npm 包 canvas-player.js 增加画布面积上限保护(Safari 单画布 16.7M 像素上限,高倍缩放 × 高 DPR 屏此前会静默渲染成空白页)。另修正侧栏功能 Tab 换行错位(六 Tab 一行放下);主页去功能重复(hero 区不放任何操作按钮,「打开 OFD」唯一入口在功能卡与顶栏,商用授权/使用文档入口收敛到页脚与顶栏);打开文档首帧即按最终缩放渲染(从页面尺寸直接预定初始视图档位,消除"先渲一版小的再放大"的二段闪变)。28 份样例 + PDF 转换产物 + 旋转/翻页全链路实测零空白零报错。

v0.4.5 — 2026-09-28

预览渲染两项标准符合性修复(来自 94 份公开语料与 ofdrw 参考实现的逐页对拍摸底,另见仓库外审计报告):

  • 路径填充缺省语义修正:PathObject 只声明 FillColor 而未写 Fill 属性时,此前会按"有填充色就填"错误填充——电子发票表格网格、椭圆印章等仅靠描边成形的装饰路径被整块涂黑。现按 GB/T 33190 9.6.2 的缺省语义(Fill 缺省即不填充,与 ofdrw、数科阅读器一致)修正:FillColor 只是显式 Fill="true" 时的取色来源,不触发填充。预览渲染与 OFD→PDF 转换共用同一绘制路径,一并生效;回归样本 examples/resources/issue37-fill-default.ofd(另一开源查看器的同类缺陷复现件)纳入测试;
  • 渐变分段位置可选化:AxialShd/RadialShd 的 Segment Position 是国标 8.3.4.2 的可选属性(缺省时首段取 0、末段取 1.0、中间段区间均分),此前按必填解析,遇到不带 Position 的文件(如 XilouReader 的渐变测试样例)直接整页报错拒绝渲染;现在按缺省规则补齐后正常渲出渐变。回归样本 examples/resources/axialshd-no-position.ofd 纳入测试。

v0.4.4 — 2026-09-28

OFD/A 修复的字体查找支持子集前缀回退:此前按声明名精确匹配,遇到 PDF/OFD 常见的子集字体名(如 BAAAAA+KaiTi、DAAAAA+KaiTi)时,系统里不可能存在同名字体,「读取系统字体重试」必然仍报缺字体,只能手工安装改名的特殊字体;现在查找字节来源时自动补剥前缀的基字体名(KaiTi)作为回退候选,装了普通楷体即可完成嵌入:

  • WASM ofdaRepair:候选名取 FontName/FamilyName 原名优先、剥 XXXXXX+ 前缀的基名回退(内核库层新增 font_candidate_names/strip_subset_prefix 辅助函数,原生端调用方可直接复用);文档内的字体声明、检测报告与修复报告仍显示原名,不受影响;
  • collectSystemFonts:按缺失字体名读取系统字体时同步支持前缀剥离——基名参与匹配,命中后该字体名的两个候选一并消除,missing 不再误报;

v0.4.3 — 2026-09-24

OFD/A 归档合规产品化:阅读器「归档」页新增一键合规检测、自动修复另存与校验报告导出(GB/T 42133-2022),浏览器本地完成。此前 ofd-archive 仅是内核里的一个 Rust 库,无任何外部入口;本次补齐三项核心修复能力并接入 WASM 工具模块与网页端:

  • 合规检测:九条规则逐项检查(DocType=OFD-A、单文档、未加密、无权限控制/视图首选项、图像格式白名单、字型全部嵌入、页块嵌套层数、无音视频、无非 Goto 动作),报告含每项通过状态与明细;未嵌入字型直接给出字体名清单,供提示安装;
  • 自动修复:在去技术化(置 DocType、去加密/权限/音视频/脚本动作)之上,新增未嵌入字型的子集化嵌入(按用字文本子集化后写入包内并回填声明,CFF 等无法子集化的字体整包嵌入)、非白名单图像转码(GIF 等可解码格式自动转 PNG,包内缺失的悬空图像引用保留原状并记入报告);
  • 字体来源双通道:原生端(CLI/FFI 场景)Rust 按名直读系统字体目录;浏览器端无文件系统,页面在缺字体时提供「读取系统字体重试」(Chromium Local Font Access,需用户授权),均不可得时明确提示"请先在系统安装字体 X"——不会静默用回退字体替代导致字形走样;
  • 另存与报告:修复产物一键下载 OFD/A 文件;校验报告可下载 JSON(含逐项修复动作明细 + 修复前后两份复检结果),页内同步展示;
  • 新增 WASM API:ofdaCheck(ofd) 与 ofdaRepair(ofd, fonts?)(工具模块,随授权),配套 collectSystemFonts(names) 辅助函数与完整 TypeScript 类型。

已知边界:多文档(DocBody>1)仅处理首个文档(检测会提示 single-doc 不合规);浏览器端字体嵌入依赖用户授权读取系统字体,未授权时仅报告不嵌入。

同版本内的文本选择体验重构(阅读器拖选/复制/检索高亮共用同一文本层):

  • 选区高低不齐修复:此前逐字符盒取"字体度量∪字形边界",假度量字体下汉字盒高、标点/西文盒矮,整行拖选时高亮带参差不齐、标点命中区只有小点;现在内核在出口处做行盒归一——同一视觉行的字符盒不分 run/字体统一为整行行带(按盒间垂直重叠聚类,对发票/扫描版式常见的 0.1~0.4mm 基线抖动不敏感),与浏览器行盒模型和 PDF.js textLayer 一致;
  • 跨行拖选吞行尾修复:命中判定从"矩形∩字符墨迹盒"改为原生光标语义——锚点与光标各定位到(行, 字符边界)两个光标位,两位置之间按阅读序全选:中间行整行选中(拖到下一行时上一行行尾不会被截掉),只有首末行按 x 截取;自右向左/自下而上拖动自动互换首末;
  • 零宽字符盒修复:同位堆叠字形(如发票左侧竖排标签"购买方"逐字同 x 排布)此前被切成 0 宽盒、完全无法选中;现在退用本字形 advance 兜底,横排/竖排分支同修;
  • 窄盒字符修复(逐字定位排版):zsbk 等审计/证照类文档逐字独立定位(每字符一个 TextCode),字符盒宽回退到字体 advance,而子集字体 advance 常为假值(实测 0.08em),选中盒只有 1~2px 宽、与显示字形完全不符;现在行盒归一时做行内宽度补齐——同行字符盒至少延伸到下一字符起点(空隙归属前一字,与原生选区一致),行末字符取行内中位步进,max 保证不收缩已正确的盒;
  • 新增内核文本坐标探针校验口径:pageTextLayout 字符盒与渲染墨迹逐字符对拍(盒内非白墨迹覆盖 + 行带一致性 + 行内大间隙),发票样张 560/560 字符盒全部命中。

同版本内的 PDF→OFD 转换引擎专项修复(三批共 30+ 项,以 146 份标准测试语料对拍验证,同口径渲染对拍 OK 56 → 72):

  • 图形与色彩:Type 4 PostScript 计算器函数完整支持(三角/条件/位运算,DeviceN/Separation 专色按 tintTransform 精确着色,图像色彩空间同样适用);平铺图案(Tiling Pattern)与底纹图案(shading pattern)转换落地,复用渲染端 Pattern 语义,支持周期归一、巨型瓦片退化;图像色键掩码(/Mask)生效;CCITT Group 3(K=0/1)传真图自研解码(此前仅 Group 4);
  • 文本:Type3 字体(CharProc 内容流字形)矢量渲染;非嵌入字体按 /BaseEncoding(WinAnsi/MacRoman)恢复 0x80 以上码位(弯引号/破折号/重音字符此前丢失);
  • 注释与表单:无外观流注释按几何近似绘制(Ink 手写/Line/高亮/下划线/方形/圆/Caret/附件图标/PolyLine/Polygon,含 /BS 线宽与虚线);表单域值文本渲染(/V + /DA 规格,AcroForm /DR 字体解析,复选框矢量勾);FreeText 正文渲染;
  • 透明与软掩码:ExtGState /SMask(/Alpha 软掩码)经离屏栅格化与逐像素合成生效(亮度掩码与非隔离组暂按无掩码基线渲染);
  • 已知边界:JPX/JPEG2000 图像暂不支持(转换时保留占位并告警);亮度型软掩码与非隔离混合组按基线渲染。

补记(2026-09-24,无内核行为变更,不升版本):项目许可变更——rust-ofd 由 MIT OR Apache-2.0 双许可改为专有商业许可(闭源)。根目录新增 LICENSE(专有软件许可协议)与 THIRD-PARTY-NOTICES.md(第三方组件声明,含 vendored pdf fork 的 MIT 声明与字体 OFL 义务),删除原双许可文本;Cargo / npm / Python 包清单同步改为专有标识;文档站页脚、指南「许可证」与授权手册的"开源构建"表述统一改为"无授权校验构建(仅限内部评估,不得对外分发)"。授权申请入口:https://license.11m18.cn。

v0.4.2 — 2026-09-24

网页阅读器(WASM 无系统字体环境)下的字型变换兼容修复——浏览器没有系统字体可扫描,文档只声明字体名(Times New Roman / Tahoma / Latha 等)而不嵌入字体时,此前会丢字、组合形态错误;现在 z.ofd 字型变换示例四页在网页端与数科阅读器观感一致:

  • 连字缺字:多对一变换组(如 fi 连字)在字体缺失整组按字符回退绘制时,被吞字符此前与组首字符叠成一团墨("He affixed" 显示成 "aff xed");现在按前字形自然步进右移补画,偏移不写入笔位(文件增量以连字字形原点为起点,组后字符位置不受影响);
  • 一对多组合形态:声明字体解析不到时变换组此前被整体跳过,泰文 ำ 的"记号+基座"两段字形被画成单个压窄的预组合字形且与前置字符分离;现在变换组布局语义(组首消耗转移、组内转移逐段消费、pitch 收紧豁免)在字体缺失时同样生效,回退按 Unicode 分解形逐段绘制(ำ→◌ํ 记号悬于 า 基座正上方),与数科阅读器形态一致;
  • 一对一变换组回退对齐:编号组回退只画本字符(画整组会让组内每个字符把全组重叠画一遍,intro-数科 "2012" 即此回归;签名字符间距变化亦同步消除),且保持参与格宽收紧,与无变换组的按字符路径行为完全一致;
  • 回退字体链(新 API addFallbackFont):主回退字体之后的按序补位字体(主字体未覆盖的泰文 / 泰米尔等语种依序查找);浏览器环境无系统字体兜底,小语种字形靠链上字体补覆盖。阅读器示例(vue-demo)已内置 Noto Sans Thai / Noto Sans Tamil(OFL 许可,共约 540KB,独立缓存);
  • 阅读器示例 wasm 下载改为 cache: "reload" 强制回源:内核重建后不再命中浏览器磁盘缓存里的旧版本(此前版本号会停在旧内核);
  • 桌面端(有系统字体解析)行为不变:z.ofd 第 2 页逐字节一致;全语料对拍 371 页渲染零差异(其余差异为并行任务重新生成的转换产物源文件变化,与本内核无关)。

v0.4.1 — 2026-09-23

字型变换(CGTransform,GB/T 33190 11.4)渲染修复——以字型变换测试文档 z.ofd 对齐数科裁定,四类变换关系(一对一 / 多对一 / 一对多 / 多对多)全部正确:

  • 字形收紧判定的格宽索引错位:DeltaX 按可见字形序列给值(被吞字符不占项),按字符下标直接取数组会在第一个连字之后错位,把后续字符误判"放不下"压成半宽(连字行 "h"/"n"/"e" 被 0.55/0.78/0.63 压缩;y.ofd、阿拉伯文样本潜伏同类问题一并修复);
  • 变换组改为整组判定:组内字形编号与解析字体全部同源才按编号绘制;不同源(非内嵌字体按名解析到的版本与制件不同)时整组按字符回退,杜绝"半组编号半组回退"的拼接错字(泰文 ำ 两段字形、泰米尔 போ 三段字形被画成乱字即此因);
  • 一对多 / 多对多展开组:组内非首字符不再重复消耗增量(此前越界重复末值使组后字符偏移);整组回退少画的字形补吞等量增量,保持游标对齐;
  • 包内嵌入字体的编号直接采信(不参与同源判定),修复 pdf2ofd 产物含 PUA/未知码位(gid 1)时被误判回退、字形错变;
  • 回退路径补齐每字形实际字符标注(文本层/检索不再把组内第二字形标成组首字符)。

v0.4.0 — 2026-09-23

授权体系 v2.2(授权锚点迁移):

  • 部署标识(Deployment ID)改为编译期烘焙进 WASM(供应商定制构建):授权对"部署"生效而非对"终端浏览器"生效,用户清缓存、换设备均不影响授权;部署即用,续费/换绑通过重新构建交付;
  • 授权面板改为只读展示(移除终端粘贴激活码入口),新增部署标识与是否烘焙展示;
  • 授权服务端配套放宽:offline / trial 授权 hosts 与 did 至少一项即可签发;
  • 版本号 0.3.0 → 0.4.0(workspace / npm 包 / 文档四处同步)。

渲染引擎兼容性批量修复(对齐标准与主流实现裁定):

  • Path 缺省描边语义:无 Stroke 属性按标准缺省勾边(黑),修复模板类文档缺框子/缺下划线;
  • 图像 Clip 在对象 CTM 下按 Boundary 局部坐标解释,修复部分文档图像与印章背景不显示;
  • 位图印章白底抠图(近白像素透明、过渡带渐隐),修复印章白底遮挡正文;
  • Tiling Pattern 平铺填充支持(涂黑/水印类),Pattern 与纯色并存时 Pattern 优先,栅格化失败弃填不回退黑块;
  • 数科模板文档文本:\r\n 占位 DeltaX、多对一字形的增量游标、CGTransform 嵌入门、系统字体按名解析 + 码位覆盖兜底(并子集化嵌入防体积膨胀);
  • 数科注释 Appearance Boundary 宽松解析 + 页块平移烘焙,修复签批/印章跑到页面原点;
  • 支持 GIF 图像;文本层字符盒度量对子集字体假 hhea 做兜底,修复选区/检索定位偏移;
  • 矢量印章 DrawParam 继承不再误触发默认填充,修复百望发票矢量印章整片红。

站点与交付:

  • 文档站(/docs/)与下载中心首次上线;
  • 三缺陷修复:多 &lt;MultiMedias&gt; 容器丢图、检索对空白视图的匹配与偏移映射、隐藏文本层 pageTextLayout(page, includeHidden);
  • 阅读器顶栏与授权面板显示内核版本号;本页(更新日志)上线。

v0.3.0 — 2026-09-22

  • WASM 授权 v2:在线续期 + 离线 IP/CIDR 激活码、test/prod 双密钥、公钥掩码嵌入;
  • 授权服务 license.11m18.cn 上线(Express + MySQL),含管理台 /admin 与申请端 /portal(申请号流程);
  • 完整功能授权分发包三形态(测试 30 天 / 正式在线 / 离线激活码)。

v0.2.0 — 2026-09-22

  • 版本基建:新增 wasmVersion() 查询接口,分发包与线上站可校验内核版本;
  • 分发包 0.2.0 三形态打包与 build-dist 统一构建入口。

v0.1.x — 2026-09 上中旬

引擎全功能首发(早期迭代未逐版留档,列要点):

  • 解析 / 渲染(PNG 位图 + Canvas 指令流)/ 文本提取与检索 / 验签(SM2、SES v1/v4 电子签章)/ 签名 / 加密(SM4-CBC + SM2 数字信封);
  • OFD→PDF 矢量转换、PDF→OFD(文本双模式、页码范围)、合并 / 拆分 / 旋转 / 水印 / 注释增删 / 附件 / OFD 生成全家桶;
  • WASM 拆三模块(core / convert / tool 按需加载)、vue-demo 产品化阅读器、C ABI / C# / Java / Python 绑定、ofd-cli 命令行;
  • JBIG2(含符号字典与 MQ 算术解码)、OFD 字体子集化保真嵌入、73 份真实样例全功能回归。