feat(wxread): align pagination, rendering, and docs
This commit is contained in:
@@ -1,46 +1,86 @@
|
||||
# Phase 8: 图片显示修复 - Context
|
||||
# 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
|
||||
|
||||
修复 native text 渲染路径(DTCoreText)中三类图片的尺寸适配问题:
|
||||
1. `qrbodyPic` 容器中的独立图片 — 当前无特殊处理,高度可能溢出页面
|
||||
2. 封面图片 (`frontCover`) — 当前用屏幕尺寸基准,需统一到最大尺寸逻辑
|
||||
3. 脚注图片 (`qqreader-footnote`) — 当前三处不一致的尺寸处理
|
||||
Phase 8 在 Phase 6(页面几何)和 Phase 7(自定义属性闭环)之上,解决三个问题:
|
||||
|
||||
核心目标:对齐 WXRead 的 `_WRPostProcessElementTree` 逻辑,对所有图片附件统一执行最大尺寸限制。
|
||||
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:** 对所有图片附件统一执行最大尺寸限制 `CGSizeMake(1080, 1920)`
|
||||
- **D-02:** 超过最大宽度时等比缩放(与 WXRead `_WRPostProcessElementTree` 一致)
|
||||
- **D-03:** 实现位置:`prepareHTMLElementForReaderRendering` 中,在现有 footnote/cover 特殊处理之前,添加统一的最大尺寸限制逻辑
|
||||
### 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-04:** 保持屏幕尺寸基准(`UIScreen.main.bounds.insetBy(dx: 20, dy: 28)`),但受统一最大尺寸 1080x1920 约束
|
||||
- **D-05:** 保持 `configureCoverIfNeeded` 的 UIImageView 专用路径不变
|
||||
- **D-06:** 封面图片仍通过 `prepareHTMLElementForReaderRendering` 中的 cover 分支处理,但需确保不超过统一最大尺寸
|
||||
### 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-07:** 对齐 WXRead:只设 `width:1em`,不设高度,让渲染器按原始宽高比自动计算
|
||||
- **D-08:** 移除 `prepareHTMLElementForReaderRendering` 中脚注的高度计算逻辑(`pointSize * 0.54`)
|
||||
- **D-09:** 移除 `normalizeInlineAttachments` 中的高度覆盖逻辑(`pointSize * 0.14`)
|
||||
- **D-10:** 保留 `normalizeAttachmentHTMLMarkers` 中的 CSS `width:1em`,移除 `height:1em`
|
||||
### D-03: 图片/附件处理规则 [P1, QUAL-03]
|
||||
图片尺寸策略、暗色模式适配、页面背景类信息有明确处理规则和诊断证据。
|
||||
- **WXRead 参考:** `wr-vertical-center-style`(图片垂直居中)、`DTPageBreakInsideAvoid`(断页避让)
|
||||
- **关键文件:** `RDEPUBTextLayoutFrame.swift`、`RDEPUBTextRendererSupport.swift`、`RDEPUBDTCoreTextRenderer.swift`
|
||||
- **技术决策:**
|
||||
- 图片 fit: 超过页面高度的图片按比例缩放到页面内,不跨页
|
||||
- 图片居中: 附件类 block 默认垂直居中处理
|
||||
- 暗色模式: 图片在暗色模式下保留原始颜色(不反色),但为纯文本装饰元素提供适配
|
||||
- 诊断: 每个 attachment block 输出尺寸、placement、是否触发缩放
|
||||
|
||||
### qrbodyPic 图片
|
||||
- **D-11:** 不单独添加特殊处理逻辑,统一最大尺寸限制会隐式修复高度溢出问题
|
||||
- **D-12:** 保持现有 CSS 规则不变(`max-width:100%; height:auto; display:block; margin:auto`)
|
||||
### D-04: 性能采样与诊断输出 [P1, QUAL-04]
|
||||
分页深化后不显著恶化首屏时间、重分页耗时或交互流畅度;输出稳定采样数据。
|
||||
- **关键文件:** `RDEPUBTextBookBuilder.swift`
|
||||
- **技术决策:**
|
||||
- 采样点: 每章渲染耗时、每章分页耗时、全书构建总耗时、缓存命中/未命中
|
||||
- 输出方式: `RDEPUBTextBookBuilder` 回调中新增 `buildDiagnostics` 字段,包含耗时和缓存状态
|
||||
- 不引入外部性能监控库,使用 `CFAbsoluteTimeGetCurrent()` 采样
|
||||
|
||||
### Claude's Discretion
|
||||
- 具体实现细节(如是否需要自动添加 `bodyPic` 类、是否需要 `wr-vertical-center-style` 语义标记)由 planner 和 executor 决定
|
||||
- 图片缓存策略不在本次讨论范围,属于 Phase 8 的 08-01 计划
|
||||
- 缓存存储的线程安全策略(串行队列 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>
|
||||
|
||||
@@ -49,66 +89,72 @@
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### WXRead 参考实现
|
||||
- `Doc/WXRead/decompiled/WREpubTypesetter.m` §672-701 — `_WRPostProcessElementTree()`:统一图片最大尺寸逻辑(1080x1920)、自动添加 bodyPic 类、设置 wr-vertical-center-style
|
||||
- `Doc/WXRead/decompiled/WRCoreTextLayoutFrame.m` §615-635 — `drawCoverImage:inContext:rect:`:封面图片等比缩放适配 frame
|
||||
- `Doc/WXRead/decompiled/WRCoreTextLayouter.m` §639-667 — `targetSizeForPattern:rect:imageSize:`:sizePattern 缩放逻辑
|
||||
- `Doc/WXRead/resources/css/replace.css` §91-103 — `.bodyPic`、`.qrbodyPic`、`.qqreader-footnote` 的 CSS 规则
|
||||
### 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` — 片段标记、属性标准化
|
||||
|
||||
### ReadViewSDK 当前实现
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift` §220-262 — `prepareHTMLElementForReaderRendering`:当前图片处理逻辑
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift` §302-343 — `replaceCSS()`:当前图片 CSS 规则
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift` §345-404 — `normalizeAttachmentHTMLMarkers`:HTML 预处理
|
||||
- `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift` §217-282 — `configureCoverIfNeeded` 和 `normalizeInlineAttachments`
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` §90-112 — `dtOptions`:DTMaxImageSize 配置
|
||||
### WXRead 逆向参考(实现时可直接参考)
|
||||
- `Doc/WXRead/decompiled-doc.md` — 44 个逆向文件的职责说明
|
||||
- `Doc/WXRead/resources-doc.md` — CSS/JS 资源文件清单与职责
|
||||
- `Doc/WXRead/读书EPUB阅读器实现架构.md` — 双渲染引擎架构总览
|
||||
|
||||
### Demo 样本
|
||||
- `ReadViewDemo/ReadViewDemo/book/宝山辽墓材料与释读_副本/OEBPS/Styles/stylesheets.css` — 实际 EPUB 书籍的图片 CSS
|
||||
- `ReadViewDemo/ReadViewDemo/book/宝山辽墓材料与释读_副本/OEBPS/Text/cover.xhtml` — 封面 HTML 结构
|
||||
- `ReadViewDemo/ReadViewDemo/book/宝山辽墓材料与释读_副本/OEBPS/Text/Chapter_5.xhtml` — qrbodyPic 图片 HTML 结构
|
||||
### 架构文档
|
||||
- `.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>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `prepareHTMLElementForReaderRendering`:已有 footnote 和 cover 的分类处理框架,可在此基础上添加统一最大尺寸逻辑
|
||||
- `normalizeAttachmentHTMLMarkers`:已有 HTML 正则替换基础设施,可复用于 qrbodyPic 的 HTML 预处理
|
||||
- `replaceCSS()`:已有图片 CSS 规则,可直接修改
|
||||
|
||||
### Established Patterns
|
||||
- 图片分类检测:通过 CSS class(`qqreader-footnote`、`rd-front-cover-image`)和文件名(`note.png`、`cover.jpg`)识别图片类型
|
||||
- DTTextAttachment API:`originalSize`(原始尺寸)、`displaySize`(显示尺寸)、`verticalAlignment`(垂直对齐)
|
||||
- HTML 正则替换:`mergeHTMLAttributes` + `replaceMatches` 模式用于 HTML 预处理
|
||||
|
||||
### Integration Points
|
||||
- `RDEPUBDTCoreTextRenderer.swift` → `willFlushCallback` → `prepareHTMLElementForReaderRendering`:DTCoreText 渲染时的图片处理入口
|
||||
- `RDEPUBTextContentView.swift` → `configureCoverIfNeeded`:封面图片显示入口
|
||||
- `RDEPUBTextContentView.swift` → `normalizeInlineAttachments`:显示时的脚注图片处理入口
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- 用户明确要求"等比缩放,让图片都能显示全"——图片不能被裁剪,必须完整显示
|
||||
- 用户明确要求脚注图片"显示的和文本的高度一样,高度不能超过文本的高度"
|
||||
- WXRead 的实现是参考标准,但不需要 100% 复制——只对齐图片尺寸逻辑
|
||||
### 缓存 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
|
||||
|
||||
- 图片缓存机制(属于 08-01 计划)
|
||||
- `wr-vertical-center-style` 垂直居中语义的完整实现(属于 Phase 7 已完成的工作)
|
||||
- 图片点击放大/查看大图功能(新能力,不属于当前范围)
|
||||
- 代码块/表格被粗暴截断(QUAL-02 子需求)— 本次聚焦图片尺寸修复,代码块/表格截断问题留待后续分页质量迭代处理
|
||||
- **CoreText 直接绘制迁移**: v1.2/v2.0,超出本阶段范围
|
||||
- **字体系统**: 内嵌固定字体集,后续版本
|
||||
- **字符级位置精度**: P2,与 CoreText 迁移关联
|
||||
- **章节数据模型内聚**: P2,独立重构
|
||||
- **TTS / DRM / Pencil / 多栏排版**: P3,独立功能模块
|
||||
- **NSKeyedArchiver 对 DTCoreText 属性持久性验证**: 需运行时测试,若失败则降级为 decomposed JSON 策略
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 8-图片显示修复*
|
||||
*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*
|
||||
|
||||
Reference in New Issue
Block a user