feat(wxread): align pagination, rendering, and docs

This commit is contained in:
shen
2026-05-24 15:36:01 +08:00
parent a318c0e3d0
commit 2a94921e88
103 changed files with 6124 additions and 4623 deletions
+66 -9
View File
@@ -2,7 +2,7 @@
## 1. 范围与目标
- 代码范围:`Sources/RDReaderView/EPUBTextRendering/`(6 个 Swift 文件)
- 代码范围:`Sources/RDReaderView/EPUBTextRendering/`(9 个 Swift 文件)
- 目标:说明文本渲染路径如何将 EPUB HTML 转换为 NSAttributedString、按字符范围分页、构建书籍模型,并支持全文搜索与 textReflowable 标注定位。
- 主链路关键词:`RDEPUBParser HTML -> DTCoreText 渲染 -> 片段标记注入/提取 -> CoreText 分页 -> RDEPUBTextBook -> 页面查找/位置转换`。
- 适用范围:仅用于 `textReflowable` 阅读配置文件(纯文本可重排 EPUB,如小说)。固定版式和交互式 EPUB 使用 WebView 渲染路径。
@@ -13,7 +13,9 @@
- 文件:`EPUBTextRendering/RDEPUBTextRenderer.swift`
- 职责:
- 定义单方法协议:`renderChapter(html:baseURL:style:) throws -> RDEPUBRenderedChapterContent`
- 定义双方法协议:
- `renderChapter(request: RDEPUBTextChapterRenderRequest) throws -> RDEPUBRenderedChapterContent`(主方法,携带完整上下文)
- `renderChapter(html:baseURL:style:) throws -> RDEPUBRenderedChapterContent`(便捷方法,默认实现转发到主方法)
- 作为渲染后端的抽象点,当前仅 `RDEPUBDTCoreTextRenderer` 一个实现
### 2.2 DTCoreText 渲染器 `RDEPUBDTCoreTextRenderer`
@@ -35,11 +37,12 @@
- 段落样式创建:强制应用配置的行间距和段间距
- 兜底渲染:DTCoreText 不可用时从原始 HTML 字符串生成纯文本 NSAttributedString
### 2.4 分页支持 `NSAttributedString.ss_pageRanges(size:)`
### 2.4 分页支持 `NSAttributedString.ss_pageRanges(size:)` / `rd_paginatedFrames(size:)`
- 文件:`EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
- 职责:
- 基于 CoreText framesetter 的分页
- `rd_paginatedFrames(size:) -> [RDEPUBTextLayoutFrame]`:基于 `RDEPUBTextLayouter` 的增强分页,返回带语义元数据的帧对象
- `ss_pageRanges(size:) -> [NSRange]`:便捷方法,内部调用 `rd_paginatedFrames` 后提取 contentRange
- 从位置 0 开始,反复创建 CTFrame 测量可见字符范围
- 返回 `[NSRange]`,每个 range 对应一页
- 安全保护:`visibleRange.length == 0` 时 break 防止死循环
@@ -61,6 +64,22 @@
- 在已渲染的 NSAttributedString 纯文本上执行线性搜索
- 返回 `RDEPUBSearchMatch` 数组(含 href、progression、预览文本、匹配位置)
### 2.7 CoreText 分页引擎 `RDEPUBTextLayouter` / `RDEPUBTextLayoutFrame`
- 文件:`EPUBTextRendering/RDEPUBTextLayouter.swift`、`EPUBTextRendering/RDEPUBTextLayoutFrame.swift`
- 职责:
- `RDEPUBTextLayouter`:封装 `CTFramesetter`,逐帧计算分页结果,支持语义边界调整(避免在附件/代码块/列表中间断页)
- `RDEPUBTextLayoutFrame`:单帧分页结果,包含 contentRange、breakReason、blockRange、attachment 信息、语义提示和诊断数据
- 替代原有的纯 `NSRange` 分页,返回带丰富元数据的帧对象
### 2.8 纯文本构建器 `RDPlainTextBookBuilder`
- 文件:`EPUBTextRendering/RDPlainTextBookBuilder.swift`
- 职责:
- 从纯文本文件(.txt 等)构建 `RDEPUBTextBook`
- 将纯文本按段落切分后渲染为 NSAttributedString
- 按页面尺寸分页,输出与 EPUB 文本路径一致的书籍模型
## 3. 主流程(代码级)
### 3.1 完整渲染-分页流程
@@ -159,15 +178,53 @@ RDEPUBTextRenderStyle
└── backgroundColor: UIColor? // 背景色
```
### 5.2 渲染输出
### 5.2 渲染请求上下文
```swift
RDEPUBTextChapterRenderRequest
├── context: RDEPUBTextChapterContext
│ ├── href: String
│ ├── title: String
│ ├── html: String
│ ├── baseURL: URL?
│ ├── stylesheet: RDEPUBTextStyleSheetPackage
│ │ ├── layers: [RDEPUBTextStyleSheetLayer]
│ │ │ └── kind (.default/.replace/.dark/.epub/.user) + css
│ │ └── combinedCSS (computed)
│ └── resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic]
└── style: RDEPUBTextRenderStyle
```
### 5.3 渲染输出
```swift
RDEPUBRenderedChapterContent
├── attributedString: NSAttributedString // 渲染后的富文本
└── fragmentOffsets: [String: Int] // fragment ID -> 字符偏移映射
├── fragmentOffsets: [String: Int] // fragment ID -> 字符偏移映射
└── resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic]
```
### 5.3 页面模型
### 5.4 分页帧模型(新增)
```swift
RDEPUBTextLayoutFrame
├── contentRange: NSRange // 本帧字符范围
├── breakReason: RDEPUBTextPageBreakReason // 分页原因
├── blockRange: NSRange? // 所属 block 范围
├── attachmentRanges: [NSRange] // 附件范围
├── attachmentKinds: [RDEPUBTextAttachmentKind]
├── blockKinds: [RDEPUBTextBlockKind] // (.paragraph/.list/.table/.code/.blockquote/.attachment/.generic)
├── semanticHints: [RDEPUBTextSemanticHint] // (.avoidPageBreakInside/.pageBreakBefore/.pageBreakAfter/.pageRelate)
├── attachmentPlacements: [RDEPUBTextAttachmentPlacement] // (.inline/.baseline/.centered)
├── trailingFragmentID: String? // 帧尾最近的 fragment ID
└── diagnostics: [String] // 分页诊断信息
RDEPUBTextLayouter
├── init(attributedString:pageSize:)
└── layoutFrames(fragmentOffsets:) -> [RDEPUBTextLayoutFrame]
```
### 5.5 页面模型
```swift
RDEPUBTextPage
@@ -184,7 +241,7 @@ RDEPUBTextPage
└── pageEndOffset: Int // 结束字符偏移
```
### 5.4 章节模型
### 5.6 章节模型
```swift
RDEPUBTextChapter
@@ -197,7 +254,7 @@ RDEPUBTextChapter
└── pages: [RDEPUBTextPage]
```
### 5.5 书籍模型
### 5.7 书籍模型
```swift
RDEPUBTextBook