feat(wxread): align pagination, rendering, and docs
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user