ReadViewSDK/Doc/阅读器功能开发计划.md
shen 948004eed1 docs: 补充注释、修正过时文档、清理重复内容
源码注释:
- 为 ~60 个 Swift 文件补充缺失的 doc comment(file header、类型、属性、方法)
- 修正 4 处错误注释:翻页模式数量、搜索行为描述、手势识别器描述、悬空文档块

文档维护:
- 删除重复文档:WXRead/读书EPUB阅读器实现架构.md(与微信读书版完全一致)
- 合并重叠文档:阅读器规划.md → 阅读器功能开发计划.md(单一真值)
- 修正过时内容:所有文档中"四种翻页模式"→"三种",移除 horizontalCoverScroll
- 更新架构图:补齐 EPUBUI/ReaderController、Paging/、Typesetter/ 等子目录
- 更新 index.md 索引:新增开发计划和架构对比文档引用
2026-06-01 09:33:23 +08:00

25 KiB
Raw Blame History

阅读器功能开发计划

本文档整合了渲染质量三方对比ReadViewSDK vs WXRead与功能开发计划作为阅读器能力演进的单一真值。 基于读书 v10.0.3 逆向分析,与当前 ReadViewSDK 代码核查结果整合。

背景

渲染内核的架构对齐解决的是"代码可维护性"问题。本文档梳理从"能用的阅读器"到"能上架的商业阅读器"还需要补齐哪些能力,每项能力标注三方状态和具体实施计划。

三方对比总览(渲染质量)

缺失项 严重度 ReadViewSDK WXRead 差距说明
竖排文字 未实现 未实现 双方均无 writing-mode 支持
Ruby 注音 未实现 未实现 双方均无 <ruby>/<rt> 处理
数学公式 未实现 ⚠️ 仅字体回退 非原生渲染范畴,需 WebView 回退
复杂图文分页质量 已实现 已实现 双方均已实现
字体选择器 已实现 ⚠️ 管线完备UI 未见 已支持四档字体选择
连字/断字 ⚠️ 仅配置标记 ⚠️ 仅属性声明 双方均未实际实现
多语言排版回退 ⚠️ 语言检测有,简繁转换无 已实现 WXRead 额外有简繁转换

ReadViewSDK 领先 WXRead 的项

说明
keepWithNext 已实现 trimmedRangeForKeepWithNextWXRead 反编译代码中未找到对应实现

不需要对标 WXRead 的项

以下项 WXRead 自身也未实现不属于必须补齐的能力竖排文字、Ruby 注音、数学公式、Hyphenation 断字。


一、渲染质量

1. 字体选择器

当前状态 基础能力已完成。

已支持系统、宋体、圆体、等宽四档字体选择;设置项可持久化;切换字体会触发重新分页;分页缓存签名已包含字体信息,避免不同字体复用旧缓存。

已落地文件清单

文件 改动 状态
RDEPUBReaderConfiguration.swift 新增 RDEPUBReaderFontChoice,配置增加 fontChoice 已完成
RDEPUBReaderSettings.swift 新增 fontChoice 持久化,更新 applying(to:)capture(configuration:brightness:) 已完成
RDEPUBReaderContext.swift currentTextRenderStyle() 改用 configuration.fontChoice.font(ofSize:) 已完成
RDURLReaderController.swift URL 阅读器同步使用 fontChoice 生成文字样式 已完成
RDEPUBReaderController+RuntimeBridge.swift requiresRepagination(from:to:) 增加 fontChoice 变更检查 已完成
RDEPUBPaginationCacheCoordinator.swift 分页缓存签名增加字体名 已完成
RDEPUBReaderSettingsViewController.swift 新增 onFontChoiceChange 回调、字体分段控件和 accessibility identifier 已完成
RDEPUBReaderChromeCoordinator.swift 设置面板接线字体变更回调 已完成
SettingsPanelTests.swift UI 自动化覆盖字体选择 已完成

当前验收结果

  • 切换字体后触发重排。
  • 字体选择可随阅读器设置持久化。
  • 分页缓存按字体隔离。
  • UI 自动化已覆盖设置面板字体选择。

后续增强计划

  1. 引入更多内置字体包建议思源宋体、思源黑体、方正书宋、方正兰亭黑、Lora、OpenDyslexic
  2. 使用 CTFontManagerRegisterFontsForURL 注册 bundle 字体。
  3. 字体列表从枚举升级为资源驱动模型,每项包含 displayNamefontNamepreviewText、可用性状态。
  4. 字体选择 UI 从分段控件升级为横向预览卡片,展示真实字体效果。
  5. 增加字体资源加载失败兜底,失败时回退系统字体并保留用户设置。

2. 暗色模式图片处理

当前状态 基础能力已完成。

暗色主题下会对正文图片做显示副本调暗,降低白底图片在深色背景上的刺眼程度。处理只作用于展示层,不修改分页原始内容;封面和小图会跳过,避免误处理封面、图标和装饰图。

已落地文件清单

文件 改动 状态
RDEPUBReaderConfiguration.swift 新增 darkImageAdjustmentEnableddarkImageBlendRatio 已完成
RDEPUBReaderController+RuntimeBridge.swift 主题和暗色图片配置变更触发可见页刷新 已完成
RDEPUBTextContentView.swift DTCoreText 展示内容增加暗色图片显示副本处理 已完成
RDEPUBTextContentView.swift 增加暗色图片缓存,避免重复生成 已完成
SettingsPanelTests.swift UI 自动化覆盖暗色主题切换 已完成

当前实现策略

  • 仅当背景亮度低于阈值、配置开启、混合比例大于 0 时启用。
  • 仅处理正文 inline image attachment。
  • 跳过封面和小于 80x80 的图片。
  • 使用 NSCache 缓存处理后的图片。
  • 使用主题背景色按比例覆盖原图,透明区域保持透明。

当前验收结果

  • 暗色模式下图片不再完全以原始亮色块直出。
  • 图片内容仍保持可辨认,不做反色。
  • 处理结果有缓存。
  • UI 自动化已覆盖暗色主题入口。

后续增强计划

  1. 增加真实 EPUB 图片样本的截图回归测试。
  2. 按图片平均亮度自适应 darkImageBlendRatio
  3. 增加用户侧开关,允许关闭暗色图片处理。
  4. 如果后续恢复 WebView 路径,再补充 CSS filter 策略。

3. 简繁转换

当前状态:零实现。语言元数据已提取(publication.metadata.language),但仅用于拉丁/CJK CSS 分轨。

WXRead 实现参考

  • WREpubTypesetter 检测 book.languageHant/TW/HK 时调用 _WRConvertHansToHantIfNeeded()
  • 使用 CFStringTransform 两步转换Hans → Latin → Hant

实施步骤

Step 1转换工具

  1. 新增 RDEPUBTextChineseConverter.swift,提供:
    enum RDEPUBChineseScript { case hans, hant }
    
    func convert(_ text: String, to script: RDEPUBChineseScript) -> String
    
  2. 实现使用 CFStringTransform
    let mutable = NSMutableString(string: text) as CFMutableString
    CFStringTransform(mutable, nil, kCFStringTransformToLatin, false)  // Hans → Pinyin
    CFStringTransform(mutable, nil, kCFStringTransformStripDiacritics, false) // Pinyin → stripped
    // 然后通过字典映射到繁体
    
    或直接使用 Apple 的 kCFStringTransformHansToHant(如果可用)。

Step 2判断是否需要转换 3. 在 RDEPUBTextBookBuilder.build()RDEPUBTextRendererSupport.makeChapterRenderRequest() 中:

  • 读取 publication.metadata.language
  • 如果语言为 zh-Hant/zh-TW/zh-HK 且用户设置为简体,或语言为 zh-Hans/zh-CN 且用户设置为繁体,触发转换
  1. 新增 RDEPUBReaderConfiguration.chineseScript: RDEPUBChineseScript?nil = 跟随书籍)

Step 3应用转换 5. 在 normalizeReadingAttributes(in:style:) 之后、返回 RDEPUBTextChapterRenderRequest 之前,对 attributed string 的文本内容执行转换 6. 需要保持 attributed string 的属性(字体、颜色、附件)不变,只替换字符

验收标准

  • 简体 EPUB 在繁体模式下显示繁体
  • 繁体 EPUB 在简体模式下显示简体
  • 转换不影响分页结果(转换后字符数可能变化,需验证)
  • 高亮、搜索功能在转换后仍然正常

4. 孤行/寡行控制

当前状态RDEPUBTextLayoutConfig 声明了 avoidOrphans/avoidWidows(默认 trueRDEPUBTextLayouter 完全不读取这两个标记。

实现原理

  • 寡行widow:段落最后一行单独出现在页底 → 应将该行拉到下一页
  • 孤行orphan:段落第一行单独出现在页首 → 应将前一页最后一行拉过来
  • 通常只处理寡行(保留至少 2 行在页底),孤行处理会引发连锁重排

实施步骤

Step 1CoreText 路径

  1. RDEPUBTextLayouter.layoutFramesUsingCoreText() 的分页循环中(~行 63-137adjustedRange 之后、追加 frame 之前,增加寡行检查:
    if config.avoidWidows {
        finalRange = trimmedRangeForAvoidWidows(
            from: ctFrame,
            proposed: finalRange,
            lineRanges: lineRanges
        )
    }
    
  2. trimmedRangeForAvoidWidows 实现:
    • 从 proposed range 的最后一行向前检查
    • 如果最后一行是一个段落的最后一行,且该段落在此页只有 1 行 → 回退到上一个段落边界
    • 最多移除 2 行(避免过度收缩)

Step 2DTCoreText 路径 3. 在 layoutFramesUsingDTCoreText() 中增加相同逻辑

Step 3段落边界检测 4. 利用已有的 paragraphRange(containing:) 方法(NSString.paragraphRange)获取段落范围 5. 对比当前页最后一行的 range 和段落的 range判断是否为段落唯一一行

验收标准

  • 分页页数可能轻微增加1-3 页),但排版质量提升
  • 已知的寡行问题页在修复后不再出现段落最后一行孤悬页底
  • avoidOrphans/avoidWidows = false 时行为不变

5. Hyphenation 断字

当前状态hyphenation: Bool = true 配置标记已声明,但 NSParagraphStyle.hyphenationFactor 从未设置。CoreText 路径更不支持断字。

技术约束

  • NSParagraphStyle.hyphenationFactorUITextView(降级路径)有效
  • CoreText 直绘路径需要设置 kCTParagraphStyleSpecifierHyphenationFactor
  • 中文场景断字影响较小(中文无空格分词),主要改善西文排版

实施步骤

Step 1UITextView 降级路径

  1. RDEPUBTextRendererSupport.paragraphStyle(lineSpacing:)(行 929-934中增加
    style.hyphenationFactor = 1.0
    
  2. normalizeReadingAttributes()(行 137-143对已有的 NSMutableParagraphStyle 增加:
    paragraphStyle.hyphenationFactor = 1.0
    

Step 2CoreText 直绘路径 3. 在 RDEPUBTextLayouter 的 frame 构造中,对 CTParagraphStyle 增加 hyphenation factor

var hyphenationFactor: Float = 1.0
let settings = [CTParagraphStyleSetting(
    spec: .hyphenationFactor,
    valueSize: MemoryLayout<Float>.size,
    value: &hyphenationFactor
)]
let paragraphStyle = CTParagraphStyleCreate(settings, settings.count)
  1. 将此 paragraph style 设置到 attributed string 的段落属性上

Step 3条件控制 5. 读取 config.hyphenation 标记,为 false 时跳过上述设置

验收标准

  • 西文长单词在行末正确断字(显示连字符)
  • 中文排版不受影响
  • hyphenation = false 时回到当前行为

二、工程成熟度

6. 自动化测试

当前状态 UI 自动化测试基础设施已完成,单元测试和分页回归基准待补。

已在 Demo 工程中接入 ReadViewDemoUITests,覆盖打开书籍、阅读器基础交互、顶部/底部工具栏、设置面板、字体选择和暗色主题切换。

已落地内容

状态
UI Test target ReadViewDemoUITests 已完成
Demo 自动打开测试 EPUB 的入口 已完成
Accessibility identifier 体系 已完成
阅读器基础打开测试 已完成
顶部/底部工具栏测试 已完成
设置面板测试 已完成
字体选择 UI 测试 已完成
暗色主题切换 UI 测试 已完成

当前验证结果

xcodebuild build: BUILD SUCCEEDED
SettingsPanelTests: TEST SUCCEEDED
ReadViewDemoUITests 全量 8 个 UI 测试: TEST SUCCEEDED

下一步实施步骤

Step 1单元测试基础设施

  1. ReadViewDemo.xcodeproj 中新增 Unit Test target ReadViewSDKTests
  2. 创建 Tests/ 目录,配置 import RDReaderView 或通过 Demo target 暴露内部测试入口
  3. 新增 .xctestplan 配置,把 UI 测试和单元测试拆分为不同测试组

Step 2分页回归测试最高优先级 4. 新增 RDEPUBPaginationRegressionTests.swift,固定 6 类样本章节:

  • 纯正文章节
  • 图 + 图注 + 正文章节
  • 脚注小图标章节
  • 多段标题章节
  • 大图跨页章节
  • 长段落连续章节
  1. 每个样本:构建 RDEPUBTextBook,断言总页数不变、已知页的 contentRange 不变
  2. 使用 golden file 对比 RDEPUBTextChapterPaginationDiagnostic 输出

Step 3位置映射测试 7. 新增 RDEPUBLocationMappingTests.swift

  • location → pageNumber 正向映射
  • pageNumber → location 逆向映射
  • 往返一致性断言

Step 4渲染管线测试 8. 新增 RDEPUBTypesetterTests.swift

  • normalizeHTML 输入输出对比
  • fragment marker 注入/提取一致性
  • 语义标记注入后 rdPage* 属性计数不变

Step 5CI可选 9. 配置 GitHub Actions 或 Jenkins在 PR 时自动运行测试

验收标准

  • UI 自动化全量测试稳定通过
  • 6 个分页基准样本的页数和 contentRange 稳定
  • 位置映射往返测试通过
  • 新 PR 触发测试自动运行

7. 性能基线

当前状态RDEPUBTextPerformanceSamplerCFAbsoluteTimeGetCurrent() 采样,结果仅 print() 输出。无 Instruments 可视化、无内存监控、无阈值告警。

实施步骤

Step 1os_signpost 集成

  1. RDEPUBTextPerformanceSampler 中引入 os.signpost
    import os.signpost
    let log = OSLog(subsystem: "com.rdreader", category: .pointsOfInterest)
    
  2. 在 build/render/paginate 各阶段插入 os_signpost(.begin, ...) / os_signpost(.end, ...)
  3. 这样 Instruments 的 Points of Interest 能直接可视化各阶段耗时

Step 2内存监控 4. 新增 RDEPUBMemoryMonitor

func currentMemoryFootprint() -> UInt64 // task_info.resident_size
  1. RDEPUBTextBookBuilder.build() 的每章循环中记录内存峰值
  2. RDEPUBTextPerformanceSample 中增加 peakMemoryBytes: UInt64

Step 3阈值告警 7. 定义基准值:首屏 < 2s、单章渲染 < 500ms、全书构建 < 30s按书的大小可调 8. 超出阈值时通过 os_log(.error, ...) 输出警告 9. 在 Demo 中展示性能摘要面板

Step 4持久化 10. 性能样本可选持久化到文件,供回归对比

验收标准

  • Instruments 能看到各阶段的 signpost 区间
  • 内存峰值在大书79MB 样本)构建过程中有记录
  • 超出阈值时有日志输出

8. 崩溃防护

当前状态RDReaderView 有 pageCurl 崩溃检测和异步恢复,但无全局异常捕获。

实施步骤

Step 1NSException 捕获

  1. 新增 RDEPUBCrashGuard 工具类:
    static func performSafely(_ block: () throws -> Void) rethrows
    static func performWithObjCExceptionHandling(_ block: () -> Void) -> Bool
    
  2. 在关键入口包裹 @try/@catch
    • RDEPUBTextBookBuilder.build() 的每章循环体
    • RDEPUBTextLayouter.layoutFrames() 的分页循环
    • RDEPUBTextRendererSupport.makeChapterRenderRequest() 的 HTML 处理

Step 2分页/渲染单章隔离 3. 单章渲染/分页失败时,降级到空白页或上一次缓存结果,不中断全书构建 4. 在 RDEPUBTextChapterPaginationDiagnostic 中记录错误状态

Step 3全局信号处理可选 5. 注册 NSSetUncaughtExceptionHandler 记录 ObjC 异常 6. 注册 signal handlerSIGABRT, SIGSEGV记录崩溃现场 7. 下次启动时上报(如果后续有上报系统)

验收标准

  • 单章渲染异常不导致整个 build 中断
  • pageCurl 崩溃场景已有保护,验证不退化
  • 崩溃日志可追踪到具体章节和阶段

9. 内存管理

当前状态:无显式内存预算。无 didReceiveMemoryWarning 处理。RDEPUBTextBookCache 磁盘缓存无大小限制。

实施步骤

Step 1内存警告响应

  1. RDEPUBReaderController 中监听 UIApplication.didReceiveMemoryWarningNotification
  2. 收到警告时:
    • 清空 RDReaderPreloadController 的预加载缓存
    • 通知 RDEPUBTextBookCache 的内存缓存(如果有)清空
    • 释放非当前可见章节的渲染结果

Step 2章节级内存释放 3. 在 RDEPUBTextBookBuilder 构建完成后,不再持有已构建章节的 NSAttributedString 4. 当前只持有 RDEPUBTextBook(包含 [RDEPUBTextPage] 的 NSRange内存占用已较低 5. 如果后续引入 attributed string 缓存,需增加 LRU 淘汰策略

Step 3图片内存控制 6. RDReaderPreloadController 预加载的 page view 数量与内存挂钩 7. 当内存压力时减少 preloadRadius(从 1 降到 0

验收标准

  • 在大书79MB构建过程中收到内存警告时app 不被系统杀掉
  • 预加载缓存在内存压力下自动收缩
  • 当前页内容不丢失

10. 增量构建

当前状态:全书一次性分页。RDEPUBTextBookBuilder.build() 遍历全部 spine item输出完整 RDEPUBTextBook

实施步骤

Step 1章节级构建接口

  1. RDEPUBTextBookBuilder 上新增:
    func buildChapter(
        at spineIndex: Int,
        publication: RDEPUBPublication,
        parser: RDEPUBParser,
        pageSize: CGSize,
        style: RDEPUBTextRenderStyle
    ) -> RDEPUBTextChapter?
    
  2. 内部逻辑从 build() 的循环体中提取,单章可独立构建

Step 2按需加载 3. RDEPUBReaderLoadCoordinator 在打开书时只构建当前章节和相邻 ±1 章 4. 用户翻到新章节时,后台异步构建该章节 5. 使用 DispatchQueue 串行队列保证线程安全

Step 3与缓存联动 6. RDEPUBPaginationCacheCoordinator 支持单章缓存命中检查 7. 缓存命中时跳过构建,直接加载

验收标准

  • 大书首次打开时间缩短(只构建 3 章而非全书)
  • 翻到未构建章节时无明显卡顿(后台预构建)
  • 全书构建 API 保持不变(兼容现有调用方)

11. 缓存管理

当前状态RDEPUBTextBookCache 磁盘缓存无大小限制、无淘汰策略、无选择性失效。无内存缓存。

实施步骤

Step 1磁盘缓存容量控制

  1. 新增 maxDiskCacheSize: UInt64(默认 200MB
  2. save(_:key:) 写入后检查总大小
  3. 超限时按修改时间删除最旧的缓存文件,直到低于阈值

Step 2选择性失效 4. 新增 invalidate(key:) 方法,删除指定缓存文件 5. 当用户修改 fontChoicefontSize 时,只失效当前书的缓存(而非 invalidateAll

Step 3内存缓存NSCache 6. 在 RDEPUBTextBookCache 上层增加 NSCache<NSString, PaginationCacheArchive> 7. load(key:) 先查内存缓存miss 后查磁盘并回填 8. 内存警告时清空 NSCache

Step 4缓存统计持久化 9. RDEPUBTextBookCache 新增 cacheStats 属性,记录总命中/缺失次数 10. 可用于调试和性能分析

验收标准

  • 磁盘缓存不超过设定上限
  • 修改字号/字体后,旧缓存被正确失效
  • 连续打开同一本书时,第二次命中内存缓存(< 1ms

三、可访问性

12. VoiceOver

当前状态9 个 accessibilityIdentifier(仅用于 UI 测试),无 accessibilityLabelaccessibilityHintaccessibilityTraits。阅读内容区域无任何无障碍支持。

实施步骤

Step 1工具栏无障碍最低门槛

  1. RDEPUBReaderTopToolView
    • backButton.accessibilityLabel = "返回"
    • bookmarkButton.accessibilityLabel = "书签" + accessibilityValue 反映当前状态(已添加/未添加)
    • titleLabel.accessibilityLabel = 书籍标题
  2. RDEPUBReaderBottomToolView
    • 各按钮补充 accessibilityLabel"目录"、"书签列表"、"高亮列表"、"添加高亮"、"设置"
    • 进度 slider 补充 accessibilityValue"第 X 页,共 Y 页"

Step 2设置面板 3. RDEPUBReaderSettingsViewController 所有控件补充 label 和 traits

Step 3阅读内容区域中等难度 4. RDEPUBTextContentView 设置 isAccessibilityElement = true 5. accessibilityLabel = 当前页纯文本内容 6. 翻页时发出 UIAccessibility.pageScrolledNotification

Step 4目录和高亮列表 7. RDEPUBReaderChapterListController 列表项设置 accessibilityLabel(章节标题 + 页码) 8. RDEPUBReaderHighlightsViewController 列表项设置 accessibilityLabel(高亮文本 + 章节)

验收标准

  • VoiceOver 用户能完整导航工具栏
  • 翻页时 VoiceOver 朗读新页内容
  • 目录和高亮列表可被 VoiceOver 逐项朗读

13. Dynamic Type

当前状态:字号由 RDEPUBReaderConfiguration.fontSize 控制,不响应系统 UIContentSizeCategory 变化。

实施步骤

  1. RDEPUBReaderSettingsViewController 中监听 UIContentSizeCategory.didChangeNotification
  2. 当系统字体大小变化时,根据新的 UIContentSizeCategory 计算等效字号
  3. 或者:在 RDEPUBReaderConfiguration 中增加 useSystemFontSize: Bool,启用时忽略 fontSize,使用系统推荐值
  4. 字号映射表(参考 Apple 的 preferredFont(forTextStyle:) 返回值)

验收标准

  • 系统字体调大后,阅读器字号自动跟随
  • 用户手动调节字号后,覆盖系统设置

14. 高对比度

当前状态6 个固定主题预设,不跟随系统 UIAccessibility.isDarkerSystemColorsEnabled

实施步骤

  1. RDEPUBReaderTheme 增加 highContrastVariant: RDEPUBReaderTheme? 属性
  2. 监听 UIAccessibility.darkerSystemColorsStatusDidChangeNotification
  3. 高对比度启用时,自动切换到高对比度主题变体(更大色彩对比度)
  4. 或提供独立的"高对比度"主题预设

验收标准

  • 系统高对比度开启后,阅读器自动切换到高对比度主题
  • 关闭后恢复原主题

15. DRM

当前状态:需求文档明确声明 DRM 不在 SDK 范围内。如果需要支持,建议通过以下方式:

建议方案

  1. 不在 SDK 内实现 DRM而是通过 RDEPUBParser 的输入端控制:
    • RDEPUBParser 接受 DataURL,调用方可以先解密再传入
    • 或新增 RDEPUBDRMProvider 协议:
      protocol RDEPUBDRMProvider {
          func decryptedData(for resourceURL: URL) -> Data?
          func isProtected(_ resourceURL: URL) -> Bool
      }
      
  2. SDK 内的 ss-reader:// URL scheme handler 在读取资源时调用 DRMProvider
  3. 商业 DRM如 Adobe ADEPT、Readium LCP由调用方集成SDK 提供接入点

验收标准

  • SDK 提供清晰的 DRM 接入协议
  • 不引入 DRM 依赖,保持 SDK 轻量
  • 调用方能通过协议接入自己的 DRM 方案

四、功能完整度(暂未展开)

以下功能需求已识别但暂未进入详细开发计划:

缺失项 严重度 说明
书架/书库管理 SDK 只能打开单本书,无书架 UI、阅读历史、分类管理
批注导出/分享 高亮/笔记只有本地存储,无导出、分享、复制到剪贴板
阅读统计 无阅读时长追踪、阅读速度、连续阅读天数
TTS 朗读 微信读书核心功能之一,当前无任何语音相关代码
全局搜索 当前搜索只在单本书内,无跨书搜索
离线/云端同步 无 iCloud/自建同步,阅读进度和笔记只在本地
夜间模式定时切换 有暗色主题但不能跟随系统或定时切换

五、按优先级排序的建议路线

P0 — 影响商业发布

  1. 分页回归基准 — UI 自动化测试已启动,下一步需要把分页结果、首屏时间、截图差异纳入回归基准
  2. 字体选择器增强 — 基础字体选择器已实现,后续需补:更多内置字体包、字体预览、字体资源加载失败兜底
  3. 书架/书库管理 — 商业阅读器的入口
  4. 暗色模式图片处理增强 — 基础处理已实现,后续可补:按图片亮度自适应混合比例

P1 — 影响用户留存

  1. 批注导出/分享 — 深度阅读用户的核心需求
  2. 阅读统计/时长追踪 — 用户粘性和产品数据的基础
  3. 性能基线与大书优化 — 大书卡顿是用户流失的主要原因
  4. 简繁转换 — 面向港澳台用户需要

P2 — 提升竞争力

  1. TTS 朗读 — 通勤场景、无障碍场景刚需
  2. 云端同步 — 多设备用户的基本需求
  3. VoiceOver 完善 — 合规和品牌形象
  4. 全局搜索 — 藏书量大时的效率工具

六、总结

经过三方对比,渲染质量层面的真实差距比最初评估要小:

  • 图文分页质量:双方基本对齐,我们甚至在 keepWithNext 上领先
  • 真正的差距:字体包数量、分页回归基准、简繁转换
  • WXRead 也没做的竖排、ruby、公式、hyphenation——这些不是必须对齐的

如果目标是"能上架的商业阅读器",当前已补齐字体选择器、暗色模式图片处理和基础 UI 自动化测试。下一步最值得投入的是分页回归基准、更多字体资源和书架/书库管理。