docs(01-01): complete current engine audit plan
This commit is contained in:
parent
743988a78b
commit
cc2573552c
@ -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 |
|
||||
|
||||
@ -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 | - |
|
||||
|
||||
@ -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*
|
||||
@ -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 semantics;Fixed Layout、交互式 EPUB 以及 `RDReaderView` 容器都应保持边界不变。
|
||||
Loading…
Reference in New Issue
Block a user