docs(01-01): complete current engine audit plan

This commit is contained in:
shen 2026-05-21 21:56:54 +08:00
parent 743988a78b
commit cc2573552c
4 changed files with 532 additions and 39 deletions

View File

@ -15,7 +15,7 @@
### 兼容性与主流程
- [ ] **COMP-01**`RDURLReaderController` / `RDEPUBReaderController` 作为公开入口继续可用,`.epub` / `.txt` 打开主流程不回归
- [ ] **COMP-02**Fixed Layout EPUB 与交互式 EPUB 继续使用 `WKWebView`,行为不因本次改造产生回归
- [x] **COMP-02**Fixed Layout EPUB 与交互式 EPUB 继续使用 `WKWebView`,行为不因本次改造产生回归
- [ ] **COMP-03**reflowable EPUB 的阅读位置映射、高亮/选区、搜索结果定位、字号/行高/主题切换后的重新分页在新内核下继续可用
- [ ] **COMP-04**:当前翻页代码(`RDReaderView` 及现有 page curl / horizontal scroll / vertical scroll 交互逻辑)不做修改,新内核必须适配既有翻页容器
@ -50,7 +50,7 @@
| REND-03 | Phase 3 | Pending |
| REND-04 | Phase 3 | Pending |
| COMP-01 | Phase 4 | Pending |
| COMP-02 | Phase 1 | Pending |
| COMP-02 | Phase 1 | Complete |
| COMP-03 | Phase 4 | Pending |
| COMP-04 | Phase 4 | Pending |
| STAB-01 | Phase 5 | Pending |

View File

@ -6,84 +6,84 @@
## Phases
- [ ] **Phase 1对齐现状、边界与重构切入点** - 明确当前旧引擎的真实调用链、分流边界与必须保留的兼容能力
- [ ] **Phase 2重构 typesetter 与 CSS 分层** - 在旧引擎基础上引入 WXRead 风格样式组织与章节级 HTML → attributed string 转换增强
- [ ] **Phase 3重构属性体系与复杂分页器** - 引入页面级 attributed string 元数据与更强的分页/布局能力
- [ ] **Phase 4接回现有 reader 能力链路** - 让新内核与阅读位置、高亮、搜索、主题切换等现有功能继续协作
- [ ] **Phase 5回归验证与稳定性收敛** - 以样本书和主流程为核心完成回归、修复与验收
- [ ] **Phase 1: 对齐现状、边界与重构切入点** - 明确当前旧引擎的真实调用链、分流边界与必须保留的兼容能力
- [ ] **Phase 2: 重构 typesetter 与 CSS 分层** - 在旧引擎基础上引入 WXRead 风格样式组织与章节级 HTML → attributed string 转换增强
- [ ] **Phase 3: 重构属性体系与复杂分页器** - 引入页面级 attributed string 元数据与更强的分页/布局能力
- [ ] **Phase 4: 接回现有 reader 能力链路** - 让新内核与阅读位置、高亮、搜索、主题切换等现有功能继续协作
- [ ] **Phase 5: 回归验证与稳定性收敛** - 以样本书和主流程为核心完成回归、修复与验收
## Phase Details
### Phase 1对齐现状、边界与重构切入点
**Goal**把“当前旧引擎是什么、哪些能力必须保留、哪些路径绝对不能动”说清楚,形成直接重构旧引擎的实施基线。
**Depends on**Nothing (first phase)
**Requirements**COMP-02
**Success Criteria**(必须为 TRUE
### Phase 1: 对齐现状、边界与重构切入点
**Goal**: 把“当前旧引擎是什么、哪些能力必须保留、哪些路径绝对不能动”说清楚,形成直接重构旧引擎的实施基线。
**Depends on**: Nothing (first phase)
**Requirements**: COMP-02
**Success Criteria** (必须为 TRUE):
1. 能准确描述当前 `.textReflowable` 的真实调用链与数据流
2. Fixed Layout / 交互式 EPUB 的 `WKWebView` 边界不含糊,且不被纳入本次重构
3. 当前翻页代码(`RDReaderView`)被明确排除在本次改造范围外
4. 输出一份针对“旧引擎直接演进”的重构切入策略,而不是双引擎方案
**Plans**2 plans
**Plans**: 2 plans
Plans:
- [ ] 01-01审计当前 `.textReflowable` 路径(`RDEPUBDTCoreTextRenderer` / `RDEPUBTextBookBuilder` / `RDEPUBTextPaginationSupport` / `RDEPUBTextContentView`
- [x] 01-01审计当前 `.textReflowable` 路径(`RDEPUBDTCoreTextRenderer` / `RDEPUBTextBookBuilder` / `RDEPUBTextPaginationSupport` / `RDEPUBTextContentView`
- [ ] 01-02结合 `Doc/WXRead/analysis/*` 提炼旧引擎可直接演进的切入点与必须保留的兼容链路
### Phase 2重构 typesetter 与 CSS 分层
**Goal**在现有旧引擎基础上引入 WXRead 风格的 CSS 分层与章节级 HTML → attributed string 增强,让 renderer 输入具备更完整的排版语义。
**Depends on**Phase 1
**Requirements**REND-01, REND-02
**Success Criteria**(必须为 TRUE
### Phase 2: 重构 typesetter 与 CSS 分层
**Goal**: 在现有旧引擎基础上引入 WXRead 风格的 CSS 分层与章节级 HTML → attributed string 增强,让 renderer 输入具备更完整的排版语义。
**Depends on**: Phase 1
**Requirements**: REND-01, REND-02
**Success Criteria** (必须为 TRUE):
1. reflowable EPUB 的章节渲染输入不再只是“简单 DTCoreText 默认 builder + 少量 options”
2. CSS 五层策略(`default / replace / dark / epub / user`)能够在旧引擎路径中落地
3. 章节级 baseURL、资源解析与样式注入策略清晰、可验证
**Plans**3 plans
**Plans**: 3 plans
Plans:
- [ ] 02-01设计并实现旧引擎中的 WXRead 风格 stylesheet builder / HTML 预处理增强
- [ ] 02-02改造 `RDEPUBDTCoreTextRenderer` 与相邻渲染链路,使其承接新的样式分层与章节上下文
- [ ] 02-03验证章节级图片/CSS/基础资源在新渲染输入下可正常解析
### Phase 3重构属性体系与复杂分页器
**Goal**在旧引擎路径中引入页面级 attributed string 元数据与更复杂的分页/页面布局能力,替代当前简单 `pageRanges` 切页模式。
**Depends on**Phase 2
**Requirements**REND-03, REND-04
**Success Criteria**(必须为 TRUE
### Phase 3: 重构属性体系与复杂分页器
**Goal**: 在旧引擎路径中引入页面级 attributed string 元数据与更复杂的分页/页面布局能力,替代当前简单 `pageRanges` 切页模式。
**Depends on**: Phase 2
**Requirements**: REND-03, REND-04
**Success Criteria** (必须为 TRUE):
1. 自定义 DTCoreText 属性体系可承载分页、块元素、图片、页面语义等布局信息
2. 新分页器具备明显强于当前 `CTFrameGetVisibleStringRange` 切页的页面布局能力
3. 分页结果可为后续 reader 集成提供稳定的页面范围与页面语义
**Plans**3 plans
**Plans**: 3 plans
Plans:
- [ ] 03-01定义并实现页面级 attributed string 元数据与自定义属性键
- [ ] 03-02在旧引擎基础上重构分页器使其具备接近 `WRCoreTextLayouter / WRCoreTextLayoutFrame` 的核心能力
- [ ] 03-03验证复杂块元素、图片与分页边界控制在新分页器下可工作
### Phase 4接回现有 reader 能力链路
**Goal**让新内核在不新增并行原生引擎、且不修改当前翻页代码的前提下,继续服务现有 reader UI、阅读位置、高亮、搜索与主题切换能力。
**Depends on**Phase 3
**Requirements**COMP-01, COMP-03, COMP-04
**Success Criteria**(必须为 TRUE
### Phase 4: 接回现有 reader 能力链路
**Goal**: 让新内核在不新增并行原生引擎、且不修改当前翻页代码的前提下,继续服务现有 reader UI、阅读位置、高亮、搜索与主题切换能力。
**Depends on**: Phase 3
**Requirements**: COMP-01, COMP-03, COMP-04
**Success Criteria** (必须为 TRUE):
1. `RDURLReaderController` / `RDEPUBReaderController` 主流程在新内核下继续可用
2. 阅读位置映射、高亮/选区、搜索结果定位可继续工作
3. 字号/行高/主题切换可驱动正确的重新分页,而不是破坏状态链路
4. `RDReaderView` 及现有翻页模式/交互逻辑无需修改即可承接新内核输出
**Plans**3 plans
**Plans**: 3 plans
Plans:
- [ ] 04-01将新内核接回 `RDEPUBTextBookBuilder` / `RDEPUBTextContentView` / `RDEPUBReaderController`,不修改 `RDReaderView`
- [ ] 04-02修复并验证阅读位置映射、高亮、搜索等兼容能力
- [ ] 04-03验证字体、行高、主题切换后的重新分页与状态恢复
### Phase 5回归验证与稳定性收敛
**Goal**围绕样本书和主流程做回归,收敛分页正确性、位置映射稳定性与关键阅读交互问题。
**Depends on**Phase 4
**Requirements**STAB-01, STAB-02
**Success Criteria**(必须为 TRUE
### Phase 5: 回归验证与稳定性收敛
**Goal**: 围绕样本书和主流程做回归,收敛分页正确性、位置映射稳定性与关键阅读交互问题。
**Depends on**: Phase 4
**Requirements**: STAB-01, STAB-02
**Success Criteria** (必须为 TRUE):
1. 至少 3 类样本书验证通过:纯文本/小说类、含图片与复杂段落样式、含外链与多个 CSS 文件引用
2. `.epub` / `.txt` 打开主流程、Fixed Layout、交互式 EPUB 不回归
3. 新内核下不出现崩溃、白屏、无限加载、严重错页或关键交互链路失效
**Plans**2 plans
**Plans**: 2 plans
Plans:
- [ ] 05-01构建样本书验证矩阵与诊断手段日志/断言/复现清单)
@ -93,7 +93,7 @@ Plans:
| Phase | Plans Complete | Status | Completed |
|---|---:|---|---|
| 1. 对齐现状、边界与重构切入点 | 0/2 | Not started | - |
| 1. 对齐现状、边界与重构切入点 | 1/2 | In Progress| |
| 2. 重构 typesetter 与 CSS 分层 | 0/3 | Not started | - |
| 3. 重构属性体系与复杂分页器 | 0/3 | Not started | - |
| 4. 接回现有 reader 能力链路 | 0/3 | Not started | - |

View File

@ -0,0 +1,84 @@
---
phase: 01-current-engine-boundaries
plan: 01
subsystem: testing
tags: [epub, dtcoretext, coretext, reader, architecture]
requires: []
provides:
- "当前 `.textReflowable` 原生渲染链路审计"
- "Fixed / interactive WebView 边界说明"
- "`RDReaderView` 作为稳定翻页容器的约束结论"
affects: [phase-02, phase-03, reflowable-engine, reader-integration]
tech-stack:
added: []
patterns: ["source-first architecture audit", "reader branch-point preservation"]
key-files:
created:
- .planning/phases/01-current-engine-boundaries/01-reflowable-audit.md
modified: []
key-decisions:
- "将 `.textReflowable` 的主战场固定在 `EPUBTextRendering`,而不是转向新的 WebView 或并行原生引擎。"
- "将 `RDEPUBTextBook` / `RDEPUBTextPage` 视为 location/highlight/search 的兼容层,不作为 Phase 2/3 的随意破坏对象。"
patterns-established:
- "先审计 `readingProfile` 分流与 reader branch point再设计引擎重构触点。"
- "以 `RDReaderView` 为稳定外壳,内核升级只在 rendering/pagination 模块内部推进。"
requirements-completed: [COMP-02]
duration: 8min
completed: 2026-05-21
---
# Phase 01 Plan 01: Current Engine Audit Summary
**审计并固定了当前 reflowable EPUB 原生引擎的真实调用链、WebView 分流边界,以及 `RDReaderView` 不可改动的容器契约**
## Performance
- **Duration:** 8 min
- **Started:** 2026-05-21T13:48:00Z
- **Completed:** 2026-05-21T13:56:09Z
- **Tasks:** 2
- **Files modified:** 1
## Accomplishments
- 写清了 `.epub``RDURLReaderController` 进入 `RDEPUBReaderController` 再进入 `readingProfile` 分流的真实入口路径
- 固定了 native `.textReflowable` 的实际渲染链路:`RDEPUBDTCoreTextRenderer` -> `RDEPUBTextBookBuilder` -> `ss_pageRanges(size:)` -> `RDEPUBTextContentView`
- 明确了 `webFixedLayout` / `webInteractive` 继续使用 `WKWebView`,并把 `RDReaderView` 记录为稳定外壳
## Task Commits
This plan was closed out with a documentation-only metadata commit after both tasks completed:
1. **Task 1: 梳理原生 reflowable 调用链与数据模型** - included in final docs commit
2. **Task 2: 写清 WebView 边界与容器不变约束** - included in final docs commit
**Plan metadata:** pending commit hash at close-out
## Files Created/Modified
- `.planning/phases/01-current-engine-boundaries/01-reflowable-audit.md` - 基于源码证据的 current-engine 审计文档
## Decisions Made
- 以 `RDEPUBReaderController.paginatePublication()` 作为 native / web 分流的主证据点,而不是以历史文档描述为准
- 把 `RDEPUBPaginator` 定义为 WebView 渲染/测量体系组成部分,而不是当前 native reflowable 的分页核心
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- `gsd-sdk` 起初无法从中文 phase 名自动推导 phase 目录 slug因此本阶段使用了稳定的英文目录名 `01-current-engine-boundaries`
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Wave 1 事实基线已完成Phase 1 的 strategy plan 可以直接基于该审计文档展开
- 当前审计已足够支撑 Phase 2/3 避免把 WebView 路径和 native 路径混为一谈
---
*Phase: 01-current-engine-boundaries*
*Completed: 2026-05-21*

View File

@ -0,0 +1,409 @@
# Phase 1 Audit: Current Reflowable Engine, Boundaries, and Stable Contracts
**Phase:** 01-current-engine-boundaries
**Date:** 2026-05-21
**Purpose:** 固化当前 `.textReflowable` 旧引擎的真实调用链、模式分流边界、以及本次重构不能碰的外层契约。
## 1. Executive Conclusion
当前仓库并不是“所有 EPUB 都走 `WKWebView`”。真实结构是三分流:
1. `webFixedLayout`Fixed Layout EPUB继续走 `WKWebView`
2. `webInteractive`:带脚本/多媒体/iframe/表单等交互内容的 EPUB继续走 `WKWebView`
3. `textReflowable`:普通 reflowable EPUB走 **DTCoreText -> `NSAttributedString` -> CoreText 分页 -> 原生文本页视图**
因此,本次“旧引擎直接演进”的主战场已经存在,且就在 `EPUBTextRendering`。Phase 2/3 不需要新起一套阅读器,也不需要改 `RDReaderView`;要做的是升级当前 native reflowable 的 typesetter、属性体系和分页能力。
## 2. Entry Path: URL -> Reader Controller
入口在 `Sources/RDReaderView/RDURLReaderController.swift`
### 2.1 `.epub` 入口
`RDURLReaderController.embedReaderController()` 对扩展名做分支:
- 扩展名是 `epub`
- 直接构造 `RDEPUBReaderController(epubURL:configuration:)`
- 把 reader controller 作为子控制器嵌入当前界面
这里没有直接决定 native 还是 web它只负责把 `.epub` 交给 `RDEPUBReaderController`
### 2.2 `.txt` 入口
TXT 则走 `RDPlainTextBookBuilder` 构造 `RDEPUBTextBook`,再以 external TextBook 模式交给 `RDEPUBReaderController`。这条链路与 EPUB 的原生文本展示在 UI 层会合,但不参与 EPUB reading profile 判定。
## 3. Reading Profile Split
模式分流在 `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift`
### 3.1 判定规则
`RDEPUBParser.readingProfile()` 的真实逻辑是:
- `metadata.layout == .fixed` -> `.webFixedLayout`
- 否则,如果 `hasInteractiveContent() == true` -> `.webInteractive`
- 否则 -> `.textReflowable`
### 3.2 `hasInteractiveContent()` 的判定来源
`hasInteractiveContent()` 会检查两类信号:
- manifest 中是否出现 JS 相关 media type 或 `scripted` property
- spine HTML 中是否出现交互模式特征
具体 regex 关注:
- `<script>`
- `<iframe>`
- `<video>`
- `<audio>`
- `<canvas>`
- `<svg>`
- `<form>`
- `onload` / `onclick` / `touchstart` 等内联事件
- `hype_generated_script`
- `swiper`
- `webview`
这意味着 fixed layout 与 interactive EPUB 的 WebView 边界不是概念约束,而是已经在 parser 层具备明确判定逻辑。
## 4. `.textReflowable` Real Call Chain
当前 native reflowable 的真实调用链如下:
```text
RDURLReaderController
-> RDEPUBReaderController(epubURL:)
-> RDEPUBParser
-> RDEPUBPublication
-> RDEPUBParser.readingProfile()
-> .textReflowable
-> RDEPUBReaderController.paginatePublication()
-> resolvedTextRenderer()
-> RDEPUBDTCoreTextRenderer
-> RDEPUBTextBookBuilder.build()
-> renderChapter(html:baseURL:style:)
-> RDEPUBRenderedChapterContent
-> ss_pageRanges(size:)
-> RDEPUBTextBook / RDEPUBTextChapter / RDEPUBTextPage
-> RDEPUBTextContentView
-> RDReaderView
```
### 4.1 Branch point in `RDEPUBReaderController`
`Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift``paginatePublication(restoreLocation:)` 是当前核心分支点:
- 如果 `publication.readingProfile == .textReflowable`
- 构造 renderer`resolvedTextRenderer()`
- 当前默认返回 `RDEPUBDTCoreTextRenderer()`
- 构造 builder`RDEPUBTextBookBuilder(renderer:)`
- 计算 `pageSize``RDEPUBTextRenderStyle`
- 在后台线程执行 `builder.build(...)`
- 完成后 `applyTextBook(...)`,最终由 `RDReaderView` 刷新内容
- 如果不是 `.textReflowable`
- fixed layout直接创建 fixed snapshot
- reflowable-web / interactive使用 `RDEPUBPaginator`
结论native reflowable 与 WebView 路径已经在 reader controller 内部彻底分开。
## 5. Render Stage: `RDEPUBDTCoreTextRenderer`
`Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` 是当前原生章节渲染入口。
### 5.1 输入
`renderChapter(html:baseURL:style:)` 接收:
- `html: String`
- `baseURL: URL?`
- `style: RDEPUBTextRenderStyle`
### 5.2 主要处理步骤
1. `RDEPUBTextRendererSupport.injectFragmentMarkers(into:)`
- 先往 HTML 注入 fragment marker
- 为后续目录/定位/fragment offset 映射保留锚点
2. 把 HTML 转为 UTF-8 `Data`
3. 调用 `DTHTMLAttributedStringBuilder`
- 通过 `makeAttributedString(from:baseURL:style:)`
- options 由 `dtOptions(baseURL:style:)` 提供
4. 如果 DTCoreText 构建失败
- 回退到 `fallbackRenderedContent(for:style:)`
5. 对生成的 attributed string 做后处理
- `extractFragmentOffsets(from:)`
- `normalizeReadingAttributes(in:style:)`
6. 返回 `RDEPUBRenderedChapterContent`
- `attributedString`
- `fragmentOffsets`
### 5.3 当前 `dtOptions` 的能力边界
当前 options 只有基础排版参数:
- `DTDefaultFontFamily`
- `DTDefaultFontName`
- `DTDefaultFontSize`
- `DTDefaultLineHeightMultiplier`
- `DTDefaultTextColor`
- `NSBaseURLDocumentOption`
- `DTUseiOS6Attributes`
这说明当前旧引擎的 renderer 仍然是“简单 DTCoreText builder + 少量 options”模型还没有形成 WXRead 风格的多层 stylesheet / 自定义属性 / 页面级后处理体系。
## 6. Pagination Stage: `RDEPUBTextBookBuilder`
`Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` 当前负责把章节渲染结果组织为 book/chapter/page 结构。
### 6.1 Chapter iteration
`build(parser:publication:pageSize:style:)` 会:
- 遍历 `publication.spine.enumerated()`
- 只处理 `linear == true`
- 只处理 `html` / `xhtml` 类型
- 从 parser 读取 `rawHTML`
- 计算章节标题
- `normalizeHTML(rawHTML)`
- 调用 renderer 生成 `RDEPUBRenderedChapterContent`
### 6.2 Base URL behavior
builder 把每个 spine item 的章节目录作为 `baseURL`
- `parser.fileURL(forRelativePath: item.href)?.deletingLastPathComponent()`
这意味着当前图片/CSS/相对资源的解析锚点已经是“章节所在目录”,这是 Phase 2 引入更完整 CSS 层和资源解析时必须保留的基础约束。
### 6.3 Data model produced
builder 生成以下原生分页数据结构:
- `RDEPUBTextBook`
- `chapters: [RDEPUBTextChapter]`
- `pages: [RDEPUBTextPage]`
- `RDEPUBTextChapter`
- `chapterIndex`
- `spineIndex`
- `href`
- `title`
- `attributedContent`
- `fragmentOffsets`
- `pages`
- `RDEPUBTextPage`
- `absolutePageIndex`
- `chapterIndex`
- `spineIndex`
- `href`
- `chapterTitle`
- `pageIndexInChapter`
- `totalPagesInChapter`
- `content`
- `contentRange`
- `pageStartOffset`
- `pageEndOffset`
这些模型就是当前原生引擎与 UI/状态层的契约面。
## 7. Current Core Pagination Primitive
当前分页核心在 `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- 扩展方法:`NSAttributedString.ss_pageRanges(size:)`
- 底层实现:
- `CTFramesetterCreateWithAttributedString`
- `CTFramesetterCreateFrame`
- `CTFrameGetVisibleStringRange`
处理循环逻辑是:
1. 从当前 `location` 创建 frame
2. 取 `visibleRange`
3. 把这个 range 记录为一页
4. `location += visibleRange.length`
5. 重复直到结束
### 7.1 Current limitation
这是一种典型的“可见字符串范围切页”模型,优点是简单、稳定、易于接回现有 `RDEPUBTextBook` 结构;缺点是:
- 没有块级避免断页策略
- 没有页面级语义结构
- 没有自定义 layout frame / layouter
- 很难表达复杂图片、表格、背景、分页约束
因此,`ss_pageRanges(size:)` 明确是后续需要被增强或替换的核心点之一。
## 8. Presentation Stage: `RDEPUBTextContentView`
`Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift` 是当前 native text page 的展示层。
### 8.1 Responsibilities
它负责:
- 接收单页 `RDEPUBTextPage`
- 将 `page.content` 赋给 `UITextView`
- 叠加高亮和搜索高亮
- 展示页码
- 输出 selection 和 annotation menu action
### 8.2 Why this matters
这说明当前 `.textReflowable` 路径已经和以下阅读器能力发生绑定:
- selection
- highlight
- annotate
- search highlight
- page number display
因此,如果后续升级分页器或页面语义,必须继续维护 `RDEPUBTextPage``RDEPUBTextContentView` 的兼容面,而不是绕开它另起视图层。
## 9. Location / Highlight / Search Compatibility Chain
当前原生文本路径不只是“能显示文本”,还承担已有 reader 能力链路:
### 9.1 Location mapping
`RDEPUBTextBook` 提供:
- `pageNumber(for:resolver:bookIdentifier:)`
- `location(forPageNumber:bookIdentifier:)`
它依赖:
- `fragmentOffsets`
- `pageStartOffset`
- `pageEndOffset`
- chapter-level `attributedContent.length`
### 9.2 Highlight and selection transport
`RDEPUBTextContentView` 会把选区转成:
- `RDEPUBSelection`
- `RDEPUBTextOffsetRangeInfo`
并通过 `RDEPUBReaderController` 的 text selection normalization / persistence 链路继续工作。
### 9.3 Search compatibility
`RDEPUBTextContentView.applySearchHighlights(...)` 也是基于页内 offset 重叠来计算高亮区域。
结论:`RDEPUBTextBook` 和 `RDEPUBTextPage` 不是单纯的 UI DTO而是 reader state、annotation、search、location mapping 的核心兼容层。
## 10. What Still Uses `WKWebView`
下列链路必须继续视为 WebView 范围:
### 10.1 Fixed Layout
`Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift`
- `loadFixedSpread(...)`
- `handleFixedLayoutLoad(...)`
职责:
- 把 fixed spread 渲染成 HTML
- 通过 `WKWebView.loadHTMLString(...)` 加载
- 处理 fixed spread 资源与 ready fallback
### 10.2 Interactive / web pagination support
`Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift`
职责:
- 使用离屏、非持久化 `WKWebView`
- 按 spine item 加载 HTML
- 测量文档页数
- 为 WebView 路径生成分页信息
尽管它名字叫 paginator但它不是当前 native `.textReflowable` 的分页核心;它是 WebView 渲染/测量体系的一部分。
### 10.3 Web reflowable presentation
`Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift`
职责:
- 构造 `RDEPUBReflowableRenderRequest`
- 对当前 HTML 文档应用 presentation script
- 处理 page index / target location / highlights 的 WebView 表示
这条链路应该保留给 `.webInteractive` 以及任何必须留在 WebView 的内容,不应误认为是当前 native reflowable 主路径。
## 11. Stable Container Contract: `RDReaderView`
`Sources/RDReaderView/RDReaderView.swift` 是当前阅读容器的稳定外壳。
### 11.1 What it owns
它负责:
- `pageCurl`
- `horizontalScroll`
- `verticalScroll`
- RTL / LTR 翻页方向
- landscape dual-page
- cover page pairing
- orientation change handling
- page container reuse和 data source protocol
### 11.2 Why Phase 1 must keep it unchanged
roadmap 已经把 `RDReaderView` 排除在本次改造范围外,而源码也支持这一结论:
- `RDEPUBReaderController` 只把页面数据和内容视图交给 `RDReaderView`
- native text 和 web content 都通过相同的 container contract 接入
- 改它会把内核升级变成“内核 + 容器 + 手势 + 双页策略”的多变量回归
结论:**`RDReaderView` 不修改** 是正确的硬约束,不只是项目管理偏好,而是当前架构稳定性的必要条件。
## 12. Boundary and Exclusion Rules for Next Phases
后续 phase 应默认遵守以下边界:
1. `RDEPUBParser.readingProfile()` 三分流保持不变
2. `webFixedLayout` 继续走 `WKWebView`
3. `webInteractive` 继续走 `WKWebView`
4. `.textReflowable` 继续以 `EPUBTextRendering` 为主战场
5. `RDReaderView` 不修改
6. `RDEPUBTextBook` / `RDEPUBTextChapter` / `RDEPUBTextPage` 是兼容层,后续升级不能轻易破坏
7. `fragmentOffsets`、offset-based location mapping、高亮/搜索 range transport 必须继续可用
## 13. Direct Refactor Entry Points
基于当前代码,后续“旧引擎直接演进”的直接触点应当是:
- `RDEPUBDTCoreTextRenderer`
- 从简单 `dtOptions` 升级为更完整的 stylesheet / HTML preprocessing / attribute pipeline
- `RDEPUBTextBookBuilder`
- 继续保留 chapter -> rendered content -> page model 的骨架
- 后续承接更丰富页面语义、附件/块元素信息
- `RDEPUBTextPaginationSupport`
- 从单纯 `CTFrameGetVisibleStringRange` 切页,升级到更强分页策略
- `RDEPUBTextPage` / `RDEPUBTextChapter`
- 承接页面级元数据,而不是只保存纯 range
- `RDEPUBReaderController`
- 保持 branch point 与 reader integration不把它变成新的排版实现层
## 14. Final Audit Result
Phase 1 的事实基线可以归纳为一句话:
> 当前旧引擎已经拥有一条完整的 native `.textReflowable` 路径,真正需要演进的是 `EPUBTextRendering` 内部的 renderer / page model / pagination semanticsFixed Layout、交互式 EPUB 以及 `RDReaderView` 容器都应保持边界不变。