diff --git a/Doc/ARCHITECTURE.md b/Doc/ARCHITECTURE.md index 0a73fdc..39a093f 100644 --- a/Doc/ARCHITECTURE.md +++ b/Doc/ARCHITECTURE.md @@ -4,8 +4,8 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即用的 EPUB 阅读能力,并保留对纯文本翻页的支持。 -- **最低 iOS 版本**:15.0(podspec 仍标注 9.0,实际 demo Podfile 要求 15.0) -- **Swift 版本**:5.0+ +- **最低 iOS 版本**:15.0 +- **Swift 版本**:5.10+ - **依赖**:ZIPFoundation(EPUB 解压)、DTCoreText(文本 EPUB 渲染) - **Demo 额外依赖**:SnapKit、SSAlertSwift @@ -21,10 +21,13 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即 │ ┌───────────────────────────▼─────────────────────────────┐ │ EPUBUI 层(library 级读者 UI) │ -│ RDEPUBReaderController(开箱即用入口) │ -│ RDEPUBReaderConfiguration / Theme / Persistence │ -│ TopToolView / BottomToolView / ChapterList / Settings │ +│ RDEPUBReaderController(开箱即用入口,~1995 行) │ +│ RDEPUBReaderConfiguration / Theme / Settings / Persistence│ +│ TopToolView / BottomToolView / ToolView 基类 │ +│ ChapterList / Highlights / Bookmarks / Settings 面板 │ │ RDEPUBTextContentView / RDEPUBWebContentView │ +│ RDEPUBPageInteractionController / SelectionOverlayView │ +│ RDEPUBPageLayoutSnapshot / RDURLReaderController │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ @@ -40,10 +43,13 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即 │ EPUBCore 层(EPUB 引擎) │ │ │ │ Publication 层 │ -│ RDEPUBParser(+Archive / +Package / +TOC / …) │ +│ RDEPUBParser(+Archive / +Package / +TOC / │ +│ +ReadingProfile / +Resources) │ │ RDEPUBPublication(出版物聚合对象) │ │ RDEPUBModels(metadata / manifest / spine 模型) │ │ RDEPUBReadingModels(location / viewport / highlight) │ +│ RDEPUBTextAnchor / RDEPUBTextRangeAnchor(文本锚点) │ +│ RDEPUBRenderRequest(渲染请求模型) │ │ │ │ Services 层 │ │ RDEPUBResourceResolver(资源 URL 统一入口) │ @@ -57,11 +63,12 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即 │ │ │ Resource View 层 │ │ RDEPUBWebView(+Configuration / +Reflowable / │ -│ +FixedLayout / +JavaScriptBridge) │ +│ +FixedLayout / +JavaScriptBridge / │ +│ +Search) │ │ RDEPUBResourceURLSchemeHandler(ss-reader:// 协议) │ │ RDEPUBStyleSheetBuilder / RDEPUBJavaScriptBridge │ │ RDEPUBFixedLayoutTemplate / RDEPUBAssetRepository │ -│ RDEPUBRenderRequest │ +│ RDEPUBWebViewDebug(调试日志工具) │ │ │ │ Search 层 │ │ RDEPUBSearchEngine(协议)/ RDEPUBHTMLSearchEngine │ @@ -75,15 +82,10 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即 │ RDEPUBTextRendererSupport / RDEPUBTextPaginationSupport │ │ RDEPUBTextBookBuilder / RDPlainTextBookBuilder │ │ RDEPUBTextLayouter / RDEPUBTextLayoutFrame │ +│ RDEPUBTextBookCache / RDEPUBChapterData │ +│ RDEPUBTextIndexTable / RDEPUBTextPerformanceSampler │ │ RDEPUBTextSearchEngine │ └──────────────────────────────────────────────────────────┘ - -┌──────────────────────────────────────────────────────────┐ -│ LegacyRDReaderController/(旧版阅读器,保留兼容) │ -│ RDReaderController(旧版)/ RDReaderManager │ -│ RDReaderContentView / RDReaderEPUBContentView │ -│ SSChapterListController / SSEventTrigger │ -└──────────────────────────────────────────────────────────┘ ``` --- @@ -239,6 +241,7 @@ session.clearPendingNavigation() // 取消待执行跳转 | `+Reflowable` | 注入分页 CSS、滚动到指定 progression、接收 JS 事件 | | `+FixedLayout` | fixed-layout HTML wrapper 生成和加载 | | `+JavaScriptBridge` | JS ↔ Swift 消息路由、选区、高亮、进度上报 | +| `+Search` | 搜索高亮装饰 | #### ss-reader:// 协议 @@ -323,7 +326,10 @@ present(controller, animated: true) | `showsTableOfContents` | `true` | 是否显示目录入口 | | `allowsHighlights` | `true` | 是否显示高亮入口 | | `showsSettingsPanel` | `true` | 是否显示设置入口 | +| `reflowableContentInsets` | (40,16,40,16) | 可重排内容内边距 | +| `fixedContentInset` | .zero | 固定版式内容内边距 | | `theme` | `.light` | 主题 | +| `fixedLayoutFit` | `.page` | 固定版式适配模式 | | `fixedLayoutSpreadMode` | `.automatic` | fixed-layout spread 模式 | | `textRenderingEngine` | `.dtCoreText` | 文本 EPUB 渲染引擎 | @@ -377,6 +383,7 @@ struct RDEPUBLocation: Codable, Equatable { var progression: Double // 视口起始位置 [0, 1] var lastProgression: Double? // 视口末尾位置 [0, 1] var fragment: String? // 锚点 + var rangeAnchor: RDEPUBTextRangeAnchor? // 文本范围锚点(用于文本 EPUB 高亮定位) } ``` @@ -393,8 +400,8 @@ struct RDEPUBLocation: Codable, Equatable { | 问题 | 说明 | |------|------| -| Demo 层 RDReaderController 过大 | 约 1900+ 行,仍混有 EPUB 加载、UI 管理、数据源逻辑,待进一步拆分 | -| RDEPUBParser.swift 仍偏大 | 约 900 行,虽已拆出多个扩展文件,OPF metadata 细节仍集中 | +| RDEPUBReaderController 过大 | 约 1995 行,仍混有 EPUB 加载、UI 管理、数据源逻辑,待进一步拆分 | +| 部分 UI 文件仍然偏大 | RDEPUBTextContentView ~728 行、RDEPUBTextBookBuilder ~717 行,需继续拆分 | | 横竖屏切换已接入一阶段支持 | `RDEPUBReaderController.viewWillTransition()` 统一接管正文重分页,`RDReaderView` 保留容器级双页布局刷新;仍需补齐固定回归矩阵 | | 固定手势分区比例 | 三等分固定写死,不支持自定义 | | 自动化测试为首批接入状态 | 已有 `ReadViewSDKDemoTests` / `ReadViewSDKDemoUITests` 与 parser / resolver / persistence / smoke 用例,分页、恢复链路和更多 UI 闭环仍需继续补齐 | @@ -416,7 +423,7 @@ pod install | 依赖 | 用途 | 使用层 | |------|------|--------| -| ZIPFoundation | EPUB ZIP 解压 | RDEPUBParser+Archive | +| ZIPFoundation (~> 0.9) | EPUB ZIP 解压 | RDEPUBParser+Archive | | DTCoreText | HTML → NSAttributedString | RDEPUBDTCoreTextRenderer | | SnapKit | Demo 布局 | Demo 层 | | SSAlertSwift | Demo 弹窗 | Demo 层 | diff --git a/Doc/CODING_STYLE.md b/Doc/CODING_STYLE.md index 4423385..fdc05c8 100644 --- a/Doc/CODING_STYLE.md +++ b/Doc/CODING_STYLE.md @@ -1,8 +1,8 @@ -# ReadSDK 代码规范 +# ReadViewSDK 代码规范 ## 适用范围 -本文档适用于 `ReadSDK` 新增代码与重构代码。 +本文档适用于 `ReadViewSDK` 新增代码与重构代码。 - 规范覆盖 `Sources/` 与 `RDReaderDemo/` 中的 Swift 代码。 - 命名、分层与职责边界以 SDK 可维护性和可扩展性为优先。 @@ -225,15 +225,17 @@ RDEPUBWebView+Reflowable.swift - 命名先定前缀再落代码:类型一律 `RD` 开头。 - 若需迁移历史 `SS` 前缀,按模块渐进替换,优先替换新增与重构触达文件。 -## 旧 SS 命名迁移到 RD 的分阶段执行清单 +## 旧 SS 命名迁移到 RD 的执行状态 -### 阶段 0:冻结新增 SS 命名(立即执行) +**迁移已完成**:源码中已无 `SS` 前缀类型定义,全部使用 `RD`/`RDEPUB` 前缀。新增代码必须继续遵守此规则。 + +### 阶段 0:冻结新增 SS 命名(已完成) - 目标:从当前时点开始,不再引入新的 `SS`/`RDEPUB` 类型名。 - 动作: - 新增类型统一使用 `RD`/`RDEPUB` 前缀。 - Code Review 增加命名检查项:发现新增 `SS` 命名必须驳回。 - - 在 PR 模板中加入“本次是否新增旧前缀命名”勾选项。 + - 在 PR 模板中加入”本次是否新增旧前缀命名”勾选项。 - 验收: - 新提交代码中,新增类型 `SS` 前缀数量为 `0`。 diff --git a/Doc/EPUBCore_功能实现逻辑.md b/Doc/EPUBCore_功能实现逻辑.md index 2ea7e0b..120b368 100644 --- a/Doc/EPUBCore_功能实现逻辑.md +++ b/Doc/EPUBCore_功能实现逻辑.md @@ -2,7 +2,7 @@ ## 1. 范围与目标 -- 代码范围:`Sources/RDReaderView/EPUBCore/`(30 个 Swift 文件 + 2 个资源文件) +- 代码范围:`Sources/RDReaderView/EPUBCore/`(31 个 Swift 文件 + 2 个资源文件) - 目标:说明 EPUBCore 如何完成 EPUB 解析、资源定位、阅读会话管理、离屏分页、JS 桥接渲染和全文搜索。 - 主链路关键词:`epubURL -> RDEPUBParser.parse -> container.xml -> OPF -> spine/TOC -> RDEPUBPublication -> RDEPUBReadingSession -> RDEPUBWebView/Paginator -> 分页/渲染`。 @@ -40,7 +40,7 @@ ### 2.4 WebView 渲染层 `RDEPUBWebView` -- 文件:`EPUBCore/RDEPUBWebView.swift` + 5 个扩展文件 +- 文件:`EPUBCore/RDEPUBWebView.swift` + 5 个扩展文件(+Configuration / +Reflowable / +FixedLayout / +JavaScriptBridge / +Search) - 职责: - 内部持有 WKWebView,配置自定义 scheme handler 和 JS 桥接 - 可重排内容加载:`loadPage(parser:spineIndex:pageIndex:...)`(`+Reflowable.swift`) @@ -77,13 +77,13 @@ - `ssReaderFixedLayoutReady`:固定版式渲染完成 - JS 端 `window.RDReaderBridge` 暴露:`applyPagination`、`setPageMetrics`、`scrollToPage`、`scrollToLocation`、`setHighlights`、`clearHighlights`、`reportProgression`、`selectionPayload` -### 2.8 搜索引擎 `RDEPUBHTMLSearchEngine` +### 2.8 搜索引擎 `RDEPUBSearchEngine` / `RDEPUBHTMLSearchEngine` - 文件:`EPUBCore/RDEPUBSearchEngine.swift`、`EPUBCore/RDEPUBSearchModels.swift` - 职责: - - 将 HTML 转为纯文本(NSAttributedString 或正则兜底) - - 执行大小写不敏感的全文搜索 - - 返回 `RDEPUBSearchMatch` 数组(含 progression 偏移和预览文本) + - `RDEPUBSearchEngine`:搜索协议定义 + - `RDEPUBHTMLSearchEngine`:WebView 路径实现,将 HTML 转为纯文本(NSAttributedString 或正则兜底),执行大小写不敏感的全文搜索 + - 搜索模型:`RDEPUBSearchMatch`、`RDEPUBSearchResult`、`RDEPUBSearchState`、`RDEPUBSearchPresentation` ### 2.9 配置与样式 @@ -92,6 +92,42 @@ - `RDEPUBStyleSheetBuilder`(`EPUBCore/RDEPUBStyleSheetBuilder.swift`):生成分页 CSS、渲染 CSS 和测量 JS 脚本 - `RDEPUBFixedLayoutTemplate`(`EPUBCore/RDEPUBFixedLayoutTemplate.swift`):生成固定版式 HTML 模板 +### 2.10 文本锚点 `RDEPUBTextAnchor` / `RDEPUBTextRangeAnchor` + +- 文件:`EPUBCore/RDEPUBTextAnchor.swift` +- 职责: + - `RDEPUBTextAnchor`:精确定位到 EPUB 中的某个字符位置,包含 fileIndex(spine 索引)、row(行号)、column(列号)、chapterOffset(章节内字符偏移)、fragmentID(最近的 fragment) + - `RDEPUBTextRangeAnchor`:由起止锚点组成的文本区间,可转换为 NSRange + - 支持 Codable 序列化,兼容 `fileIndex` 和 `spineIndex` 两种 key + - 用于文本 EPUB 的高亮精确锚定和跨会话恢复 + +### 2.11 渲染请求模型 `RDEPUBRenderRequest` + +- 文件:`EPUBCore/RDEPUBRenderRequest.swift` +- 职责: + - `RDEPUBFixedLayoutFit`:固定版式适配模式枚举(`.auto` / `.page` / `.width`) + - `RDEPUBFixedLayoutSpreadMode`:Spread 显示模式枚举(`.automatic` / `.always` / `.never`) + - `RDEPUBPresentationStyle`:WebView 和 Paginator 共用的视觉参数(viewportSize、contentInsets、fontSize、lineHeightMultiple、主题色) + - `RDEPUBReflowableRenderRequest`:可重排内容渲染请求(spineIndex、href、pageIndex、presentation、highlights、searchPresentation) + - `RDEPUBFixedRenderRequest`:固定版式渲染请求(spread、viewportSize、fit、searchPresentation) + - `RDEPUBRenderRequest`:统一枚举(`.reflowable` / `.fixed`),WebView 根据此类型选择渲染路径 + +### 2.12 调试工具 `RDEPUBWebViewDebug` + +- 文件:`EPUBCore/RDEPUBWebViewDebug.swift` +- 职责: + - WebView 调试日志工具集,DEBUG 模式默认开启 + - 支持导航事件、JS 执行、消息接收、URL Scheme 任务的日志记录 + - 可通过 UserDefaults `"RDEPUBWebViewDebugEnabled"` 覆盖开关 + +### 2.13 资源加载器 `RDEPUBAssetRepository` + +- 文件:`EPUBCore/RDEPUBAssetRepository.swift` +- 职责: + - 从资源包加载 JS 桥接脚本(`epub-bridge.js`)和固定版式 HTML 模板(`epub-fixed-layout.html`) + - 支持 `{{token}}` 模板变量替换 + - 自动定位 `RDReaderViewAssets.bundle`(优先已解析子 bundle,兜底宿主 bundle) + ## 3. 主流程(代码级) ### 3.1 EPUB 解析全流程 @@ -237,6 +273,7 @@ RDEPUBLocation ├── progression: Double? (0..1,章节内起始位置) ├── lastProgression: Double? (0..1,章节内结束位置) ├── fragment: String? (#anchor) + ├── rangeAnchor: RDEPUBTextRangeAnchor? (文本范围锚点,用于高亮精确定位) └── navigationProgression (computed: midpoint of progression and lastProgression) ``` @@ -279,7 +316,8 @@ RDEPUBRenderRequest (enum) RDEPUBHighlight ├── id, bookIdentifier ├── location: RDEPUBLocation - ├── text, rangeInfo (JSON-serialized DOM range) + ├── text, rangeInfo (JSON-serialized DOM range 或 text-offset) + ├── style: RDEPUBHighlightStyle (.highlight | .underline) ├── color: String, note: String └── createdAt: Date @@ -288,6 +326,13 @@ RDEPUBSelection ├── location: RDEPUBLocation ├── text, rangeInfo └── createdAt: Date + +RDEPUBBookmark + ├── id, bookIdentifier + ├── location: RDEPUBLocation + ├── title: String? + ├── note: String? + └── createdAt: Date ``` ### 5.6 搜索模型 diff --git a/Doc/EPUBTextRendering_功能实现逻辑.md b/Doc/EPUBTextRendering_功能实现逻辑.md index ddd63b7..18d6d03 100644 --- a/Doc/EPUBTextRendering_功能实现逻辑.md +++ b/Doc/EPUBTextRendering_功能实现逻辑.md @@ -2,7 +2,7 @@ ## 1. 范围与目标 -- 代码范围:`Sources/RDReaderView/EPUBTextRendering/`(9 个 Swift 文件) +- 代码范围:`Sources/RDReaderView/EPUBTextRendering/`(13 个 Swift 文件) - 目标:说明文本渲染路径如何将 EPUB HTML 转换为 NSAttributedString、按字符范围分页、构建书籍模型,并支持全文搜索与 textReflowable 标注定位。 - 主链路关键词:`RDEPUBParser HTML -> DTCoreText 渲染 -> 片段标记注入/提取 -> CoreText 分页 -> RDEPUBTextBook -> 页面查找/位置转换`。 - 适用范围:仅用于 `textReflowable` 阅读配置文件(纯文本可重排 EPUB,如小说)。固定版式和交互式 EPUB 使用 WebView 渲染路径。 @@ -64,6 +64,14 @@ - 在已渲染的 NSAttributedString 纯文本上执行线性搜索 - 返回 `RDEPUBSearchMatch` 数组(含 href、progression、预览文本、匹配位置) +### 2.6.1 章节数据模型 `RDEPUBChapterData` + +- 文件:`EPUBTextRendering/RDEPUBChapterData.swift` +- 职责: + - 每章节的数据聚合模型,管理高亮、搜索结果和页面查询 + - 支持按页码范围过滤当前页的高亮列表 + - 支持搜索结果在章节内的定位和匹配索引计算 + ### 2.7 CoreText 分页引擎 `RDEPUBTextLayouter` / `RDEPUBTextLayoutFrame` - 文件:`EPUBTextRendering/RDEPUBTextLayouter.swift`、`EPUBTextRendering/RDEPUBTextLayoutFrame.swift` @@ -80,6 +88,28 @@ - 将纯文本按段落切分后渲染为 NSAttributedString - 按页面尺寸分页,输出与 EPUB 文本路径一致的书籍模型 +### 2.9 文本索引表 `RDEPUBTextIndexTable` + +- 文件:`EPUBTextRendering/RDEPUBTextIndexTable.swift` +- 职责: + - 构建全书的文本结构映射:章节起始偏移、href 与章节/spine 索引对应、fragment 偏移映射、行列索引映射 + - `RDEPUBRowColumnIndex`:行-列索引条目,记录每行的字符范围 + - 支持锚点与位置之间的双向转换: + - `anchor(forAbsoluteIndex:in:)`:绝对索引 → 文本锚点 + - `anchor(for:)`:阅读位置 → 文本锚点(优先 rangeAnchor,其次 fragment/progression) + - `absoluteIndex(for:)`:锚点 → 全书绝对索引 + - `location(for:in:bookIdentifier:)`:锚点/范围锚点 → 阅读位置 + - 支持页码查找:`pageNumber(for:in:)` 通过锚点定位到对应页面 + +### 2.10 性能采样器 `RDEPUBTextPerformanceSampler` + +- 文件:`EPUBTextRendering/RDEPUBTextPerformanceSampler.swift` +- 职责: + - `RDEPUBTextPerformanceSample`:单章节性能数据(chapterHref、renderDuration、paginateDuration、pageCount、attributedStringLength、cacheHit) + - `RDEPUBTextPerformanceSampler`:书籍构建过程的性能采样器 + - 由 `RDEPUBTextBookBuilder` 在构建过程中使用,每章记录一个采样点 + - `summary()` 输出汇总报告:总渲染/分页耗时和缓存命中率 + ## 3. 主流程(代码级) ### 3.1 完整渲染-分页流程 @@ -295,7 +325,8 @@ RDEPUBTextBook - **内存**:`RDEPUBTextBook` 同时持有 chapters(含完整 attributedContent)和 pages(含子串切片),存在一定程度的内存重复 - **后台执行**:`build` 方法在 `DispatchQueue.global(qos: .userInitiated)` 执行,不阻塞主线程 - **搜索性能**:线性扫描每章的 `attributedContent.string`,无索引,搜索时间与总文本量线性相关 -- **无分页缓存**:字号/行高变化时全书重新渲染和分页,无增量更新 +- **分页缓存**:`RDEPUBTextBookCache` 支持磁盘缓存分页结果,字号/行高变化时优先从缓存恢复 +- **性能采样**:`RDEPUBTextPerformanceSampler` 记录每章的渲染和分页耗时,支持缓存命中率统计 ## 9. 联调与排查建议 diff --git a/Doc/EPUBUI_功能实现逻辑.md b/Doc/EPUBUI_功能实现逻辑.md index 9ae95ca..d304d06 100644 --- a/Doc/EPUBUI_功能实现逻辑.md +++ b/Doc/EPUBUI_功能实现逻辑.md @@ -2,7 +2,7 @@ ## 1. 范围与目标 -- 代码范围:`Sources/RDReaderView/EPUBUI/`(16 个 Swift 文件) +- 代码范围:`Sources/RDReaderView/EPUBUI/`(19 个 Swift 文件) - 目标:说明 EPUBUI 如何作为开箱即用的阅读器 UI 层,协调 EPUBCore 解析、EPUBTextRendering 文本渲染、RDReaderView 分页容器,提供完整的阅读体验(工具栏、目录、高亮批注、设置面板、阅读位置持久化、搜索)。 - 主链路关键词:`RDEPUBReaderController.init -> 解析 EPUB -> 分页 -> 渲染 -> 用户交互(翻页/工具栏/设置/高亮/搜索)-> 持久化`。 @@ -10,7 +10,7 @@ ### 2.1 主控制器 `RDEPUBReaderController` -- 文件:`EPUBUI/RDEPUBReaderController.swift`(~1255 行) +- 文件:`EPUBUI/RDEPUBReaderController.swift`(~1995 行) - 入口方法: - `init(epubURL:configuration:persistence:)` — 标准 EPUB 阅读入口 - `init(textBook:bookIdentifier:title:textFileURL:configuration:)` — 纯文本书籍阅读入口(由 `RDPlainTextBookBuilder` 构建 `RDEPUBTextBook` 后传入) @@ -29,7 +29,7 @@ - `fontSize`(默认 15)、`lineHeightMultiple`(默认 1.6) - `displayType`(默认 .pageCurl)、`landscapeDualPageEnabled`(默认 true) - `showsTableOfContents`(默认 true)、`allowsHighlights`(默认 true)、`showsSettingsPanel`(默认 true) - - `reflowableContentInsets`(默认 40/16/40/16)、`fixedContentInset`(默认 .zero) + - `reflowableContentInsets`(默认 top:40 left:16 bottom:40 right:16)、`fixedContentInset`(默认 .zero) - `theme`(默认 .light) - `fixedLayoutFit`(默认 .page)、`fixedLayoutSpreadMode`(默认 .automatic) - `textRenderingEngine`(默认 .dtCoreText) @@ -41,45 +41,47 @@ - 文件:`EPUBUI/RDEPUBReaderDelegate.swift` - 12 个可选方法(全部有默认空实现): - - `didOpen`:成功打开出版物 - - `didUpdateLocation`:翻页或滚动时位置更新 - - `didReachEnd`:到达最后一页 - - `didChangeSelection`:文本选择变化或清除 - - `didUpdateHighlights`:高亮增删改 - - `didUpdateBookmarks`:书签增删改 - - `didUpdateSearchResult`:搜索状态变化 - - `didChangeCurrentSearchMatch`:当前搜索匹配项变化 - - `didUpdateCurrentTableOfContentsItem`:翻页时匹配的目录项 - - `didActivateExternalLink`:外部链接点击 - - `didFailWithError`:解析或分页错误 - - `configureTopToolView`:自定义顶部工具栏 + - `epubReader(_:didOpen:)`:成功打开出版物 + - `epubReader(_:didUpdateLocation:)`:翻页或滚动时位置更新 + - `epubReaderDidReachEnd(_:)`:到达最后一页 + - `epubReader(_:didChangeSelection:)`:文本选择变化或清除 + - `epubReader(_:didUpdateHighlights:)`:高亮增删改 + - `epubReader(_:didUpdateBookmarks:)`:书签增删改 + - `epubReader(_:didUpdateSearchResult:)`:搜索状态变化 + - `epubReader(_:didChangeCurrentSearchMatch:)`:当前搜索匹配项变化 + - `epubReader(_:didUpdateCurrentTableOfContentsItem:)`:翻页时匹配的目录项 + - `epubReader(_:didActivateExternalLink:)`:外部链接点击 + - `epubReader(_:didFailWithError:)`:解析或分页错误 + - `epubReader(_:configureTopToolView:)`:自定义顶部工具栏 ### 2.4 持久化 `RDEPUBReaderPersistence` - 文件:`EPUBUI/RDEPUBReaderPersistence.swift` -- 协议定义 6 个方法:loadLocation / saveLocation / loadHighlights / saveHighlights / loadReaderSettings / saveReaderSettings +- 协议定义 8 个方法:loadLocation / saveLocation / loadHighlights / saveHighlights / loadBookmarks / saveBookmarks / loadReaderSettings / saveReaderSettings - 默认实现 `RDEPUBUserDefaultsPersistence`: - 位置:`ssreader.epub.location.`(JSON 编码) - 高亮:`ssreader.epub.highlights.`(JSON 编码) + - 书签:`ssreader.epub.bookmarks.`(JSON 编码) - 设置:`ssreader.epub.settings`(全局,非按书) ### 2.5 主题 `RDEPUBReaderTheme` -- 文件:`EPUBUI/RDEPUBReaderTheme.swift` +- 文件:`EPUBUI/RDEPUBReaderTheme.swift`(~125 行) - 6 个颜色属性:contentBackgroundColor、contentTextColor、toolBackgroundColor、toolControlTextColor、toolControlBorderUnselectColor、toolLineColor - 6 个内置预设:`.light`、`.dark`、`.yellow`、`.green`、`.pink`、`.blue` +- `RDEPUBReaderThemePreset` 枚举将预设映射为可序列化值,用于持久化 - 计算属性 `themeBackgroundColorCSS` / `themeTextColorCSS` 用于 Web 渲染路径 ### 2.6 设置面板 `RDEPUBReaderSettingsViewController` -- 文件:`EPUBUI/RDEPUBReaderSettingsViewController.swift` +- 文件:`EPUBUI/RDEPUBReaderSettingsViewController.swift`(~311 行) - 5 个控制项: - 亮度滑块(0-1) - 字号 A-/A+(范围 12-36,步长 1) - 行高分段(紧凑 1.3 / 标准 1.6 / 宽松 1.9) - 显示模式分段(仿真 / 横滑 / 竖滑 / 覆盖) - 主题选择(6 个圆形色块按钮) -- 所有变更通过闭包实时回调 +- 所有变更通过闭包实时回调:onBrightnessChange / onFontSizeChange / onLineHeightChange / onDisplayTypeChange / onThemeChange ### 2.7 内容视图 @@ -88,10 +90,17 @@ ### 2.8 其他 UI 组件 -- `RDEPUBReaderTopToolView`:顶部导航栏(返回按钮 + 标题) -- `RDEPUBReaderBottomToolView`:底部工具栏(目录 / 批注 / 标注 / 设置 4 个按钮) +- `RDEPUBReaderToolView`(`EPUBUI/RDEPUBReaderToolView.swift`):工具栏基类,提供分隔线和主题适配的通用逻辑 +- `RDEPUBReaderTopToolView`:顶部导航栏(返回按钮 + 书签切换 + 标题) +- `RDEPUBReaderBottomToolView`:底部工具栏(目录 / 书签 / 标注 / 设置 4 个按钮) - `RDEPUBReaderChapterListController`:目录列表(modal UITableViewController,当前项高亮 systemBlue) - `RDEPUBReaderHighlightsViewController`:标注管理(全部 / 批注 / 划线过滤,列表 + 编辑备注 + 删除 + 跳转,空态按过滤条件提示) +- `RDEPUBReaderSettings`(`EPUBUI/RDEPUBReaderSettings.swift`):可持久化用户设置模型(brightness / fontSize / lineHeightMultiple / displayMode / themePreset),含 `RDEPUBReaderDisplayMode` 和 `RDEPUBReaderThemePreset` 枚举 +- `RDEPUBReaderTableOfContentsItem`(`EPUBUI/RDEPUBReaderTableOfContentsItem.swift`):展平后的目录条目(title / href / depth / pageNumber) +- `RDEPUBPageInteractionController`(`EPUBUI/RDEPUBPageInteractionController.swift`):CoreText 页面级交互控制器,处理文本选区和高亮装饰 +- `RDEPUBSelectionOverlayView`(`EPUBUI/RDEPUBSelectionOverlayView.swift`):选区覆盖视图,显示选中文本的高亮装饰 +- `RDEPUBPageLayoutSnapshot`(`EPUBUI/RDEPUBPageLayoutSnapshot.swift`):页面几何模型,存储 CoreText 渲染的页面布局信息 +- `RDURLReaderController`(`EPUBUI/RDURLReaderController.swift`,~221 行):最简 URL 入口,传入 URL 即可打开书籍(.epub → RDEPUBReaderController,其他 → RDPlainTextBookBuilder 构建后传入),分页失败时回退到纯 UITextView 展示 ## 3. 主流程(代码级) @@ -229,22 +238,26 @@ | 书签列表 | `ssreader.epub.bookmarks.` | JSON → `[RDEPUBBookmark]` | | 阅读设置 | `ssreader.epub.settings` | JSON → `RDEPUBReaderSettings` | -### 5.2 Book Identifier - -- 优先使用 `parser.metadata.identifier` -- 回退使用 `epubURL.lastPathComponent` - -### 5.3 设置快照 +### 5.2 设置模型 ```swift RDEPUBReaderSettings (Codable) - ├── brightness: CGFloat - ├── fontSize: CGFloat - ├── lineHeightMultiple: CGFloat - ├── displayMode: RDEPUBReaderDisplayMode + ├── brightness: CGFloat? + ├── fontSize: CGFloat? + ├── lineHeightMultiple: CGFloat? + ├── displayMode: RDEPUBReaderDisplayMode? └── themePreset: RDEPUBReaderThemePreset? ``` +`RDEPUBReaderDisplayMode`:可序列化的翻页模式枚举(pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll),与 `RDReaderView.DisplayType` 相互转换。 + +`RDEPUBReaderThemePreset`:可序列化的主题预设枚举(light / yellow / green / pink / blue / dark),与 `RDEPUBReaderTheme` 相互转换。 + +### 5.3 Book Identifier + +- 优先使用 `parser.metadata.identifier` +- 回退使用 `epubURL.lastPathComponent` + ### 5.4 目录扁平化项 ```swift diff --git a/Doc/EPUB_MAINTENANCE.md b/Doc/EPUB_MAINTENANCE.md index 0937655..38957d6 100644 --- a/Doc/EPUB_MAINTENANCE.md +++ b/Doc/EPUB_MAINTENANCE.md @@ -15,20 +15,27 @@ | 文件 | 职责 | |------|------| -| `RDEPUBParser.swift` + 扩展 | EPUB 解压、container.xml、OPF、manifest、spine、TOC/NCX 解析 | +| `RDEPUBParser.swift` + 5 扩展 | EPUB 解压、container.xml、OPF、manifest、spine、TOC/NCX 解析、阅读配置文件判断 | | `RDEPUBPublication.swift` | 解析结果的唯一聚合入口,外部不直接访问 parser 字段 | | `RDEPUBModels.swift` | metadata / manifest / spine / TOC 基础模型 | -| `RDEPUBReadingModels.swift` | RDEPUBLocation、RDEPUBViewport、RDEPUBHighlight、选区模型 | +| `RDEPUBReadingModels.swift` | RDEPUBLocation、RDEPUBViewport、RDEPUBHighlight、RDEPUBBookmark、选区模型 | | `RDEPUBResourceResolver.swift` | href → fileURL / schemeURL 的统一入口,`normalizedHref` 标准化路径 | | `RDEPUBPaginator.swift` | 离屏 WKWebView 分页,计算每个 spine 资源被视口切成多少页 | | `RDEPUBReadingSession.swift` | 状态机 + 会话协调,管理 staged/active/pending 页模型 | | `RDEPUBNavigatorState.swift` | 状态枚举(initializing/loading/idle/jumping/moving/repaginating) | -| `RDEPUBWebView.swift` + 扩展 | 承载 spine 资源的 WKWebView,分页 CSS 注入、JS bridge、fixed wrapper | +| `RDEPUBWebView.swift` + 5 扩展 | 承载 spine 资源的 WKWebView,分页 CSS 注入、JS bridge、fixed wrapper、搜索装饰 | | `RDEPUBResourceURLSchemeHandler.swift` | `ss-reader://` 协议,从本地解压目录提供所有 EPUB 资源 | | `RDEPUBStyleSheetBuilder.swift` | 生成注入 WebView 的 CSS | | `RDEPUBJavaScriptBridge.swift` | JS ↔ Swift 消息定义和编解码 | | `RDEPUBSearchEngine.swift` | 搜索协议定义 + WebView 全文搜索引擎 | | `RDEPUBSearchModels.swift` | 搜索模型(SearchMatch/Result/State/Presentation) | +| `RDEPUBTextAnchor.swift` | 文本锚点和范围锚点(精确定位字符位置) | +| `RDEPUBRenderRequest.swift` | 渲染请求模型、展示样式、固定版式适配/Spread 枚举 | +| `RDEPUBWebViewDebug.swift` | WebView 调试日志工具(导航/JS/消息/Scheme) | +| `RDEPUBAssetRepository.swift` | 静态资源加载器(JS 脚本、HTML 模板、模板变量替换) | +| `RDEPUBPreferences.swift` | 用户阅读偏好聚合 | +| `RDEPUBNavigatorLayoutContext.swift` | 容器布局上下文 | +| `RDEPUBFixedLayoutTemplate.swift` | 固定版式 HTML 模板生成 | ### 2.2 EPUBTextRendering 层 @@ -39,17 +46,38 @@ | `RDEPUBTextRendererSupport.swift` | fragment 标记注入、fragment offset 提取、属性标准化 | | `RDEPUBTextPaginationSupport.swift` | NSAttributedString 切页算法 | | `RDEPUBTextBookBuilder.swift` | 驱动逐章节渲染和分页,生成 RDEPUBTextBook | +| `RDEPUBTextLayouter.swift` | CoreText 分页引擎(~810 行,含 4 级语义边界调整) | +| `RDEPUBTextLayoutFrame.swift` | 单帧分页结果模型(contentRange、breakReason、语义提示) | +| `RDEPUBTextBookCache.swift` | 分页结果磁盘缓存 | +| `RDEPUBChapterData.swift` | 章节数据聚合模型(高亮、搜索结果、页面查询) | +| `RDEPUBTextSearchEngine.swift` | 纯文本全文搜索引擎 | +| `RDPlainTextBookBuilder.swift` | 纯文本(.txt)书籍构建器 | +| `RDEPUBTextIndexTable.swift` | 全书文本索引表(章节偏移、行列映射、锚点转换) | +| `RDEPUBTextPerformanceSampler.swift` | 性能采样器(渲染/分页耗时、缓存命中率) | ### 2.3 EPUBUI 层 | 文件 | 职责 | |------|------| -| `RDEPUBReaderController.swift` | 开箱即用读者控制器,整合 session / readerView / UI | -| `RDEPUBReaderConfiguration.swift` | 外部配置项(字号、主题、翻页模式等) | -| `RDEPUBReaderPersistence.swift` | 阅读位置和高亮的本地持久化 | -| `RDEPUBReaderSettings.swift` | 运行时设置状态 | +| `RDEPUBReaderController.swift` | 开箱即用读者控制器(~1995 行),整合 session / readerView / UI | +| `RDEPUBReaderConfiguration.swift` | 外部配置项(13 个:字号、主题、翻页模式等) | +| `RDEPUBReaderPersistence.swift` | 阅读位置、高亮、书签的本地持久化(8 个方法) | +| `RDEPUBReaderSettings.swift` | 可持久化用户设置模型 + DisplayMode/ThemePreset 枚举 | +| `RDEPUBReaderTheme.swift` | 6 个内置主题预设 + 颜色属性 | +| `RDEPUBReaderDelegate.swift` | 12 个可选委托方法 | +| `RDEPUBReaderToolView.swift` | 工具栏基类(分隔线 + 主题适配) | +| `RDEPUBReaderTopToolView.swift` | 顶部导航栏(返回 + 书签 + 标题) | +| `RDEPUBReaderBottomToolView.swift` | 底部工具栏(目录/书签/标注/设置) | +| `RDEPUBReaderChapterListController.swift` | 目录列表面板 | +| `RDEPUBReaderHighlightsViewController.swift` | 标注管理面板(过滤、编辑、删除、跳转) | +| `RDEPUBReaderSettingsViewController.swift` | 设置面板(亮度、字号、行高、模式、主题) | +| `RDEPUBReaderTableOfContentsItem.swift` | 展平目录条目模型 | | `RDEPUBWebContentView.swift` | WKWebView 内容视图,供 RDReaderView 渲染 WebView 类 spine | -| `RDEPUBTextContentView.swift` | 文本内容视图,供 RDReaderView 渲染文本类 spine | +| `RDEPUBTextContentView.swift` | 文本内容视图(~728 行),供 RDReaderView 渲染文本类 spine | +| `RDEPUBPageInteractionController.swift` | CoreText 页面级交互控制器 | +| `RDEPUBSelectionOverlayView.swift` | 选区覆盖视图 | +| `RDEPUBPageLayoutSnapshot.swift` | 页面几何模型 | +| `RDURLReaderController.swift` | 最简 URL 入口(.epub/.txt 自动识别) | --- @@ -289,7 +317,7 @@ webView.loadHTMLString(htmlString, baseURL: nil) ## 7. 后续扩展建议 -1. **拆分 demo 层 RDReaderController**:约 1900+ 行,EPUB 加载、UI、数据源职责仍混在一起,建议继续向 library 层迁移 +1. **拆分 RDEPUBReaderController**:约 1995 行,EPUB 加载、UI、数据源职责仍混在一起,建议继续向 library 层迁移 2. **基线验证**:用四本样书跑一轮分页耗时、目录命中率、末页事件、设置恢复稳定性数据 3. **横竖屏支持回归矩阵**:控制器级 `viewWillTransition()` 与 viewport 去重已接入,下一步重点是补齐 pageCurl / scroll、三条正文路径和 iPad 视口变化场景的固定回归 4. **Pod 分层**:评估是否将 EPUBCore / EPUBTextRendering / EPUBUI 拆为独立 subspec diff --git a/Doc/RDReaderView_功能实现逻辑.md b/Doc/RDReaderView_功能实现逻辑.md index 20596d9..fece1f8 100644 --- a/Doc/RDReaderView_功能实现逻辑.md +++ b/Doc/RDReaderView_功能实现逻辑.md @@ -2,7 +2,7 @@ ## 1. 范围与目标 -- 代码范围:`Sources/RDReaderView/` 根目录(6 个 Swift 文件) +- 代码范围:`Sources/RDReaderView/ReaderView/`(5 个 Swift 文件) - 目标:说明分页阅读器容器如何管理四种显示模式、DataSource/Delegate 协议、翻页交互、工具栏动画、双屏适配和 RTL 支持。 - 主链路关键词:`RDReaderDataSource -> reloadData -> DisplayType 切换 -> 翻页/滚动 -> RDReaderDelegate.pageNum -> 工具栏显隐`。 @@ -10,7 +10,7 @@ ### 2.1 核心容器 `RDReaderView` -- 文件:`Sources/RDReaderView/RDReaderView.swift`(~717 行) +- 文件:`Sources/RDReaderView/ReaderView/RDReaderView.swift`(~1219 行) - 入口方法:`reloadData()` - 职责: - 管理四种显示模式的视图层级切换 @@ -23,7 +23,7 @@ ### 2.2 自定义布局 `RDReaderFlowLayout` -- 文件:`Sources/RDReaderView/RDReaderFlowLayout.swift`(~387 行) +- 文件:`Sources/RDReaderView/ReaderView/RDReaderFlowLayout.swift`(~375 行) - 职责: - 继承 `UICollectionViewFlowLayout`,为三种滚动模式提供布局计算 - 水平滚动:全屏宽 item,pagingEnabled @@ -33,7 +33,7 @@ ### 2.3 内容 Cell `RDReaderContentCell` -- 文件:`Sources/RDReaderView/RDReaderContentCell.swift`(~37 行) +- 文件:`Sources/RDReaderView/ReaderView/RDReaderContentCell.swift`(~55 行) - 职责: - `UICollectionViewCell` 子类,作为内容视图的薄壳宿主 - `containerView` 属性 setter 自动移除旧视图、添加新视图 @@ -41,7 +41,7 @@ ### 2.4 页面子控制器 `RDReaderPageChildViewController` -- 文件:`Sources/RDReaderView/RDReaderPageChildViewController.swift`(~65 行) +- 文件:`Sources/RDReaderView/ReaderView/RDReaderPageChildViewController.swift`(~89 行) - 职责: - 仅用于 pageCurl 模式,作为 `UIPageViewController` 的子控制器 - 持有 `contentView: UIView?` 和 `pageNum: Int` @@ -49,21 +49,11 @@ ### 2.5 手势控制器 `RDReaderGestureController` -- 文件:`Sources/RDReaderView/RDReaderGestureController.swift`(~42 行) +- 文件:`Sources/RDReaderView/ReaderView/RDReaderGestureController.swift`(~58 行) - 职责: - 当前为占位组件,存储 topToolView / bottomToolView 引用 - 实际手势逻辑在 `RDReaderView.tapCenter()` 中实现 -### 2.6 URL 入口 `RDURLReaderController` - -- 文件:`Sources/RDReaderView/RDURLReaderController.swift`(~105 行) -- 职责: - - 最简入口:传入 URL 即可打开书籍 - - `.epub` 扩展名 → 创建 `RDEPUBReaderController(epubURL:configuration:)` - - 其他扩展名 → 使用 `RDPlainTextBookBuilder` 构建 `RDEPUBTextBook`,然后创建 `RDEPUBReaderController(textBook:bookIdentifier:title:textFileURL:configuration:)` - - 分页失败时回退到纯 `UITextView` 展示(尝试 UTF-8 → GBK → GB2312 解码) - - 导航栏标题设为文件名(去掉扩展名) - ## 3. 主流程(代码级) ### 3.1 协议定义 diff --git a/Doc/index.md b/Doc/index.md index 1db8c94..15137c1 100644 --- a/Doc/index.md +++ b/Doc/index.md @@ -12,10 +12,10 @@ | 文档 | 对应模块 | 说明 | |------|----------|------| -| [EPUBCore_功能实现逻辑.md](EPUBCore_功能实现逻辑.md) | `Sources/RDReaderView/EPUBCore/`(30 文件) | EPUB 解析全流程(ZIP→container.xml→OPF→spine/TOC)、阅读会话状态机、离屏分页测量、`ss-reader://` 资源协议、JS 桥接(6 种消息)、WebView 渲染管线、缓存策略 | -| [EPUBTextRendering_功能实现逻辑.md](EPUBTextRendering_功能实现逻辑.md) | `Sources/RDReaderView/EPUBTextRendering/`(9 文件) | DTCoreText HTML→NSAttributedString 渲染管线、片段标记注入/提取、CoreText 分页引擎(含语义边界调整)、Location↔PageNumber 双向转换、全文搜索引擎、纯文本构建器 | -| [RDReaderView_功能实现逻辑.md](RDReaderView_功能实现逻辑.md) | `Sources/RDReaderView/` 根目录(6 文件) | 四种显示模式(pageCurl/horizontalScroll/verticalScroll/horizontalCoverScroll)、DataSource/Delegate 协议、点击三区域翻页、工具栏动画、双页配对与哨兵页、横竖屏适配、RTL 支持 | -| [EPUBUI_功能实现逻辑.md](EPUBUI_功能实现逻辑.md) | `Sources/RDReaderView/EPUBUI/`(16 文件) | RDEPUBReaderController 全生命周期、三条渲染路径分发、配置变更检测与响应、工具栏/目录/高亮/搜索/设置面板交互流、阅读位置持久化、主题管理 | +| [EPUBCore_功能实现逻辑.md](EPUBCore_功能实现逻辑.md) | `Sources/RDReaderView/EPUBCore/`(31 Swift + 2 资源) | EPUB 解析全流程(ZIP→container.xml→OPF→spine/TOC)、阅读会话状态机、离屏分页测量、`ss-reader://` 资源协议、JS 桥接(6 种消息)、WebView 渲染管线、文本锚点定位、渲染请求模型、缓存策略 | +| [EPUBTextRendering_功能实现逻辑.md](EPUBTextRendering_功能实现逻辑.md) | `Sources/RDReaderView/EPUBTextRendering/`(13 文件) | DTCoreText HTML→NSAttributedString 渲染管线、片段标记注入/提取、CoreText 分页引擎(含语义边界调整)、文本索引表、Location↔PageNumber 双向转换、分页缓存、性能采样、全文搜索引擎、纯文本构建器 | +| [RDReaderView_功能实现逻辑.md](RDReaderView_功能实现逻辑.md) | `Sources/RDReaderView/ReaderView/`(5 文件) | 四种显示模式(pageCurl/horizontalScroll/verticalScroll/horizontalCoverScroll)、DataSource/Delegate 协议、点击三区域翻页、工具栏动画、双页配对与哨兵页、横竖屏适配、RTL 支持 | +| [EPUBUI_功能实现逻辑.md](EPUBUI_功能实现逻辑.md) | `Sources/RDReaderView/EPUBUI/`(19 文件) | RDEPUBReaderController 全生命周期、三条渲染路径分发、配置变更检测与响应、工具栏/目录/高亮/书签/搜索/设置面板交互流、阅读位置持久化、主题管理、CoreText 页面交互 | ## 方案讨论文档