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

161 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
---
<domain>
## 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` 已在使用
</domain>
<decisions>
## 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
- 存储格式: `NSKeyedArchiver``NSAttributedString` 支持 `NSCoding``NSRange` 同理)
- 需运行时验证 DTCoreText 自定义属性在 `NSKeyedArchiver` 下的持久性
- 失效策略: 参数变化自动失效 + schema version bump 强制失效
- 存储位置: `Library/Caches` 子目录
### D-02: 分页质量增强 — 避让规则执行 [P0, QUAL-02]
`RDEPUBTextLayouter``avoidPageBreakInside` 的语义标记需要实际执行避让(当前仅有标记无行为),并增加孤行/寡行控制。
- **WXRead 参考:** `WRCoreTextLayoutFrame.avoidPageBreakInsideByRemovingLastLinesIfNeeded` — CSS `avoid-page-break-inside` 实现
- **关键文件:** `RDEPUBTextLayouter.swift`、`RDEPUBTextLayoutFrame.swift`
- **技术决策:**
- `avoidPageBreakInside`: 检测 block 最后 1-2 行被切到下一页时,将整个 block 移到下一页
- 孤行控制: 页首不允许出现段落最后一行orphan页尾不允许出现段落第一行widow
- 增加 `RDEPUBTextLayoutConfig` 结构体封装分页配置(孤行/寡行阈值等),或先硬编码合理默认值
### D-03: 图片/附件处理规则 [P1, QUAL-03]
图片尺寸策略、暗色模式适配、页面背景类信息有明确处理规则和诊断证据。
- **WXRead 参考:** `wr-vertical-center-style`(图片垂直居中)、`DTPageBreakInsideAvoid`(断页避让)
- **关键文件:** `RDEPUBTextLayoutFrame.swift`、`RDEPUBTextRendererSupport.swift`、`RDEPUBDTCoreTextRenderer.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 → 通过后继续
</decisions>
<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>
<specifics>
## 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 推到下一页
```
</specifics>
<deferred>
## Deferred Ideas
- **CoreText 直接绘制迁移**: v1.2/v2.0,超出本阶段范围
- **字体系统**: 内嵌固定字体集,后续版本
- **字符级位置精度**: P2与 CoreText 迁移关联
- **章节数据模型内聚**: P2独立重构
- **TTS / DRM / Pencil / 多栏排版**: P3独立功能模块
- **NSKeyedArchiver 对 DTCoreText 属性持久性验证**: 需运行时测试,若失败则降级为 decomposed JSON 策略
</deferred>
---
*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*