ReadViewSDK/Doc/标准级EPUB定位与兼容能力开发蓝图.md
shenlei c65c190b71 feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能
- 优化CFI模块,修复代码审查发现的11个问题
- 实现大书远距目录跳转与后台补全优化方案
- 优化设置面板与章节运行时联动
- 重构及大量改进优化
2026-06-22 20:26:34 +08:00

22 KiB
Raw Blame History

标准级 EPUB 定位与兼容能力开发蓝图

最后更新2026-06-18 适用范围:EPUBCore/CFIEPUBCore/NotesEPUBTextRenderingEPUBUI 目标:把当前阅读器从“主流文本书可用”推进到“商业级 EPUB 阅读器必须具备的定位与兼容能力”。


1. 文档目标

本文不是概念方案,而是可直接拆任务、排期、开发和回归的实施蓝图,覆盖三项“必须做”的能力:

  1. EPUB CFI 标准级定位与持久化
  2. 脚注 / 尾注弹层阅读
  3. 嵌入字体回退 + CSS 兼容层

其中 EPUB CFI 按完整版路线执行,核心要求是:

  1. 不依赖 id 也能做章节内精确 DOM 定位
  2. 支持“阅读位置 -> CFI”和“CFI -> 阅读位置”的双向恢复
  3. 在换字号、换字体、换行距、重新分页后仍能稳定恢复
  4. 为高亮、书签、搜索命中、脚注锚点、未来跨设备同步提供统一锚点

2. 当前基线

当前仓库已经有一部分基础设施,不是从零开始:

2.1 已有能力

  1. 已有 RDEPUBLocation.cfi / lastCFI / rangeCFI
  2. 已有 RDEPUBCFIParser / Serializer / Resolver / Generator
  3. 已有 RDEPUBCFIMap 和章节级 marker
  4. 已有基于文本节点路径的初版 cfiMap 构建
  5. 已有脚注检测、解析和弹层 UI 的第一版入口
  6. 已有 CSS 兼容层与字体 fallback resolver 的第一版模型

2.2 当前仍然不够的地方

当前实现更像“标准级路线的 Phase 0.5”,还差以下闭环:

  1. id 节点的 DOM 路径恢复还只是“文本对齐驱动”,未达到标准级双向校准
  2. CFI -> 章内偏移 仍主要依赖单个 marker 命中,缺少区间级、断言级回退链
  3. 还没有持久化“DOM 恢复所需的结构指纹”,章节缓存命中后无法做更强恢复
  4. 脚注弹层还没有完整纳入 CFI 锚点、回跳和高亮链路
  5. CSS 兼容层仍偏“样式修正函数”,还不是完整的兼容策略包
  6. 缺少样书库、诊断报告和标准级回归基线

3. 为什么要做“标准级无 id 节点双向恢复”

3.1 要解决的真实问题

如果只用 href + progressionhref + chapterOffset

  1. 用户改字号、字体、行距、分栏后,位置会漂
  2. 高亮恢复会落到错误字词
  3. 搜索命中重开书后会跳偏
  4. 脚注回跳在长章节里不稳定
  5. 将来做跨设备同步时,同步点不可复用

3.2 做到标准级后的收益

  1. 阅读位置恢复从“章节大致位置”升级到“字符级稳定锚点”
  2. 高亮、批注、书签、搜索、脚注全部共用同一套定位协议
  3. 章节重排后仍能尽可能落到同一语义位置
  4. 对不规范 EPUB 的容错能力显著提升
  5. 后续可以继续扩展到 WebView 路径、CFI 导出、跨端同步

4. 目标能力定义

4.1 CFI 能力分级

L1 基础可用

  1. 能解析 / 序列化单点 CFI 与 Range CFI
  2. 能持久化阅读位置和高亮范围
  3. 对带 id 的节点定位稳定

L2 当前已接近

  1. 能为文本节点生成路径
  2. 能在章节内做初步文本节点映射
  3. 能在部分重排场景下恢复位置

L3 本文目标:标准级

  1. id 节点可精确恢复
  2. 位置恢复有多级回退链
  3. 断言、结构指纹、上下文窗口共同参与校准
  4. 章节缓存可直接携带恢复元数据
  5. 所有消费方统一走 CFI 主链路

5. 总体架构

建议把定位系统拆成四层:

  1. CFI Syntax Layer
  2. DOM Anchor Extraction Layer
  3. Recovery & Calibration Layer
  4. Consumer Integration Layer

5.1 CFI Syntax Layer

职责:

  1. 负责解析、序列化、生成、Range 拼装
  2. 不关心具体渲染结果

对应目录:

  • Sources/RDReaderView/EPUBCore/CFI/

5.2 DOM Anchor Extraction Layer

职责:

  1. 从原始 HTML 建立 DOM 路径
  2. 为文本节点、fragment、note anchor 建立索引
  3. 产出章节级恢复元数据

对应目录:

  • Sources/RDReaderView/EPUBCore/CFI/
  • Sources/RDReaderView/EPUBTextRendering/BuildPipeline/

5.3 Recovery & Calibration Layer

职责:

  1. 输入 CFI,输出稳定的章节字符偏移
  2. 输入字符偏移,输出稳定 CFI
  3. 在 DOM、文本、分页变动时完成校准与回退

对应目录:

  • Sources/RDReaderView/EPUBTextRendering/
  • Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/

5.4 Consumer Integration Layer

职责:

  1. 阅读位置恢复
  2. 高亮 / 批注 / 书签
  3. 搜索命中
  4. 脚注弹层与回跳

对应目录:

  • Sources/RDReaderView/EPUBUI/

6. 核心数据结构

6.1 在现有模型上补强,不推翻

RDEPUBCFIMap

现有结构:

public struct RDEPUBCFIMap: Codable, Equatable {
    public var href: String
    public var markers: [RDEPUBCFIMarker]
    public var textAssertions: [String: RDEPUBCFITextAssertion]
}

建议扩展为:

public struct RDEPUBCFIMap: Codable, Equatable {
    public var href: String
    public var renderVersion: Int
    public var domVersion: Int
    public var markers: [RDEPUBCFIMarker]
    public var textAssertions: [String: RDEPUBCFITextAssertion]
    public var pathRanges: [RDEPUBCFIPathRange]
    public var recoveryMetadata: RDEPUBCFIRecoveryMetadata
}

新增原因:

  1. markers 适合单点命中,但不足以做标准级回退
  2. 需要显式记录“某个 DOM 路径覆盖哪段文本”
  3. 需要缓存 DOM 恢复辅助信息,避免每次重扫 HTML

RDEPUBCFIMarker

建议扩展字段:

public struct RDEPUBCFIMarker: Codable, Equatable {
    public var cfiPath: RDEPUBCFIPath
    public var chapterOffset: Int?
    public var fragmentID: String?
    public var textNodeLength: Int?
    public var textNodeChecksum: UInt64?
    public var normalizedTextPreview: String?
    public var domSiblingSignature: String?
}

用途:

  1. textNodeChecksum 用于节点文本快速比对
  2. normalizedTextPreview 用于短窗口断言
  3. domSiblingSignature 用于路径偏移时的邻接恢复

新增 RDEPUBCFIPathRange

public struct RDEPUBCFIPathRange: Codable, Equatable {
    public var cfiPath: RDEPUBCFIPath
    public var startOffset: Int
    public var endOffset: Int
    public var textNodeLength: Int
}

用途:

  1. 表示某个文本节点在章节纯文本中的覆盖区间
  2. 支撑“按区间查找最近路径”
  3. 支撑 chapterOffset -> nearest CFI path

新增 RDEPUBCFIRecoveryMetadata

public struct RDEPUBCFIRecoveryMetadata: Codable, Equatable {
    public var domFingerprint: String
    public var normalizedTextChecksum: String
    public var tokenIndex: [RDEPUBCFITokenAnchor]
    public var fragmentPathMap: [String: RDEPUBCFIPath]
}

用途:

  1. domFingerprint 判断原始 HTML 是否变化
  2. normalizedTextChecksum 判断章节标准化文本是否变化
  3. tokenIndex 为无 id 恢复提供次级定位锚
  4. fragmentPathMap 保留锚点和脚注入口

新增 RDEPUBCFITokenAnchor

public struct RDEPUBCFITokenAnchor: Codable, Equatable {
    public var token: String
    public var occurrence: Int
    public var chapterOffset: Int
    public var cfiPath: RDEPUBCFIPath
}

用途:

  1. 当文本节点路径不再精确命中时,用稀疏 token 锚做恢复
  2. 对长章节做局部二次定位

7. 标准级无 id 节点双向恢复算法

7.1 正向:阅读位置 -> CFI

输入:

  1. fileIndex
  2. chapterOffset
  3. RDEPUBCFIMap

输出:

  1. 标准 CFI
  2. 如有需要,附带 text assertion

算法顺序:

  1. pathRanges 中查找覆盖 chapterOffset 的文本节点
  2. 计算该节点内的 localOffset
  3. 生成 contentPath + characterOffset
  4. 从前后文提取 assertion
  5. 若该节点不存在,则回退到最近 fragment
  6. fragment 也缺失,则退化为 offset-backed CFI

关键要求

  1. chapterOffset 不能直接依赖页码
  2. assertion 必须来自标准化文本,而不是原始 HTML 片段
  3. 对选区范围,startCFI / endCFI 必须分别独立生成,不能只存一个起点

7.2 逆向CFI -> 阅读位置

输入:

  1. RDEPUBCFI
  2. RDEPUBCFIMap
  3. chapterText

输出:

  1. chapterOffset
  2. 必要时输出 confidence

建议新增恢复结果:

public struct RDEPUBCFIRecoveryResult: Equatable {
    public enum Confidence: Int {
        case exactPath
        case assertionCalibrated
        case siblingRecovered
        case tokenRecovered
        case fragmentFallback
        case offsetFallback
    }

    public var chapterOffset: Int
    public var confidence: Confidence
}

恢复链路必须固定为:

  1. exact-path
    • cfiPath 直接命中 marker 或 pathRange
  2. assertion-calibrated
    • 路径命中后,用 text assertion 微调偏移
  3. sibling-recovered
    • 路径不命中时,用父路径 + 兄弟签名查找邻近文本节点
  4. token-recovered
    • 使用 token anchor 在局部窗口重定位
  5. fragment-fallback
    • 回退到最近 fragment 起点
  6. offset-fallback
    • 使用旧式 chapterOffset 或 progression 兜底

这里的关键不是“某一步一定成功”,而是恢复链必须稳定、可解释、可诊断。


8. 为什么要加“结构指纹 + token 锚”

只做文本节点路径有两个问题:

  1. 某些 EPUB 会在无 id 场景下插入大量包装标签,导致路径整体漂移
  2. 某些章节文本重复度高,单靠短 assertion 可能落到错误位置

所以标准级方案需要两层辅助:

8.1 结构指纹

针对每个文本节点记录:

  1. 父路径
  2. 左右兄弟摘要
  3. 标签名序列
  4. 局部文本 checksum

这样即便 cfiPath 因中间插入一个 wrapper 节点失效,也能在兄弟集合里恢复出最可能目标。

8.2 token 锚

在章节标准化文本中抽样稀疏 token例如

  1. N 个词或每 M 个中文字符窗口抽一个 token
  2. 每个 token 记录其 occurrence、offset 和所属 cfiPath

当路径和断言都不稳时:

  1. 先用 token 锁定大致位置
  2. 再在局部窗口里做文本节点回溯

这就是“无 id 节点也能做标准级恢复”的核心。


9. 分阶段实施

Phase 1结构补强与缓存升级

目标

让章节缓存携带足够的 DOM 恢复元数据,为后续标准级恢复打底。

需要改的模块

  1. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIMap.swift
  2. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIDOMPathBuilder.swift
  3. Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookModels.swift
  4. Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift
  5. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift
  6. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterLoader.swift

具体任务

  1. 扩展 RDEPUBCFIMapRDEPUBCFIMarker
  2. 新增 RDEPUBCFIPathRange
  3. 新增 RDEPUBCFIRecoveryMetadata
  4. 在章节构建阶段产出 pathRanges / tokenIndex / domFingerprint
  5. 升级 chapter summary schema version
  6. 为缓存加版本兼容分支,旧缓存 miss 后自动重建

验收

  1. 新章节缓存可落盘完整 cfiMap
  2. 旧缓存不会引发崩溃
  3. 二次打开时不需要重新扫描整章 HTML 才能恢复定位

Phase 2标准级 CFI -> offset 恢复链

目标

把当前“marker 命中 + chapterOffset 回退”的恢复方式升级为多级恢复链。

需要改的模块

  1. Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift
  2. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterOffsetMap.swift
  3. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIResolver.swift
  4. 建议新增:
    • Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIRecoveryEngine.swift

具体任务

  1. 抽出 RDEPUBCFIRecoveryEngine
  2. 输出 RDEPUBCFIRecoveryResult
  3. 实现六级恢复链
  4. 为每次恢复产出 confidence
  5. 为 diagnostics 埋点:
    • exact-path 命中率
    • assertion 校准命中率
    • token 恢复命中率
    • offset 回退比例

验收

  1. id 文本节点可稳定恢复
  2. 换字号 / 字体 / 行距后,阅读位置恢复成功率显著提升
  3. 高亮与搜索命中恢复不再大面积偏移

Phase 3标准级 offset -> CFI 生成链

目标

让所有阅读位置、选区、高亮和搜索结果都优先生成标准文本节点 CFI。

需要改的模块

  1. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIGenerator.swift
  2. Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift
  3. Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift
  4. Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift
  5. Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift
  6. Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift

具体任务

  1. chapterOffset -> pathRange 精确映射
  2. 统一单点 CFI 与范围 CFI 生成
  3. 所有消费方优先写入 cfi/rangeCFI
  4. 保留 fragment/progression 作为兜底兼容字段

验收

  1. 书签、新建高亮、搜索命中都能生成标准 CFI
  2. 相同文本范围在重新分页后仍能正确恢复

Phase 4脚注 / 尾注弹层闭环

目标

把脚注从“跳出当前阅读流”改为“就地预览 + 可回跳”。

需要改的模块

  1. Sources/RDReaderView/EPUBCore/Notes/RDEPUBNoteDetector.swift
  2. Sources/RDReaderView/EPUBCore/Notes/RDEPUBNoteResolver.swift
  3. Sources/RDReaderView/EPUBCore/Notes/RDEPUBNoteModels.swift
  4. Sources/RDReaderView/EPUBUI/Notes/RDEPUBNotePopupCoordinator.swift
  5. Sources/RDReaderView/EPUBUI/Notes/RDEPUBNotePopupViewController.swift
  6. Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift

具体任务

  1. 完善脚注链接识别:
    • epub:type=noteref
    • role=doc-noteref
    • href=#footnote-*
    • 章节内 / 跨章节 note link
  2. note target 解析后落为 CFI
  3. 弹层展示注释正文,而不是强制整章跳转
  4. 弹层内支持:
    • 查看上下文
    • 跳到原文
    • 跳到注释原位置
  5. 建立“入口位置 CFI -> 弹层 -> 回跳位置 CFI”的闭环

验收

  1. 点击脚注不打断当前阅读主流程
  2. 长注释可滚动
  3. 关闭弹层后能回到点击时的位置
  4. 跨章节尾注也能稳定打开

Phase 5样式兼容层与字体回退闭环

目标

减少“某些 EPUB 打开排版异常”的商业级缺陷。

需要改的模块

  1. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBStyleCompatibilityModels.swift
  2. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBCSSCompatibilityLayer.swift
  3. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBFontFallbackResolver.swift
  4. Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBStyleSheetComposer.swift
  5. Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBHTMLNormalizer.swift
  6. Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBRenderDiagnosticsCollector.swift

具体任务

  1. 建立 CSS 兼容策略包:
    • 非法 font-family 归一化
    • 极端 line-height 修正
    • 过大 margin/padding 收口
    • 表格 / 图片 / SVG 溢出保护
    • 嵌套 white-space 异常归一化
  2. 建立字体 fallback 链:
    • 书内嵌入字体
    • 用户选中字體
    • 语言 fallback
    • 系统兜底字体
  3. diagnostics 输出:
    • 缺失字体
    • 被修正的 CSS 规则
    • 可能导致排版异常的资源

验收

  1. 缺字库 EPUB 不崩溃、不出现大面积 tofu
  2. 非法 CSS 不导致正文不可读
  3. diagnostics 可定位问题书源

10. 关键实现细节

10.1 DOM 路径构建规则

  1. 元素节点使用偶数 step
  2. 文本节点使用奇数 step
  3. 忽略注释节点、doctype、处理指令
  4. script/style 默认不参与文本定位
  5. rubyrtrp 需要单独定义标准化策略,避免正文偏移

10.2 标准化文本规则必须固定

以下规则必须全链路共用同一份实现,否则 CFI 会漂:

  1. HTML entity 解码
  2. 连续空白压缩
  3. 换行归一化
  4. 零宽字符处理
  5. nbsp 处理
  6. 附件占位字符策略
  7. 中文全角 / 半角是否归一

建议把规则集中到一个入口,避免 BuilderSearchSelection 各写一套。

10.3 renderSignaturedomFingerprint 分工

  1. renderSignature
    • 描述“分页语义”
    • 用于判定页图、chapter summary、layout cache 是否可复用
  2. domFingerprint
    • 描述“章节 DOM 结构”
    • 用于判定 cfiMap 是否可复用

这两个概念不能混用。

10.4 本地缓存策略

建议缓存分三层:

  1. raw parse cache
    • OPF、manifest、spine、resource bytes
  2. chapter summary cache
    • 章节文本、fragmentOffsets、cfiMap、pathRanges、tokenIndex
  3. page map / runtime cache
    • 与具体排版参数绑定

原则:

  1. 字号、字体、行距变化时,第三层必须失效
  2. 第二层尽量复用
  3. cfiMap 属于第二层,不应因单纯重排被清空

11. 文件改动清单

11.1 必改文件

CFI 核心

  1. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIMap.swift
  2. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIDOMPathBuilder.swift
  3. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIGenerator.swift
  4. Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIResolver.swift
  5. 新增 Sources/RDReaderView/EPUBCore/CFI/RDEPUBCFIRecoveryEngine.swift

构建与缓存

  1. Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookModels.swift
  2. Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift
  3. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterLoader.swift
  4. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift

定位消费方

  1. Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift
  2. Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift
  3. Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
  4. Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift
  5. Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextContentView.swift
  6. Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift
  7. Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.swift
  8. Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterOffsetMap.swift
  9. Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderLocationCoordinator.swift

脚注

  1. Sources/RDReaderView/EPUBCore/Notes/RDEPUBNoteDetector.swift
  2. Sources/RDReaderView/EPUBCore/Notes/RDEPUBNoteResolver.swift
  3. Sources/RDReaderView/EPUBUI/Notes/RDEPUBNotePopupCoordinator.swift
  4. Sources/RDReaderView/EPUBUI/Notes/RDEPUBNotePopupViewController.swift

样式兼容

  1. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBStyleCompatibilityModels.swift
  2. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBCSSCompatibilityLayer.swift
  3. Sources/RDReaderView/EPUBTextRendering/Typesetter/Compatibility/RDEPUBFontFallbackResolver.swift
  4. Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBStyleSheetComposer.swift

11.2 建议新增测试

  1. ReadViewDemo/ReadViewDemoTests/CFI/RDEPUBCFIRecoveryEngineTests.swift
  2. ReadViewDemo/ReadViewDemoTests/CFI/RDEPUBCFITextNodeMapBuilderTests.swift
  3. ReadViewDemo/ReadViewDemoTests/Notes/RDEPUBNoteResolverTests.swift
  4. ReadViewDemo/ReadViewDemoTests/Compatibility/RDEPUBCSSCompatibilityLayerTests.swift
  5. ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LocationPersistenceTests.swift
  6. ReadViewDemo/ReadViewDemoUITests/ReaderUITests/FootnotePopupTests.swift

12. 回归样书与验收用例

至少准备以下样书:

  1. 标准文本 EPUB节点 id 完整
  2. id 文本 EPUB
  3. 重复文本很多的长章节 EPUB
  4. 脚注密集型 EPUB
  5. 嵌入字体缺失或损坏 EPUB
  6. CSS 激进覆盖型 EPUB
  7. 跨章节尾注 EPUB

关键验收:

  1. 阅读到某章第 5 页,改字号后仍落在同一阅读语义位置
  2. 高亮一段正文,改字体后还能精确回到同一段
  3. 搜索命中结果重开书后可稳定定位
  4. 点击脚注弹层展示,不强制整章跳转
  5. 关闭脚注后回到点击前位置
  6. 不规范 CSS 的书籍仍可阅读
  7. 缺失嵌入字体时能稳定 fallback

13. 性能与风险要求

性能目标

  1. 单章节 cfiMap 构建增量耗时 P50 < 12msP95 < 35ms
  2. CFI -> offset 恢复耗时 P50 < 2msP95 < 8ms
  3. 二次打开时不因 CFI 恢复而重新解析整本书

主要风险

  1. 章节标准化文本规则不统一,导致生成和恢复不一致
  2. token 锚过密,导致缓存膨胀
  3. 断言窗口过短,在重复文本章节误命中
  4. 兼容层修正规则过激,伤及正常书籍排版

对应策略:

  1. 文本标准化统一收口
  2. token 锚做稀疏抽样并设章节上限
  3. diagnostics 输出恢复置信度
  4. CSS 修正规则全部可开关并支持样书回归

14. 推荐排期

建议按 4 个迭代执行:

  1. 第 1 迭代
    • Phase 1
    • Phase 2
  2. 第 2 迭代
    • Phase 3
    • 核心位置恢复 UI 回归
  3. 第 3 迭代
    • Phase 4
    • 脚注体验打磨
  4. 第 4 迭代
    • Phase 5
    • 样书库、diagnostics、稳定性回归

15. 最终交付定义

完成本蓝图后,阅读器至少应达到以下标准:

  1. 阅读位置、高亮、书签、搜索命中全部优先基于标准 CFI
  2. id 节点的 EPUB 仍能做精确字符级恢复
  3. 脚注 / 尾注不再破坏主阅读流
  4. 字体缺失和异常 CSS 不再轻易把正文排坏
  5. 章节缓存、页图缓存、定位缓存职责清晰,二次打开可复用

这时阅读器才算从“功能可用”进入“商业级可发布”的基线。