ReadViewSDK/.planning/phases/08-pagination-quality-cache-performance/08-CONTEXT.md

8.0 KiB
Raw Blame History

Phase 8: 分页质量、缓存与性能采样 - Context

Gathered: 2026-05-23 Updated: 2026-05-23 (research corrected: D-01/D-02/D-04/D-05 already implemented) Status: Ready for planning Source: ARCHITECTURE-CONTEXT.md + REQUIREMENTS.md (QUAL-01 ~ QUAL-04) + 08-RESEARCH.md


## Phase Boundary

Phase 8 在 Phase 6页面几何和 Phase 7自定义属性闭环之上解决三个问题

  1. 缓存:相同视口 + 排版配置下,同一章节不重复全量排版 (QUAL-01)
  2. 分页质量:复杂图文章节减少粗暴截断、孤行/寡行、图片留白异常 (QUAL-02, QUAL-03)
  3. 性能诊断:输出稳定的采样数据,确保不出现明显退化 (QUAL-04)

本阶段涉及 CoreText 直接绘制迁移v1.2+涉及字体系统。 实现时可直接参考读书 (WXRead) 的对应实现模式。

已完成的能力(研究发现)

以下能力在之前的 Phase 中已经实现Phase 8 不需要重新实现:

  • D-01 分页引擎集成: RDEPUBTextBookBuilder.build() 已调用 content.rd_paginatedFrames(size:)(非旧的 ss_pageRanges
  • D-02 分页元数据暴露: RDEPUBTextPageMetadata 已定义并包含 breakReason/blockKinds/semanticHints
  • D-04 CSS <link> 内联: 已在渲染前处理 <link> 标签
  • D-05 5 层 CSS 级联: RDEPUBTextStyleSheetPackage/RDEPUBTextStyleSheetLayer 已在使用
## Implementation Decisions

D-01: 分页缓存 — RDEPUBTextBook 序列化 [P0, QUAL-01]

bookID + fontSize + lineHeightMultiple + contentInsets 生成缓存 key将完整 RDEPUBTextBook 序列化到磁盘。

  • WXRead 参考: WRChapterPageCount.currentCacheKeyWithBookId: 按排版设置缓存
  • 关键文件: RDEPUBTextBookBuilder.swift,新增 RDEPUBTextBookCache.swift
  • 技术决策:
    • 缓存 key: SHA256("\(bookID)_\(fontSize)_\(lineHeightMultiple)_\(contentInsets)") + schema version
    • 存储格式: NSKeyedArchiverNSAttributedString 支持 NSCodingNSRange 同理)
    • 需运行时验证 DTCoreText 自定义属性在 NSKeyedArchiver 下的持久性
    • 失效策略: 参数变化自动失效 + schema version bump 强制失效
    • 存储位置: Library/Caches 子目录

D-02: 分页质量增强 — 避让规则执行 [P0, QUAL-02]

RDEPUBTextLayouteravoidPageBreakInside 的语义标记需要实际执行避让(当前仅有标记无行为),并增加孤行/寡行控制。

  • WXRead 参考: WRCoreTextLayoutFrame.avoidPageBreakInsideByRemovingLastLinesIfNeeded — CSS avoid-page-break-inside 实现
  • 关键文件: RDEPUBTextLayouter.swiftRDEPUBTextLayoutFrame.swift
  • 技术决策:
    • avoidPageBreakInside: 检测 block 最后 1-2 行被切到下一页时,将整个 block 移到下一页
    • 孤行控制: 页首不允许出现段落最后一行orphan页尾不允许出现段落第一行widow
    • 增加 RDEPUBTextLayoutConfig 结构体封装分页配置(孤行/寡行阈值等),或先硬编码合理默认值

D-03: 图片/附件处理规则 [P1, QUAL-03]

图片尺寸策略、暗色模式适配、页面背景类信息有明确处理规则和诊断证据。

  • WXRead 参考: wr-vertical-center-style(图片垂直居中)、DTPageBreakInsideAvoid(断页避让)
  • 关键文件: RDEPUBTextLayoutFrame.swiftRDEPUBTextRendererSupport.swiftRDEPUBDTCoreTextRenderer.swift
  • 技术决策:
    • 图片 fit: 超过页面高度的图片按比例缩放到页面内,不跨页
    • 图片居中: 附件类 block 默认垂直居中处理
    • 暗色模式: 图片在暗色模式下保留原始颜色(不反色),但为纯文本装饰元素提供适配
    • 诊断: 每个 attachment block 输出尺寸、placement、是否触发缩放

D-04: 性能采样与诊断输出 [P1, QUAL-04]

分页深化后不显著恶化首屏时间、重分页耗时或交互流畅度;输出稳定采样数据。

  • 关键文件: RDEPUBTextBookBuilder.swift
  • 技术决策:
    • 采样点: 每章渲染耗时、每章分页耗时、全书构建总耗时、缓存命中/未命中
    • 输出方式: RDEPUBTextBookBuilder 回调中新增 buildDiagnostics 字段,包含耗时和缓存状态
    • 不引入外部性能监控库,使用 CFAbsoluteTimeGetCurrent() 采样

Claude's Discretion

  • 缓存存储的线程安全策略(串行队列 vs 锁)
  • RDEPUBTextLayoutConfig 是新增结构体还是扩展已有类型
  • 性能诊断的阈值告警(可选,初期仅输出数据不告警)

强制约束:编译验证

每个 task 代码编写完成后,必须通过 XcodeBuildMCP 工具执行编译验证,确认无编译错误后方可标记 task 完成。若有编译问题需立即修复并重新验证。

  • 工具: XcodeBuildMCP (build_sim / run_build_sim)
  • 配置: workspace = ReadViewDemo/ReadViewDemo.xcworkspace, scheme = ReadViewDemo, simulator = iPhone 17 Pro
  • 流程: 代码修改 → XcodeBuildMCP build → 有错误则修复 → 重新 build → 通过后继续

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Phase 8 核心文件

  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift — 书籍构建器,已调用 rd_paginatedFrames
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift — 分页支持
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift — CoreText 分页引擎4 级语义断页
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift — 帧模型
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift — 渲染协议、样式表类型
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift — DTCoreText 渲染实现
  • Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift — 片段标记、属性标准化

WXRead 逆向参考(实现时可直接参考)

  • Doc/WXRead/decompiled-doc.md — 44 个逆向文件的职责说明
  • Doc/WXRead/resources-doc.md — CSS/JS 资源文件清单与职责
  • Doc/WXRead/读书EPUB阅读器实现架构.md — 双渲染引擎架构总览

架构文档

  • .planning/ARCHITECTURE-CONTEXT.md — 4 Area 决策总览
  • Doc/架构对比分析_WXRead_vs_ReadViewSDK.md — 10 维度对比
  • 08-RESEARCH.md — 研究产出(本目录下)

前置 Phase 成果

  • Phase 6 SUMMARY: 页面几何与交互命中层
  • Phase 7 SUMMARY: 自定义属性闭环avoidPageBreakInside/pageBreakBefore/pageBreakAfter/pageRelate 已注入 attributedString

</canonical_refs>

## Specific Ideas

缓存 key 设计

cacheKey = SHA256("\(bookID)_\(fontSize)_\(lineHeightMultiple)_\(contentInsets.top)_\(contentInsets.left)_\(contentInsets.bottom)_\(contentInsets.right)_v\(schemaVersion)")
  • schemaVersion = 1代码变更导致缓存格式不兼容时 bump
  • 失效条件:参数变化自动失效 + schema version bump 强制失效
  • 存储Library/Caches/RDEPUBTextBookCache/ 目录

性能采样点

  • 每章渲染耗时DTHTMLAttributedStringBuilder
  • 每章分页耗时CTFramesetter pagination
  • 全书构建总耗时
  • 缓存命中/未命中计数

avoidPageBreakInside 执行策略

当 CTFrame 可见范围尾部落在 avoidPageBreakInside block 内部时:
1. 向前扫描找到 block 起始位置
2. 将分页点移到 block 起始之前
3. 将整个 block 推到下一页
## Deferred Ideas
  • CoreText 直接绘制迁移: v1.2/v2.0,超出本阶段范围
  • 字体系统: 内嵌固定字体集,后续版本
  • 字符级位置精度: P2与 CoreText 迁移关联
  • 章节数据模型内聚: P2独立重构
  • TTS / DRM / Pencil / 多栏排版: P3独立功能模块
  • NSKeyedArchiver 对 DTCoreText 属性持久性验证: 需运行时测试,若失败则降级为 decomposed JSON 策略

Phase: 08-pagination-quality-cache-performance Context gathered: 2026-05-23 Updated after research: D-01/D-02/D-04/D-05 confirmed already implemented