docs: 补充注释、修正过时文档、清理重复内容
源码注释: - 为 ~60 个 Swift 文件补充缺失的 doc comment(file header、类型、属性、方法) - 修正 4 处错误注释:翻页模式数量、搜索行为描述、手势识别器描述、悬空文档块 文档维护: - 删除重复文档:WXRead/读书EPUB阅读器实现架构.md(与微信读书版完全一致) - 合并重叠文档:阅读器规划.md → 阅读器功能开发计划.md(单一真值) - 修正过时内容:所有文档中"四种翻页模式"→"三种",移除 horizontalCoverScroll - 更新架构图:补齐 EPUBUI/ReaderController、Paging/、Typesetter/ 等子目录 - 更新 index.md 索引:新增开发计划和架构对比文档引用
This commit is contained in:
+101
-29
@@ -21,70 +21,143 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ EPUBUI 层(library 级读者 UI) │
|
||||
│ RDEPUBReaderController(开箱即用入口,~1995 行) │
|
||||
│ RDEPUBReaderConfiguration / Theme / Settings / Persistence│
|
||||
│ TopToolView / BottomToolView / ToolView 基类 │
|
||||
│ ChapterList / Highlights / Bookmarks / Settings 面板 │
|
||||
│ RDEPUBTextContentView / RDEPUBWebContentView │
|
||||
│ RDEPUBPageInteractionController / SelectionOverlayView │
|
||||
│ RDEPUBPageLayoutSnapshot / RDURLReaderController │
|
||||
│ │
|
||||
│ 主控制器 │
|
||||
│ RDEPUBReaderController(开箱即用入口) │
|
||||
│ +ContentDelegates / +DataSource / +PublicAPI │
|
||||
│ +RenderSupport / +RuntimeBridge / +TableOfContents │
|
||||
│ RDURLReaderController(URL 阅读入口) │
|
||||
│ │
|
||||
│ ReaderController/(协调器) │
|
||||
│ RDEPUBReaderRuntime(中央运行时协调器) │
|
||||
│ RDEPUBReaderContext(上下文状态容器) │
|
||||
│ RDEPUBReaderDependencies(依赖注入) │
|
||||
│ RDEPUBReaderLoadCoordinator(EPUB 加载) │
|
||||
│ RDEPUBReaderPaginationCoordinator(分页协调) │
|
||||
│ RDEPUBReaderLocationCoordinator(位置持久化) │
|
||||
│ RDEPUBReaderAnnotationCoordinator(标注管理) │
|
||||
│ RDEPUBReaderSearchCoordinator(搜索) │
|
||||
│ RDEPUBReaderChromeCoordinator(工具栏) │
|
||||
│ RDEPUBReaderAssemblyCoordinator(UI 组装) │
|
||||
│ RDEPUBReaderViewportMonitor(视口变化监听) │
|
||||
│ │
|
||||
│ Settings/(配置与主题) │
|
||||
│ RDEPUBReaderConfiguration / RDEPUBReaderSettings │
|
||||
│ RDEPUBReaderSettingsViewController / RDEPUBReaderTheme │
|
||||
│ │
|
||||
│ TextPage/(文本页面交互) │
|
||||
│ RDEPUBTextContentView / RDEPUBTextPageRenderView │
|
||||
│ RDEPUBSelectableTextView / RDEPUBTextSelectionController│
|
||||
│ RDEPUBSelectionOverlayView / RDEPUBTextAnnotationOverlay│
|
||||
│ RDEPUBPageInteractionController / RDEPUBPageLayoutSnapshot│
|
||||
│ RDEPUBTextPageDecorationView │
|
||||
│ │
|
||||
│ 工具栏与面板 │
|
||||
│ RDEPUBReaderTopToolView / RDEPUBReaderBottomToolView │
|
||||
│ RDEPUBReaderToolView(基类) │
|
||||
│ RDEPUBReaderChapterListController(目录面板) │
|
||||
│ RDEPUBReaderHighlightsViewController(高亮管理) │
|
||||
│ RDEPUBReaderPersistence(位置持久化) │
|
||||
│ RDEPUBReaderDelegate / RDEPUBReaderTableOfContentsItem│
|
||||
│ RDEPUBWebContentView / RDEPUBWebDecorationOverlayView │
|
||||
│ RDEPUBViewportTypes / UIColor+RDEPUBHex │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ 翻页容器层(RDReaderView) │
|
||||
│ │
|
||||
│ RDReaderView(UIView,统一翻页外壳) │
|
||||
│ 4 种翻页模式:pageCurl / horizontalScroll / │
|
||||
│ verticalScroll / horizontalCoverScroll │
|
||||
│ 3 种翻页模式:pageCurl / horizontalScroll / │
|
||||
│ verticalScroll │
|
||||
│ RDReaderViewProtocols(DataSource / Delegate / DisplayType)│
|
||||
│ +CollectionView / +ContentAccess / +PageCurl / +ToolView │
|
||||
│ RDReaderFlowLayout / RDReaderContentCell │
|
||||
│ RDReaderPageChildViewController(pageCurl 页包装) │
|
||||
│ RDReaderGestureController │
|
||||
│ │
|
||||
│ Paging/(翻页控制) │
|
||||
│ RDReaderPagingController(转场与排队) │
|
||||
│ RDReaderPreloadController(预加载与缓存) │
|
||||
│ RDReaderSpreadResolver(双页配对) │
|
||||
│ RDReaderTapRegionHandler(手势分区) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ EPUBCore 层(EPUB 引擎) │
|
||||
│ │
|
||||
│ Publication 层 │
|
||||
│ 解析与模型 │
|
||||
│ RDEPUBParser(+Archive / +Package / +TOC / │
|
||||
│ +ReadingProfile / +Resources) │
|
||||
│ RDEPUBPublication(出版物聚合对象) │
|
||||
│ RDEPUBModels(metadata / manifest / spine 模型) │
|
||||
│ RDEPUBReadingModels(location / viewport / highlight) │
|
||||
│ Models/ │
|
||||
│ RDEPUBReadingLocationModels(location 模型) │
|
||||
│ RDEPUBPaginationModels(分页模型) │
|
||||
│ RDEPUBAnnotationModels(标注模型) │
|
||||
│ RDEPUBTextAnchor / RDEPUBTextRangeAnchor(文本锚点) │
|
||||
│ RDEPUBRenderRequest(渲染请求模型) │
|
||||
│ │
|
||||
│ Services 层 │
|
||||
│ 服务层 │
|
||||
│ RDEPUBResourceResolver(资源 URL 统一入口) │
|
||||
│ RDEPUBResourceURLSchemeHandler(ss-reader:// 协议) │
|
||||
│ RDEPUBPreferences(展示参数聚合) │
|
||||
│ RDEPUBPaginator(离屏分页服务) │
|
||||
│ RDEPUBStyleSheetBuilder / RDEPUBJavaScriptBridge │
|
||||
│ RDEPUBFixedLayoutTemplate / RDEPUBAssetRepository │
|
||||
│ │
|
||||
│ Navigator 层 │
|
||||
│ 会话与导航 │
|
||||
│ RDEPUBReadingSession(状态机 + 会话协调) │
|
||||
│ RDEPUBNavigatorState(状态枚举) │
|
||||
│ RDEPUBNavigatorLayoutContext │
|
||||
│ │
|
||||
│ Resource View 层 │
|
||||
│ WebView 渲染 │
|
||||
│ RDEPUBWebView(+Configuration / +Reflowable / │
|
||||
│ +FixedLayout / +JavaScriptBridge / │
|
||||
│ +Search) │
|
||||
│ RDEPUBResourceURLSchemeHandler(ss-reader:// 协议) │
|
||||
│ RDEPUBStyleSheetBuilder / RDEPUBJavaScriptBridge │
|
||||
│ RDEPUBFixedLayoutTemplate / RDEPUBAssetRepository │
|
||||
│ RDEPUBWebViewDebug(调试日志工具) │
|
||||
│ │
|
||||
│ Search 层 │
|
||||
│ 搜索 │
|
||||
│ RDEPUBSearchEngine(协议)/ RDEPUBHTMLSearchEngine │
|
||||
│ RDEPUBSearchModels(SearchMatch/Result/State/Presentation)│
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ EPUBTextRendering 层(文本 EPUB 渲染) │
|
||||
│ RDEPUBTextRenderer(协议) │
|
||||
│ RDEPUBDTCoreTextRenderer(DTCoreText 实现) │
|
||||
│ RDEPUBTextRendererSupport / RDEPUBTextPaginationSupport │
|
||||
│ RDEPUBTextBookBuilder / RDPlainTextBookBuilder │
|
||||
│ RDEPUBTextLayouter / RDEPUBTextLayoutFrame │
|
||||
│ RDEPUBTextBookCache / RDEPUBChapterData │
|
||||
│ RDEPUBTextIndexTable / RDEPUBTextPerformanceSampler │
|
||||
│ RDEPUBTextSearchEngine │
|
||||
│ │
|
||||
│ 渲染 │
|
||||
│ RDEPUBTextRenderer(协议) │
|
||||
│ RDEPUBDTCoreTextRenderer(DTCoreText 实现) │
|
||||
│ RDPlainTextBookBuilder(纯文本书籍构建) │
|
||||
│ RDEPUBTextPositionConverter(位置转换器) │
|
||||
│ RDEPUBTextSearchEngine(文本搜索引擎) │
|
||||
│ RDEPUBTextIndexTable / RDEPUBChapterData │
|
||||
│ │
|
||||
│ BuildPipeline/(构建管线) │
|
||||
│ RDEPUBTextBookBuilder(分页书籍构建器) │
|
||||
│ RDEPUBTextBookCache / RDEPUBTextBookModels │
|
||||
│ RDEPUBTextBuildPipelineInterfaces(管线协议) │
|
||||
│ RDEPUBPaginationCacheCoordinator(缓存协调) │
|
||||
│ RDEPUBChapterTailNormalizer(章尾规范化) │
|
||||
│ RDEPUBBuildDiagnosticsReporter(诊断报告) │
|
||||
│ RDEPUBTextPerformanceSampler(性能采样) │
|
||||
│ │
|
||||
│ Pagination/(分页引擎) │
|
||||
│ RDEPUBTextLayouter / RDEPUBTextLayoutFrame │
|
||||
│ RDEPUBChapterPageCounter / RDEPUBCoreTextPageFrameFactory│
|
||||
│ RDEPUBPageBreakPolicy(断页策略) │
|
||||
│ RDEPUBTextPaginationInterfaces(分页协议) │
|
||||
│ RDEPUBTextPaginationSupport(分页支持) │
|
||||
│ │
|
||||
│ Typesetter/(排版管线) │
|
||||
│ RDEPUBTypesettingPipeline(排版管线编排) │
|
||||
│ RDEPUBHTMLNormalizer(HTML 规范化) │
|
||||
│ RDEPUBStyleSheetComposer(CSS 组合) │
|
||||
│ RDEPUBFontNormalizer(字体规范化) │
|
||||
│ RDEPUBAttachmentNormalizer(附件规范化) │
|
||||
│ RDEPUBFragmentMarkerInjector(Fragment 标记注入) │
|
||||
│ RDEPUBSemanticMarkerInjector(语义标记注入) │
|
||||
│ RDEPUBRenderDiagnosticsCollector(渲染诊断) │
|
||||
│ RDEPUBTextRendererSupport(渲染辅助工具) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -92,14 +165,13 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即
|
||||
|
||||
## 3. 翻页容器层(RDReaderView)
|
||||
|
||||
### 3.1 四种翻页模式
|
||||
### 3.1 三种翻页模式
|
||||
|
||||
| 模式 | 实现方式 | 特点 |
|
||||
|------|----------|------|
|
||||
| `pageCurl` | UIPageViewController | 原生翻书效果,手势由系统提供 |
|
||||
| `horizontalScroll` | UICollectionView + RDReaderFlowLayout | 每屏显示 2 项,水平分页滚动 |
|
||||
| `verticalScroll` | UICollectionView + RDReaderFlowLayout | 全宽项目,垂直连续滚动 |
|
||||
| `horizontalCoverScroll` | UICollectionView + RDReaderFlowLayout | 覆盖滚动效果,Z 轴动画 |
|
||||
|
||||
### 3.2 数据源协议
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ RDEPUBReaderSettings (Codable)
|
||||
└── themePreset: RDEPUBReaderThemePreset?
|
||||
```
|
||||
|
||||
`RDEPUBReaderDisplayMode`:可序列化的翻页模式枚举(pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll),与 `RDReaderView.DisplayType` 相互转换。
|
||||
`RDEPUBReaderDisplayMode`:可序列化的翻页模式枚举(pageCurl / horizontalScroll / verticalScroll),与 `RDReaderView.DisplayType` 相互转换。注意:历史版本遗留的 `horizontalCoverScroll` 会自动映射为 `horizontalScroll`。
|
||||
|
||||
`RDEPUBReaderThemePreset`:可序列化的主题预设枚举(light / yellow / green / pink / blue / dark),与 `RDEPUBReaderTheme` 相互转换。
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## 1. 范围与目标
|
||||
|
||||
- 代码范围:`Sources/RDReaderView/ReaderView/`(5 个 Swift 文件)
|
||||
- 目标:说明分页阅读器容器如何管理四种显示模式、DataSource/Delegate 协议、翻页交互、工具栏动画、双屏适配和 RTL 支持。
|
||||
- 目标:说明分页阅读器容器如何管理三种显示模式、DataSource/Delegate 协议、翻页交互、工具栏动画、双屏适配和 RTL 支持。
|
||||
- 主链路关键词:`RDReaderDataSource -> reloadData -> DisplayType 切换 -> 翻页/滚动 -> RDReaderDelegate.pageNum -> 工具栏显隐`。
|
||||
|
||||
## 2. 关键对象职责
|
||||
@@ -13,7 +13,7 @@
|
||||
- 文件:`Sources/RDReaderView/ReaderView/RDReaderView.swift`(~1219 行)
|
||||
- 入口方法:`reloadData()`
|
||||
- 职责:
|
||||
- 管理四种显示模式的视图层级切换
|
||||
- 管理三种显示模式的视图层级切换
|
||||
- 持有 `UIPageViewController`(pageCurl 模式)或 `UICollectionView`(滚动模式)
|
||||
- 处理点击手势(左/中/右三区域)
|
||||
- 管理工具栏(topToolView / bottomToolView)的显示/隐藏动画
|
||||
@@ -25,10 +25,9 @@
|
||||
|
||||
- 文件:`Sources/RDReaderView/ReaderView/RDReaderFlowLayout.swift`(~375 行)
|
||||
- 职责:
|
||||
- 继承 `UICollectionViewFlowLayout`,为三种滚动模式提供布局计算
|
||||
- 继承 `UICollectionViewFlowLayout`,为两种滚动模式提供布局计算
|
||||
- 水平滚动:全屏宽 item,pagingEnabled
|
||||
- 垂直滚动:可变高度 item,累加计算
|
||||
- 水平覆盖滚动:zIndex 分层 + 阴影效果模拟深度
|
||||
- 封面感知帧计算:封面页全屏宽,后续页面两两配对半屏宽
|
||||
|
||||
### 2.3 内容 Cell `RDReaderContentCell`
|
||||
@@ -80,7 +79,7 @@ func pageNum(readerView: RDReaderView, pageNum: Int)
|
||||
3. `reloadData` 内部调用 `switchReaderDisplayType(currentDisplayType)` 重建视图层级。
|
||||
4. 同时从 `dataSource` 获取 `topToolView` 和 `bottomToolView` 并添加到视图层级。
|
||||
|
||||
### 3.3 四种显示模式切换
|
||||
### 3.3 三种显示模式切换
|
||||
|
||||
**pageCurl 模式**:
|
||||
- 创建 `UIPageViewController`(transitionStyle: .pageCurl)
|
||||
@@ -99,13 +98,6 @@ func pageNum(readerView: RDReaderView, pageNum: Int)
|
||||
- 页面高度可变,通过 `RDReaderFlowLayoutDataSoure.heigtOfVerticalScrollPage` 查询
|
||||
- collectionViewContentSize 为所有页面高度之和
|
||||
|
||||
**horizontalCoverScroll 模式**:
|
||||
- 水平分页,但带封面滑动动画
|
||||
- `layoutAttributesForElements` 中:
|
||||
- 仅计算当前页附近的窄窗口内的 item attributes
|
||||
- 当前页之前的 item zIndex = -1,当前及之后 zIndex = 1
|
||||
- 顶层页面边缘添加阴影效果
|
||||
|
||||
### 3.4 翻页交互
|
||||
|
||||
**点击手势**(`tapAction(tap:)`):
|
||||
@@ -170,8 +162,7 @@ func pageNum(readerView: RDReaderView, pageNum: Int)
|
||||
RDReaderView.DisplayType
|
||||
├── .pageCurl // UIPageViewController 翻页效果
|
||||
├── .horizontalScroll // UICollectionView 水平滚动
|
||||
├── .verticalScroll // UICollectionView 垂直滚动
|
||||
└── .horizontalCoverScroll // UICollectionView 水平覆盖动画
|
||||
└── .verticalScroll // UICollectionView 垂直滚动
|
||||
```
|
||||
|
||||
### 5.2 翻页方向
|
||||
|
||||
@@ -1,560 +0,0 @@
|
||||
# 读书 EPUB 阅读器实现架构分析
|
||||
|
||||
> 分析日期: 2026-05-18
|
||||
> 基于读书 v10.0.3
|
||||
|
||||
---
|
||||
|
||||
## 核心结论:双渲染引擎架构
|
||||
|
||||
读书使用了 **两套渲染引擎**,根据内容类型选择不同的渲染路径:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ WRReaderViewController │
|
||||
│ (阅读器主控制器, 管理翻页和状态) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ WRPageViewController │
|
||||
│ (基于 UIPageViewController) │
|
||||
│ 支持 UIPageCurl(仿真翻页) + Scroll(滑动) │
|
||||
├──────────────────────┬──────────────────────────┤
|
||||
│ 路径A: 原生渲染 │ 路径B: WebView 渲染 │
|
||||
│ (EPUB/书籍正文) │ (公众号/文集文章) │
|
||||
│ │ │
|
||||
│ WREpubTypesetter │ WKWebView + JS Bridge │
|
||||
│ DTCoreText │ weread-highlighter.js │
|
||||
│ CoreText 排版 │ rangy-*.js │
|
||||
│ NSAttributedString │ MediaPlatform.js/css │
|
||||
│ WRCoreTextLayouter │ Readability.js │
|
||||
│ WRPageView (draw) │ WRMPPageView │
|
||||
└──────────────────────┴──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 路径A:EPUB 正文原生渲染 (核心路径)
|
||||
|
||||
这是 EPUB 阅读的**主要渲染方式**,完全用原生 CoreText 实现,不走 WebView。
|
||||
|
||||
### 1. EPUB 下载与解密流程
|
||||
|
||||
```
|
||||
服务器 ZIP 包 (加密)
|
||||
│
|
||||
▼
|
||||
WRBookNetwork.loadTarForEpubBookId:chapter:isPreload:
|
||||
│
|
||||
▼
|
||||
WRBookNetwork.handleUnzipWithBookId:zipPath:encryptKey:plainBookDirectory:
|
||||
│ - 使用 encryptKey 解密
|
||||
│ - 解压到 plainBookDirectory
|
||||
│ - 处理解压错误 handleUnzipErrorWithPath:
|
||||
▼
|
||||
WRBookNetwork.processEncryptedBookFileAtPath:encryptKey:book:chapterUid:isFromReview:
|
||||
│
|
||||
▼
|
||||
WREncryptedFileManager.decryptContentsOfFile:forBookId:isFileLost:
|
||||
│ - keyForBookId: 获取每本书的密钥
|
||||
│ - 解密 EPUB 章节文件
|
||||
▼
|
||||
WREncryptedFileManager.encryptFileForBookId:originalEncryptKey:atPath:toPath:
|
||||
│ - 本地二次加密存储 (DRM 保护)
|
||||
▼
|
||||
本地缓存: epubImage 目录 + 解密后的 XHTML 文件
|
||||
```
|
||||
|
||||
**密钥管理**:
|
||||
- `WRPreloadBookManager.saveEncryptKey:forPath:bookId:` - 预加载章节密钥
|
||||
- `WRPreloadBookManager.encryptKeyForPath:bookId:` - 读取密钥
|
||||
- `WREncryptedFileManager.keyForBookId:` - 每本书独立密钥
|
||||
|
||||
### 2. EPUB 解析与排版流程
|
||||
|
||||
```
|
||||
XHTML 章节文件
|
||||
│
|
||||
▼
|
||||
WRBookNetwork.fileContentWithChapter:book:shouldRemoveHtmlTags:filterTranslateContent:
|
||||
│ - 读取 XHTML 内容
|
||||
│ - 可选去除 HTML 标签
|
||||
│ - 过滤翻译内容
|
||||
▼
|
||||
WREpubTypesetter.attributeStringWithFilePath:priority:insertArticleToolAttachment:
|
||||
insertBookChapterToolAttachment:insertRecommendView:book:chapter:
|
||||
pageFlippingStyle:renderErrorReason:isStyleFileNotFound:options:
|
||||
│
|
||||
│ ┌─────────────────────────────────────────┐
|
||||
│ │ DTHTMLAttributedStringBuilder │
|
||||
│ │ (HTML -> NSAttributedString 转换器) │
|
||||
│ │ │
|
||||
│ │ 1. 解析 XHTML DOM 树 │
|
||||
│ │ 2. 读取 EPUB 内嵌 CSS (replace.css) │
|
||||
│ │ 3. 合并默认样式 (default.css) │
|
||||
│ │ 4. 应用用户主题样式 (dark.css) │
|
||||
│ │ 5. 转换为 NSAttributedString │
|
||||
│ │ - 保留字体、颜色、行高、对齐等属性 │
|
||||
│ │ - 处理图片 (NSTextAttachment) │
|
||||
│ │ - 处理超链接 │
|
||||
│ └─────────────────────────────────────────┘
|
||||
▼
|
||||
NSAttributedString (富文本)
|
||||
│
|
||||
▼
|
||||
WRCoreTextLayouter (CoreText 排版引擎)
|
||||
│
|
||||
│ ┌─────────────────────────────────────────┐
|
||||
│ │ DTCoreText 框架 (自定义修改版) │
|
||||
│ │ │
|
||||
│ │ DTCoreTextLayouter │
|
||||
│ │ └─ CTTypesetter │
|
||||
│ │ └─ CTFramesetter │
|
||||
│ │ └─ DTCoreTextLayoutFrame │
|
||||
│ │ └─ CTFrame (每页) │
|
||||
│ │ └─ DTCoreTextLayoutLine │
|
||||
│ │ └─ CTLine (每行) │
|
||||
│ │ └─ DTCoreTextGlyphRun │
|
||||
│ │ └─ CTLine (字形) │
|
||||
│ │ │
|
||||
│ │ 特殊处理: │
|
||||
│ │ - wr-vertical-center-style (图片居中) │
|
||||
│ │ - weread-page-relate (分页控制) │
|
||||
│ │ - avoidPageBreakInside (避免断页) │
|
||||
│ │ - 繁简转换 (convertHansToHant) │
|
||||
│ └─────────────────────────────────────────┘
|
||||
▼
|
||||
WRChapterData (章节数据模型)
|
||||
│
|
||||
│ - 包含排版后的 NSAttributedString
|
||||
│ - 管理划线/高亮/书评等标注
|
||||
│ - addHighlightInRange:key:itemId:color:
|
||||
│ - addUnderLineToAttributedString:range:itemId:style:color:
|
||||
│ - addReviewUnderlineInRange:itemId:type:
|
||||
│ - generateOutlineContents (生成目录)
|
||||
│ - freeTrialChapterCutOffStringLocaion (免费试读截断)
|
||||
▼
|
||||
分页计算: WRChapterPageCount
|
||||
│
|
||||
│ - rangeValueWithPageInfo: 计算每页的 NSRange
|
||||
│ - rangeOfPage: 获取指定页的文本范围
|
||||
▼
|
||||
WRPageView (页面视图, UIView 子类)
|
||||
│
|
||||
│ - 继承 UIView
|
||||
│ - drawRect: 中调用 CoreText 绘制
|
||||
│ - WRCoreTextLayoutFrame.drawInContext:image:size:inRect:position:
|
||||
│ - 直接用 CGContext 绘制文字和图片
|
||||
│ - 不使用 UILabel/UITextView
|
||||
▼
|
||||
屏幕显示 (像素级精确渲染)
|
||||
```
|
||||
|
||||
### 3. 翻页机制
|
||||
|
||||
```
|
||||
WRPageViewController
|
||||
│
|
||||
│ 基于 UIPageViewController 封装
|
||||
│
|
||||
│ 初始化: initWithDelegate:withPageType:pageFlippingStyle:
|
||||
│
|
||||
│ pageFlippingStyle 支持:
|
||||
│ ┌────────────────────────────────────┐
|
||||
│ │ UIPageCurl - 仿真翻页 (纸张卷曲) │
|
||||
│ │ Scroll - 左右滑动翻页 │
|
||||
│ └────────────────────────────────────┘
|
||||
│
|
||||
│ 核心方法:
|
||||
│ - pageViewController:viewControllerBeforeViewController: (上一页)
|
||||
│ - pageViewController:viewControllerAfterViewController: (下一页)
|
||||
│ - pageViewController:spineLocationForInterfaceOrientation: (书脊位置)
|
||||
│ - weread_setViewControllers:withCurlOfType:fromLocation:direction:
|
||||
│ animated:notifyDelegate:completion: (自定义设置方法)
|
||||
│
|
||||
│ 故障修复:
|
||||
│ - patchNavigationDirectionFault (导航方向修复)
|
||||
│ - patchNoViewControllerManagingPageViewFault (页面管理修复)
|
||||
│ - patchUIPageCurlFault (翻页动画修复)
|
||||
│ - detectNavigationDirectionCrashWithPageViewController: (崩溃检测)
|
||||
│
|
||||
▼
|
||||
WRPageView (每个页面的渲染视图)
|
||||
│
|
||||
│ - 通过 WRChapterData 获取排版结果
|
||||
│ - 通过 rangeOfPage: 获取当前页的文本范围
|
||||
│ - 使用 CoreText 直接绘制到 CGContext
|
||||
```
|
||||
|
||||
### 4. 文本选择与标注
|
||||
|
||||
```
|
||||
用户触摸/长按
|
||||
│
|
||||
▼
|
||||
WRPageView 手势识别
|
||||
│
|
||||
▼
|
||||
文本位置计算 (CoreText hit test)
|
||||
│ - CTLineGetStringIndexForPosition (坐标->字符索引)
|
||||
│ - CTLineGetOffsetForStringIndex (字符索引->坐标)
|
||||
▼
|
||||
选择范围确定
|
||||
│
|
||||
▼
|
||||
弹出操作菜单 (UIMenuController)
|
||||
│ - 划线/高亮
|
||||
│ - 写想法/书评
|
||||
│ - 复制
|
||||
│ - 查询/翻译
|
||||
│ - 分享
|
||||
▼
|
||||
WRChapterData 添加标注
|
||||
│ - addHighlightInRange:key:itemId:color:
|
||||
│ - addUnderLineToAttributedString:range:itemId:style:color:
|
||||
│ - addReviewUnderlineInRange:itemId:type:
|
||||
▼
|
||||
保存到服务器
|
||||
│ - WRBookNetwork.addReview:shareToWechat:...
|
||||
│ - 同步书签: loadBookmarkListWithBookId:syncKey:callback:
|
||||
▼
|
||||
重新排版当前页 (recomposeCurrentPageViewWithSource:)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 路径B:WebView 渲染 (公众号/文集文章)
|
||||
|
||||
用于渲染**微信公众号文章、文集、书评**等富媒体内容。
|
||||
|
||||
```
|
||||
HTML 内容 (来自服务器)
|
||||
│
|
||||
▼
|
||||
WRMPReadingManager.composeMPReviewHTMLString:withReview:
|
||||
│ - 组装 HTML 模板
|
||||
│ - 注入 CSS (MediaPlatform.css, MPExtra.css)
|
||||
│ - 注入 JS (MediaPlatform.js, mpForArticle.js)
|
||||
▼
|
||||
WKWebView 加载
|
||||
│
|
||||
│ 注入脚本:
|
||||
│ <script src="WeReadApi.js"></script>
|
||||
│ <script src="rich_display.js"></script>
|
||||
▼
|
||||
weread-highlighter.js 初始化
|
||||
│
|
||||
│ 1. rangy.init() - 初始化 Rangy 选择库
|
||||
│ 2. 创建 Highlighter (TextRange 模式)
|
||||
│ 3. 注册 ClassApplier:
|
||||
│ - "highlight" (高亮)
|
||||
│ - "review" / "friend-review" (书评)
|
||||
│ - "reference" (引用)
|
||||
│ - "tts" (语音朗读标记)
|
||||
│ 4. 监听 selectionchange 事件
|
||||
│ 5. 通过 wereadBridge.execMPReaderMethod 通知原生
|
||||
▼
|
||||
JS Bridge 双向通信
|
||||
│
|
||||
│ 原生 -> JS:
|
||||
│ - evaluateJavaScript: 调用 JS 方法
|
||||
│ - WKUserScript 注入脚本
|
||||
│
|
||||
│ JS -> 原生:
|
||||
│ - window.webkit.messageHandlers.XXX.postMessage()
|
||||
│ - wereadBridge.execMPReaderMethod('MPReader', data)
|
||||
▼
|
||||
WRMPReadingViewModel (ViewModel 层)
|
||||
│
|
||||
│ - addHighlightWithStart:withEnd:withContent:callback:
|
||||
│ - addReviewWithRange:content:reference:secretMode:withCallback:
|
||||
│ - genJSInfosWithHighlights:refrencedHighlight:
|
||||
│ - genJSInfosWithReviews:refrencedReview:
|
||||
│ - readReviewsWithLoadCount:maxObj:
|
||||
│ - setupTTSAudioList
|
||||
▼
|
||||
WRMPPageView (WebView 包装视图)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 关键源码路径 (从二进制中提取)
|
||||
|
||||
```
|
||||
WeRead/Src/Modules/EpubParser/
|
||||
├── WREpubTypesetter.m # EPUB 排版器
|
||||
├── WREpubPositionConverter.m # 位置转换器
|
||||
└── Utils/DTCoreTextFunctions.m # CoreText 工具函数
|
||||
|
||||
WeRead/Src/Modules/TypeSetter/
|
||||
├── WRCoreTextLayouter.m # CoreText 排版器
|
||||
├── WRCoreTextLayoutFrame.m # 排版帧 (管理页面)
|
||||
└── DTCoreTextGlyphRun.m # 字形渲染
|
||||
|
||||
WeRead/Src/Modules/Reading/
|
||||
├── Controller/
|
||||
│ ├── WRReaderViewController.m # 阅读器主控制器
|
||||
│ └── WRPageViewController.m # 翻页控制器
|
||||
├── Model/
|
||||
│ ├── WRChapterData.m # 章节数据模型
|
||||
│ ├── WRChapterDownloadManger.m # 章节下载管理
|
||||
│ ├── WRReaderViewModel.m # 阅读器 ViewModel
|
||||
│ └── WRMPReadingManager.m # 公众号阅读管理
|
||||
└── View/
|
||||
└── WRPageView.m # 页面渲染视图
|
||||
|
||||
WeRead/Src/Modules/MediaPlatform/
|
||||
├── Controller/
|
||||
│ ├── WRMPListViewController.m
|
||||
│ └── WRMPSubscribeViewController.m
|
||||
└── Model/
|
||||
├── WRMPCoverManager.m
|
||||
├── WRMPCoverPainter.m
|
||||
├── WRMPStore.m
|
||||
└── WRMPViewModel.m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## EPUB CSS 样式系统
|
||||
|
||||
```
|
||||
加载优先级 (从低到高):
|
||||
1. default.css - 基础 HTML 标签样式 (Safari 默认)
|
||||
2. replace.css - 读书默认替换样式
|
||||
├── 标题样式 (h1-h6, 使用 Source Han Serif CN 字体)
|
||||
├── 代码块 (pre, 使用 Menlo 字体)
|
||||
├── 图片 (.bodyPic, wr-vertical-center-style: 2)
|
||||
├── 引用 (.conQuot)
|
||||
├── 翻译 (.wr-translation)
|
||||
├── 章节工具 (.book-chapter-tool, .chapter-tool)
|
||||
└── 分页控制 (.weread-page-relate)
|
||||
3. dark.css - 暗黑主题样式
|
||||
4. EPUB 内嵌 CSS - 书籍自带样式
|
||||
5. 用户设置 - 字号、行高、主题覆盖
|
||||
```
|
||||
|
||||
**自定义 CSS 属性** (读书私有):
|
||||
- `wr-vertical-center-style: 1|2` - 图片垂直居中方式
|
||||
- `weread-page-relate: true` - 控制分页时的内容关联
|
||||
|
||||
---
|
||||
|
||||
## 核心类职责表
|
||||
|
||||
| 类名 | 职责 | 渲染路径 |
|
||||
|---|---|---|
|
||||
| `WRReaderViewController` | 阅读器主控制器,管理阅读状态、进度保存、章节跳转 | 共用 |
|
||||
| `WRPageViewController` | 翻页控制器,基于 UIPageViewController 封装 | 共用 |
|
||||
| `WRPageView` | 页面渲染视图,CoreText 直接绘制 | 路径A |
|
||||
| `WRMPPageView` | 公众号页面视图,WKWebView 包装 | 路径B |
|
||||
| `WREpubTypesetter` | EPUB 排版器,HTML->NSAttributedString | 路径A |
|
||||
| `WRCoreTextLayouter` | CoreText 排版引擎,管理 CTFrame/CTLine | 路径A |
|
||||
| `WRCoreTextLayoutFrame` | 排版帧,管理单页的绘制 | 路径A |
|
||||
| `WRChapterData` | 章节数据模型,存储排版结果和标注 | 路径A |
|
||||
| `WRChapterPageCount` | 分页计算,管理每页的 NSRange | 路径A |
|
||||
| `WREpubPositionConverter` | EPUB 位置转换器 (文件位置<->字符位置) | 路径A |
|
||||
| `WRChapterDownloadManger` | 章节下载管理器 | 共用 |
|
||||
| `WREncryptedFileManager` | 加密文件管理 (DRM) | 共用 |
|
||||
| `WRBookNetwork` | 书籍网络请求 (下载/解密/解压) | 共用 |
|
||||
| `WRMPReadingManager` | 公众号阅读管理器 | 路径B |
|
||||
| `WRMPReadingViewModel` | 公众号阅读 ViewModel (JS Bridge 交互) | 路径B |
|
||||
| `WRReaderViewModel` | 阅读器 ViewModel | 共用 |
|
||||
| `DTCoreTextLayouter` | DTCoreText 排版器 (第三方库修改版) | 路径A |
|
||||
| `DTHTMLAttributedStringBuilder` | HTML->NSAttributedString 构建器 | 路径A |
|
||||
| `WRReaderPencilNoteManager` | Apple Pencil 手写笔记管理 | 共用 |
|
||||
| `WRReaderTranslationManager` | 翻译管理 (繁简转换/中英翻译) | 共用 |
|
||||
| `WRReaderCht2sManager` | 繁体转简体管理 | 共用 |
|
||||
|
||||
---
|
||||
|
||||
## JavaScript 文件职责
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `weread-highlighter.js` | 核心高亮引擎,初始化 Rangy,管理选择和高亮 |
|
||||
| `rangy-core.js` | Rangy 核心库,跨浏览器 Range/Selection 封装 |
|
||||
| `rangy-highlighter.js` | Rangy 高亮模块,管理高亮的创建/删除/序列化 |
|
||||
| `rangy-classapplier.js` | Rangy ClassApplier 模块,CSS 类应用器 |
|
||||
| `rangy-textrange.js` | Rangy TextRange 模块,文本范围操作 |
|
||||
| `Readability.js` | Arc90 Readability 库,提取文章正文 |
|
||||
| `MediaPlatform.js` | 公众号平台 JS,原生-JS 桥接 |
|
||||
| `mpForArticle.js` | 文章相关 JS 逻辑 |
|
||||
| `MPExtra.css` | 公众号额外样式 |
|
||||
| `MediaPlatform.css` | 公众号基础样式 |
|
||||
| `WeReadApi.js` | 读书 JS API (供 WebView 调用原生功能) |
|
||||
| `rich_display.js` | 富文本显示逻辑 |
|
||||
| `cssInjector.js` | CSS 注入器 |
|
||||
| `highlight.min.js` | 代码语法高亮 (highlight.js) |
|
||||
|
||||
---
|
||||
|
||||
## DRM 与安全机制
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ DRM 保护链 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. 传输层: HTTPS + 加密 ZIP │
|
||||
│ - 服务器下发加密的 .zip 文件 │
|
||||
│ - 文件名格式: {bookId}_DECRYPT.zip │
|
||||
│ │
|
||||
│ 2. 解密层: 逐章解密 │
|
||||
│ - WREncryptedFileManager │
|
||||
│ - keyForBookId: (每本书独立密钥) │
|
||||
│ - decryptContentsOfFile:forBookId: │
|
||||
│ │
|
||||
│ 3. 存储层: 本地二次加密 │
|
||||
│ - encryptFileForBookId:atPath:toPath: │
|
||||
│ - 解密后立即重新加密存储 │
|
||||
│ - 防止直接拷贝文件读取 │
|
||||
│ │
|
||||
│ 4. 密钥管理: │
|
||||
│ - WRPreloadBookManager 管理预加载密钥 │
|
||||
│ - 密钥与设备绑定 │
|
||||
│ - 通过 Keychain 安全存储 │
|
||||
│ │
|
||||
│ 5. 免费试读控制: │
|
||||
│ - freeTrialChapterCutOffStringLocaion │
|
||||
│ - 服务端控制试读范围 │
|
||||
│ - 客户端截断显示 │
|
||||
│ │
|
||||
│ 6. 章节付费: │
|
||||
│ - isChapterAvailableForBookId:chapterUid │
|
||||
│ - getCouponBuyChapterWithBookId: │
|
||||
│ - resetChapterPaidIfNeeded │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 字体系统
|
||||
|
||||
```
|
||||
内嵌字体:
|
||||
├── SourceHanSerifCN-Medium.ttf # 思源宋体 (正文默认)
|
||||
├── FZJuZhenXinFang.ttf # 方正聚珍新仿
|
||||
├── Lora-Regular.ttf / Italic.ttf # Lora 衬线体
|
||||
├── PlayfairDisplay-Regular.ttf # Playfair Display
|
||||
├── WeReadLS-Regular/Medium/Bold # 读书 LS 系列
|
||||
├── WeReadRN-Regular.ttf # RN 专用字体
|
||||
├── WeRead-Icon.ttf # 图标字体
|
||||
├── WeRead-Rating-Icon.ttf # 评分图标
|
||||
├── OpenDyslexic-Regular.otf # 阅读障碍友好字体
|
||||
├── WeChatNumber.ttf # 微信数字字体
|
||||
├── SharpGroteskTRIAL*.ttf # Sharp Grotesk 系列
|
||||
└── icon_font.ttf # 通用图标字体
|
||||
|
||||
动态字体:
|
||||
├── CDN 下载: cdn.weread.qq.com/app/assets/font/
|
||||
│ ├── FZLTHProGBK_B/DB/SB.zip # 方正兰亭黑系列
|
||||
│ ├── FZQingKBYSJF-M.zip # 方正清刻本悦宋
|
||||
│ ├── FZSKBXKK.zip # 方正书宋
|
||||
│ ├── FZYBKSK.zip # 方正中楷
|
||||
│ └── SourceHanSansCN-Heavy.zip # 思源黑体
|
||||
│
|
||||
└── WRFontsManager 管理:
|
||||
- unZipFileAndRegisterFont:completionHandler:filePath:lateOverWrite:
|
||||
- 动态下载 + 解压 + 注册
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 预加载与缓存策略
|
||||
|
||||
```
|
||||
预加载策略:
|
||||
├── WRForecastUtils.shouldPreloadChapterUidsForBook:type:bookRank:archiveRank:
|
||||
│ - 根据书籍排名和用户行为预测需要预加载的章节
|
||||
│
|
||||
├── WRChapterDownloadManger._preloadChapterContentWithBook:type:bookRank:
|
||||
│ - 后台预下载相邻章节
|
||||
│
|
||||
├── WRPreloadBookManager
|
||||
│ - saveEncryptKey:forPath:bookId: (缓存密钥)
|
||||
│ - saveFileNameDict:bookId: (缓存文件名映射)
|
||||
│ - clearKV (清理缓存)
|
||||
│
|
||||
└── SDWebImage 缓存:
|
||||
- epubImage 目录缓存书籍图片
|
||||
- com.hackemist.SDWebImageCache.epubImage
|
||||
|
||||
缓存目录结构:
|
||||
├── Documents/
|
||||
│ └── {bookId}/
|
||||
│ ├── plainBookDirectory/ (解密后的 EPUB 文件)
|
||||
│ ├── epubImage/ (书籍图片缓存)
|
||||
│ └── _DECRYPT.zip (下载的加密 ZIP)
|
||||
└── Library/
|
||||
└── {cachePath}/
|
||||
├── epubImage/ (SDWebImage 缓存)
|
||||
└── com.hackemist.SDWebImageCache.epubImage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 朗读 (TTS) 集成
|
||||
|
||||
```
|
||||
朗读流程:
|
||||
├── WRReadAloudAudio.parseCGIInfos: (解析音频信息)
|
||||
├── TTS 引擎: wxtts (微信语音合成)
|
||||
│ ├── 离线资源: wxtts_offline_resource5.zip
|
||||
│ ├── 在线资源: getWxttsSeginfo / getWxttsVoice
|
||||
│ └── VITS/VALL-E 模型 (高保真语音)
|
||||
│
|
||||
├── 文本分段:
|
||||
│ - weread-highlighter.js 中的 "tts" ClassApplier
|
||||
│ - 标记当前朗读位置
|
||||
│
|
||||
└── 进度同步:
|
||||
- lastListenedChapterOffset / lastListenedChapterUid
|
||||
- MPReading/lastListenedReviewId (公众号朗读进度)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Apple Pencil 手写笔记
|
||||
|
||||
```
|
||||
WRReaderPencilNoteManager:
|
||||
├── 数据存储:
|
||||
│ - writeDrawingDataToLocal:reviewItemId:reviewId:isDraft:
|
||||
│ - drawingFilePathWithReviewItemId:reviewId:isDraft:
|
||||
│ - 本地草稿 + 云端同步
|
||||
│
|
||||
├── 数据上传:
|
||||
│ - uploadPencilDrawing:colorStyle:onlyUploadImage:canRetry:
|
||||
│ - uploadPencilNoteData:suffix:
|
||||
│ - 使用腾讯云 COS 存储
|
||||
│ - authCosForPencilDataWithSuffix: (COS 认证)
|
||||
│
|
||||
├── 数据下载:
|
||||
│ - downloadDrawingDataFromCosWithUrl:desPath:callback:
|
||||
│ - downloadDrawingWithReviewItemId:reviewId:drawingUrl:dataBlock:
|
||||
│
|
||||
└── 图片导出:
|
||||
- imageFilePathWithReviewItemId:
|
||||
- 手绘笔记可导出为图片
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 特性 | 实现方式 |
|
||||
|---|---|
|
||||
| **EPUB 解析** | 自研 EPUB Parser,解析 OPF/NCX/XHTML |
|
||||
| **文字排版** | DTCoreText (CoreText 封装) + 自定义 WRCoreTextLayouter |
|
||||
| **页面渲染** | CGContext 直接绘制,不用 UILabel/UITextView |
|
||||
| **翻页效果** | UIPageViewController (UIPageCurl + Scroll) |
|
||||
| **文本选择** | CoreText hit test + Rangy.js (WebView 场景) |
|
||||
| **标注系统** | NSAttributedString 属性注入 + 服务器同步 |
|
||||
| **图片处理** | NSTextAttachment + CDN 尺寸优化 + 白底透明化 |
|
||||
| **DRM** | 逐章加密 + 逐书密钥 + 本地二次加密 |
|
||||
| **公众号文章** | WKWebView + JS Bridge + Rangy 高亮 |
|
||||
| **繁简转换** | CoreText 层面的 convertHansToHant |
|
||||
| **字体** | 内嵌 Source Han Serif CN + 动态下载字体 |
|
||||
| **预加载** | 后台预下载相邻章节 ZIP + 解密缓存 |
|
||||
| **TTS 朗读** | wxtts 引擎 + VITS/VALL-E 高保真模型 |
|
||||
| **手写笔记** | PencilKit + 腾讯云 COS 存储 |
|
||||
+4
-2
@@ -14,13 +14,15 @@
|
||||
|------|----------|------|
|
||||
| [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 支持 |
|
||||
| [RDReaderView_功能实现逻辑.md](RDReaderView_功能实现逻辑.md) | `Sources/RDReaderView/ReaderView/`(5 文件) | 三种显示模式(pageCurl/horizontalScroll/verticalScroll)、DataSource/Delegate 协议、点击三区域翻页、工具栏动画、双页配对与哨兵页、横竖屏适配、RTL 支持 |
|
||||
| [EPUBUI_功能实现逻辑.md](EPUBUI_功能实现逻辑.md) | `Sources/RDReaderView/EPUBUI/`(19 文件) | RDEPUBReaderController 全生命周期、三条渲染路径分发、配置变更检测与响应、工具栏/目录/高亮/书签/搜索/设置面板交互流、阅读位置持久化、主题管理、CoreText 页面交互 |
|
||||
|
||||
## 方案讨论文档
|
||||
## 方案讨论与规划文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [阅读器功能开发计划.md](阅读器功能开发计划.md) | 渲染质量三方对比(ReadViewSDK vs WXRead)、功能开发计划与落地状态、优先级路线图 |
|
||||
| [架构对比分析_WXRead_vs_ReadViewSDK.md](架构对比分析_WXRead_vs_ReadViewSDK.md) | ReadViewSDK 与 WXRead 的逐项架构核查,确认主链路复刻完成度 |
|
||||
| [ReflowableEPUB_WXReadRenderer_Design.md](FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md) | 基于读书的 CoreText 渲染架构,设计 Reflowable EPUB 的增强文本渲染方案(CSS 分层、类型器升级) |
|
||||
|
||||
## 测试与质量
|
||||
|
||||
+81
-2
@@ -1,7 +1,33 @@
|
||||
# 阅读器功能开发计划
|
||||
|
||||
基于 [阅读器规划.md](阅读器规划.md) 中的三方对比,本文档给出阅读器功能的开发计划与当前落地状态。
|
||||
功能完整度部分(书架、批注导出、阅读统计、TTS、全局搜索、云端同步、夜间定时)暂不展开。
|
||||
> 本文档整合了渲染质量三方对比(ReadViewSDK vs WXRead)与功能开发计划,作为阅读器能力演进的单一真值。
|
||||
> 基于读书 v10.0.3 逆向分析,与当前 ReadViewSDK 代码核查结果整合。
|
||||
|
||||
## 背景
|
||||
|
||||
渲染内核的架构对齐解决的是"代码可维护性"问题。本文档梳理从"能用的阅读器"到"能上架的商业阅读器"还需要补齐哪些能力,每项能力标注三方状态和具体实施计划。
|
||||
|
||||
### 三方对比总览(渲染质量)
|
||||
|
||||
| 缺失项 | 严重度 | ReadViewSDK | WXRead | 差距说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **竖排文字** | 高 | ❌ 未实现 | ❌ 未实现 | 双方均无 `writing-mode` 支持 |
|
||||
| **Ruby 注音** | 高 | ❌ 未实现 | ❌ 未实现 | 双方均无 `<ruby>`/`<rt>` 处理 |
|
||||
| **数学公式** | 中 | ❌ 未实现 | ⚠️ 仅字体回退 | 非原生渲染范畴,需 WebView 回退 |
|
||||
| **复杂图文分页质量** | 高 | ✅ 已实现 | ✅ 已实现 | 双方均已实现 |
|
||||
| **字体选择器** | 中 | ✅ 已实现 | ⚠️ 管线完备,UI 未见 | 已支持四档字体选择 |
|
||||
| **连字/断字** | 中 | ⚠️ 仅配置标记 | ⚠️ 仅属性声明 | 双方均未实际实现 |
|
||||
| **多语言排版回退** | 中 | ⚠️ 语言检测有,简繁转换无 | ✅ 已实现 | WXRead 额外有简繁转换 |
|
||||
|
||||
### ReadViewSDK 领先 WXRead 的项
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| **`keepWithNext`** | 已实现 `trimmedRangeForKeepWithNext`,WXRead 反编译代码中未找到对应实现 |
|
||||
|
||||
### 不需要对标 WXRead 的项
|
||||
|
||||
以下项 WXRead 自身也未实现,不属于必须补齐的能力:竖排文字、Ruby 注音、数学公式、Hyphenation 断字。
|
||||
|
||||
---
|
||||
|
||||
@@ -536,3 +562,56 @@ ReadViewDemoUITests 全量 8 个 UI 测试: TEST SUCCEEDED
|
||||
- SDK 提供清晰的 DRM 接入协议
|
||||
- 不引入 DRM 依赖,保持 SDK 轻量
|
||||
- 调用方能通过协议接入自己的 DRM 方案
|
||||
|
||||
---
|
||||
|
||||
## 四、功能完整度(暂未展开)
|
||||
|
||||
以下功能需求已识别但暂未进入详细开发计划:
|
||||
|
||||
| 缺失项 | 严重度 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| **书架/书库管理** | 高 | SDK 只能打开单本书,无书架 UI、阅读历史、分类管理 |
|
||||
| **批注导出/分享** | 高 | 高亮/笔记只有本地存储,无导出、分享、复制到剪贴板 |
|
||||
| **阅读统计** | 中 | 无阅读时长追踪、阅读速度、连续阅读天数 |
|
||||
| **TTS 朗读** | 中 | 微信读书核心功能之一,当前无任何语音相关代码 |
|
||||
| **全局搜索** | 中 | 当前搜索只在单本书内,无跨书搜索 |
|
||||
| **离线/云端同步** | 高 | 无 iCloud/自建同步,阅读进度和笔记只在本地 |
|
||||
| **夜间模式定时切换** | 低 | 有暗色主题但不能跟随系统或定时切换 |
|
||||
|
||||
---
|
||||
|
||||
## 五、按优先级排序的建议路线
|
||||
|
||||
### P0 — 影响商业发布
|
||||
|
||||
1. **分页回归基准** — UI 自动化测试已启动,下一步需要把分页结果、首屏时间、截图差异纳入回归基准
|
||||
2. **字体选择器增强** — 基础字体选择器已实现,后续需补:更多内置字体包、字体预览、字体资源加载失败兜底
|
||||
3. **书架/书库管理** — 商业阅读器的入口
|
||||
4. **暗色模式图片处理增强** — 基础处理已实现,后续可补:按图片亮度自适应混合比例
|
||||
|
||||
### P1 — 影响用户留存
|
||||
|
||||
5. **批注导出/分享** — 深度阅读用户的核心需求
|
||||
6. **阅读统计/时长追踪** — 用户粘性和产品数据的基础
|
||||
7. **性能基线与大书优化** — 大书卡顿是用户流失的主要原因
|
||||
8. **简繁转换** — 面向港澳台用户需要
|
||||
|
||||
### P2 — 提升竞争力
|
||||
|
||||
9. **TTS 朗读** — 通勤场景、无障碍场景刚需
|
||||
10. **云端同步** — 多设备用户的基本需求
|
||||
11. **VoiceOver 完善** — 合规和品牌形象
|
||||
12. **全局搜索** — 藏书量大时的效率工具
|
||||
|
||||
---
|
||||
|
||||
## 六、总结
|
||||
|
||||
经过三方对比,渲染质量层面的真实差距比最初评估要小:
|
||||
|
||||
- **图文分页质量**:双方基本对齐,我们甚至在 `keepWithNext` 上领先
|
||||
- **真正的差距**:字体包数量、分页回归基准、简繁转换
|
||||
- **WXRead 也没做的**:竖排、ruby、公式、hyphenation——这些不是必须对齐的
|
||||
|
||||
如果目标是"能上架的商业阅读器",当前已补齐字体选择器、暗色模式图片处理和基础 UI 自动化测试。下一步最值得投入的是分页回归基准、更多字体资源和书架/书库管理。
|
||||
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
# 阅读器规划:从架构重构到商业版的差距
|
||||
|
||||
## 背景
|
||||
|
||||
渲染内核的架构对齐(WXRead 拆分类重构)解决的是"代码可维护性"问题。
|
||||
本文档梳理从"能用的阅读器"到"能上架的商业阅读器"还需要补齐哪些能力。
|
||||
|
||||
每项能力标注了三方状态:当前 ReadViewSDK 实现情况、WXRead(微信读书)反编译源码中的实现情况。
|
||||
|
||||
## 一、渲染质量(最直接影响用户体验)
|
||||
|
||||
### 三方对比总览
|
||||
|
||||
| 缺失项 | 严重度 | ReadViewSDK | WXRead | 差距说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **竖排文字** | 高 | ❌ 未实现 | ❌ 未实现 | 双方均无 `writing-mode` 支持。WXRead 同样不支持,不是对标项 |
|
||||
| **Ruby 注音** | 高 | ❌ 未实现 | ❌ 未实现 | 双方均无 `<ruby>`/`<rt>` 处理。DTCoreText 限制,需 WebView 回退 |
|
||||
| **数学公式** | 中 | ❌ 未实现 | ⚠️ 仅字体回退 | 我们无任何支持。WXRead 打包 mathFonts.bundle 做数学符号字体回退,但无 MathML 解析 |
|
||||
| **复杂图文分页质量** | 高 | ✅ 已实现 | ✅ 已实现 | 双方均已实现,见下方详情对比 |
|
||||
| **字体选择器** | 中 | ✅ 已实现 | ⚠️ 管线完备,UI 未见 | 我们已支持系统/宋体/圆体/等宽四档字体选择、持久化、重新分页和缓存隔离。WXRead 有 15+ 内置字体,后续差距主要是字体包数量 |
|
||||
| **连字/断字** | 中 | ⚠️ 仅配置标记 | ⚠️ 仅属性声明 | 双方均未实际实现断字逻辑 |
|
||||
| **多语言排版回退** | 中 | ⚠️ 语言检测有,简繁转换无 | ✅ 已实现 | 我们有拉丁/CJK 分轨 CSS。WXRead 额外有简繁转换和更完善的字体回退 |
|
||||
|
||||
### 逐项详情
|
||||
|
||||
#### 1. 复杂图文分页质量
|
||||
|
||||
双方均已实现,核心能力对齐情况:
|
||||
|
||||
| 子项 | ReadViewSDK | WXRead |
|
||||
| --- | --- | --- |
|
||||
| `avoidPageBreakInside` | ✅ 反向扫描行,`kMaxLinesToRemove=3`,保护 table/code/list/blockquote | ✅ 同算法,`kMaxLinesToRemove=3`,保护同类元素 |
|
||||
| `pageBreakBefore` / `pageBreakAfter` | ✅ 语义标记注入 + 分页引擎消费 | ✅ U+2028 LINE SEPARATOR + DTPageBreakBefore/AfterAttribute |
|
||||
| `pageRelate` 跨页关联 | ✅ `weread-page-relate` 语义注入 | ✅ `weread-page-relate:true` CSS 属性 |
|
||||
| `keepWithNext` | ✅ `trimmedRangeForKeepWithNext` 实现 | ❌ 未实现(我们反而领先) |
|
||||
| 图片垂直居中 | ✅ `wr-vertical-center-style:2` + bodyPic 包装 | ✅ 同机制 |
|
||||
| 图片缩放 | ✅ 动态 `imageMaxHeightRatio=0.85` | ✅ 四档 full/half/third/quarter + 1080x1920 上限 |
|
||||
| 暗色模式图片处理 | ✅ 已实现 | ✅ 图片与主题背景色 85% 混合 |
|
||||
| 孤行/寡行控制 | ⚠️ 配置标记有,分页逻辑未引用 | ⚠️ `avoidOrphans`/`avoidWidows` 声明,实际机制未完全还原 |
|
||||
|
||||
**结论**:图文分页质量双方基本对齐,我们甚至在 `keepWithNext` 上领先。暗色模式图片处理已补齐,后续主要是继续扩充分页回归基准和更多真实书籍样本。
|
||||
|
||||
#### 2. 字体选择器
|
||||
|
||||
| 子项 | ReadViewSDK | WXRead |
|
||||
| --- | --- | --- |
|
||||
| 字号调节 | ✅ A-/A+ 按钮 | ✅ A-/A+ 按钮,12-36pt |
|
||||
| `fontFamily` 持久化 | ✅ `RDEPUBReaderFontChoice` 持久化 | ✅ NSUserDefaults `WRTypesetterFontFamily` |
|
||||
| CSS / 排版动态生成 | ✅ 渲染样式、分页缓存签名和重新分页均接入当前字体 | ✅ `_WRBuildUserSettingsCSS` 动态生成 `font-family` CSS |
|
||||
| 内置字体 | ⚠️ 系统/宋体/圆体/等宽四档 | ✅ 15+(思源宋体、方正兰亭黑、OpenDyslexic 等) |
|
||||
| 字体选择面板 UI | ✅ 设置面板分段控件 | ⚠️ 反编译代码中未见,可能在未包含的模块 |
|
||||
|
||||
**当前实现状态**:字体选择器已可用,切换字体会触发重新分页,并纳入分页缓存 key,避免不同字体复用旧分页。后续如果继续对标 WXRead,重点是引入更多内置字体包和字体预览样式。
|
||||
|
||||
#### 3. 多语言排版
|
||||
|
||||
| 子项 | ReadViewSDK | WXRead |
|
||||
| --- | --- | --- |
|
||||
| 拉丁/CJK 语言检测 | ✅ `prefersLatinLanguageCSS` | ✅ `isLatinLanguageBook` |
|
||||
| 分轨 CSS | ✅ `wxread-replace.css` / `wxread-replace-latin.css` | ✅ `replace.css` / `replaceForLatinLanguageBook.css` |
|
||||
| 简繁转换 | ❌ 无 | ✅ `CFStringTransform` Hans→Latin→Hant |
|
||||
| `lang` 属性处理 | ✅ 提取语言代码 | ✅ `DTHTMLElement.lang` |
|
||||
| `direction` (ltr/rtl) | ✅ `RDEPUBReadingProgression` | ✅ `DTHTMLElement.direction` |
|
||||
|
||||
#### 4. Hyphenation 断字
|
||||
|
||||
| 子项 | ReadViewSDK | WXRead |
|
||||
| --- | --- | --- |
|
||||
| 配置标记 | ✅ `hyphenation: Bool = true` | ✅ `BOOL hyphenation = YES` |
|
||||
| 实际断字逻辑 | ❌ 未应用 `NSParagraphStyle.hyphenationFactor` | ❌ 未使用,行分割仍用 `CTTypesetterSuggestLineBreak` |
|
||||
|
||||
**结论**:双方状态一致,都是声明了但未实现。
|
||||
|
||||
#### 5. 未实现且 WXRead 也未实现的项
|
||||
|
||||
| 项 | ReadViewSDK | WXRead | 建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| **竖排文字** | ❌ | ❌ | 非核心需求,双方均未做 |
|
||||
| **Ruby 注音** | ❌ | ❌ | DTCoreText 限制,需 WebView 回退方案 |
|
||||
| **数学公式** | ❌ | ⚠️ 仅字体 | 非原生渲染范畴,需 WebView 回退 |
|
||||
|
||||
## 二、功能完整度
|
||||
|
||||
| 缺失项 | 严重度 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| **书架/书库管理** | 高 | SDK 只能打开单本书,无书架 UI、阅读历史、分类管理 |
|
||||
| **批注导出/分享** | 高 | 高亮/笔记只有本地存储,无导出、分享、复制到剪贴板 |
|
||||
| **阅读统计** | 中 | 无阅读时长追踪、阅读速度、连续阅读天数 |
|
||||
| **TTS 朗读** | 中 | 微信读书核心功能之一,当前无任何语音相关代码 |
|
||||
| **全局搜索** | 中 | 当前搜索只在单本书内,无跨书搜索 |
|
||||
| **离线/云端同步** | 高 | 无 iCloud/自建同步,阅读进度和笔记只在本地 |
|
||||
| **夜间模式定时切换** | 低 | 有暗色主题但不能跟随系统或定时切换 |
|
||||
|
||||
## 三、工程成熟度
|
||||
|
||||
| 缺失项 | 严重度 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| **自动化测试** | 高 | ✅ UI 自动化测试已启动并接入 Demo 流程,覆盖打开书籍、阅读器基础交互、工具栏、设置面板、字体选择、暗色主题切换等。仍缺单元测试和分页回归基准 |
|
||||
| **性能基线** | 高 | 有 `RDEPUBTextPerformanceSampler` 但无持续监控。大书(100MB+)首屏时间、内存峰值无基准 |
|
||||
| **崩溃防护** | 中 | 有 pageCurl 崩溃检测和异步恢复,但无全局异常捕获和上报 |
|
||||
| **内存管理** | 中 | 无显式内存预算,大书场景下无章节级内存释放策略 |
|
||||
| **增量构建** | 中 | 全书一次性分页,无章节级增量重建能力 |
|
||||
| **缓存管理** | 中 | UserDefaults 存储有限,无磁盘缓存大小控制和淘汰策略 |
|
||||
|
||||
## 四、可访问性与合规
|
||||
|
||||
| 缺失项 | 严重度 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| **VoiceOver** | 高 | 有 accessibility identifier 但无阅读内容的 VoiceOver 流程 |
|
||||
| **Dynamic Type** | 中 | 不响应系统字体大小设置 |
|
||||
| **高对比度** | 低 | 主题固定,不跟随系统 trait |
|
||||
| **DRM** | 看业务 | 需求文档已声明不在此范围,但商业分发通常需要 |
|
||||
|
||||
## 五、按优先级排序的建议路线
|
||||
|
||||
### P0 — 影响商业发布
|
||||
|
||||
1. **分页回归基准**(Phase 9 后续)— UI 自动化测试已启动,下一步需要把分页结果、首屏时间、截图差异纳入回归基准
|
||||
2. **字体选择器增强** — 基础字体选择器已实现。后续需补:更多内置字体包、字体预览、字体资源加载失败兜底
|
||||
3. **书架/书库管理** — 商业阅读器的入口,没有书架就没有产品形态
|
||||
4. **暗色模式图片处理增强** — 基础处理已实现。后续可补:按图片亮度自适应混合比例、真实书籍样本回归
|
||||
|
||||
### P1 — 影响用户留存
|
||||
|
||||
5. **批注导出/分享** — 深度阅读用户的核心需求
|
||||
6. **阅读统计/时长追踪** — 用户粘性和产品数据的基础
|
||||
7. **性能基线与大书优化** — 大书卡顿是用户流失的主要原因
|
||||
8. **简繁转换** — WXRead 有 `CFStringTransform` 简繁转换,面向港澳台用户需要
|
||||
|
||||
### P2 — 提升竞争力
|
||||
|
||||
9. **TTS 朗读** — 通勤场景、无障碍场景刚需
|
||||
10. **云端同步** — 多设备用户的基本需求
|
||||
11. **VoiceOver 完善** — 合规和品牌形象
|
||||
12. **全局搜索** — 藏书量大时的效率工具
|
||||
|
||||
### 不需要对标 WXRead 的项
|
||||
|
||||
以下项 WXRead 自身也未实现,不属于必须补齐的能力:
|
||||
|
||||
| 项 | ReadViewSDK | WXRead | 建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| **竖排文字** | ❌ | ❌ | 非核心需求,可延后 |
|
||||
| **Ruby 注音** | ❌ | ❌ | DTCoreText 限制,需 WebView 回退方案 |
|
||||
| **数学公式** | ❌ | ⚠️ 仅字体 | 非原生渲染范畴,需 WebView 回退 |
|
||||
| **Hyphenation 断字** | ⚠️ 仅标记 | ⚠️ 仅标记 | 双方均未实现,中文场景影响小 |
|
||||
|
||||
### ReadViewSDK 领先 WXRead 的项
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| **`keepWithNext`** | 我们实现了 `trimmedRangeForKeepWithNext`,WXRead 反编译代码中未找到对应实现 |
|
||||
|
||||
## 六、总结
|
||||
|
||||
经过三方对比,渲染质量层面的真实差距比最初评估要小:
|
||||
|
||||
- **图文分页质量**:双方基本对齐,我们甚至在 `keepWithNext` 上领先
|
||||
- **真正的差距**:字体包数量、分页回归基准、简繁转换
|
||||
- **WXRead 也没做的**:竖排、ruby、公式、hyphenation——这些不是必须对齐的
|
||||
|
||||
如果目标是"能上架的商业阅读器",当前已补齐字体选择器、暗色模式图片处理和基础 UI 自动化测试。下一步最值得投入的是分页回归基准、更多字体资源和书架/书库管理。
|
||||
Reference in New Issue
Block a user