- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
643 lines
22 KiB
Markdown
643 lines
22 KiB
Markdown
# EPUBCore 模块代码级参考文档
|
||
|
||
> 最后更新:2026-06-18
|
||
|
||
---
|
||
|
||
## 1. 模块概述
|
||
|
||
`EPUBCore` 是 ReadViewSDK 的**基础层**,负责 EPUB 文件解析、资源管理、WebView 渲染、JavaScript 桥接、CFI 定位、搜索和分页计算。它是上层 EPUBTextRendering 和 EPUBUI 的数据提供者。
|
||
|
||
**文件清单(~50 个 Swift 文件):**
|
||
|
||
| 子系统 | 核心文件 | 职责 |
|
||
|--------|----------|------|
|
||
| **解析器** | `RDEPUBParser*.swift` | EPUB 解压、OPF 解析、TOC 解析、资源提取 |
|
||
| **数据模型** | `RDEPUBModels.swift` | Metadata、ManifestItem、SpineItem、TOC 等基础模型 |
|
||
| **Publication** | `RDEPUBPublication.swift` | 出版物抽象,聚合 parser 和 resourceResolver |
|
||
| **阅读会话** | `RDEPUBReadingSession.swift` | 管理分页快照、阅读位置、待导航状态 |
|
||
| **WebView** | `RDEPUBWebView*.swift` | WKWebView 封装,支持 Reflowable 和 Fixed Layout |
|
||
| **JS 桥接** | `RDEPUBJavaScriptBridge.swift` | JavaScript 消息协议和脚本注入 |
|
||
| **资源解析** | `RDEPUBResourceResolver.swift` | href 归一化、文件路径解析 |
|
||
| **URL 方案** | `RDEPUBResourceURLSchemeHandler.swift` | 自定义 `ss-reader://` 协议处理 |
|
||
| **分页器** | `RDEPUBPaginator.swift` | WebView 分页计算(按章测量页数) |
|
||
| **CFI** | `CFI/RDEPUBCFI*.swift` | EPUB CFI 解析、生成、序列化、恢复 |
|
||
| **搜索** | `RDEPUBSearchEngine.swift` | HTML 全文搜索 |
|
||
| **样式** | `RDEPUBStyleSheetBuilder.swift` | CSS 注入与分页样式生成 |
|
||
| **偏好** | `RDEPUBPreferences.swift` | 阅读偏好设置模型 |
|
||
| **资源文件** | `RDEPUBAssetRepository.swift` | JS/CSS/HTML 资源文件加载 |
|
||
| **笔记** | `Notes/RDEPUBNote*.swift` | 脚注/尾注检测与解析 |
|
||
|
||
---
|
||
|
||
## 2. 解析器子系统
|
||
|
||
### 2.1 RDEPUBParser
|
||
|
||
**文件:** `RDEPUBParser.swift` + 扩展文件
|
||
|
||
EPUB 文件解析的核心类,负责从 .epub 文件中提取结构化数据。
|
||
|
||
```swift
|
||
public final class RDEPUBParser {
|
||
public internal(set) var metadata: RDEPUBMetadata // 书籍元数据
|
||
public internal(set) var manifest: [String: RDEPUBManifestItem] // 资源清单
|
||
public internal(set) var spine: [RDEPUBSpineItem] // 阅读顺序
|
||
public internal(set) var tableOfContents: [EPUBTableOfContentsItem] // 目录
|
||
public internal(set) var extractionRootURL: URL? // 解压根目录
|
||
public internal(set) var opfURL: URL? // OPF 文件路径
|
||
|
||
public var opfDirectoryURL: URL? // OPF 所在目录
|
||
|
||
/// 解析 EPUB 文件(解压 + 解析 container.xml + 解析 OPF)
|
||
public func parse(epubURL: URL) throws
|
||
|
||
/// 直接解析 OPF 文件
|
||
public func parseOPF(at opfURL: URL) throws
|
||
|
||
/// 解析 Navigation Document 或 NCX
|
||
public func parseTOC() -> [EPUBTableOfContentsItem]
|
||
|
||
/// 从 parser 创建 Publication
|
||
public func makePublication() -> RDEPUBPublication
|
||
}
|
||
```
|
||
|
||
**扩展文件职责:**
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `RDEPUBParser+Archive.swift` | ZIP 解压(ZIPFoundation),提取到 Caches 目录 |
|
||
| `RDEPUBParser+Package.swift` | OPF XML SAX 解析(metadata/manifest/spine) |
|
||
| `RDEPUBParser+TOC.swift` | Navigation Document 和 NCX 解析 |
|
||
| `RDEPUBParser+Resources.swift` | 资源文件读取(HTML 字符串、文件 URL) |
|
||
| `RDEPUBParser+ReadingProfile.swift` | 阅读配置文件检测 |
|
||
|
||
### 2.2 RDEPUBParserError
|
||
|
||
```swift
|
||
public enum RDEPUBParserError: LocalizedError {
|
||
case archiveOpenFailed(URL)
|
||
case missingContainerXML
|
||
case missingRootFile
|
||
case invalidRootFilePath(String)
|
||
case invalidXML(URL)
|
||
case missingManifestItem(idref: String)
|
||
case emptySpine
|
||
case invalidArchiveEntryPath(String)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 数据模型
|
||
|
||
### 3.1 RDEPUBMetadata
|
||
|
||
**文件:** `RDEPUBModels.swift`
|
||
|
||
```swift
|
||
public struct RDEPUBMetadata: Codable, Equatable {
|
||
public var identifier: String? // ISBN/UUID
|
||
public var title: String // 书名
|
||
public var author: String? // 作者
|
||
public var language: String? // 语言
|
||
public var version: String? // EPUB 版本
|
||
public var layout: RDEPUBLayout // reflowable / fixed
|
||
public var spread: String? // none / auto
|
||
public var readingProgression: RDEPUBReadingProgression // ltr / rtl / auto
|
||
}
|
||
```
|
||
|
||
### 3.2 RDEPUBManifestItem
|
||
|
||
```swift
|
||
public struct RDEPUBManifestItem: Codable, Equatable {
|
||
public var id: String // 资源 ID
|
||
public var href: String // 相对路径
|
||
public var mediaType: String // MIME 类型
|
||
public var properties: [String] // 属性(nav, mathml, svg 等)
|
||
public var fallback: String? // 回退资源 ID
|
||
public var mediaOverlay: String? // 媒体叠加 ID
|
||
public var title: String? // 标题
|
||
|
||
public var isNavigationDocument: Bool // 是否为 Navigation Document
|
||
public var isNCX: Bool // 是否为 NCX 文件
|
||
}
|
||
```
|
||
|
||
### 3.3 RDEPUBSpineItem
|
||
|
||
```swift
|
||
public struct RDEPUBSpineItem: Codable, Equatable {
|
||
public var idref: String // 引用 manifest ID
|
||
public var href: String // 解析后的相对路径
|
||
public var mediaType: String // MIME 类型
|
||
public var title: String // 章节标题
|
||
public var linear: Bool // 是否为线性内容
|
||
public var properties: [String] // 属性(page-spread-left/right)
|
||
public var pageSpread: RDEPUBPageSpread? // 页面位置
|
||
public var layout: RDEPUBLayout? // 章节级布局覆盖
|
||
}
|
||
```
|
||
|
||
### 3.4 EPUBTableOfContentsItem
|
||
|
||
```swift
|
||
public struct EPUBTableOfContentsItem: Codable, Equatable {
|
||
public var title: String // 目录标题
|
||
public var href: String // 链接地址
|
||
public var children: [EPUBTableOfContentsItem] // 子目录(支持多级)
|
||
}
|
||
```
|
||
|
||
### 3.5 枚举类型
|
||
|
||
```swift
|
||
public enum RDEPUBLayout: String, Codable {
|
||
case reflowable // 文本重排
|
||
case fixed // 固定布局
|
||
}
|
||
|
||
public enum RDEPUBReadingProfile: String, Codable {
|
||
case webInteractive // WebView 交互模式(原 EPUB 渲染)
|
||
case webFixedLayout // WebView 固定布局
|
||
case textReflowable // 原生文本渲染(DTCoreText)
|
||
}
|
||
|
||
public enum RDEPUBReadingProgression: String, Codable {
|
||
case ltr, rtl, auto
|
||
}
|
||
|
||
public enum RDEPUBPageSpread: String, Codable {
|
||
case left, right, center
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Publication
|
||
|
||
**文件:** `RDEPUBPublication.swift`
|
||
|
||
聚合 `RDEPUBParser` 和 `RDEPUBResourceResolver`,提供统一的出版物访问接口。
|
||
|
||
```swift
|
||
public final class RDEPUBPublication {
|
||
public let parser: RDEPUBParser
|
||
public let resourceResolver: RDEPUBResourceResolver
|
||
|
||
public var metadata: RDEPUBMetadata
|
||
public var manifest: [String: RDEPUBManifestItem]
|
||
public var spine: [RDEPUBSpineItem]
|
||
public var tableOfContents: [EPUBTableOfContentsItem]
|
||
public var layout: RDEPUBLayout
|
||
public var readingProfile: RDEPUBReadingProfile
|
||
public var readingProgression: RDEPUBReadingProgression
|
||
public var bookIdentifier: String?
|
||
|
||
/// 判断 Fixed Layout 是否启用双页展开
|
||
public func fixedLayoutSpreadEnabled(for preferences: RDEPUBPreferences, viewportSize: CGSize) -> Bool
|
||
|
||
/// 生成 Fixed Layout 的 Spread 列表
|
||
public func makeFixedSpreads(preferences: RDEPUBPreferences, viewportSize: CGSize) -> [EPUBFixedSpread]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 阅读会话
|
||
|
||
**文件:** `RDEPUBReadingSession.swift`
|
||
|
||
管理阅读过程中的分页快照、阅读位置和待导航状态。
|
||
|
||
```swift
|
||
public final class RDEPUBReadingSession {
|
||
public typealias PaginationSnapshot = (pages: [EPUBPage], chapters: [EPUBChapterInfo])
|
||
|
||
public let publication: RDEPUBPublication
|
||
public private(set) var navigatorState: RDEPUBNavigatorState
|
||
public private(set) var activePages: [EPUBPage] // 当前活跃页列表
|
||
public private(set) var activeChapters: [EPUBChapterInfo] // 当前活跃章节列表
|
||
public private(set) var stagedPages: [EPUBPage]? // 暂存页列表(待应用)
|
||
public private(set) var stagedChapters: [EPUBChapterInfo]?
|
||
public private(set) var currentViewport: RDEPUBViewport?
|
||
public private(set) var currentReadingContext: RDEPUBReadingContext?
|
||
|
||
// 状态管理
|
||
public func transition(to state: RDEPUBNavigatorState)
|
||
public func resetRuntimeState()
|
||
|
||
// 快照管理
|
||
public func setActiveSnapshot(_ snapshot: PaginationSnapshot)
|
||
public func stageSnapshot(_ snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?)
|
||
public func consumeStagedSnapshotIfAllowed() -> (snapshot: PaginationSnapshot, restoreLocation: RDEPUBLocation?)?
|
||
|
||
// 导航
|
||
public func queueNavigation(to location: RDEPUBLocation, ...) -> Int?
|
||
public func updateReadingContext(pageNumber:location:spineIndex:chapterIndex:bookIdentifier:)
|
||
public func currentReadingLocation(bookIdentifier: String?) -> RDEPUBLocation?
|
||
|
||
// 分页快照生成
|
||
public func makePaginationSnapshot(pageCounts:preferences:layoutContext:) -> PaginationSnapshot
|
||
}
|
||
```
|
||
|
||
### 5.1 RDEPUBNavigatorState
|
||
|
||
```swift
|
||
public enum RDEPUBNavigatorState: String, Codable {
|
||
case initializing // 初始化中
|
||
case loading // 加载中
|
||
case idle // 空闲(可应用快照)
|
||
case jumping // 跳转中
|
||
case moving // 翻页中
|
||
case repaginating // 重新分页中
|
||
|
||
public var isStableForSnapshotApplication: Bool // 仅 idle 状态为 true
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. WebView 子系统
|
||
|
||
### 6.1 RDEPUBWebView
|
||
|
||
**文件:** `RDEPUBWebView.swift` + 扩展文件
|
||
|
||
`UIView` 子类,内部封装 `WKWebView`,支持 Reflowable 和 Fixed Layout 两种渲染模式。
|
||
|
||
```swift
|
||
public final class RDEPUBWebView: UIView {
|
||
public weak var delegate: RDEPUBWebViewDelegate?
|
||
public var onRendered: (() -> Void)?
|
||
|
||
var publication: RDEPUBPublication?
|
||
var currentRenderRequest: RDEPUBRenderRequest?
|
||
var webView: WKWebView?
|
||
var schemeHandler: RDEPUBResourceURLSchemeHandler?
|
||
var currentSpineIndex: Int
|
||
var currentHref: String
|
||
var currentPageIndex: Int
|
||
var currentTotalPagesInChapter: Int
|
||
var viewportSize: CGSize
|
||
var currentFontSize: CGFloat
|
||
var currentLineHeightMultiple: CGFloat
|
||
var targetLocation: RDEPUBLocation?
|
||
var pendingHighlights: [RDEPUBHighlight]
|
||
}
|
||
```
|
||
|
||
**扩展文件:**
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `RDEPUBWebView+Configuration.swift` | WKWebView 配置(scheme handler、user script) |
|
||
| `RDEPUBWebView+Reflowable.swift` | Reflowable 模式渲染逻辑 |
|
||
| `RDEPUBWebView+FixedLayout.swift` | Fixed Layout 模式渲染逻辑 |
|
||
| `RDEPUBWebView+JavaScriptBridge.swift` | JS 消息接收与处理 |
|
||
| `RDEPUBWebView+Search.swift` | 搜索高亮渲染 |
|
||
|
||
### 6.2 RDEPUBWebViewDelegate
|
||
|
||
```swift
|
||
public protocol RDEPUBWebViewDelegate: AnyObject {
|
||
func epubWebView(_ webView: RDEPUBWebView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int)
|
||
func epubWebView(_ webView: RDEPUBWebView, didChangeSelection selection: RDEPUBSelection?, spineIndex: Int)
|
||
func epubWebView(_ webView: RDEPUBWebView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction)
|
||
func epubWebView(_ webView: RDEPUBWebView, didActivateInternalLink location: RDEPUBLocation, fromSpineIndex: Int)
|
||
func epubWebView(_ webView: RDEPUBWebView, didActivateExternalLink url: URL)
|
||
func epubWebView(_ webView: RDEPUBWebView, didLogJavaScriptError message: String)
|
||
func epubWebViewDidFinishRendering(_ webView: RDEPUBWebView)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. JavaScript 桥接
|
||
|
||
**文件:** `RDEPUBJavaScriptBridge.swift`
|
||
|
||
定义 Native ↔ WebView 的消息协议和脚本注入。
|
||
|
||
### 7.1 消息类型
|
||
|
||
```swift
|
||
enum RDEPUBJavaScriptBridgeMessage: String, CaseIterable {
|
||
case progressionChanged = "ssReaderProgressionChanged" // 阅读进度变化
|
||
case selectionChanged = "ssReaderSelectionChanged" // 文本选择变化
|
||
case internalLink = "ssReaderInternalLink" // 内部链接点击
|
||
case externalLink = "ssReaderExternalLink" // 外部链接点击
|
||
case javaScriptError = "ssReaderJSError" // JS 错误
|
||
case fixedLayoutReady = "ssReaderFixedLayoutReady" // Fixed Layout 就绪
|
||
}
|
||
```
|
||
|
||
### 7.2 注入的 JS 资源
|
||
|
||
- `rangy-core.js` — 范围与选择 API
|
||
- `rangy-serializer.js` — 选择序列化
|
||
- `cssInjector.js` — CSS 注入
|
||
- `WeReadApi.js` — 阅读器 API(分页、跳转、高亮)
|
||
- `epub-bridge.js` — 消息桥接
|
||
|
||
### 7.3 关键方法
|
||
|
||
```swift
|
||
enum RDEPUBJavaScriptBridge {
|
||
static var messageNames: [String] // 所有消息名称
|
||
static var userScript: String // 注入的 User Script
|
||
|
||
/// 生成 Reflowable 渲染脚本
|
||
static func applyPresentationScript(for request: RDEPUBReflowableRenderRequest) -> String
|
||
|
||
/// 生成 Fixed Layout 渲染脚本
|
||
static func applyFixedPresentationScript(for request: RDEPUBFixedRenderRequest) -> String
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 资源解析
|
||
|
||
**文件:** `RDEPUBResourceResolver.swift`
|
||
|
||
负责 href 归一化、文件路径解析和 spine 索引查找。
|
||
|
||
```swift
|
||
public final class RDEPUBResourceResolver {
|
||
public var opfDirectoryURL: URL?
|
||
|
||
/// 将相对路径转为文件 URL
|
||
public func fileURL(forRelativePath relativePath: String) -> URL?
|
||
|
||
/// 将相对路径转为 ss-reader:// 资源 URL
|
||
public func resourceURL(forRelativePath relativePath: String) -> URL?
|
||
|
||
/// href 归一化(去除 fragment,解析相对路径)
|
||
public func normalizedHref(_ href: String, relativeToSpineIndex spineIndex: Int? = nil) -> String?
|
||
public func normalizedHref(_ href: String, relativeToHref baseHref: String) -> String?
|
||
|
||
/// 位置归一化(合并 href + fragment + progression)
|
||
public func normalizedLocation(_ location: RDEPUBLocation, relativeToSpineIndex spineIndex: Int?, bookIdentifier: String?) -> RDEPUBLocation?
|
||
|
||
/// 根据 href 查找 spine 索引
|
||
public func spineIndex(forNormalizedHref normalizedHref: String) -> Int?
|
||
public func spineIndex(for location: RDEPUBLocation) -> Int?
|
||
|
||
/// 获取 spine 项的 href 和 title
|
||
public func href(forSpineIndex spineIndex: Int) -> String?
|
||
public func title(forSpineIndex spineIndex: Int) -> String?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. URL 方案处理器
|
||
|
||
**文件:** `RDEPUBResourceURLSchemeHandler.swift`
|
||
|
||
实现 `WKURLSchemeHandler`,拦截 `ss-reader://book/` 请求,从 EPUB 解压目录流式读取资源文件。
|
||
|
||
```swift
|
||
public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler {
|
||
public static let scheme = "ss-reader"
|
||
public static let host = "book"
|
||
|
||
public init(parser: RDEPUBParser)
|
||
|
||
// WKURLSchemeHandler
|
||
public func webView(_ webView: WKWebView, start urlSchemeTask: any WKURLSchemeTask)
|
||
public func webView(_ webView: WKWebView, stop urlSchemeTask: any WKURLSchemeTask)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 分页器
|
||
|
||
**文件:** `RDEPUBPaginator.swift`
|
||
|
||
使用隐藏的 WKWebView 逐章加载 HTML,通过 JS 测量每章的页数。
|
||
|
||
```swift
|
||
public final class RDEPUBPaginator: NSObject {
|
||
/// 计算所有章节的页数
|
||
public func calculate(
|
||
parser: RDEPUBParser,
|
||
hostingView: UIView,
|
||
presentation: RDEPUBPresentationStyle,
|
||
completion: @escaping ([Int]) -> Void
|
||
)
|
||
|
||
/// 计算单个章节的页数
|
||
public func calculateSingleChapter(
|
||
parser: RDEPUBParser,
|
||
spineIndex: Int,
|
||
hostingView: UIView,
|
||
presentation: RDEPUBPresentationStyle,
|
||
completion: @escaping (Int) -> Void
|
||
)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 搜索引擎
|
||
|
||
**文件:** `RDEPUBSearchEngine.swift`
|
||
|
||
```swift
|
||
protocol RDEPUBSearchEngine {
|
||
func search(keyword: String) -> [RDEPUBSearchMatch]
|
||
}
|
||
```
|
||
|
||
### RDEPUBHTMLSearchEngine
|
||
|
||
基于 HTML 的搜索引擎,逐章加载 HTML → 转为纯文本 → 关键词匹配。
|
||
|
||
```swift
|
||
final class RDEPUBHTMLSearchEngine: RDEPUBSearchEngine {
|
||
init(parser: RDEPUBParser, publication: RDEPUBPublication)
|
||
func search(keyword: String) -> [RDEPUBSearchMatch]
|
||
}
|
||
```
|
||
|
||
### RDEPUBTextSearchEngine(EPUBTextRendering 模块)
|
||
|
||
基于原生文本的搜索引擎,在 `NSAttributedString` 上直接搜索,性能更好。
|
||
|
||
```swift
|
||
final class RDEPUBTextSearchEngine: RDEPUBSearchEngine {
|
||
init(textBook: RDEPUBTextBook, publication: RDEPUBPublication)
|
||
func search(keyword: String) -> [RDEPUBSearchMatch]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 12. CFI 子系统
|
||
|
||
**文件:** `CFI/RDEPUBCFI*.swift`(13 个文件)
|
||
|
||
实现 EPUB CFI(Canonical Fragment Identifier)标准,用于精确定位 EPUB 内容。
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `RDEPUBCFI.swift` | CFI 值类型,核心解析入口 |
|
||
| `RDEPUBCFIParser.swift` | CFI 字符串解析器 |
|
||
| `RDEPUBCFISerializer.swift` | CFI 序列化为字符串 |
|
||
| `RDEPUBCFIPath.swift` | CFI 路径节点 |
|
||
| `RDEPUBCFIRange.swift` | CFI 范围(起止点) |
|
||
| `RDEPUBCFIResolver.swift` | CFI → DOM 位置解析 |
|
||
| `RDEPUBCFIGenerator.swift` | DOM 位置 → CFI 生成 |
|
||
| `RDEPUBCFIMap.swift` | 章节级 CFI 映射表 |
|
||
| `RDEPUBCFIDOMPathBuilder.swift` | DOM 路径构建 |
|
||
| `RDEPUBCFITextAssertion.swift` | 文本断言(偏移校验) |
|
||
| `RDEPUBCFIRecoveryEngine.swift` | CFI 恢复引擎(节点变化后重新定位) |
|
||
| `RDEPUBCFICompatibility.swift` | CFI 兼容性处理 |
|
||
| `RDEPUBCFIError.swift` | 错误类型 |
|
||
|
||
---
|
||
|
||
## 13. 样式构建
|
||
|
||
**文件:** `RDEPUBStyleSheetBuilder.swift`
|
||
|
||
```swift
|
||
public enum RDEPUBStyleSheetBuilder {
|
||
/// 向 HTML 注入分页 CSS(用于 Paginator 测量)
|
||
public static func injectPaginationCSS(into html: String, presentation: RDEPUBPresentationStyle) -> String
|
||
|
||
/// 生成渲染用 CSS(viewport 尺寸、字体、行高、主题色)
|
||
public static func renderCSS(for presentation: RDEPUBPresentationStyle) -> String
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 14. 偏好设置
|
||
|
||
**文件:** `RDEPUBPreferences.swift`
|
||
|
||
```swift
|
||
public struct RDEPUBPreferences: Equatable {
|
||
public var fontSize: CGFloat // 字体大小
|
||
public var lineHeightMultiple: CGFloat // 行高倍数
|
||
public var reflowableContentInsets: UIEdgeInsets // Reflowable 内容边距
|
||
public var fixedContentInset: UIEdgeInsets // Fixed Layout 内容边距
|
||
public var themeBackgroundColor: String? // 主题背景色(CSS)
|
||
public var themeTextColor: String? // 主题文字色(CSS)
|
||
public var fixedBackgroundColor: String? // Fixed 背景色
|
||
public var fixedLayoutFit: RDEPUBFixedLayoutFit // Fixed 适配模式
|
||
public var fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode // 双页模式
|
||
public var numberOfColumns: Int // 列数
|
||
public var columnGap: CGFloat // 列间距
|
||
|
||
/// 生成 PresentationStyle
|
||
public func presentationStyle(viewportSize: CGSize) -> RDEPUBPresentationStyle
|
||
|
||
/// 生成渲染请求
|
||
public func renderRequest(for page: EPUBPage, publication: RDEPUBPublication, viewportSize: CGSize, ...) -> RDEPUBRenderRequest?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 15. 渲染请求模型
|
||
|
||
**文件:** `RDEPUBRenderRequest.swift`
|
||
|
||
```swift
|
||
public enum RDEPUBRenderRequest: Equatable {
|
||
case reflowable(RDEPUBReflowableRenderRequest)
|
||
case fixed(RDEPUBFixedRenderRequest)
|
||
|
||
public var isFixedLayout: Bool
|
||
public var primarySpineIndex: Int
|
||
public var primaryHref: String
|
||
}
|
||
|
||
public struct RDEPUBReflowableRenderRequest: Equatable {
|
||
public var spineIndex: Int
|
||
public var href: String
|
||
public var pageIndex: Int
|
||
public var totalPagesInChapter: Int
|
||
public var presentation: RDEPUBPresentationStyle
|
||
public var targetLocation: RDEPUBLocation?
|
||
public var highlights: [RDEPUBHighlight]
|
||
public var searchPresentation: RDEPUBSearchPresentation?
|
||
}
|
||
|
||
public struct RDEPUBFixedRenderRequest: Equatable {
|
||
public var spread: EPUBFixedSpread
|
||
public var viewportSize: CGSize
|
||
public var contentInset: UIEdgeInsets
|
||
public var fit: RDEPUBFixedLayoutFit
|
||
}
|
||
```
|
||
|
||
### RDEPUBPresentationStyle
|
||
|
||
```swift
|
||
public struct RDEPUBPresentationStyle: Equatable {
|
||
public var viewportSize: CGSize
|
||
public var contentInsets: UIEdgeInsets
|
||
public var fontSize: CGFloat
|
||
public var lineHeightMultiple: CGFloat
|
||
public var numberOfColumns: Int
|
||
public var columnGap: CGFloat
|
||
public var themeBackgroundColor: String?
|
||
public var themeTextColor: String?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 16. 资源仓库
|
||
|
||
**文件:** `RDEPUBAssetRepository.swift`
|
||
|
||
管理 SDK 内置的 JS/CSS/HTML 资源文件加载。
|
||
|
||
```swift
|
||
enum RDEPUBAsset: String {
|
||
case rangyCoreScript, rangySerializerScript, cssInjectorScript
|
||
case weReadAPIScript, bridgeScript, fixedLayoutTemplate
|
||
case wxReadDefaultCSS, wxReadReplaceCSS, wxReadDarkCSS, wxReadLatinReplaceCSS
|
||
}
|
||
|
||
enum RDEPUBAssetRepository {
|
||
/// 加载资源文件内容,支持模板替换
|
||
static func string(for asset: RDEPUBAsset, replacements: [String: String] = [:]) -> String
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 17. 笔记子系统
|
||
|
||
**文件:** `Notes/RDEPUBNote*.swift`
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `RDEPUBNoteModels.swift` | 脚注/尾注数据模型 |
|
||
| `RDEPUBNoteDetector.swift` | 从 HTML 中检测脚注标记 |
|
||
| `RDEPUBNoteResolver.swift` | 解析脚注内容 |
|
||
|
||
---
|
||
|
||
## 18. 设计模式总结
|
||
|
||
| 模式 | 应用 |
|
||
|------|------|
|
||
| **Builder 模式** | `RDEPUBParser` → `RDEPUBPublication` 构建链 |
|
||
| **Strategy 模式** | `RDEPUBSearchEngine` 协议,HTML 和 Text 两种实现 |
|
||
| **URL Scheme 拦截** | `RDEPUBResourceURLSchemeHandler` 自定义协议 |
|
||
| **消息桥接** | `RDEPUBJavaScriptBridge` Native ↔ JS 双向通信 |
|
||
| **快照管理** | `RDEPUBReadingSession` staged/active 双快照 |
|
||
| **状态机** | `RDEPUBNavigatorState` 管理导航状态 |
|